github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/gc/asm_test.go (about)

     1  // Copyright 2016 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 gc
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"regexp"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  )
    20  
    21  // TestAssembly checks to make sure the assembly generated for
    22  // functions contains certain expected instructions.
    23  // Note: this test will fail if -ssa=0.
    24  func TestAssembly(t *testing.T) {
    25  	testenv.MustHaveGoBuild(t)
    26  	if runtime.GOOS == "windows" {
    27  		// TODO: remove if we can get "go tool compile -S" to work on windows.
    28  		t.Skipf("skipping test: recursive windows compile not working")
    29  	}
    30  	dir, err := ioutil.TempDir("", "TestAssembly")
    31  	if err != nil {
    32  		t.Fatalf("could not create directory: %v", err)
    33  	}
    34  	defer os.RemoveAll(dir)
    35  
    36  	for _, test := range asmTests {
    37  		asm := compileToAsm(dir, test.arch, fmt.Sprintf(template, test.function))
    38  		// Get rid of code for "".init. Also gets rid of type algorithms & other junk.
    39  		if i := strings.Index(asm, "\n\"\".init "); i >= 0 {
    40  			asm = asm[:i+1]
    41  		}
    42  		for _, r := range test.regexps {
    43  			if b, err := regexp.MatchString(r, asm); !b || err != nil {
    44  				t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, test.function, asm)
    45  			}
    46  		}
    47  	}
    48  }
    49  
    50  // compile compiles the package pkg for architecture arch and
    51  // returns the generated assembly.  dir is a scratch directory.
    52  func compileToAsm(dir, arch, pkg string) string {
    53  	// Create source.
    54  	src := filepath.Join(dir, "test.go")
    55  	f, err := os.Create(src)
    56  	if err != nil {
    57  		panic(err)
    58  	}
    59  	f.Write([]byte(pkg))
    60  	f.Close()
    61  
    62  	var stdout, stderr bytes.Buffer
    63  	cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
    64  	cmd.Env = mergeEnvLists([]string{"GOARCH=" + arch}, os.Environ())
    65  	cmd.Stdout = &stdout
    66  	cmd.Stderr = &stderr
    67  	if err := cmd.Run(); err != nil {
    68  		panic(err)
    69  	}
    70  	if s := stderr.String(); s != "" {
    71  		panic(fmt.Errorf("Stderr = %s\nWant empty", s))
    72  	}
    73  	return stdout.String()
    74  }
    75  
    76  // template to convert a function to a full file
    77  const template = `
    78  package main
    79  %s
    80  `
    81  
    82  type asmTest struct {
    83  	// architecture to compile to
    84  	arch string
    85  	// function to compile
    86  	function string
    87  	// regexps that must match the generated assembly
    88  	regexps []string
    89  }
    90  
    91  var asmTests = [...]asmTest{
    92  	{"amd64", `
    93  func f(x int) int {
    94  	return x * 64
    95  }
    96  `,
    97  		[]string{"\tSHLQ\t\\$6,"},
    98  	},
    99  	{"amd64", `
   100  func f(x int) int {
   101  	return x * 96
   102  }`,
   103  		[]string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
   104  	},
   105  }
   106  
   107  // mergeEnvLists merges the two environment lists such that
   108  // variables with the same name in "in" replace those in "out".
   109  // This always returns a newly allocated slice.
   110  func mergeEnvLists(in, out []string) []string {
   111  	out = append([]string(nil), out...)
   112  NextVar:
   113  	for _, inkv := range in {
   114  		k := strings.SplitAfterN(inkv, "=", 2)[0]
   115  		for i, outkv := range out {
   116  			if strings.HasPrefix(outkv, k) {
   117  				out[i] = inkv
   118  				continue NextVar
   119  			}
   120  		}
   121  		out = append(out, inkv)
   122  	}
   123  	return out
   124  }
   125  
   126  // TestLineNumber checks to make sure the generated assembly has line numbers
   127  // see issue #16214
   128  func TestLineNumber(t *testing.T) {
   129  	testenv.MustHaveGoBuild(t)
   130  	dir, err := ioutil.TempDir("", "TestLineNumber")
   131  	if err != nil {
   132  		t.Fatalf("could not create directory: %v", err)
   133  	}
   134  	defer os.RemoveAll(dir)
   135  
   136  	src := filepath.Join(dir, "x.go")
   137  	err = ioutil.WriteFile(src, []byte(issue16214src), 0644)
   138  	if err != nil {
   139  		t.Fatalf("could not write file: %v", err)
   140  	}
   141  
   142  	cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
   143  	out, err := cmd.CombinedOutput()
   144  	if err != nil {
   145  		t.Fatalf("fail to run go tool compile: %v", err)
   146  	}
   147  
   148  	if strings.Contains(string(out), "unknown line number") {
   149  		t.Errorf("line number missing in assembly:\n%s", out)
   150  	}
   151  }
   152  
   153  var issue16214src = `
   154  package main
   155  
   156  func Mod32(x uint32) uint32 {
   157  	return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has Lineno 0
   158  }
   159  `