github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/internal/obj/x86/obj6_test.go (about) 1 package x86_test 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "github.com/gagliardetto/golang-go/not-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 // TODO: make this work? It was failing due to the 143 // GOARCH= filtering above and skipping is easiest for 144 // now. 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 }