github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/cmd/internal/obj/x86/obj6_test.go (about) 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; LEAQ 10(AX), AX 24 MOVQ $name(SB), AX -> MOVQ name@GOT(SB), AX 25 MOVQ $name+10(SB), AX -> MOVQ name@GOT(SB), AX; LEAQ 10(AX), AX 26 27 MOVQ name(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ (R15), AX 28 MOVQ name+10(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ 10(R15), AX 29 30 CMPQ name(SB), $0 -> NOP; MOVQ name@GOT(SB), R15; CMPQ (R15), $0 31 32 MOVQ $1, name(SB) -> NOP; MOVQ name@GOT(SB), R15; MOVQ $1, (R15) 33 MOVQ $1, name+10(SB) -> NOP; 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 80 func normalize(s string) string { 81 return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ") 82 } 83 84 func asmOutput(t *testing.T, s string) []byte { 85 tmpdir, err := ioutil.TempDir("", "progedittest") 86 if err != nil { 87 t.Fatal(err) 88 } 89 defer os.RemoveAll(tmpdir) 90 tmpfile, err := os.Create(filepath.Join(tmpdir, "input.s")) 91 if err != nil { 92 t.Fatal(err) 93 } 94 defer tmpfile.Close() 95 _, err = tmpfile.WriteString(s) 96 if err != nil { 97 t.Fatal(err) 98 } 99 gofolder := filepath.Join(build.Default.GOROOT, "bin") 100 if gobin := os.Getenv("GOBIN"); len(gobin) != 0 { 101 gofolder = gobin 102 } 103 104 cmd := exec.Command( 105 filepath.Join(gofolder, "go"), "tool", "asm", "-S", "-dynlink", 106 "-o", filepath.Join(tmpdir, "output.6"), tmpfile.Name()) 107 108 var env []string 109 for _, v := range os.Environ() { 110 if !strings.HasPrefix(v, "GOARCH=") { 111 env = append(env, v) 112 } 113 } 114 cmd.Env = append(env, "GOARCH=amd64") 115 asmout, err := cmd.CombinedOutput() 116 if err != nil { 117 t.Fatalf("error %s output %s", err, asmout) 118 } 119 return asmout 120 } 121 122 func parseOutput(t *testing.T, td *ParsedTestData, asmout []byte) { 123 scanner := bufio.NewScanner(bytes.NewReader(asmout)) 124 marker := regexp.MustCompile("MOVQ \\$([0-9]+), AX") 125 mark := -1 126 td.marker_to_output = make(map[int][]string) 127 for scanner.Scan() { 128 line := scanner.Text() 129 if line[0] != '\t' { 130 continue 131 } 132 parts := strings.SplitN(line, "\t", 3) 133 if len(parts) != 3 { 134 continue 135 } 136 n := normalize(parts[2]) 137 mark_matches := marker.FindStringSubmatch(n) 138 if mark_matches != nil { 139 mark, _ = strconv.Atoi(mark_matches[1]) 140 if _, ok := td.marker_to_input[mark]; !ok { 141 t.Fatalf("unexpected marker %d", mark) 142 } 143 } else if mark != -1 { 144 td.marker_to_output[mark] = append(td.marker_to_output[mark], n) 145 } 146 } 147 } 148 149 func TestDynlink(t *testing.T) { 150 testenv.MustHaveGoBuild(t) 151 152 if os.Getenv("GOHOSTARCH") != "" { 153 // TODO: make this work? It was failing due to the 154 // GOARCH= filtering above and skipping is easiest for 155 // now. 156 t.Skip("skipping when GOHOSTARCH is set") 157 } 158 159 testdata := parseTestData(t) 160 asmout := asmOutput(t, testdata.input) 161 parseOutput(t, testdata, asmout) 162 for _, m := range testdata.marks { 163 i := strings.Join(testdata.marker_to_input[m], "; ") 164 o := strings.Join(testdata.marker_to_output[m], "; ") 165 e := strings.Join(testdata.marker_to_expected[m], "; ") 166 if o != e { 167 if o == i { 168 t.Errorf("%s was unchanged; should have become %s", i, e) 169 } else { 170 t.Errorf("%s became %s; should have become %s", i, o, e) 171 } 172 } else if i != e { 173 t.Logf("%s correctly became %s", i, o) 174 } 175 } 176 }