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