Commit 2eb8d94d authored by Russ Cox's avatar Russ Cox

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/18844Reviewed-by: default avatarRob Pike <r@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
parent d3ff40fb
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
...@@ -22,9 +23,9 @@ import ( ...@@ -22,9 +23,9 @@ import (
// Output is generated by, in effect, turning on -S and comparing the // Output is generated by, in effect, turning on -S and comparing the
// result against a golden file. // result against a golden file.
func testEndToEnd(t *testing.T, goarch string) { func testEndToEnd(t *testing.T, goarch, file string) {
lex.InitHist() lex.InitHist()
input := filepath.Join("testdata", goarch+".s") input := filepath.Join("testdata", file+".s")
architecture, ctxt := setArch(goarch) architecture, ctxt := setArch(goarch)
lexer := lex.NewLexer(input, ctxt) lexer := lex.NewLexer(input, ctxt)
parser := NewParser(ctxt, architecture, lexer) parser := NewParser(ctxt, architecture, lexer)
...@@ -33,23 +34,31 @@ func testEndToEnd(t *testing.T, goarch string) { ...@@ -33,23 +34,31 @@ func testEndToEnd(t *testing.T, goarch string) {
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
ctxt.Bso = obj.Binitw(os.Stdout) ctxt.Bso = obj.Binitw(os.Stdout)
defer ctxt.Bso.Flush() 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) obj.Binitw(ioutil.Discard)
pList.Firstpc, ok = parser.Parse() pList.Firstpc, ok = parser.Parse()
if !ok || t.Failed() { if !ok || failed {
t.Fatalf("asm: %s assembly failed", goarch) t.Errorf("asm: %s assembly failed", goarch)
return
} }
output := strings.Split(testOut.String(), "\n") output := strings.Split(testOut.String(), "\n")
// Reconstruct expected output by independently "parsing" the input. // Reconstruct expected output by independently "parsing" the input.
data, err := ioutil.ReadFile(input) data, err := ioutil.ReadFile(input)
if err != nil { if err != nil {
t.Fatal(err) t.Error(err)
return
} }
lineno := 0 lineno := 0
seq := 0 seq := 0
hexByLine := map[string]string{}
lines := strings.SplitAfter(string(data), "\n")
Diff: Diff:
for _, line := range strings.SplitAfter(string(data), "\n") { for _, line := range lines {
lineno++ lineno++
// The general form of a test input line is: // The general form of a test input line is:
...@@ -62,14 +71,31 @@ Diff: ...@@ -62,14 +71,31 @@ Diff:
} }
seq++ seq++
var hexes string
switch len(parts) { switch len(parts) {
default: default:
t.Errorf("%s:%d: unable to understand comments: %s", input, lineno, line) t.Errorf("%s:%d: unable to understand comments: %s", input, lineno, line)
case 1: case 1:
// no comment // no comment
case 2: 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]) 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. // Canonicalize spacing in printed form.
...@@ -142,28 +168,114 @@ Diff: ...@@ -142,28 +168,114 @@ Diff:
t.Errorf("unexpected output: %q", output[0]) t.Errorf("unexpected output: %q", output[0])
output = output[1:] 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) { func isHexes(s string) bool {
testEndToEnd(t, "ppc64") 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) { 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) { func TestARM64EndToEnd(t *testing.T) {
testEndToEnd(t, "arm64") testEndToEnd(t, "arm64", "arm64")
} }
func TestAMD64EndToEnd(t *testing.T) { func TestAMD64EndToEnd(t *testing.T) {
testEndToEnd(t, "amd64") testEndToEnd(t, "amd64", "amd64")
} }
func Test386EndToEnd(t *testing.T) { func TestMIPS64EndToEnd(t *testing.T) {
testEndToEnd(t, "386") testEndToEnd(t, "mips64", "mips64")
} }
func TestMIPS64EndToEnd(t *testing.T) { func TestPPC64EndToEnd(t *testing.T) {
testEndToEnd(t, "mips64") testEndToEnd(t, "ppc64", "ppc64")
} }
...@@ -122,4 +122,4 @@ loop: ...@@ -122,4 +122,4 @@ loop:
LOOP loop // LOOP LOOP loop // LOOP
// LTYPE0 nonnon { outcode($1, &$2); } // LTYPE0 nonnon { outcode($1, &$2); }
RET RET // c3
...@@ -189,7 +189,7 @@ TEXT foo(SB), 7, $0 ...@@ -189,7 +189,7 @@ TEXT foo(SB), 7, $0
// outcode($1, $2, &$3, 0, &$5); // outcode($1, $2, &$3, 0, &$5);
// } // }
ADDD.S F1, F2 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 // LTYPEK cond frcon ',' LFREG ',' freg
// { // {
......
...@@ -2737,7 +2737,7 @@ func olhrr(ctxt *obj.Link, i int, b int, r int, sc int) uint32 { ...@@ -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 { func ofsr(ctxt *obj.Link, a int, r int, v int32, b int, sc int, p *obj.Prog) uint32 {
if sc&C_SBIT != 0 { 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 o := ((uint32(sc) & C_SCOND) ^ C_SCOND_XOR) << 28
if sc&C_PBIT == 0 { if sc&C_PBIT == 0 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment