"io/ioutil"
"os"
"path/filepath"
+ "sort"
"strconv"
"strings"
"testing"
// 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)
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:
}
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.
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")
}