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