Home | History | Annotate | Download | only in x86
      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