arm64/instgen: add assembler code generator for SVE
WIP
diff --git a/arm64/instgen/main.go b/arm64/instgen/main.go
index ff1f33c..103fdcf 100644
--- a/arm64/instgen/main.go
+++ b/arm64/instgen/main.go
@@ -49,6 +49,7 @@
// Parse each xml file to insts.
insts := xmlspec.ParseXMLFiles(*input)
xmlspec.ProcessXMLFiles(insts)
+ xmlspec.Generate(insts, *output)
errCnt := 0
for _, inst := range insts {
if inst.ParseError != "" {
diff --git a/arm64/instgen/xmlspec/generator.go b/arm64/instgen/xmlspec/generator.go
new file mode 100644
index 0000000..01a8301
--- /dev/null
+++ b/arm64/instgen/xmlspec/generator.go
@@ -0,0 +1,288 @@
+package xmlspec
+
+import (
+ "bytes"
+ "fmt"
+ "go/format"
+ "log"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+ "text/template"
+)
+
+var identifierSanitizer = regexp.MustCompile(`[^a-zA-Z0-9_]`)
+
+func sanitize(s string) string {
+ s = identifierSanitizer.ReplaceAllString(s, "_")
+ // Collapse multiple underscores
+ for strings.Contains(s, "__") {
+ s = strings.ReplaceAll(s, "__", "_")
+ }
+ // Trim leading/trailing underscores
+ s = strings.Trim(s, "_")
+ return s
+}
+
+type tmplData struct {
+ A64Types []constData
+ A64Feats []constData
+ A64OpConstraints []constData
+ EncodingFuncs []funcData
+ Insts []instData
+}
+
+type constData struct {
+ Name string
+ Value int
+}
+
+type funcData struct {
+ Index int
+ Comments []string
+}
+
+type instData struct {
+ GoOp string
+ ArmOp string
+ Feature string
+ FixedBits string
+ Alias bool
+ Args []argData
+}
+
+type argData struct {
+ Class string
+ Constraints []string
+ Elms []elmData
+}
+
+type elmData struct {
+ Index int
+ FuncIndex int
+}
+
+const genTmpl = `// Code generated by argm64/instgen. DO NOT EDIT.
+
+package arm64
+
+import "cmd/internal/obj"
+
+const (
+ A64TypeNone A64Type = 0
+{{- range .A64Types}}
+ {{.Name}} A64Type = {{.Value}}
+{{- end}}
+)
+
+const (
+ A64FeatNone A64Feat = 0
+{{- range .A64Feats}}
+ FEAT_{{.Name}} A64Feat = {{.Value}}
+{{- end}}
+)
+
+const (
+ A64OpConstraintNone A64OpConstraint = 0
+{{- range .A64OpConstraints}}
+ {{.Name}} A64OpConstraint = {{.Value}}
+{{- end}}
+)
+
+// Encoding functions
+{{- range .EncodingFuncs}}
+{{- range .Comments}}
+// {{.}}
+{{- end}}
+func encodeGen{{.Index}}(v uint32) (uint32, bool) {
+ return 0, false // TODO
+}
+{{end}}
+
+var Insts = []Inst{
+{{- range .Insts}}
+ {
+ GoOp: obj.{{.GoOp}},
+ ArmOp: {{.ArmOp}},
+ Feature: {{.Feature}},
+ FixedBits: {{.FixedBits}},
+ Alias: {{.Alias}},
+ Args: []Operand{
+{{- range .Args}}
+ {
+ Class: {{.Class}},
+{{- if .Constraints}}
+ constraints: []A64OpConstraint{
+{{- range .Constraints}}
+ {{.}},
+{{- end}}
+ },
+{{- end}}
+{{- if .Elms}}
+ Elms: []ElmType{
+{{- range .Elms}}
+ {
+ index: {{.Index}},
+ encode: encodeGen{{.FuncIndex}},
+ },
+{{- end}}
+ },
+{{- end}}
+ },
+{{- end}}
+ },
+ },
+{{- end}}
+}
+`
+
+// Generate generates the Go code for the instruction table.
+func Generate(insts []*Instruction, outputDir string) {
+ // Collect constants
+ a64Types := make(map[string]int)
+ a64Feats := make(map[string]int)
+ a64OpConstraints := make(map[string]int)
+ encodingFuncs := make(map[string]int) // Map textExp to index
+ encodingFuncComments := make(map[string][]string)
+
+ // Pre-populate A64OpConstraints
+ sortedConstraints := make([]string, 0, len(allOpConstraints))
+ for k := range allOpConstraints {
+ sortedConstraints = append(sortedConstraints, k)
+ }
+ sort.Strings(sortedConstraints)
+ for i, k := range sortedConstraints {
+ a64OpConstraints[k] = i + 1
+ }
+
+ // Pre-populate EncodingFuncs
+ sortedEncodings := make([]string, 0, len(allEncodingDescs))
+ for k := range allEncodingDescs {
+ sortedEncodings = append(sortedEncodings, k)
+ }
+ sort.Strings(sortedEncodings)
+ for i, k := range sortedEncodings {
+ encodingFuncs[k] = i + 1
+ encodingFuncComments[k] = strings.Split(k, "\n")
+ }
+
+ // Collect A64Types and A64Feats, and Insts
+ var instsData []instData
+
+ for _, inst := range insts {
+ for _, iclass := range inst.Classes.Iclass {
+ if !iclass.Regdiagram.parsed {
+ continue
+ }
+ feat := "A64FeatNone"
+ if iclass.ArchVariant.Feature != "" {
+ sFeat := sanitize(iclass.ArchVariant.Feature)
+ if _, ok := a64Feats[sFeat]; !ok {
+ a64Feats[sFeat] = len(a64Feats) + 1
+ }
+ feat = "FEAT_" + sFeat
+ }
+
+ for _, encoding := range iclass.Encodings {
+ if !encoding.parsed {
+ continue
+ }
+ if _, ok := a64Types[encoding.arm64Op]; !ok {
+ a64Types[encoding.arm64Op] = len(a64Types) + 1
+ }
+
+ instData := instData{
+ GoOp: encoding.goOp,
+ ArmOp: sanitize(encoding.arm64Op),
+ Feature: feat,
+ FixedBits: fmt.Sprintf("%#x", encoding.binary),
+ Alias: encoding.alias,
+ }
+
+ for _, op := range encoding.operands[1:] {
+ arg := argData{
+ Class: op.typ,
+ }
+ if len(op.constraints) > 0 {
+ arg.Constraints = op.constraints
+ }
+ if len(op.elems) > 0 {
+ for j, elm := range op.elems {
+ idx, ok := encodingFuncs[elm.textExpWithRanges]
+ if !ok {
+ log.Printf("Warning: encoding func not found for %s in %s", elm.textExpWithRanges, inst.file)
+ continue
+ }
+ arg.Elms = append(arg.Elms, elmData{
+ Index: j,
+ FuncIndex: idx,
+ })
+ }
+ }
+ instData.Args = append(instData.Args, arg)
+ }
+ instsData = append(instsData, instData)
+ }
+ }
+ }
+
+ // Prepare template data
+ data := tmplData{
+ Insts: instsData,
+ }
+
+ sortedA64Types := make([]string, 0, len(a64Types))
+ for k := range a64Types {
+ sortedA64Types = append(sortedA64Types, k)
+ }
+ sort.Strings(sortedA64Types)
+ for _, k := range sortedA64Types {
+ data.A64Types = append(data.A64Types, constData{Name: sanitize(k), Value: a64Types[k]})
+ }
+
+ sortedA64Feats := make([]string, 0, len(a64Feats))
+ for k := range a64Feats {
+ sortedA64Feats = append(sortedA64Feats, k)
+ }
+ sort.Strings(sortedA64Feats)
+ for _, k := range sortedA64Feats {
+ data.A64Feats = append(data.A64Feats, constData{Name: sanitize(k), Value: a64Feats[k]})
+ }
+
+ for _, k := range sortedConstraints {
+ data.A64OpConstraints = append(data.A64OpConstraints, constData{Name: k, Value: a64OpConstraints[k]})
+ }
+
+ for i, k := range sortedEncodings {
+ data.EncodingFuncs = append(data.EncodingFuncs, funcData{
+ Index: i + 1,
+ Comments: encodingFuncComments[k],
+ })
+ }
+
+ // Execute template
+ t, err := template.New("gen").Parse(genTmpl)
+ if err != nil {
+ log.Fatalf("Failed to parse template: %v", err)
+ }
+
+ var buf bytes.Buffer
+ if err := t.Execute(&buf, data); err != nil {
+ log.Fatalf("Failed to execute template: %v", err)
+ }
+
+ // Write to file
+ formatted, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Printf("Failed to format generated source: %v", err)
+ formatted = buf.Bytes()
+ }
+
+ outputPath := filepath.Join(outputDir, "inst_gen.go")
+ if err := os.WriteFile(outputPath, formatted, 0644); err != nil {
+ log.Fatalf("Failed to write output file: %v", err)
+ }
+ log.Printf("Generated %s", outputPath)
+}
diff --git a/arm64/instgen/xmlspec/parser.go b/arm64/instgen/xmlspec/parser.go
index f40f8c9..711b63f 100644
--- a/arm64/instgen/xmlspec/parser.go
+++ b/arm64/instgen/xmlspec/parser.go
@@ -788,7 +788,7 @@
log.Printf("unknown bit range: %s in %s, available: %v in %s\n", elm.encodedIn, elm.textExp, varBin, inst.file)
}
}
- textExp += "\bit range mappings:\n"
+ textExp += " bit range mappings:\n"
for k, v := range ranges {
textExp += fmt.Sprintf("%s: %s\n", k, v)
}
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +1 |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
{{$e}} obj.As = obj.ABaseARM64 + obj.A_ARCHSPECIFIC + 1000 + iotaWhy 1000?
And as usual, I am not super thrilled at all of this control flow in a template.
goOps := make(map[string]struct{})This is technically more efficient but efficiency does not matter for this tool. map[string]bool is easier to read.
t, err := template.New("gen").Parse(genTmpl)
if err != nil {
log.Fatalf("Failed to parse template: %v", err)
}
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
log.Fatalf("Failed to execute template: %v", err)
}
// Write to file
formatted, err := format.Source(buf.Bytes())
if err != nil {
log.Printf("Failed to format generated source: %v", err)
formatted = buf.Bytes()
}
outputPath := filepath.Join(outputDir, "inst_gen.go")
if err := os.WriteFile(outputPath, formatted, 0644); err != nil {
log.Fatalf("Failed to write output file: %v", err)
}
log.Printf("Generated %s", outputPath)We do these same steps combining a template, some data, and and a file name four times, that should just be one function with relevant inputs called four times.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +1 |
{{$e}} obj.As = obj.ABaseARM64 + obj.A_ARCHSPECIFIC + 1000 + iotaWhy 1000?
And as usual, I am not super thrilled at all of this control flow in a template.
Changed to a constant defined in a.out.go for clarity.
Control flow is removed
This is technically more efficient but efficiency does not matter for this tool. map[string]bool is easier to read.
I figured it out that this data is not used. simply removed.
t, err := template.New("gen").Parse(genTmpl)
if err != nil {
log.Fatalf("Failed to parse template: %v", err)
}
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
log.Fatalf("Failed to execute template: %v", err)
}
// Write to file
formatted, err := format.Source(buf.Bytes())
if err != nil {
log.Printf("Failed to format generated source: %v", err)
formatted = buf.Bytes()
}
outputPath := filepath.Join(outputDir, "inst_gen.go")
if err := os.WriteFile(outputPath, formatted, 0644); err != nil {
log.Fatalf("Failed to write output file: %v", err)
}
log.Printf("Generated %s", outputPath)We do these same steps combining a template, some data, and and a file name four times, that should just be one function with relevant inputs called four times.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
The generator should be in instgen. So there is a clear abstraction layer between parsing the XML files vs. generating data. This also ensures the XML parser package exports the right data types for the generator (and later for simdgen).
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
arm64/instgen: add assembler code generator for SVE
This CL implements the code generation logic.
It generates 4 files to the assembler:
- inst_gen.go: this file contains the instruction table for these new SVE
instructions.
- goops_gen.go: this file contains the go op constants for these new SVE
instructions.
- anames_gen.go: this file contains the anames (debugger symbols) for
these new SVE instructions.
- encoding_gen.go: this file contains the encoding functions for parts
(elements) of these new SVE instructions. They are emitted with their
natural language specification and we need to fill up their logic, which
will be in the next CL.
This CL generated files into CL 747180
This CL adds logic to allow only certain AClasses to be generated.
This CL also merged PREGZ and PREGM, and defer its check to encoding
phase. This is required to distinguish <P>/<ZM> cases.
This CL also filters out the generation of aliased encodings, as they
are not fully specified.
This CL also added encoding specifications for another weird encoding
defined in the decoding section, please see the added code in
`extractBinary` for details. An example instruction is "Unsigned divide
(predicated)".
It is useful for partial code gen for assembler without all AClasses
support.
This CL added a generation target that generates e2e test data, it uses
the GNU toolchain as an oracle. This CL assumes the toolchain version
2.45. There currently exists a bleeding edge new toolchain 2.46, some
special cases in `constructInstance` might be removed if we upgrade the
GNU toolchain.
diff --git a/arm64/instgen/main.go b/arm64/instgen/main.go
index fbdc14a..ba0d33b 100644
--- a/arm64/instgen/main.go
+++ b/arm64/instgen/main.go
@@ -22,7 +22,10 @@
//
// The program parses and processes all the XML files, and generates four .go files:
// inst_gen.go, elem_gen.go, goops_gen.go and arm64ops_gen.go to the output directory.
-// If -o option is not specified, the files will be written to the current directory.
+// The output directory is assumed to be GOROOT, these four files will be generated to
+// <outputDir>/src/cmd/internal/obj/arm64/.
+//
+// If -o option is not specified, no output files will be generated.
//
// Since the format of the ARM64 instruction specification document may update,
// this parser may not work for some versions of the XML document.
@@ -47,6 +50,7 @@
var input = flag.String("i", "", "the input directory of the xml files, this is an optional argument")
var output = flag.String("o", "", "the output directory of the generated files, this is an optional argument")
+var genE2E = flag.Bool("e2e", false, "generate end-to-end test data")
var url = flag.String("url", xmlspec.ExpectedURL, "the url of the xml files")
var version = flag.String("version", xmlspec.ExpectedVersion, "the version of the xml files")
@@ -76,8 +80,14 @@
// Parse each xml file to insts.
insts := xmlspec.ParseXMLFiles(xmlDir)
xmlspec.ProcessXMLFiles(insts)
+ if *output != "" {
+ xmlspec.Generate(insts, *output, *genE2E)
+ }
errCnt := 0
for _, inst := range insts {
+ if inst == nil {
+ continue
+ }
if inst.ParseError != "" {
errCnt++
log.Printf("error: %s", inst.ParseError)
diff --git a/arm64/instgen/xmlspec/encodings.go b/arm64/instgen/xmlspec/encodings.go
new file mode 100644
index 0000000..29225d0
--- /dev/null
+++ b/arm64/instgen/xmlspec/encodings.go
@@ -0,0 +1,603 @@
+// Copyright 2026 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 xmlspec
+
+var defaultEncodingTmpl string = `// TODO: implement this.
+ // Note: the raw value v is from obj.Prog, starting at bit 0.
+ // e.g. If it's a register, then its value is following the ones defined in a.out.go
+ // If it's a constant, then its value is the constant value.
+ return 0, %s, false` // The %s will be replaced by the symbol name.
+
+// encodingImpls are the known implementations of encoding functions, key is their description, value is their implementation.
+var encodingImpls = map[string]string{
+ "Check this is a B arrangement": `if v == ARNG_B {
+ return 0, enc_NIL, true
+ }
+ return 0, enc_NIL, false`,
+ "Check this is a D arrangement": `if v == ARNG_D {
+ return 0, enc_NIL, true
+ }
+ return 0, enc_NIL, false`,
+ "Check this is a H arrangement": `if v == ARNG_H {
+ return 0, enc_NIL, true
+ }
+ return 0, enc_NIL, false`,
+ "Check this is a Q arrangement": `if v == ARNG_Q {
+ return 0, enc_NIL, true
+ }
+ return 0, enc_NIL, false`,
+ "Check this is a S arrangement": `if v == ARNG_S {
+ return 0, enc_NIL, true
+ }
+ return 0, enc_NIL, false`,
+ "Check this is a merging predication": `if v == ARNG_M {
+ return 0, enc_NIL, true
+ }
+ return 0, enc_NIL, false`,
+ "Check this is a zeroing predication": `if v == ARNG_Z {
+ return 0, enc_NIL, true
+ }
+ return 0, enc_NIL, false`,
+ `For the "Byte and halfword" variant: is the size specifier,
+sz <T>
+0 B
+1 H
+bit range mappings:
+sz: [22:23)
+`: `switch v {
+ case ARNG_B:
+ return 0, enc_sz, true
+ case ARNG_H:
+ return 1 << 22, enc_sz, true
+ }
+ return 0, enc_sz, false`,
+ `For the "Byte, merging" and "Byte, zeroing" variants: is the size specifier,
+size <T>
+00 RESERVED
+01 H
+10 S
+11 D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 2 << 22, enc_size, true
+ case ARNG_D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `For the "Halfword, merging" and "Halfword, zeroing" variants: is the size specifier,
+size[0] <T>
+0 S
+1 D
+bit range mappings:
+size: [22:23)
+`: `switch v {
+ case ARNG_S:
+ return 0, enc_size0, true
+ case ARNG_D:
+ return 1 << 22, enc_size0, true
+ }
+ return 0, enc_size0, false`,
+ `For the "Word and doubleword" variant: is the size specifier,
+sz <T>
+0 S
+1 D
+bit range mappings:
+sz: [22:23)
+`: `
+ switch v {
+ case ARNG_S:
+ return 0, enc_sz, true
+ case ARNG_D:
+ return 1 << 22, enc_sz, true
+ }
+ return 0, enc_sz, false`,
+ `Is an arrangement specifier,
+size <T>
+00 16B
+01 8H
+10 4S
+11 2D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_16B:
+ return 0, enc_size, true
+ case ARNG_8H:
+ return 1 << 22, enc_size, true
+ case ARNG_4S:
+ return 2 << 22, enc_size, true
+ case ARNG_2D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is an arrangement specifier,
+size <T>
+00 RESERVED
+01 8H
+10 4S
+11 2D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_8H:
+ return 1 << 22, enc_size, true
+ case ARNG_4S:
+ return 2 << 22, enc_size, true
+ case ARNG_2D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the name of the destination SIMD&FP register, encoded in the "Vd" field.
+bit range mappings:
+Vd: [0:5)
+`: `return v, enc_Vd, true`,
+ `Is the name of the destination scalable predicate register PN8-PN15, with predicate-as-counter encoding, encoded in the "PNd" field.
+bit range mappings:
+PNd: [0:3)
+`: `if v >= 24 && v <= 31 {
+ // PN registers starts from 16.
+ return v - 24, enc_PNd, true
+ }
+ return 0, enc_PNd, false`,
+ `Is the name of the destination scalable predicate register, encoded in the "Pd" field.
+bit range mappings:
+Pd: [0:4)
+`: `return v, enc_Pd, true`,
+ `Is the name of the destination scalable vector register, encoded in the "Zd" field.
+bit range mappings:
+Zd: [0:5)
+`: `return v, enc_Zd, true`,
+ `Is the name of the first source and destination scalable predicate register, encoded in the "Pdn" field.
+bit range mappings:
+Pdn: [0:4)
+`: `return v, enc_Pdn, true`,
+ `Is the name of the first source and destination scalable vector register, encoded in the "Zdn" field.
+bit range mappings:
+Zdn: [0:5)
+`: `return v, enc_Zdn, true`,
+ `Is the name of the first source scalable predicate register, encoded in the "Pn" field.
+bit range mappings:
+Pn: [5:9)
+`: `return v << 5, enc_Pn, true`,
+ `Is the name of the first source scalable vector register, encoded in the "Zn" field.
+bit range mappings:
+Zn: [5:10)
+`: `return v << 5, enc_Zn, true`,
+ `Is the name of the governing scalable predicate register P0-P7, encoded in the "Pg" field.
+bit range mappings:
+Pg: [10:13)
+`: `if v <= 7 {
+ return v << 10, enc_Pg, true
+ }
+ return 0, enc_Pg, false`,
+ `Is the name of the governing scalable predicate register, encoded in the "Pg" field.
+bit range mappings:
+Pg: [10:14)
+`: `return v << 10, enc_Pg, true`,
+ `Is the name of the governing scalable predicate register, encoded in the "Pg" field.
+bit range mappings:
+Pg: [5:9)
+`: `return v << 5, enc_Pg, true`,
+ `Is the name of the second source and destination scalable predicate register, encoded in the "Pdm" field.
+bit range mappings:
+Pdm: [0:4)
+`: `return v, enc_Pdm, true`,
+ `Is the name of the second source and destination scalable vector register, encoded in the "Zda" field.
+bit range mappings:
+Zda: [0:5)
+`: `return v, enc_Zda, true`,
+ `Is the name of the second source scalable predicate register, encoded in the "Pm" field.
+bit range mappings:
+Pm: [16:20)
+`: `return v << 16, enc_Pm, true`,
+ `Is the name of the second source scalable vector register, encoded in the "Zm" field.
+bit range mappings:
+Zm: [16:21)
+`: `return v << 16, enc_Zm, true`,
+ `Is the name of the second source scalable vector register, encoded in the "Zm" field.
+bit range mappings:
+Zm: [5:10)
+`: `return v << 5, enc_Zm, true`,
+ `Is the name of the source and destination scalable predicate register, encoded in the "Pdn" field.
+bit range mappings:
+Pdn: [0:4)
+`: `return v, enc_Pdn, true`,
+ `Is the name of the source and destination scalable vector register, encoded in the "Zdn" field.
+bit range mappings:
+Zdn: [0:5)
+`: `return v, enc_Zdn, true`,
+ `Is the name of the source scalable predicate register, encoded in the "Pm" field.
+bit range mappings:
+Pm: [5:9)
+`: `return v << 5, enc_Pm, true`,
+ `Is the name of the source scalable predicate register, encoded in the "Pn" field.
+bit range mappings:
+Pn: [5:9)
+`: `return v << 5, enc_Pn, true`,
+ `Is the name of the source scalable vector register, encoded in the "Zn" field.
+bit range mappings:
+Zn: [5:10)
+`: `return v << 5, enc_Zn, true`,
+ `Is the name of the third source and destination scalable vector register, encoded in the "Zda" field.
+bit range mappings:
+Zda: [0:5)
+`: `return v, enc_Zda, true`,
+ `Is the name of the third source scalable vector register, encoded in the "Za" field.
+bit range mappings:
+Za: [16:21)
+`: `return v << 16, enc_Za, true`,
+ `Is the name of the third source scalable vector register, encoded in the "Za" field.
+bit range mappings:
+Za: [5:10)
+`: `return v << 5, enc_Za, true`,
+ `Is the name of the third source scalable vector register, encoded in the "Zk" field.
+bit range mappings:
+Zk: [5:10)
+`: `return v << 5, enc_Zk, true`,
+ `Is the name of the vector select predicate register P0-P7, encoded in the "Pv" field.
+bit range mappings:
+Pv: [10:13)
+`: `return v << 10, enc_Pv, true`,
+ `Is the name of the vector select predicate register, encoded in the "Pv" field.
+bit range mappings:
+Pv: [10:14)
+`: `return v << 10, enc_Pv, true`,
+ `Is the name of the vector select predicate register, encoded in the "Pv" field.
+bit range mappings:
+Pv: [5:9)
+`: `return v << 5, enc_Pv, true`,
+ `Is the predication qualifier,
+M <ZM>
+0 Z
+1 M
+bit range mappings:
+M: [16:17)
+`: `switch v {
+ case ARNG_Z:
+ return 0, enc_M, true
+ case ARNG_M:
+ return 1 << 16, enc_M, true
+ }
+ return 0, enc_M, false`,
+ `Is the predication qualifier,
+M <ZM>
+0 Z
+1 M
+bit range mappings:
+M: [4:5)
+`: `switch v {
+ case ARNG_Z:
+ return 0, enc_M, true
+ case ARNG_M:
+ return 1 << 4, enc_M, true
+ }
+ return 0, enc_M, false`,
+ `Is the size specifier,
+size <T>
+00 B
+01 H
+10 S
+11 D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_B:
+ return 0 << 22, enc_size, true
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 2 << 22, enc_size, true
+ case ARNG_D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <T>
+00 B
+01 H
+10 S
+11 RESERVED
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_B:
+ return 0 << 22, enc_size, true
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 2 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <T>
+00 RESERVED
+01 B
+10 H
+11 S
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_B:
+ return 1 << 22, enc_size, true
+ case ARNG_H:
+ return 2 << 22, enc_size, true
+ case ARNG_S:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <T>
+00 RESERVED
+01 H
+10 S
+11 D
+bit range mappings:
+size: [13:15)
+`: `switch v {
+ case ARNG_H:
+ return 1 << 13, enc_size, true
+ case ARNG_S:
+ return 2 << 13, enc_size, true
+ case ARNG_D:
+ return 3 << 13, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <T>
+00 RESERVED
+01 H
+10 S
+11 D
+bit range mappings:
+size: [17:19)
+`: `switch v {
+ case ARNG_H:
+ return 1 << 17, enc_size, true
+ case ARNG_S:
+ return 2 << 17, enc_size, true
+ case ARNG_D:
+ return 3 << 17, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <T>
+00 RESERVED
+01 H
+10 S
+11 D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 2 << 22, enc_size, true
+ case ARNG_D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <T>
+01 H
+10 S
+11 D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 2 << 22, enc_size, true
+ case ARNG_D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <T>
+01 H
+1x D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <Tb>
+00 B
+01 H
+10 S
+11 D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_B:
+ return 0 << 22, enc_size, true
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 2 << 22, enc_size, true
+ case ARNG_D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <Tb>
+00 RESERVED
+01 B
+10 H
+11 S
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_B:
+ return 1 << 22, enc_size, true
+ case ARNG_H:
+ return 2 << 22, enc_size, true
+ case ARNG_S:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <Tb>
+00 RESERVED
+01 H
+10 S
+11 D
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_H:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 2 << 22, enc_size, true
+ case ARNG_D:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size <Tb>
+01 B
+1x S
+bit range mappings:
+size: [22:24)
+`: `switch v {
+ case ARNG_B:
+ return 1 << 22, enc_size, true
+ case ARNG_S:
+ return 3 << 22, enc_size, true
+ }
+ return 0, enc_size, false`,
+ `Is the size specifier,
+size[0] <T>
+0 B
+1 H
+bit range mappings:
+size: [22:23)
+`: `switch v {
+ case ARNG_B:
+ return 0 << 22, enc_size0, true
+ case ARNG_H:
+ return 1 << 22, enc_size0, true
+ }
+ return 0, enc_size0, false`,
+ `Is the size specifier,
+size[0] <T>
+0 S
+1 D
+bit range mappings:
+size: [22:23)
+`: `switch v {
+ case ARNG_S:
+ return 0 << 22, enc_size0, true
+ case ARNG_D:
+ return 1 << 22, enc_size0, true
+ }
+ return 0, enc_size0, false`,
+ `Is the size specifier,
+size[0] <Tb>
+0 B
+1 H
+bit range mappings:
+size: [22:23)
+`: `switch v {
+ case ARNG_B:
+ return 0 << 22, enc_size0, true
+ case ARNG_H:
+ return 1 << 22, enc_size0, true
+ }
+ return 0, enc_size0, false`,
+ `Is the size specifier,
+sz <T>
+0 S
+1 D
+bit range mappings:
+sz: [14:15)
+`: `switch v {
+ case ARNG_S:
+ return 0 << 14, enc_sz, true
+ case ARNG_D:
+ return 1 << 14, enc_sz, true
+ }
+ return 0, enc_sz, false`,
+ `Is the size specifier,
+sz <T>
+0 S
+1 D
+bit range mappings:
+sz: [17:18)
+`: `switch v {
+ case ARNG_S:
+ return 0 << 17, enc_sz, true
+ case ARNG_D:
+ return 1 << 17, enc_sz, true
+ }
+ return 0, enc_sz, false`,
+ `Is the size specifier,
+sz <T>
+0 S
+1 D
+bit range mappings:
+sz: [22:23)
+`: `
+ switch v {
+ case ARNG_S:
+ return 0 << 22, enc_sz, true
+ case ARNG_D:
+ return 1 << 22, enc_sz, true
+ }
+ return 0, enc_sz, false
+ `,
+ `Is the size specifier,
+tszh tszl <T>
+0 00 RESERVED
+0 01 B
+0 10 H
+0 11 RESERVED
+1 00 S
+1 01 RESERVED
+1 1x RESERVED
+bit range mappings:
+tszh: [22:23)
+tszl: [19:21)
+`: `switch v {
+ case ARNG_B:
+ return 0<<22 | 1<<19, enc_tszh_tszl, true
+ case ARNG_H:
+ return 0<<22 | 2<<19, enc_tszh_tszl, true
+ case ARNG_S:
+ return 1 << 22, enc_tszh_tszl, true
+ }
+ return 0, enc_tszh_tszl, false`,
+ `Is the size specifier,
+tszh tszl <Tb>
+0 00 RESERVED
+0 01 H
+0 10 S
+0 11 RESERVED
+1 00 D
+1 01 RESERVED
+1 1x RESERVED
+bit range mappings:
+tszh: [22:23)
+tszl: [19:21)
+`: `switch v {
+ case ARNG_H:
+ return 0<<22 | 1<<19, enc_tszh_tszl, true
+ case ARNG_S:
+ return 0<<22 | 2<<19, enc_tszh_tszl, true
+ case ARNG_D:
+ return 1 << 22, enc_tszh_tszl, true
+ }
+ return 0, enc_tszh_tszl, false`,
+ `No-op check, returns true`: `return 0, enc_NIL, true`,
+}
diff --git a/arm64/instgen/xmlspec/generator.go b/arm64/instgen/xmlspec/generator.go
new file mode 100644
index 0000000..70b638a
--- /dev/null
+++ b/arm64/instgen/xmlspec/generator.go
@@ -0,0 +1,857 @@
+// Copyright 2026 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 xmlspec
+
+import (
+ "bytes"
+ "fmt"
+ "go/format"
+ "log"
+ "math/rand/v2"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+ "text/template"
+)
+
+type tmplData struct {
+ EncodingFuncs []funcData
+ Insts []instData
+ // All possible arg patterns are aggregated here.
+ // They are unique with regard to [Class] and [Elms].
+ // [Asm] is not considered for uniqueness.
+ UniqueArgs []argData
+ // All possible operand combinations are aggregated here.
+ // They are unique with regard to the list of [Args].
+ UniqueOpCombs []opCombData
+ Constants []constantData
+}
+
+type constantData struct {
+ ConstName string
+ BinName string
+}
+
+type funcData struct {
+ Symbol string
+ Index int
+ Comments []string
+ Body string
+}
+
+type instData struct {
+ GoOp string
+ Feature string
+ FixedBits string
+ Args []argData
+ Ops opCombData
+ Asm string
+}
+
+type argData struct {
+ // The global variable name of this arg pattern.
+ Name string
+ Class string
+ Asm string
+ Elms []elmData
+}
+
+type opCombData struct {
+ Name string
+ OpAsms string
+ ArgNames []string
+ BetterName string
+}
+
+type elmData struct {
+ Index int
+ FuncIndex int
+}
+
+type e2eData struct {
+ Asm string
+ Binary string
+}
+
+const genTmpl = `// Code generated by 'instgen -o=$GOROOT # from go install golang.org/x/arch/arm64/instgen@latest'. DO NOT EDIT.
+
+// The following constants are generated from the XML specification.
+// args are filled with constants named oc_<ACLASS_OP0>_<ENCODER_ELM0>_<ENCODER_ELM1>_..._<ACLASS_OP1>_...
+// the encoder functions are represented by their ID, i.e. encodeGen<ENCODER_ELM0> is the encoding
+// function you should look at for the 0-th op, 0-th element.
+
+package arm64
+
+var insts = []instEncoder{
+{{- range .Insts}}
+ // {{.Asm}} {{.Ops.OpAsms}}
+ {
+ goOp: {{.GoOp}},
+ fixedBits: {{.FixedBits}},
+ args: {{.Ops.BetterName}},
+ },
+{{- end}}
+}
+
+{{range .UniqueArgs}}
+var {{.Name}} = operand{
+ class: {{.Class}}, elemEncoders: []func(uint32) (uint32, component, bool){
+{{- range .Elms}}
+ encodeGen{{.FuncIndex}},
+{{- end}}
+ },
+}
+{{end}}
+
+{{range .UniqueOpCombs}}
+var {{.BetterName}} = []operand{
+{{- range .ArgNames}}
+ {{.}},
+{{- end}}
+}
+{{end}}
+`
+
+const encodingTmpl = `// Code generated by 'instgen -o=$GOROOT # from go install golang.org/x/arch/arm64/instgen@latest'.
+
+package arm64
+
+const (
+ enc_NIL component = iota
+{{- range .Constants}}
+ // {{.BinName}}
+ {{.ConstName}}
+{{- end}}
+)
+{{- range .EncodingFuncs}}
+{{- range .Comments}}
+// {{.}}
+{{- end}}
+func encodeGen{{.Index}}(v uint32) (uint32, component, bool) {
+ {{.Body}}
+}
+{{- end}}
+`
+
+const goopsTmpl = `// Code generated by 'instgen -o=$GOROOT # from go install golang.org/x/arch/arm64/instgen@latest'. DO NOT EDIT.
+
+package arm64
+
+import "cmd/internal/obj"
+
+const (
+{{- range .}}
+ {{.}}
+{{- end}}
+ ALAST
+ AB = obj.AJMP
+ ABL = obj.ACALL
+)
+`
+
+const anamesTmpl = `// Code generated by 'instgen -o=$GOROOT # from go install golang.org/x/arch/arm64/instgen@latest'. DO NOT EDIT.
+
+package arm64
+
+var sveAnames = []string{
+{{- range .}}
+ "{{.}}",
+{{- end}}
+ "LAST",
+}
+
+func init() {
+ Anames = append(Anames, sveAnames...)
+}
+`
+
+const e2edataTmpl = `// Code generated by 'instgen -o=$GOROOT # from go install golang.org/x/arch/arm64/instgen@latest'. DO NOT EDIT.
+
+#include "../../../../../runtime/textflag.h"
+
+TEXT asmtest(SB),DUPOK|NOSPLIT,$-8
+{{- range .}}
+ {{.Asm}}{{.Binary}}
+{{- end}}
+ RET
+`
+
+var allowedOperandTypes = map[string]bool{
+ "AC_ARNG": true,
+ "AC_PREG": true,
+ "AC_PREGZ": true,
+ "AC_PREGZM": true,
+ "AC_ZREG": true,
+}
+
+func readExistingGoOps(aoutPath string) map[string]bool {
+ ops := make(map[string]bool)
+ content, err := os.ReadFile(aoutPath)
+ if err != nil {
+ return ops
+ }
+ re := regexp.MustCompile(`\b(A[A-Z0-9_]+)\b`)
+ matches := re.FindAllStringSubmatch(string(content), -1)
+ for _, m := range matches {
+ ops[m[1]] = true
+ }
+ return ops
+}
+
+// formatAndFlush executes the given template and writes it to the given file,
+// the go file is formatted using gofmt.
+// If outputDir is empty, it does nothing.
+func formatAndFlush(outputDir string, filename string, data any, tmpl string) {
+ if outputDir == "" {
+ return
+ }
+ t, err := template.New("template").Parse(tmpl)
+ if err != nil {
+ log.Fatalf("Failed to parse template %s: %v", filename, err)
+ }
+
+ var buf bytes.Buffer
+ if err := t.Execute(&buf, data); err != nil {
+ log.Fatalf("Failed to execute template %s: %v", filename, err)
+ }
+
+ formatted := buf.Bytes()
+ if strings.HasSuffix(filename, ".go") {
+ formatted, err = format.Source(formatted)
+ if err != nil {
+ log.Printf("Failed to format generated source: %v", err)
+ formatted = buf.Bytes()
+ }
+ }
+
+ outputPath := filepath.Join(outputDir, filename)
+ if err := os.WriteFile(outputPath, formatted, 0644); err != nil {
+ log.Fatalf("Failed to write output file %s: %v", outputPath, err)
+ }
+ log.Printf("Generated %s", outputPath)
+}
+
+// Generate generates the Go code for the instruction table.
+func Generate(insts []*Instruction, outputDir string, genE2E bool) {
+ // Collect constants
+ encodingFuncs := make(map[string]int) // Map textExp to index
+ encodingFuncComments := make(map[string][]string)
+
+ // Pre-populate EncodingFuncs
+ sortedEncodings := make([]string, 0, len(allEncodingDescs))
+ for k := range allEncodingDescs {
+ sortedEncodings = append(sortedEncodings, k)
+ }
+ sort.Strings(sortedEncodings)
+ for i, k := range sortedEncodings {
+ encodingFuncs[k] = i + 1
+ encodingFuncComments[k] = strings.Split(k, "\n")
+ }
+
+ // Collect A64Types and A64Feats, and Insts
+ var instsData []instData
+ newGoOps := make(map[string]bool)
+ existingGoOps := readExistingGoOps(filepath.Join(outputDir, "a.out.go"))
+
+ usedFuncIndices := make(map[int]bool)
+
+ e2eTestData := make([]e2eData, 0)
+ for _, inst := range insts {
+ if inst == nil {
+ continue
+ }
+ isAlias := false
+ for _, doc := range inst.DocVars {
+ if doc.Key == "alias_mnemonic" {
+ isAlias = true
+ break
+ }
+ }
+ if isAlias {
+ // Alias instructions are not fully specified in their own XML files,
+ // skip them.
+ continue
+ }
+
+ for _, iclass := range inst.Classes.Iclass {
+ if !iclass.RegDiagram.parsed {
+ continue
+ }
+
+ for _, encoding := range iclass.Encodings {
+ if !encoding.parsed {
+ continue
+ }
+
+ if encoding.alias {
+ // Alias encodings are not fully specified in their own section,
+ // skip them.
+ continue
+ }
+
+ // Filtering logic:
+ // 1. If 0 operands (len <= 1), keep with warning.
+ // 2. If > 0 operands, ALL must be in allowedOperandTypes.
+ keep := true
+ if len(encoding.operands) <= 1 {
+ log.Printf("Warning: instruction with 0 operands kept: %s", encoding.asm)
+ } else {
+ for _, op := range encoding.operands[1:] {
+ if !allowedOperandTypes[op.typ] {
+ keep = false
+ break
+ }
+ }
+ }
+
+ if !keep {
+ continue
+ }
+
+ if !existingGoOps[encoding.goOp] {
+ newGoOps[encoding.goOp] = true
+ } else {
+ log.Printf("Warning: goOp %s already exists in a.out.go", encoding.goOp)
+ }
+
+ e2eTestData = append(e2eTestData, constructInstance(&encoding))
+ instData := instData{
+ Asm: strings.Fields(encoding.asm)[0],
+ GoOp: encoding.goOp,
+ FixedBits: fmt.Sprintf("%#x", encoding.binary),
+ }
+
+ for _, op := range encoding.operands[1:] {
+ arg := argData{
+ Asm: op.name,
+ Class: op.typ,
+ }
+ if len(op.elems) > 0 {
+ for j, elm := range op.elems {
+ idx, ok := encodingFuncs[elm.textExpWithRanges]
+ if !ok {
+ log.Printf("Warning: encoding func not found for %s in %s", elm.textExpWithRanges, inst.file)
+ continue
+ }
+ arg.Elms = append(arg.Elms, elmData{
+ Index: j,
+ FuncIndex: idx,
+ })
+ usedFuncIndices[idx] = true
+ }
+ }
+ instData.Args = append(instData.Args, arg)
+ }
+ instsData = append(instsData, instData)
+ }
+ }
+ }
+
+ // Prepare template data
+ data := tmplData{
+ Insts: instsData,
+ }
+
+ // Collect constants
+ encodedInMap := make(map[string]string) // raw string -> constant name
+ var uniqueEncodedIn []string
+
+ // Collect ALL constants from global map to ensure invariant names (enc_X)
+ for _, v := range encodingDescsToEncodedIn {
+ if v == "" || v == "nil" {
+ continue
+ }
+ if _, ok := encodedInMap[v]; !ok {
+ encodedInMap[v] = ""
+ uniqueEncodedIn = append(uniqueEncodedIn, v)
+ }
+ }
+ sort.Strings(uniqueEncodedIn)
+
+ // Identify used constants
+ neededEncodedIn := make(map[string]bool)
+ for k, idx := range encodingFuncs {
+ if usedFuncIndices[idx] {
+ if v := encodingDescsToEncodedIn[k]; v != "" && v != "nil" {
+ neededEncodedIn[v] = true
+ }
+ }
+ }
+
+ var constants []constantData
+ for _, v := range uniqueEncodedIn {
+ v0 := v
+ v = strings.ReplaceAll(v, " :: ", "_")
+ v = strings.ReplaceAll(v, "(", "")
+ v = strings.ReplaceAll(v, ")", "")
+ v = strings.ReplaceAll(v, "[", "")
+ v = strings.ReplaceAll(v, "]", "")
+ name := fmt.Sprintf("enc_%s", v)
+ encodedInMap[v0] = name
+ // Only add to output if used
+ if neededEncodedIn[v0] {
+ constants = append(constants, constantData{
+ ConstName: name,
+ BinName: v0,
+ })
+ }
+ }
+ // process encodingImpls, field them to make the comparison easier
+ for k, v := range encodingImpls {
+ uniformed := strings.Join(strings.Fields(k), " ")
+ encodingImpls[uniformed] = v
+ }
+ for i, k := range sortedEncodings {
+ if !usedFuncIndices[i+1] {
+ continue
+ }
+ sym := encodingDescsToEncodedIn[k]
+ if name, ok := encodedInMap[sym]; ok {
+ sym = name
+ } else {
+ sym = "enc_NIL"
+ }
+ var body string
+ kUniformed := strings.Join(strings.Fields(k), " ")
+ if b, ok := encodingImpls[kUniformed]; ok {
+ body = b
+ } else {
+ log.Printf("Warning: encoding func not found for\n%s\n", k)
+ body = fmt.Sprintf(defaultEncodingTmpl, sym)
+ }
+ data.EncodingFuncs = append(data.EncodingFuncs, funcData{
+ Symbol: sym,
+ Index: i + 1,
+ Comments: encodingFuncComments[k],
+ Body: body,
+ })
+ }
+ data.Constants = constants
+
+ // Now aggregate all arg patterns to unique globals.
+ argMap := make(map[string]bool)
+ var uniqueArgs []argData
+ for i, inst := range instsData {
+ for j, arg := range inst.Args {
+ name := "a_" + arg.Class[3:] // remove AC_ prefix
+ for _, elm := range arg.Elms {
+ name += fmt.Sprintf("_%d", elm.FuncIndex)
+ }
+ instsData[i].Args[j].Name = name
+ arg.Name = name
+ if !argMap[name] {
+ argMap[name] = true
+ uniqueArgs = append(uniqueArgs, arg)
+ }
+ }
+ }
+ data.UniqueArgs = uniqueArgs
+ // Now aggregate all arg lists to unique globals.
+ opCombMap := make(map[string]opCombData)
+ var uniqueOpCombs []opCombData
+ for i, inst := range instsData {
+ name := "oc"
+ argNames := []string{}
+ argAsms := []string{}
+ for _, arg := range inst.Args {
+ name += "_" + arg.Name[2:] // remove a_ prefix
+ for _, elm := range arg.Elms {
+ name += fmt.Sprintf("_%d", elm.FuncIndex)
+ }
+ argNames = append(argNames, arg.Name)
+ argAsms = append(argAsms, arg.Asm)
+ }
+ if ops, ok := opCombMap[name]; ok {
+ instsData[i].Ops = ops
+ } else {
+ ops := opCombData{
+ Name: name,
+ ArgNames: argNames,
+ OpAsms: strings.Join(argAsms, ", "),
+ }
+ opCombMap[name] = ops
+ uniqueOpCombs = append(uniqueOpCombs, ops)
+ instsData[i].Ops = ops
+ }
+ }
+ data.UniqueOpCombs = uniqueOpCombs
+
+ // Figure out better names for the slices of asm operand
+ // checkers/encoders.
+
+ // Goal is to replace an "ugly name" with a sanitized
+ // version of the instruction operand types; however, sometimes
+ // there is more than one ugly name mapping to the same
+ // nominal operands (an ugly name corresponds to an encoding).
+ // Detect this, and for those cases where there's more than
+ // one ugly name for the same operands, append a sequence
+ // number.
+ operandsToName := func(s string) string {
+ s = strings.Replace(s, "<", "", -1)
+ s = strings.Replace(s, ">", "", -1)
+ s = strings.Replace(s, "/", "", -1)
+ s = strings.Replace(s, ",", "_", -1)
+ s = strings.Replace(s, ".", "_", -1)
+ s = strings.Replace(s, " ", "_", -1)
+ return s
+ }
+
+ ot2ugly2Count := make(map[string]map[string]int)
+ otSlice := []string{} // insertion order of operand types
+ uglyInsertionOrder := make(map[string]int)
+
+ for _, inst := range data.Insts {
+ uglyName := inst.Ops.Name
+ if x := uglyInsertionOrder[uglyName]; x == 0 {
+ uglyInsertionOrder[uglyName] = 1 + len(uglyInsertionOrder)
+ }
+ operandTypes := inst.Ops.OpAsms
+ if ot2ugly2Count[operandTypes] == nil {
+ otSlice = append(otSlice, operandTypes)
+ ot2ugly2Count[operandTypes] = make(map[string]int)
+ }
+ // keep track of how many instructions with this operand type
+ // sequence use the exact same encoding.
+ ot2ugly2Count[operandTypes][uglyName] += 1
+ }
+
+ // Map from ugly name to better name
+ ugly2better := make(map[string]string)
+
+ for _, x := range otSlice {
+ // Get a canonical order of ugly names
+ var uglies []string
+ u2c := ot2ugly2Count[x]
+ for c := range u2c {
+ uglies = append(uglies, c)
+ }
+
+ if len(uglies) > 1 {
+ // if there is more than one, sort into decreasing
+ // frequency order, then sort by insertion order.
+ sort.Slice(uglies, func(i, j int) bool {
+ if c := u2c[uglies[j]] - u2c[uglies[i]]; c != 0 {
+ return c < 0
+ }
+ return uglyInsertionOrder[uglies[i]] < uglyInsertionOrder[uglies[j]]
+ })
+ for j, u := range uglies {
+ ugly2better[u] = fmt.Sprintf("%s__%d", operandsToName(x), j+1)
+ }
+ } else {
+ // if only one, do not add a __N suffix.
+ ugly2better[uglies[0]] = operandsToName(x)
+ }
+ }
+
+ // Apply better name translations.
+ for i, inst := range data.Insts {
+ if arg := ugly2better[inst.Ops.Name]; arg != "" {
+ data.Insts[i].Ops.BetterName = arg
+ } else {
+ data.Insts[i].Ops.BetterName = inst.Ops.Name
+ }
+ }
+
+ for i, uoc := range data.UniqueOpCombs {
+ if arg := ugly2better[uoc.Name]; arg != "" {
+ data.UniqueOpCombs[i].BetterName = arg
+ } else {
+ data.UniqueOpCombs[i].BetterName = uoc.Name
+ }
+ }
+
+ // Put these two slices into sensible orders.
+ sort.Slice(data.UniqueOpCombs, func(i, j int) bool {
+ return data.UniqueOpCombs[i].BetterName < data.UniqueOpCombs[j].BetterName
+ })
+
+ sort.Slice(data.UniqueArgs, func(i, j int) bool {
+ return data.UniqueArgs[i].Name < data.UniqueArgs[j].Name
+ })
+
+ // Generate inst_gen.go
+ formatAndFlush(outputDir, "src/cmd/internal/obj/arm64/inst_gen.go", data, genTmpl)
+
+ // Generate encoding_gen.go
+ formatAndFlush(outputDir, "src/cmd/internal/obj/arm64/encoding_gen.go", data, encodingTmpl)
+
+ // Generate goops_gen.go
+ var sortedNewGoOps []string
+ for k := range newGoOps {
+ sortedNewGoOps = append(sortedNewGoOps, k)
+ }
+ sort.Strings(sortedNewGoOps)
+ goopsPrefix := " obj.As = AAUTIB1716 + 1 + iota"
+ sortedNewGoOps[0] += goopsPrefix
+ formatAndFlush(outputDir, "src/cmd/internal/obj/arm64/goops_gen.go", sortedNewGoOps, goopsTmpl)
+
+ // Generate anames_gen.go
+ for i := range sortedNewGoOps {
+ sortedNewGoOps[i] = strings.TrimSuffix(sortedNewGoOps[i], goopsPrefix)
+ sortedNewGoOps[i] = strings.TrimPrefix(sortedNewGoOps[i], "A")
+ }
+ formatAndFlush(outputDir, "src/cmd/internal/obj/arm64/anames_gen.go", sortedNewGoOps, anamesTmpl)
+
+ if genE2E {
+ // Generate arm64sveenc.s
+ formatAndFlush(outputDir, "src/cmd/asm/internal/asm/testdata/arm64sveenc.s", e2eTestData, e2edataTmpl)
+ }
+}
+
+// Mappings for operand patterns to concrete registers
+var regMap = map[string]string{
+ "Zd": "Z",
+ "Zda": "Z",
+ "Zdn": "Z",
+ "Zn": "Z",
+ "Zm": "Z",
+ "Za": "Z",
+ "Zk": "Z",
+
+ "Pd": "P",
+ "Pdn": "P",
+ "Pdm": "P",
+ "Pn": "P",
+ "Pm": "P",
+ "Pg": "P",
+ "PNd": "PN",
+ "Pv": "P",
+
+ "Vd": "V",
+ "Vn": "V",
+ "Vm": "V",
+}
+
+// Arrangement variables, also includes predication cases.
+var arrMap = map[string]bool{
+ "Tb": true, // arrangement variables
+ "T": true, // arrangement variables
+ "ZM": true, // predication variables
+}
+
+// Z, P reg arrangements, used for variable mutation.
+var sveArr = []string{"B", "H", "S", "D", "Q"}
+var svePred = []string{"M", "T"}
+
+// V reg arrangements, used for variable mutation.
+var neonArrGNU = []string{"16B", "1D", "4H", "8H", "2S", "4S", "2D", "1Q"}
+var neonArrGo = []string{"B16", "D1", "H4", "H8", "S2", "S4", "D2", "Q1"}
+
+// assembleGNU assembles the given assembly string using GNU toolchain and returns the hex.
+func assembleGNU(s string) (string, error) {
+ // Create temp file
+ tmpName := "temp_probe.s"
+ if err := os.WriteFile(tmpName, []byte(s+"\n"), 0644); err != nil {
+ return "", err
+ }
+ defer os.Remove(tmpName)
+
+ // Run as
+ cmd := exec.Command("aarch64-linux-gnu-as", "-march=all", tmpName, "-o", "temp_probe.o")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ return "", fmt.Errorf("as failed: %s", out)
+ }
+ defer exec.Command("rm", "temp_probe.o").Run()
+
+ // Run objdump
+ cmd = exec.Command("aarch64-linux-gnu-objdump", "-d", "temp_probe.o")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ return "", err
+ }
+
+ // Parse output
+ lines := strings.Split(string(out), "\n")
+ for _, l := range lines {
+ l = strings.TrimSpace(l)
+ // The first text line contains the hex.
+ if strings.Contains(l, "0:") {
+ parts := strings.Fields(l)
+ // parts[0] is offset:, parts[1] is hex
+ if len(parts) >= 2 {
+ if len(parts[1]) == 8 {
+ return parts[1], nil
+ }
+ }
+ }
+ }
+ return "", fmt.Errorf("no hex found")
+}
+
+// reverseHex reverses the byte order of a hex string.
+// this is required for Go's e2e test.
+func reverseHex(h string) string {
+ if len(h) != 8 {
+ return h
+ }
+ return h[6:8] + h[4:6] + h[2:4] + h[0:2]
+}
+
+// constructInstance takes an [Encoding] and tries to generate a
+// valid Go assembly instance of it and returns the e2eData for this instance.
+// The construction is deterministic.
+//
+// This function requires the availablity of the GNU toolchain.
+// Special instructions are elided, they are documented in the comments.
+// Technically we can skip them early before we do the construction, but
+// we kept them after the construction for future verification.
+func constructInstance(enc *Encoding) e2eData {
+ var name string
+ // Try to construct two assembly, one in Go the other in GNU.
+ // The trial succeeds once the GNU one is accepted by the GNU toolchain.
+ rng := rand.New(rand.NewPCG(uint64(42), uint64(1024)))
+ // Try 100 times
+ var gnuErr error
+ for range 100 {
+ goAsmOps := make([]string, 0)
+ gnuAsmOps := make([]string, 0)
+ // Useful for pruning, e.g.
+ // ABS <Zd>.<T>, <Pg>/Z, <Zn>.<T>
+ // the 2 instances of <T> should be exactly the same...
+ // Key is the random number selected.
+ regCache := map[string]int{}
+ arrCache := map[string]int{}
+ cachedOrNew := func(cache map[string]int, key string, n int) int {
+ if v, ok := cache[key]; ok {
+ return v
+ }
+ v := rng.IntN(n)
+ cache[key] = v
+ return v
+ }
+ for i, op := range enc.operands {
+ if i == 0 {
+ // First operand is the instruction name.
+ name = op.name
+ continue
+ }
+ opName := op.name
+ if !allowedOperandTypes[op.typ] {
+ log.Fatalf("Unsupported operand type in constructInstance: %s, enc: %s", op.typ, enc.String())
+ }
+ // Right now allowed types are all pure regs.
+ // TODO: when this function need to handle more operand types,
+ // guard these logic with the correct conditions.
+ var parts []string
+ var isPred bool
+ if strings.Contains(opName, ".") {
+ parts = strings.Split(opName, ".")
+ } else {
+ parts = strings.Split(opName, "/")
+ isPred = true
+ }
+ if len(parts) == 0 {
+ log.Fatalf("Unrecognized operand: %s", opName)
+ }
+ regName := strings.TrimSuffix(strings.TrimPrefix(parts[0], "<"), ">")
+ var arrName string
+ if len(parts) > 1 {
+ arrName = strings.TrimSuffix(strings.TrimPrefix(parts[1], "<"), ">")
+ }
+ reg := regMap[regName]
+ if arrMap[arrName] {
+ // Variables, needs mutation
+ switch reg {
+ case "Z":
+ regIdx := cachedOrNew(regCache, regName, 32)
+ arrIdx := cachedOrNew(arrCache, arrName, len(sveArr))
+ asmOp := fmt.Sprintf("Z%d.%s", regIdx, sveArr[arrIdx])
+ gnuAsmOps = append([]string{asmOp}, gnuAsmOps...)
+ // go operand order is gnu reversed
+ goAsmOps = append(goAsmOps, asmOp)
+ case "P", "PN":
+ regIdx := cachedOrNew(regCache, regName, 15)
+ if isPred {
+ arrIdx := cachedOrNew(arrCache, arrName, len(svePred))
+ // Go use "." for predication, GNU use "/"
+ gnuAsmOps = append([]string{fmt.Sprintf("%s%d/%s", reg, regIdx, svePred[arrIdx])}, gnuAsmOps...)
+ goAsmOps = append(goAsmOps, fmt.Sprintf("%s%d.%s", reg, regIdx, svePred[arrIdx]))
+ } else {
+ arrIdx := cachedOrNew(arrCache, arrName, len(sveArr))
+ asmOp := fmt.Sprintf("%s%d.%s", reg, regIdx, sveArr[arrIdx])
+ gnuAsmOps = append([]string{asmOp}, gnuAsmOps...)
+ goAsmOps = append(goAsmOps, asmOp)
+ }
+ case "V":
+ regIdx := cachedOrNew(regCache, regName, 32)
+ arrIdx := cachedOrNew(arrCache, arrName, len(neonArrGNU))
+ gnuAsmOps = append([]string{fmt.Sprintf("V%d.%s", regIdx, neonArrGNU[arrIdx])}, gnuAsmOps...)
+ goAsmOps = append(goAsmOps, fmt.Sprintf("V%d.%s", regIdx, neonArrGo[arrIdx]))
+ }
+ } else {
+ // Fixed arrangement or predications
+ if arrName != "" {
+ arrName = "." + arrName
+ }
+ switch reg {
+ case "Z":
+ regIdx := cachedOrNew(regCache, regName, 32)
+ asmOp := fmt.Sprintf("Z%d%s", regIdx, arrName)
+ gnuAsmOps = append([]string{asmOp}, gnuAsmOps...)
+ goAsmOps = append(goAsmOps, asmOp)
+ case "P", "PN":
+ regIdx := cachedOrNew(regCache, regName, 15)
+ if isPred {
+ var arrNameGNU string
+ if arrName != "" {
+ arrNameGNU = "/" + arrName[1:]
+ }
+ gnuAsmOps = append([]string{fmt.Sprintf("%s%d%s", reg, regIdx, arrNameGNU)}, gnuAsmOps...)
+ goAsmOps = append(goAsmOps, fmt.Sprintf("%s%d%s", reg, regIdx, arrName))
+ } else {
+ asmOp := fmt.Sprintf("%s%d%s", reg, regIdx, arrName)
+ gnuAsmOps = append([]string{asmOp}, gnuAsmOps...)
+ goAsmOps = append(goAsmOps, asmOp)
+ }
+ case "V":
+ log.Fatalf("Unexpected V with fixed arrangement: %s", opName)
+ }
+ }
+ }
+ // Try to assemble the GNU version.
+ gnuAsm := fmt.Sprintf(".arch_extension sve2p1\n%s %s", name, strings.Join(gnuAsmOps, ", "))
+ hex, err := assembleGNU(gnuAsm)
+ if err != nil {
+ gnuErr = err
+ continue
+ }
+ if name == "CMPLT" || name == "CMPLE" || name == "CMPLO" || name == "CMPLS" {
+ // These instructions has an aliased instruction that's not accepted by the GNU assembler.
+ // However at this moment Go does not support the aliased instruction.
+ // These e2e test cases will be compared to another non-aliased instruction of the same
+ // mnemonic, causing a test failure.
+ // Before we support alias in Go, we skip these test cases.
+ return e2eData{Asm: fmt.Sprintf("// TODO: %s", name)}
+ }
+ return e2eData{
+ Asm: fmt.Sprintf("%s %s", enc.goOp[1:], strings.Join(goAsmOps, ", ")),
+ Binary: "\t// " + reverseHex(hex),
+ }
+ }
+ if name == "ADDQP" || name == "ADDSUBP" || name == "SCVTFLT" || name == "UCVTFLT" {
+ // Very new instructions
+ // GNU toolchain 2.45 doesn't know about these instruction yet.
+ return e2eData{Asm: fmt.Sprintf("// TODO: %s", name)}
+ }
+ if enc.asm == "BFMMLA <Zda>.H, <Zn>.H, <Zm>.H" ||
+ enc.asm == "FMMLA <Zda>.H, <Zn>.H, <Zm>.H" ||
+ enc.asm == "SABAL <Zda>.<T>, <Zn>.<Tb>, <Zm>.<Tb>" ||
+ enc.asm == "SCVTF <Zd>.<T>, <Zn>.<Tb>" ||
+ enc.asm == "SDOT <Zda>.H, <Zn>.B, <Zm>.B" ||
+ enc.asm == "SUBP <Zdn>.<T>, <Pg>/M, <Zdn>.<T>, <Zm>.<T>" ||
+ enc.asm == "UABAL <Zda>.<T>, <Zn>.<Tb>, <Zm>.<Tb>" ||
+ enc.asm == "UCVTF <Zd>.<T>, <Zn>.<Tb>" ||
+ enc.asm == "UDOT <Zda>.H, <Zn>.B, <Zm>.B" {
+ // Very new instruction encodings
+ // GNU toolchain 2.45 doesn't know about these specific encodings yet.
+ return e2eData{Asm: fmt.Sprintf("// TODO: %s", enc.asm)}
+ }
+ log.Fatalf("Failed to construct instance for %s: %v", enc.asm, gnuErr)
+ return e2eData{}
+}
diff --git a/arm64/instgen/xmlspec/inst.go b/arm64/instgen/xmlspec/inst.go
index b80532c..66e7314 100644
--- a/arm64/instgen/xmlspec/inst.go
+++ b/arm64/instgen/xmlspec/inst.go
@@ -244,6 +244,7 @@
ArchVariant ArchVariant `xml:"arch_variants>arch_variant"`
RegDiagram RegDiagram `xml:"regdiagram"`
Encodings []Encoding `xml:"encoding"`
+ PsSection []PsSection `xml:"ps_section"`
}
// Classes represents a <classes> element, grouping instruction classes.
@@ -318,6 +319,11 @@
Explanations []Explanation `xml:"explanation"`
}
+// PsSection represents a <ps_section> element, which contains the raw XML content of a section.
+type PsSection struct {
+ InnerXML string `xml:",innerxml"`
+}
+
// Instruction represents the root <instructionsection> element of an instruction XML specification.
type Instruction struct {
XMLName xml.Name `xml:"instructionsection"`
diff --git a/arm64/instgen/xmlspec/parser.go b/arm64/instgen/xmlspec/parser.go
index f841ac9..93896ff 100644
--- a/arm64/instgen/xmlspec/parser.go
+++ b/arm64/instgen/xmlspec/parser.go
@@ -88,10 +88,10 @@
{regexp.MustCompile(`^<P[a-z]{1}>$`), "AC_PREG"},
// AC_PREG: Predicate-as-counter registers (PN).
{regexp.MustCompile(`^<PN[a-z]{1}>$`), "AC_PREG"},
- // AC_PREGM: Predicate registers with merging predication (/M).
- {regexp.MustCompile(`^<P[N]?[a-z]{1}>\/M$`), "AC_PREGM"},
- // AC_PREGZ: Predicate registers with zeroing predication (/Z).
- {regexp.MustCompile(`^<P[N]?[a-z]{1}>\/(Z|<ZM>)$`), "AC_PREGZ"},
+ // AC_PREGZM: Predicate registers with merging predication (/M).
+ {regexp.MustCompile(`^<P[N]?[a-z]{1}>\/M$`), "AC_PREGZM"},
+ // AC_PREGZM: Predicate registers with zeroing predication (/Z).
+ {regexp.MustCompile(`^<P[N]?[a-z]{1}>\/(Z|<ZM>)$`), "AC_PREGZM"},
// AC_REGIDX: Registers with immediate index.
{regexp.MustCompile(`^(<[PZ][N]?[a-z]{1}>|ZT0)\[<[a-z]+>\]$`), "AC_REGIDX"},
// AC_ZREG: Scalable vector registers (Z).
@@ -369,6 +369,22 @@
// By reading the decoding ASL we know that this "size" box should be 0b10...
regDiagram.fixedBin |= uint32(1 << 23)
}
+ if len(inst.Classes.Iclass[i].PsSection) == 1 {
+ squashedPs := inst.Classes.Iclass[i].PsSection[0].InnerXML
+ if strings.Contains(squashedPs, "if size IN {'0x'} then EndOfDecode") {
+ // Very ugly encoding specification in the decoding ASL. We have to set
+ // the high bit of the size box to 1.
+ // Example instruction is "Unsigned divide (predicated)":
+ // UDIV <Zdn>.<T>, <Pg>/M, <Zdn>.<T>, <Zm>.<T>
+ if _, ok := regDiagram.varBin["size"]; !ok {
+ log.Fatalf("size box not found in %s", inst.file)
+ }
+ if regDiagram.varBin["size"].hi != 24 || regDiagram.varBin["size"].lo != 22 {
+ log.Fatalf("unexpecetd size box in %s", inst.file)
+ }
+ regDiagram.fixedBin |= uint32(1 << 23)
+ }
+ }
}
}
@@ -803,7 +819,7 @@
idx := matches[2]
idxI, err := strconv.Atoi(idx)
if err != nil {
- panic(fmt.Sprintf("invalid index: %s in %s, available: %v in %s\n", idx, elm.encodedIn, varBin, inst.file))
+ log.Fatalf("invalid index: %s in %s, available: %v in %s\n", idx, elm.encodedIn, varBin, inst.file)
}
br, ok2 := varBin[key]
if ok2 {
@@ -964,6 +980,21 @@
continue
}
}
+ // If the diff is only by Pg/M or Pg/Z, it's ok to ignore, they are handled by the assembler.
+ if len(v) == 2 {
+ var hasPgM, hasPgZ bool
+ for _, s := range v {
+ if strings.Contains(s, "/M") {
+ hasPgM = true
+ }
+ if strings.Contains(s, "/Z") {
+ hasPgZ = true
+ }
+ }
+ if hasPgM && hasPgZ {
+ continue
+ }
+ }
sort.Strings(v)
log.Printf("%s:\n\t%v\n", k, strings.Join(v, "\n\t"))
}
@@ -976,11 +1007,10 @@
// the arrow-bracket enclosed parts are elements.
var expectedElemCount = map[string]int{
// <reg>.<T>
- "AC_ARNG": 2,
- "AC_PREG": 2,
- "AC_PREGZ": 2,
- "AC_PREGM": 2,
- "AC_ZREG": 2,
+ "AC_ARNG": 2,
+ "AC_PREG": 2,
+ "AC_PREGZM": 2,
+ "AC_ZREG": 2,
// <reg>.<T>[<index>]
"AC_ARNGIDX": 3,
"AC_ZREGIDX": 3,
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
(Same comment as CL 747080)
The generator should be in instgen, not xmlspec. So there is a clear abstraction layer between parsing the XML files and generating the instructions. This also ensures the XML parser package exports the right data types for the generator (and later for simdgen).
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Junyang ShaoThe generator should be in instgen. So there is a clear abstraction layer between parsing the XML files vs. generating data. This also ensures the XML parser package exports the right data types for the generator (and later for simdgen).
Done, new CL 755180
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +1 |
(Same comment as CL 747080)
Junyang ShaoThe generator should be in instgen, not xmlspec. So there is a clear abstraction layer between parsing the XML files and generating the instructions. This also ensures the XML parser package exports the right data types for the generator (and later for simdgen).
Done
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +1 |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
if inst == nil {
continue
}If prior CL adds nil filter in ProcessXMLFiles, this won't be necessary here.
Binary uint32 // more specific instruction encoding than regdiagram.binaryRegDiagram.fixedBin?
if inst == nil {
continue
}This could also be removed if the nils were filtered in ProcessXMLFiles
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +2 |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
It would be good to be clear which exported types are necessary for generating the instructions (and later simdgen), vs. necessary for XML parsing. There may be some overlap as well. This would help make the API cleaner and clearer.
Let's not rush submitting before we do some API review, as this is publicly importable API. Thanks.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +1 |
If prior CL adds nil filter in ProcessXMLFiles, this won't be necessary here.
It would be good to be clear which exported types are necessary for generating the instructions (and later simdgen), vs. necessary for XML parsing. There may be some overlap as well. This would help make the API cleaner and clearer.
Let's not rush submitting before we do some API review, as this is publicly importable API. Thanks.
Ok, I will wait for the review in Wednesday
Binary uint32 // more specific instruction encoding than regdiagram.binaryJunyang ShaoRegDiagram.fixedBin?
Done
This could also be removed if the nils were filtered in ProcessXMLFiles
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +1 |
Junyang ShaoIt would be good to be clear which exported types are necessary for generating the instructions (and later simdgen), vs. necessary for XML parsing. There may be some overlap as well. This would help make the API cleaner and clearer.
Let's not rush submitting before we do some API review, as this is publicly importable API. Thanks.
Ok, I will wait for the review in Wednesday
I had updated the CL as discussed in Wednesday:
Please take a look
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Junyang ShaoIt would be good to be clear which exported types are necessary for generating the instructions (and later simdgen), vs. necessary for XML parsing. There may be some overlap as well. This would help make the API cleaner and clearer.
Let's not rush submitting before we do some API review, as this is publicly importable API. Thanks.
Junyang ShaoOk, I will wait for the review in Wednesday
I had updated the CL as discussed in Wednesday:
- type separations, the XML types are in xml.go; parsed data types are in parsed.go
- unused fields removal: removed unused xml fields, this CL also included the fields that @alexande...@gmail.com needs.
- error case e2e data generation.
Please take a look
Unmarshalling requires the types to be exported, afaik, so they are still all exported.
The unneeded ones are simply removed.