aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/golang.org/x/arch/arm64/arm64asm/ext_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/vendor/golang.org/x/arch/arm64/arm64asm/ext_test.go')
-rw-r--r--src/cmd/vendor/golang.org/x/arch/arm64/arm64asm/ext_test.go604
1 files changed, 0 insertions, 604 deletions
diff --git a/src/cmd/vendor/golang.org/x/arch/arm64/arm64asm/ext_test.go b/src/cmd/vendor/golang.org/x/arch/arm64/arm64asm/ext_test.go
deleted file mode 100644
index f432203e15..0000000000
--- a/src/cmd/vendor/golang.org/x/arch/arm64/arm64asm/ext_test.go
+++ /dev/null
@@ -1,604 +0,0 @@
-// Copyright 2017 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.
-
-// Support for testing against external disassembler program.
-// Copied and simplified from ../../arm/armasm/ext_test.go.
-
-package arm64asm
-
-import (
- "bufio"
- "bytes"
- "encoding/hex"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "math/rand"
- "os"
- "os/exec"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "testing"
- "time"
-)
-
-var (
- dumpTest = flag.Bool("dump", false, "dump all encodings")
- mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
- longTest = flag.Bool("long", false, "long test")
- keep = flag.Bool("keep", false, "keep object files around")
- debug = false
-)
-
-// An ExtInst represents a single decoded instruction parsed
-// from an external disassembler's output.
-type ExtInst struct {
- addr uint64
- enc [4]byte
- nenc int
- text string
-}
-
-func (r ExtInst) String() string {
- return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
-}
-
-// An ExtDis is a connection between an external disassembler and a test.
-type ExtDis struct {
- Arch Mode
- Dec chan ExtInst
- File *os.File
- Size int
- KeepFile bool
- Cmd *exec.Cmd
-}
-
-// InstJson describes instruction fields value got from ARMv8-A Reference Manual
-type InstJson struct {
- Name string
- Bits string
- Arch string
- Syntax string
- Code string
- Alias string
- Enc uint32
-}
-
-// A Mode is an instruction execution mode.
-type Mode int
-
-const (
- _ Mode = iota
- ModeARM64
-)
-
-// Run runs the given command - the external disassembler - and returns
-// a buffered reader of its standard output.
-func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
- if *keep {
- log.Printf("%s\n", strings.Join(cmd, " "))
- }
- ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
- out, err := ext.Cmd.StdoutPipe()
- if err != nil {
- return nil, fmt.Errorf("stdoutpipe: %v", err)
- }
- if err := ext.Cmd.Start(); err != nil {
- return nil, fmt.Errorf("exec: %v", err)
- }
-
- b := bufio.NewReaderSize(out, 1<<20)
- return b, nil
-}
-
-// Wait waits for the command started with Run to exit.
-func (ext *ExtDis) Wait() error {
- return ext.Cmd.Wait()
-}
-
-// testExtDis tests a set of byte sequences against an external disassembler.
-// The disassembler is expected to produce the given syntax and run
-// in the given architecture mode (16, 32, or 64-bit).
-// The extdis function must start the external disassembler
-// and then parse its output, sending the parsed instructions on ext.Dec.
-// The generate function calls its argument f once for each byte sequence
-// to be tested. The generate function itself will be called twice, and it must
-// make the same sequence of calls to f each time.
-// When a disassembly does not match the internal decoding,
-// allowedMismatch determines whether this mismatch should be
-// allowed, or else considered an error.
-func testExtDis(
- t *testing.T,
- syntax string,
- arch Mode,
- extdis func(ext *ExtDis) error,
- generate func(f func([]byte)),
- allowedMismatch func(text string, inst *Inst, dec ExtInst) bool,
-) {
- start := time.Now()
- ext := &ExtDis{
- Dec: make(chan ExtInst),
- Arch: arch,
- }
- errc := make(chan error)
-
- // First pass: write instructions to input file for external disassembler.
- file, f, size, err := writeInst(generate)
- if err != nil {
- t.Fatal(err)
- }
- ext.Size = size
- ext.File = f
- defer func() {
- f.Close()
- if !*keep {
- os.Remove(file)
- }
- }()
-
- // Second pass: compare disassembly against our decodings.
- var (
- totalTests = 0
- totalSkips = 0
- totalErrors = 0
-
- errors = make([]string, 0, 100) // Sampled errors, at most cap
- )
- go func() {
- errc <- extdis(ext)
- }()
-
- generate(func(enc []byte) {
- dec, ok := <-ext.Dec
- if !ok {
- t.Errorf("decoding stream ended early")
- return
- }
- inst, text := disasm(syntax, pad(enc))
-
- totalTests++
- if *dumpTest {
- fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
- }
- if text != dec.text && !strings.Contains(dec.text, "unknown") && syntax == "gnu" {
- suffix := ""
- if allowedMismatch(text, &inst, dec) {
- totalSkips++
- if !*mismatch {
- return
- }
- suffix += " (allowed mismatch)"
- }
- totalErrors++
- cmp := fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s\n", enc, text, len(enc), dec.text, dec.nenc, suffix)
-
- if len(errors) >= cap(errors) {
- j := rand.Intn(totalErrors)
- if j >= cap(errors) {
- return
- }
- errors = append(errors[:j], errors[j+1:]...)
- }
- errors = append(errors, cmp)
- }
- })
-
- if *mismatch {
- totalErrors -= totalSkips
- }
-
- for _, b := range errors {
- t.Log(b)
- }
-
- if totalErrors > 0 {
- t.Fail()
- }
- t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
- t.Logf("decoder coverage: %.1f%%;\n", decodeCoverage())
- if err := <-errc; err != nil {
- t.Fatalf("external disassembler: %v", err)
- }
-
-}
-
-// Start address of text.
-const start = 0x8000
-
-// writeInst writes the generated byte sequences to a new file
-// starting at offset start. That file is intended to be the input to
-// the external disassembler.
-func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
- f, err = ioutil.TempFile("", "arm64asm")
- if err != nil {
- return
- }
-
- file = f.Name()
-
- f.Seek(start, io.SeekStart)
- w := bufio.NewWriter(f)
- defer w.Flush()
- size = 0
- generate(func(x []byte) {
- if debug {
- fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
- }
- w.Write(x)
- w.Write(zeros[len(x):])
- size += len(zeros)
- })
- return file, f, size, nil
-}
-
-var zeros = []byte{0, 0, 0, 0}
-
-// pad pads the code sequence with pops.
-func pad(enc []byte) []byte {
- if len(enc) < 4 {
- enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
- }
- return enc
-}
-
-// disasm returns the decoded instruction and text
-// for the given source bytes, using the given syntax and mode.
-func disasm(syntax string, src []byte) (inst Inst, text string) {
- var err error
- inst, err = Decode(src)
- if err != nil {
- text = "error: " + err.Error()
- return
- }
- text = inst.String()
- switch syntax {
- case "gnu":
- text = GNUSyntax(inst)
- case "plan9": // [sic]
- text = GoSyntax(inst, 0, nil, nil)
- default:
- text = "error: unknown syntax " + syntax
- }
- return
-}
-
-// decodecoverage returns a floating point number denoting the
-// decoder coverage.
-func decodeCoverage() float64 {
- n := 0
- for _, t := range decoderCover {
- if t {
- n++
- }
- }
- return 100 * float64(1+n) / float64(1+len(decoderCover))
-}
-
-// Helpers for writing disassembler output parsers.
-
-// hasPrefix reports whether any of the space-separated words in the text s
-// begins with any of the given prefixes.
-func hasPrefix(s string, prefixes ...string) bool {
- for _, prefix := range prefixes {
- for cur_s := s; cur_s != ""; {
- if strings.HasPrefix(cur_s, prefix) {
- return true
- }
- i := strings.Index(cur_s, " ")
- if i < 0 {
- break
- }
- cur_s = cur_s[i+1:]
- }
- }
- return false
-}
-
-// isHex reports whether b is a hexadecimal character (0-9a-fA-F).
-func isHex(b byte) bool {
- return ('0' <= b && b <= '9') || ('a' <= b && b <= 'f') || ('A' <= b && b <= 'F')
-}
-
-// parseHex parses the hexadecimal byte dump in hex,
-// appending the parsed bytes to raw and returning the updated slice.
-// The returned bool reports whether any invalid hex was found.
-// Spaces and tabs between bytes are okay but any other non-hex is not.
-func parseHex(hex []byte, raw []byte) ([]byte, bool) {
- hex = bytes.TrimSpace(hex)
- for j := 0; j < len(hex); {
- for hex[j] == ' ' || hex[j] == '\t' {
- j++
- }
- if j >= len(hex) {
- break
- }
- if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
- return nil, false
- }
- raw = append(raw, unhex(hex[j])<<4|unhex(hex[j+1]))
- j += 2
- }
- return raw, true
-}
-
-func unhex(b byte) byte {
- if '0' <= b && b <= '9' {
- return b - '0'
- } else if 'A' <= b && b <= 'F' {
- return b - 'A' + 10
- } else if 'a' <= b && b <= 'f' {
- return b - 'a' + 10
- }
- return 0
-}
-
-// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
-func index(s []byte, t string) int {
- i := 0
- for {
- j := bytes.IndexByte(s[i:], t[0])
- if j < 0 {
- return -1
- }
- i = i + j
- if i+len(t) > len(s) {
- return -1
- }
- for k := 1; k < len(t); k++ {
- if s[i+k] != t[k] {
- goto nomatch
- }
- }
- return i
- nomatch:
- i++
- }
-}
-
-// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
-// If s must be rewritten, it is rewritten in place.
-func fixSpace(s []byte) []byte {
- s = bytes.TrimSpace(s)
- for i := 0; i < len(s); i++ {
- if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
- goto Fix
- }
- }
- return s
-
-Fix:
- b := s
- w := 0
- for i := 0; i < len(s); i++ {
- c := s[i]
- if c == '\t' || c == '\n' {
- c = ' '
- }
- if c == ' ' && w > 0 && b[w-1] == ' ' {
- continue
- }
- b[w] = c
- w++
- }
- if w > 0 && b[w-1] == ' ' {
- w--
- }
- return b[:w]
-}
-
-// Fllowing regular expressions matches instructions using relative addressing mode.
-// pcrel matches B instructions and BL instructions.
-// pcrelr matches instrucions which consisted of register arguments and label arguments.
-// pcrelim matches instructions which consisted of register arguments, immediate
-// arguments and lable arguments.
-// pcrelrzr and prcelimzr matches instructions when register arguments is zero register.
-// pcrelprfm matches PRFM instructions when arguments consisted of register and lable.
-// pcrelprfmim matches PRFM instructions when arguments consisted of immediate and lable.
-var (
- pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:\.)?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|nv)?) 0x([0-9a-f]+)$`)
- pcrelr = regexp.MustCompile(`^((?:.*)?(?:ldr|adrp|adr|cbnz|cbz|ldrsw) (?:x|w|s|d|q)(?:[0-9]+,)) 0x([0-9a-f]+)$`)
- pcrelrzr = regexp.MustCompile(`^((?:.*)?(?:ldr|adrp|adr|cbnz|cbz|ldrsw) (?:x|w)zr,) 0x([0-9a-f]+)$`)
- pcrelim = regexp.MustCompile(`^((?:.*)?(?:tbnz|tbz) (?:x|w)(?:[0-9]+,) (?:#[0-9a-f]+,)) 0x([0-9a-f]+)$`)
- pcrelimzr = regexp.MustCompile(`^((?:.*)?(?:tbnz|tbz) (?:x|w)zr, (?:#[0-9a-f]+,)) 0x([0-9a-f]+)$`)
- pcrelprfm = regexp.MustCompile(`^((?:.*)?(?:prfm) (?:[0-9a-z]+,)) 0x([0-9a-f]+)$`)
- pcrelprfmim = regexp.MustCompile(`^((?:.*)?(?:prfm) (?:#0x[0-9a-f]+,)) 0x([0-9a-f]+)$`)
-)
-
-// Round is the multiple of the number of instructions that read from Json file.
-// Round used as seed value for pseudo-random number generator provides the same sequence
-// in the same round run for the external disassembler and decoder.
-var Round int
-
-// condmark is used to mark conditional instructions when need to generate and test
-// conditional instructions.
-var condmark bool = false
-
-// Generate instruction binary according to Json file
-// Encode variable field of instruction with random value
-func doFuzzy(inst *InstJson, Ninst int) {
- var testdata uint32
- var NonDigRE = regexp.MustCompile(`[\D]`)
- rand.Seed(int64(Round + Ninst))
- off := 0
- DigBit := ""
- if condmark == true && !strings.Contains(inst.Bits, "cond") {
- inst.Enc = 0xffffffff
- } else {
- for _, f := range strings.Split(inst.Bits, "|") {
- if i := strings.Index(f, ":"); i >= 0 {
- // consider f contains "01:2" and "Rm:5"
- DigBit = f[:i]
- m := NonDigRE.FindStringSubmatch(DigBit)
- if m == nil {
- DigBit = strings.TrimSpace(DigBit)
- s := strings.Split(DigBit, "")
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case "1", "(1)":
- testdata |= 1 << uint(31-off)
- }
- off++
- }
- } else {
- // DigBit is "Rn" or "imm3"
- n, _ := strconv.Atoi(f[i+1:])
- if DigBit == "cond" && condmark == true {
- r := uint8(Round)
- for i := n - 1; i >= 0; i-- {
- switch (r >> uint(i)) & 1 {
- case 1:
- testdata |= 1 << uint(31-off)
- }
- off++
- }
- } else {
- for i := 0; i < n; i++ {
- r := rand.Intn(2)
- switch r {
- case 1:
- testdata |= 1 << uint(31-off)
- }
- off++
- }
- }
- }
- continue
- }
- for _, bit := range strings.Fields(f) {
- switch bit {
- case "0", "(0)":
- off++
- continue
- case "1", "(1)":
- testdata |= 1 << uint(31-off)
- default:
- r := rand.Intn(2)
- switch r {
- case 1:
- testdata |= 1 << uint(31-off)
- }
- }
- off++
- }
- }
- if off != 32 {
- log.Printf("incorrect bit count for %s %s: have %d", inst.Name, inst.Bits, off)
- }
- inst.Enc = testdata
- }
-}
-
-// Generators.
-//
-// The test cases are described as functions that invoke a callback repeatedly,
-// with a new input sequence each time. These helpers make writing those
-// a little easier.
-
-// JSONCases generates ARM64 instructions according to inst.json.
-func JSONCases(t *testing.T) func(func([]byte)) {
- return func(try func([]byte)) {
- data, err := ioutil.ReadFile("inst.json")
- if err != nil {
- t.Fatal(err)
- }
- var insts []InstJson
- var instsN []InstJson
- // Change N value to get more cases only when condmark=false.
- N := 100
- if condmark == true {
- N = 16
- }
- if err := json.Unmarshal(data, &insts); err != nil {
- t.Fatal(err)
- }
- // Append instructions to get more test cases.
- for i := 0; i < N; {
- for _, inst := range insts {
- instsN = append(instsN, inst)
- }
- i++
- }
- Round = 0
- for i := range instsN {
- if i%len(insts) == 0 {
- Round++
- }
- doFuzzy(&instsN[i], i)
- }
- for _, inst := range instsN {
- if condmark == true && inst.Enc == 0xffffffff {
- continue
- }
- enc := inst.Enc
- try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
- }
- }
-}
-
-// condCases generates conditional instructions.
-func condCases(t *testing.T) func(func([]byte)) {
- return func(try func([]byte)) {
- condmark = true
- JSONCases(t)(func(enc []byte) {
- try(enc)
- })
- }
-}
-
-// hexCases generates the cases written in hexadecimal in the encoded string.
-// Spaces in 'encoded' separate entire test cases, not individual bytes.
-func hexCases(t *testing.T, encoded string) func(func([]byte)) {
- return func(try func([]byte)) {
- for _, x := range strings.Fields(encoded) {
- src, err := hex.DecodeString(x)
- if err != nil {
- t.Errorf("parsing %q: %v", x, err)
- }
- try(src)
- }
- }
-}
-
-// testdataCases generates the test cases recorded in testdata/cases.txt.
-// It only uses the inputs; it ignores the answers recorded in that file.
-func testdataCases(t *testing.T, syntax string) func(func([]byte)) {
- var codes [][]byte
- input := filepath.Join("testdata", syntax+"cases.txt")
- data, err := ioutil.ReadFile(input)
- if err != nil {
- t.Fatal(err)
- }
- for _, line := range strings.Split(string(data), "\n") {
- line = strings.TrimSpace(line)
- if line == "" || strings.HasPrefix(line, "#") {
- continue
- }
- f := strings.Fields(line)[0]
- i := strings.Index(f, "|")
- if i < 0 {
- t.Errorf("parsing %q: missing | separator", f)
- continue
- }
- if i%2 != 0 {
- t.Errorf("parsing %q: misaligned | separator", f)
- }
- code, err := hex.DecodeString(f[:i] + f[i+1:])
- if err != nil {
- t.Errorf("parsing %q: %v", f, err)
- continue
- }
- codes = append(codes, code)
- }
-
- return func(try func([]byte)) {
- for _, code := range codes {
- try(code)
- }
- }
-}