arm64/arm64gen: adds a generator sysreggen.go

The assembler needs to support the EL0 and EL1 system registers used
in the MSR/MRS instructions. Because there are many system registers,
this patch adds a generator sysreggen.go. The generator parses the
system register XML files to get the encoding information and writes
them to a $filename file (default "sysRegEnc.go" file). The automatically
generated file is moved to $GOROOT/src/cmd/internal/obj/arm64/ folder
and used by the assembler.

Change-Id: Ia5ee6b85c0a972fdaf3b28c194c36d6cfe4b5b10
Reviewed-on: https://go-review.googlesource.com/c/arch/+/191222
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/arm64/arm64gen/sysreggen.go b/arm64/arm64gen/sysreggen.go
new file mode 100644
index 0000000..1394831
--- /dev/null
+++ b/arm64/arm64gen/sysreggen.go
@@ -0,0 +1,251 @@
+// Copyright 2019 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.
+
+// This file is an automatic parser program that parses arm64
+// system register XML files to get the encoding information
+// and writes them to the sysRegEnc.go file. The sysRegEnc.go
+// file is used for the system register encoding.
+// Follow the following steps to run the automatic parser program:
+// 1. The system register XML files are from
+// https://developer.arm.com/-/media/Files/ATG/Beta10/SysReg_xml_v85A-2019-06.tar.gz
+// 2. Extract SysReg_xml_v85A-2019-06.tar/SysReg_xml_v85A-2019-06/SysReg_xml_v85A-2019-06/AArch64-*.xml
+// to a "xmlfolder" folder.
+// 3. Run the command: ./sysrengen -i "xmlfolder" -o "filename"
+// By default, the xmlfolder is "./files" and the filename is "sysRegEnc.go".
+// 4. Put the automaically generated file into $GOROOT/src/cmd/internal/obj/arm64 directory.
+
+package main
+
+import (
+	"bufio"
+	"encoding/xml"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+)
+
+// Types corresponded to the data structures in the XML file.
+
+type RegisterPage struct {
+	XMLName   xml.Name  `xml:"register_page"`
+	Registers Registers `xml:"registers"`
+}
+
+type Registers struct {
+	XMLName  xml.Name `xml:"registers"`
+	Register Register `xml:"register"`
+}
+
+type Register struct {
+	XMLName          xml.Name         `xml:"register"`
+	RegShortName     string           `xml:"reg_short_name"`
+	RegVariables     RegVariables     `xml:"reg_variables"`
+	AccessMechanisms AccessMechanisms `xml:"access_mechanisms"`
+}
+
+type RegVariables struct {
+	XMLName     xml.Name    `xml:"reg_variables"`
+	RegVariable RegVariable `xml:"reg_variable"`
+}
+
+type RegVariable struct {
+	XMLName  xml.Name `xml:"reg_variable"`
+	Variable string   `xml:"variable,attr"`
+	Max      string   `xml:"max,attr"`
+}
+
+type AccessMechanisms struct {
+	XMLName         xml.Name          `xml:"access_mechanisms"`
+	AccessMechanism []AccessMechanism `xml:"access_mechanism"`
+}
+
+type AccessMechanism struct {
+	XMLName  xml.Name `xml:"access_mechanism"`
+	Accessor string   `xml:"accessor,attr"`
+	Encoding Encoding `xml:"encoding"`
+}
+
+type Encoding struct {
+	XMLName xml.Name `xml:"encoding"`
+	Enc     []Enc    `xml:"enc"`
+}
+
+type Enc struct {
+	XMLName xml.Name `xml:"enc"`
+	V       string   `xml:"v,attr"`
+}
+
+type SystemReg struct {
+	RegName   string
+	EncBinary uint32
+}
+
+func check(e error) {
+	if e != nil {
+		log.Fatal(e)
+	}
+}
+
+func main() {
+	// Write system register encoding to the sysRegEnc.go file.
+	// This file should be put into $GOROOT/src/cmd/internal/obj/arm64/ directory.
+	filename := flag.String("o", "sysRegEnc.go", "the name of the automatically generated file")
+	xmlfolder := flag.String("i", "./files", "the folder where the data XML files are")
+	flag.Parse()
+
+	out, err := os.Create(*filename)
+	check(err)
+	defer out.Close()
+
+	files, err := ioutil.ReadDir(*xmlfolder)
+	check(err)
+
+	var systemregs []SystemReg
+	regNum := 0
+
+	for _, file := range files {
+		xmlFile, err := os.Open(filepath.Join(*xmlfolder, file.Name()))
+		check(err)
+		value, err := ioutil.ReadAll(xmlFile)
+		check(err)
+
+		var regpage RegisterPage
+		err = xml.Unmarshal(value, &regpage)
+		if err != nil {
+			log.Printf("%s: The data of this file does not fit into Register_page struct\n", file.Name())
+			xmlFile.Close()
+			continue
+		}
+
+		sysreg := regpage.Registers.Register
+		sysregName := sysreg.RegShortName
+		if strings.Contains(sysregName, "EL2") || strings.Contains(sysregName, "EL3") {
+			log.Printf("%s: we do not support EL2 and EL3 system registers at the moment!\n", file.Name())
+			xmlFile.Close()
+			continue
+		}
+		if strings.Contains(sysregName, "<op1>_<Cn>_<Cm>_<op2>") {
+			log.Printf("%s: The register %s is reserved\n", file.Name(), sysregName)
+			xmlFile.Close()
+			continue
+		}
+		if len(sysreg.AccessMechanisms.AccessMechanism) == 0 {
+			log.Printf("%s: The data of this file does not fit into AccessMechanisms struct\n", file.Name())
+			xmlFile.Close()
+			continue
+		}
+
+		m0 := sysreg.AccessMechanisms.AccessMechanism[0]
+		ins := m0.Accessor
+		if !(strings.Contains(ins, "MRS") || strings.Contains(ins, "MSR")) {
+			log.Printf("%s: \"%s\" is not a system register for MSR and MRS instructions.\n", file.Name(), sysregName)
+			xmlFile.Close()
+			continue
+		}
+
+		max := 0
+		var enc [5]uint64
+		if len(m0.Encoding.Enc) != 5 {
+			log.Printf("%s: The data of this file does not fit into S<op0>_<op1>_<Cn>_<Cm>_<op2> encoding\n", file.Name())
+			xmlFile.Close()
+			continue
+		}
+		// Special handling for system register name containing <n>.
+		if strings.Contains(sysregName, "<n>") {
+			max, err = strconv.Atoi(sysreg.RegVariables.RegVariable.Max)
+			check(err)
+			for n := 0; n <= max; n++ {
+				name := strings.Replace(sysregName, "<n>", strconv.Itoa(n), -1)
+				systemregs = append(systemregs, SystemReg{name, 0})
+				regNum++
+			}
+		} else {
+			systemregs = append(systemregs, SystemReg{sysregName, 0})
+			regNum++
+		}
+		for i := 0; i <= max; i++ {
+			index := regNum - 1 - max + i
+			for j := 0; j < len(m0.Encoding.Enc); j++ {
+				value := m0.Encoding.Enc[j].V
+				// value="0b010:n[3]"
+				// value="0b1:n[1:0]"
+				// value="ob10:n[4:3]"
+				if strings.Contains(value, "n") && strings.Contains(value, "b") {
+					v0 := strings.Split(value, "b")
+					v1 := strings.Split(v0[1], "n")
+					v2 := strings.Trim(v1[1], "[]")
+					bits, err := strconv.ParseUint(strings.Trim(v1[0], ":"), 2, 32)
+					check(err)
+					if strings.Contains(v1[1], ":") {
+						// v1[1]="[1:0]", v2="1:0"
+						// Get the index.
+						first, err := strconv.Atoi(strings.Split(v2, ":")[0])
+						check(err)
+						last, err := strconv.Atoi(strings.Split(v2, ":")[1])
+						check(err)
+						// Get the corresponding appended bits.
+						bitsAppend := (i >> uint(last) & (1<<uint(first-last+1) - 1))
+						// Join the bits to get the final bits.
+						finalBits := int(bits)<<uint(first-last+1) | bitsAppend
+						enc[j] = uint64(finalBits)
+					} else {
+						// v1[1]="[3]", v2="3"
+						// Get the corresponding appended bits.
+						first, err := strconv.Atoi(v2)
+						check(err)
+						bitsAppend := (i >> uint(first)) & 1
+						// Join the bits to get the final bits.
+						finalBits := int(bits)<<1 | bitsAppend
+						enc[j] = uint64(finalBits)
+					}
+				} else if strings.Contains(value, "n") && !strings.Contains(value, "b") {
+					// value="n[3:0]" | value="n[2:0]"
+					v0 := strings.Split(value, "n")
+					v1 := strings.Trim(v0[1], "[]")
+					v2 := strings.Split(v1, ":")
+					// Convert string format to integer.
+					first, err := strconv.Atoi(v2[0])
+					check(err)
+					last, err := strconv.Atoi(v2[1])
+					check(err)
+					finalBits := (i >> uint(last) & (1<<uint(first-last+1) - 1))
+					enc[j] = uint64(finalBits)
+				} else {
+					// value="0b110"
+					v := strings.Split(value, "b")
+					var err error = nil
+					enc[j], err = strconv.ParseUint(v[1], 2, 64)
+					check(err)
+				}
+			}
+			systemregs[index].EncBinary = uint32(enc[0]<<19 | enc[1]<<16 | enc[2]<<12 | enc[3]<<8 | enc[4]<<5)
+		}
+		// Close the xml file.
+		xmlFile.Close()
+	}
+	log.Printf("The total number of parsing registers is %d\n", regNum)
+	w := bufio.NewWriter(out)
+	fmt.Fprintf(w, "// Code generated by arm64gen -i %s -o %s. DO NOT EDIT.\n", *xmlfolder, *filename)
+	fmt.Fprintln(w, "\npackage arm64\n\nconst (\n\tSYSREG_BEGIN = REG_SPECIAL + iota")
+	for i := 0; i < regNum; i++ {
+		fmt.Fprintf(w, "\tREG_%s\n", systemregs[i].RegName)
+	}
+	fmt.Fprintln(w, "\tSYSREG_END\n)")
+	fmt.Fprintln(w, "\nvar SystemReg = []struct {\n\tName string\n\tReg int16\n\tEnc uint32\n}{")
+	for i := 0; i < regNum; i++ {
+		fmt.Fprintf(w, "\t{\"%s\", REG_%s, 0x%x},\n", systemregs[i].RegName, systemregs[i].RegName, systemregs[i].EncBinary)
+	}
+	fmt.Fprintln(w, "}")
+	fmt.Fprintln(w, "\nfunc SysRegEnc(r int16) (string, uint32) {")
+	fmt.Fprintln(w, "\t// The automatic generator guarantees that the order")
+	fmt.Fprintln(w, "\t// of Reg in SystemReg struct is consistent with the")
+	fmt.Fprintln(w, "\t// order of system register declarations")
+	fmt.Fprintln(w, "\tif r <= SYSREG_BEGIN || r >= SYSREG_END {\n\t\treturn \"\", 0\n\t}\n\tv := SystemReg[r-SYSREG_BEGIN-1]\n\treturn v.Name, v.Enc\n}")
+	w.Flush()
+}