From: Russ Cox Date: Fri, 22 Jan 2016 01:48:21 +0000 (-0500) Subject: cmd/asm: add test for verification of instruction encodings X-Git-Tag: go1.6rc1~61 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2eb8d94dbd822fab695f2781c8b6d14345be1d07;p=gostls13.git cmd/asm: add test for verification of instruction encodings Not much testing yet, but the test now exists. Another step toward #13822. Change-Id: Idb2b06bf53a6113c83008150b4c0b631bb195279 Reviewed-on: https://go-review.googlesource.com/18844 Reviewed-by: Rob Pike Run-TryBot: Russ Cox --- diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index bba82b5fca..620bb97417 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strconv" "strings" "testing" @@ -22,9 +23,9 @@ import ( // Output is generated by, in effect, turning on -S and comparing the // result against a golden file. -func testEndToEnd(t *testing.T, goarch string) { +func testEndToEnd(t *testing.T, goarch, file string) { lex.InitHist() - input := filepath.Join("testdata", goarch+".s") + input := filepath.Join("testdata", file+".s") architecture, ctxt := setArch(goarch) lexer := lex.NewLexer(input, ctxt) parser := NewParser(ctxt, architecture, lexer) @@ -33,23 +34,31 @@ func testEndToEnd(t *testing.T, goarch string) { testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. ctxt.Bso = obj.Binitw(os.Stdout) defer ctxt.Bso.Flush() - ctxt.Diag = t.Errorf + failed := false + ctxt.Diag = func(format string, args ...interface{}) { + failed = true + t.Errorf(format, args...) + } obj.Binitw(ioutil.Discard) pList.Firstpc, ok = parser.Parse() - if !ok || t.Failed() { - t.Fatalf("asm: %s assembly failed", goarch) + if !ok || failed { + t.Errorf("asm: %s assembly failed", goarch) + return } output := strings.Split(testOut.String(), "\n") // Reconstruct expected output by independently "parsing" the input. data, err := ioutil.ReadFile(input) if err != nil { - t.Fatal(err) + t.Error(err) + return } lineno := 0 seq := 0 + hexByLine := map[string]string{} + lines := strings.SplitAfter(string(data), "\n") Diff: - for _, line := range strings.SplitAfter(string(data), "\n") { + for _, line := range lines { lineno++ // The general form of a test input line is: @@ -62,14 +71,31 @@ Diff: } seq++ + var hexes string switch len(parts) { default: t.Errorf("%s:%d: unable to understand comments: %s", input, lineno, line) case 1: // no comment case 2: - // one comment, printed form + // might be printed form or hex + note := strings.TrimSpace(parts[1]) + if isHexes(note) { + hexes = note + } else { + printed = note + } + case 3: + // printed form, then hex printed = strings.TrimSpace(parts[1]) + hexes = strings.TrimSpace(parts[2]) + if !isHexes(hexes) { + t.Errorf("%s:%d: malformed hex instruction encoding: %s", input, lineno, line) + } + } + + if hexes != "" { + hexByLine[fmt.Sprintf("%s:%d", input, lineno)] = hexes } // Canonicalize spacing in printed form. @@ -142,28 +168,114 @@ Diff: t.Errorf("unexpected output: %q", output[0]) output = output[1:] } + + // Checked printing. + // Now check machine code layout. + + top := pList.Firstpc + var text *obj.LSym + ok = true + ctxt.Diag = func(format string, args ...interface{}) { + t.Errorf(format, args...) + ok = false + } + obj.Flushplist(ctxt) + + for p := top; p != nil; p = p.Link { + if p.As == obj.ATEXT { + text = p.From.Sym + } + hexes := hexByLine[p.Line()] + if hexes == "" { + continue + } + delete(hexByLine, p.Line()) + if text == nil { + t.Errorf("%s: instruction outside TEXT", p) + } + size := int64(len(text.P)) - p.Pc + if p.Link != nil { + size = p.Link.Pc - p.Pc + } else if p.Isize != 0 { + size = int64(p.Isize) + } + var code []byte + if p.Pc < int64(len(text.P)) { + code = text.P[p.Pc:] + if size < int64(len(code)) { + code = code[:size] + } + } + codeHex := fmt.Sprintf("%x", code) + if codeHex == "" { + codeHex = "empty" + } + ok := false + for _, hex := range strings.Split(hexes, " or ") { + if codeHex == hex { + ok = true + break + } + } + if !ok { + t.Errorf("%s: have encoding %s, want %s", p, codeHex, hexes) + } + } + + if len(hexByLine) > 0 { + var missing []string + for key := range hexByLine { + missing = append(missing, key) + } + sort.Strings(missing) + for _, line := range missing { + t.Errorf("%s: did not find instruction encoding", line) + } + } + } -func TestPPC64EndToEnd(t *testing.T) { - testEndToEnd(t, "ppc64") +func isHexes(s string) bool { + if s == "" { + return false + } + if s == "empty" { + return true + } + for _, f := range strings.Split(s, " or ") { + if f == "" || len(f)%2 != 0 || strings.TrimLeft(f, "0123456789abcdef") != "" { + return false + } + } + return true +} + +func Test386EndToEnd(t *testing.T) { + testEndToEnd(t, "386", "386") } func TestARMEndToEnd(t *testing.T) { - testEndToEnd(t, "arm") + defer os.Setenv("GOARM", os.Getenv("GOARM")) + + for _, goarm := range []string{"5", "6", "7"} { + os.Setenv("GOARM", goarm) + t.Logf("GOARM=%v", os.Getenv("GOARM")) + testEndToEnd(t, "arm", "arm") + } } func TestARM64EndToEnd(t *testing.T) { - testEndToEnd(t, "arm64") + testEndToEnd(t, "arm64", "arm64") } func TestAMD64EndToEnd(t *testing.T) { - testEndToEnd(t, "amd64") + testEndToEnd(t, "amd64", "amd64") } -func Test386EndToEnd(t *testing.T) { - testEndToEnd(t, "386") +func TestMIPS64EndToEnd(t *testing.T) { + testEndToEnd(t, "mips64", "mips64") } -func TestMIPS64EndToEnd(t *testing.T) { - testEndToEnd(t, "mips64") +func TestPPC64EndToEnd(t *testing.T) { + testEndToEnd(t, "ppc64", "ppc64") } diff --git a/src/cmd/asm/internal/asm/testdata/amd64.s b/src/cmd/asm/internal/asm/testdata/amd64.s index 35af32a5cd..5512df0034 100644 --- a/src/cmd/asm/internal/asm/testdata/amd64.s +++ b/src/cmd/asm/internal/asm/testdata/amd64.s @@ -122,4 +122,4 @@ loop: LOOP loop // LOOP // LTYPE0 nonnon { outcode($1, &$2); } - RET + RET // c3 diff --git a/src/cmd/asm/internal/asm/testdata/arm.s b/src/cmd/asm/internal/asm/testdata/arm.s index 0102cd8414..8062750250 100644 --- a/src/cmd/asm/internal/asm/testdata/arm.s +++ b/src/cmd/asm/internal/asm/testdata/arm.s @@ -189,7 +189,7 @@ TEXT foo(SB), 7, $0 // outcode($1, $2, &$3, 0, &$5); // } ADDD.S F1, F2 - MOVF.S $0.5, F2 // MOVF.S $(0.5), F2 + MOVF $0.5, F2 // MOVF $(0.5), F2 // LTYPEK cond frcon ',' LFREG ',' freg // { diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go index f78b08445f..d75a16354c 100644 --- a/src/cmd/internal/obj/arm/asm5.go +++ b/src/cmd/internal/obj/arm/asm5.go @@ -2737,7 +2737,7 @@ func olhrr(ctxt *obj.Link, i int, b int, r int, sc int) uint32 { func ofsr(ctxt *obj.Link, a int, r int, v int32, b int, sc int, p *obj.Prog) uint32 { if sc&C_SBIT != 0 { - ctxt.Diag(".nil on FLDR/FSTR instruction") + ctxt.Diag(".nil on FLDR/FSTR instruction: %v", p) } o := ((uint32(sc) & C_SCOND) ^ C_SCOND_XOR) << 28 if sc&C_PBIT == 0 {