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 (
"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")
}
......@@ -122,4 +122,4 @@ loop:
LOOP loop // LOOP
// LTYPE0 nonnon { outcode($1, &$2); }
RET
RET // c3
......@@ -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
// {
......
......@@ -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 {
......
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