• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package x86_test
6
7import (
8	"bufio"
9	"bytes"
10	"fmt"
11	"internal/testenv"
12	"os"
13	"path/filepath"
14	"regexp"
15	"strconv"
16	"strings"
17	"testing"
18)
19
20const testdata = `
21MOVQ AX, AX -> MOVQ AX, AX
22
23LEAQ name(SB), AX -> MOVQ name@GOT(SB), AX
24LEAQ name+10(SB), AX -> MOVQ name@GOT(SB), AX; LEAQ 10(AX), AX
25MOVQ $name(SB), AX -> MOVQ name@GOT(SB), AX
26MOVQ $name+10(SB), AX -> MOVQ name@GOT(SB), AX; LEAQ 10(AX), AX
27
28MOVQ name(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ (R15), AX
29MOVQ name+10(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ 10(R15), AX
30
31CMPQ name(SB), $0 -> NOP; MOVQ name@GOT(SB), R15; CMPQ (R15), $0
32
33MOVQ $1, name(SB) -> NOP; MOVQ name@GOT(SB), R15; MOVQ $1, (R15)
34MOVQ $1, name+10(SB) -> NOP; MOVQ name@GOT(SB), R15; MOVQ $1, 10(R15)
35`
36
37type ParsedTestData struct {
38	input              string
39	marks              []int
40	marker_to_input    map[int][]string
41	marker_to_expected map[int][]string
42	marker_to_output   map[int][]string
43}
44
45const marker_start = 1234
46
47func parseTestData(t *testing.T) *ParsedTestData {
48	r := &ParsedTestData{}
49	scanner := bufio.NewScanner(strings.NewReader(testdata))
50	r.marker_to_input = make(map[int][]string)
51	r.marker_to_expected = make(map[int][]string)
52	marker := marker_start
53	input_insns := []string{}
54	for scanner.Scan() {
55		line := scanner.Text()
56		if len(strings.TrimSpace(line)) == 0 {
57			continue
58		}
59		parts := strings.Split(line, "->")
60		if len(parts) != 2 {
61			t.Fatalf("malformed line %v", line)
62		}
63		r.marks = append(r.marks, marker)
64		marker_insn := fmt.Sprintf("MOVQ $%d, AX", marker)
65		input_insns = append(input_insns, marker_insn)
66		for _, input_insn := range strings.Split(parts[0], ";") {
67			input_insns = append(input_insns, input_insn)
68			r.marker_to_input[marker] = append(r.marker_to_input[marker], normalize(input_insn))
69		}
70		for _, expected_insn := range strings.Split(parts[1], ";") {
71			r.marker_to_expected[marker] = append(r.marker_to_expected[marker], normalize(expected_insn))
72		}
73		marker++
74	}
75	r.input = "TEXT ·foo(SB),$0\n" + strings.Join(input_insns, "\n") + "\n"
76	return r
77}
78
79var spaces_re *regexp.Regexp = regexp.MustCompile(`\s+`)
80
81func normalize(s string) string {
82	return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ")
83}
84
85func asmOutput(t *testing.T, s string) []byte {
86	tmpdir, err := os.MkdirTemp("", "progedittest")
87	if err != nil {
88		t.Fatal(err)
89	}
90	defer os.RemoveAll(tmpdir)
91	tmpfile, err := os.Create(filepath.Join(tmpdir, "input.s"))
92	if err != nil {
93		t.Fatal(err)
94	}
95	defer tmpfile.Close()
96	_, err = tmpfile.WriteString(s)
97	if err != nil {
98		t.Fatal(err)
99	}
100	cmd := testenv.Command(t,
101		testenv.GoToolPath(t), "tool", "asm", "-S", "-dynlink",
102		"-o", filepath.Join(tmpdir, "output.6"), tmpfile.Name())
103
104	cmd.Env = append(os.Environ(),
105		"GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
106	asmout, err := cmd.CombinedOutput()
107	if err != nil {
108		t.Fatalf("error %s output %s", err, asmout)
109	}
110	return asmout
111}
112
113func parseOutput(t *testing.T, td *ParsedTestData, asmout []byte) {
114	scanner := bufio.NewScanner(bytes.NewReader(asmout))
115	marker := regexp.MustCompile(`MOVQ \$([0-9]+), AX`)
116	mark := -1
117	td.marker_to_output = make(map[int][]string)
118	for scanner.Scan() {
119		line := scanner.Text()
120		if line[0] != '\t' {
121			continue
122		}
123		parts := strings.SplitN(line, "\t", 3)
124		if len(parts) != 3 {
125			continue
126		}
127		n := normalize(parts[2])
128		mark_matches := marker.FindStringSubmatch(n)
129		if mark_matches != nil {
130			mark, _ = strconv.Atoi(mark_matches[1])
131			if _, ok := td.marker_to_input[mark]; !ok {
132				t.Fatalf("unexpected marker %d", mark)
133			}
134		} else if mark != -1 {
135			td.marker_to_output[mark] = append(td.marker_to_output[mark], n)
136		}
137	}
138}
139
140func TestDynlink(t *testing.T) {
141	testenv.MustHaveGoBuild(t)
142
143	if os.Getenv("GOHOSTARCH") != "" {
144		// TODO: make this work? It was failing due to the
145		// GOARCH= filtering above and skipping is easiest for
146		// now.
147		t.Skip("skipping when GOHOSTARCH is set")
148	}
149
150	testdata := parseTestData(t)
151	asmout := asmOutput(t, testdata.input)
152	parseOutput(t, testdata, asmout)
153	for _, m := range testdata.marks {
154		i := strings.Join(testdata.marker_to_input[m], "; ")
155		o := strings.Join(testdata.marker_to_output[m], "; ")
156		e := strings.Join(testdata.marker_to_expected[m], "; ")
157		if o != e {
158			if o == i {
159				t.Errorf("%s was unchanged; should have become %s", i, e)
160			} else {
161				t.Errorf("%s became %s; should have become %s", i, o, e)
162			}
163		} else if i != e {
164			t.Logf("%s correctly became %s", i, o)
165		}
166	}
167}
168