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