2016-05-24 14:09:02 -07:00
|
|
|
// Copyright 2016 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
|
|
|
|
"internal/testenv"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"regexp"
|
|
|
|
|
"runtime"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
2017-08-17 21:20:25 +02:00
|
|
|
// This file contains code generation tests.
|
|
|
|
|
//
|
|
|
|
|
// Each test is defined in a variable of type asmTest. Tests are
|
|
|
|
|
// architecture-specific, and they are grouped in arrays of tests, one
|
|
|
|
|
// for each architecture.
|
|
|
|
|
//
|
2017-08-18 14:03:33 -05:00
|
|
|
// Each asmTest consists of a function to compile, an array of
|
2017-08-28 09:55:18 -07:00
|
|
|
// positive regexps that must match the generated assembly and
|
|
|
|
|
// an array of negative regexps that must not match generated assembly.
|
2017-08-18 14:03:33 -05:00
|
|
|
// For example, the following amd64 test
|
2017-08-17 21:20:25 +02:00
|
|
|
//
|
|
|
|
|
// {
|
2017-08-28 09:55:18 -07:00
|
|
|
// fn: `
|
2017-08-17 21:20:25 +02:00
|
|
|
// func f0(x int) int {
|
|
|
|
|
// return x * 64
|
|
|
|
|
// }
|
|
|
|
|
// `,
|
2017-08-28 09:55:18 -07:00
|
|
|
// pos: []string{"\tSHLQ\t[$]6,"},
|
|
|
|
|
// neg: []string{"MULQ"}
|
2017-08-17 21:20:25 +02:00
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// verifies that the code the compiler generates for a multiplication
|
2017-08-18 14:03:33 -05:00
|
|
|
// by 64 contains a 'SHLQ' instruction and does not contain a MULQ.
|
2017-08-17 21:20:25 +02:00
|
|
|
//
|
|
|
|
|
// Since all the tests for a given architecture are dumped in the same
|
|
|
|
|
// file, the function names must be unique. As a workaround for this
|
|
|
|
|
// restriction, the test harness supports the use of a '$' placeholder
|
|
|
|
|
// for function names. The func f0 above can be also written as
|
|
|
|
|
//
|
|
|
|
|
// {
|
2017-08-28 09:55:18 -07:00
|
|
|
// fn: `
|
2017-08-17 21:20:25 +02:00
|
|
|
// func $(x int) int {
|
|
|
|
|
// return x * 64
|
|
|
|
|
// }
|
|
|
|
|
// `,
|
2017-08-28 09:55:18 -07:00
|
|
|
// pos: []string{"\tSHLQ\t[$]6,"},
|
|
|
|
|
// neg: []string{"MULQ"}
|
2017-08-17 21:20:25 +02:00
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// Each '$'-function will be given a unique name of form f<N>_<arch>,
|
|
|
|
|
// where <N> is the test index in the test array, and <arch> is the
|
|
|
|
|
// test's architecture.
|
|
|
|
|
//
|
|
|
|
|
// It is allowed to mix named and unnamed functions in the same test
|
2017-08-28 09:55:18 -07:00
|
|
|
// array; the named functions will retain their original names.
|
2017-08-17 21:20:25 +02:00
|
|
|
|
2016-05-24 14:09:02 -07:00
|
|
|
// TestAssembly checks to make sure the assembly generated for
|
|
|
|
|
// functions contains certain expected instructions.
|
|
|
|
|
func TestAssembly(t *testing.T) {
|
|
|
|
|
testenv.MustHaveGoBuild(t)
|
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
|
// TODO: remove if we can get "go tool compile -S" to work on windows.
|
|
|
|
|
t.Skipf("skipping test: recursive windows compile not working")
|
|
|
|
|
}
|
|
|
|
|
dir, err := ioutil.TempDir("", "TestAssembly")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("could not create directory: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
2017-03-18 11:16:30 -07:00
|
|
|
nameRegexp := regexp.MustCompile("func \\w+")
|
2017-02-20 17:17:28 +01:00
|
|
|
t.Run("platform", func(t *testing.T) {
|
|
|
|
|
for _, ats := range allAsmTests {
|
|
|
|
|
ats := ats
|
|
|
|
|
t.Run(ats.os+"/"+ats.arch, func(tt *testing.T) {
|
|
|
|
|
tt.Parallel()
|
|
|
|
|
|
|
|
|
|
asm := ats.compileToAsm(tt, dir)
|
|
|
|
|
|
2017-08-17 21:20:25 +02:00
|
|
|
for i, at := range ats.tests {
|
|
|
|
|
var funcName string
|
2017-08-28 09:55:18 -07:00
|
|
|
if strings.Contains(at.fn, "func $") {
|
2017-08-17 21:20:25 +02:00
|
|
|
funcName = fmt.Sprintf("f%d_%s", i, ats.arch)
|
|
|
|
|
} else {
|
2017-08-28 09:55:18 -07:00
|
|
|
funcName = nameRegexp.FindString(at.fn)[len("func "):]
|
2017-08-17 21:20:25 +02:00
|
|
|
}
|
2017-04-06 15:30:53 -07:00
|
|
|
fa := funcAsm(tt, asm, funcName)
|
|
|
|
|
if fa != "" {
|
|
|
|
|
at.verifyAsm(tt, fa)
|
|
|
|
|
}
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
|
|
|
|
})
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
2017-02-20 17:17:28 +01:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-06 15:30:53 -07:00
|
|
|
var nextTextRegexp = regexp.MustCompile(`\n\S`)
|
|
|
|
|
|
2017-03-18 11:16:30 -07:00
|
|
|
// funcAsm returns the assembly listing for the given function name.
|
2017-04-06 15:30:53 -07:00
|
|
|
func funcAsm(t *testing.T, asm string, funcName string) string {
|
2017-03-18 11:16:30 -07:00
|
|
|
if i := strings.Index(asm, fmt.Sprintf("TEXT\t\"\".%s(SB)", funcName)); i >= 0 {
|
2017-02-20 17:17:28 +01:00
|
|
|
asm = asm[i:]
|
2017-04-06 15:30:53 -07:00
|
|
|
} else {
|
|
|
|
|
t.Errorf("could not find assembly for function %v", funcName)
|
|
|
|
|
return ""
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-06 15:30:53 -07:00
|
|
|
// Find the next line that doesn't begin with whitespace.
|
|
|
|
|
loc := nextTextRegexp.FindStringIndex(asm)
|
|
|
|
|
if loc != nil {
|
|
|
|
|
asm = asm[:loc[0]]
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return asm
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type asmTest struct {
|
2017-08-17 21:20:25 +02:00
|
|
|
// function to compile
|
2017-08-28 09:55:18 -07:00
|
|
|
fn string
|
|
|
|
|
// regular expressions that must match the generated assembly
|
|
|
|
|
pos []string
|
|
|
|
|
// regular expressions that must not match the generated assembly
|
|
|
|
|
neg []string
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (at asmTest) verifyAsm(t *testing.T, fa string) {
|
2017-08-28 09:55:18 -07:00
|
|
|
for _, r := range at.pos {
|
2017-02-20 17:17:28 +01:00
|
|
|
if b, err := regexp.MatchString(r, fa); !b || err != nil {
|
2017-08-28 09:55:18 -07:00
|
|
|
t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, at.fn, fa)
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
|
|
|
|
}
|
2017-08-28 09:55:18 -07:00
|
|
|
for _, r := range at.neg {
|
2017-08-18 14:03:33 -05:00
|
|
|
if b, err := regexp.MatchString(r, fa); b || err != nil {
|
2017-08-28 09:55:18 -07:00
|
|
|
t.Errorf("not expected:%s\ngo:%s\nasm:%s\n", r, at.fn, fa)
|
2017-08-18 14:03:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
|
|
|
|
|
2017-02-20 17:17:28 +01:00
|
|
|
type asmTests struct {
|
|
|
|
|
arch string
|
|
|
|
|
os string
|
|
|
|
|
imports []string
|
|
|
|
|
tests []*asmTest
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ats *asmTests) generateCode() []byte {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
fmt.Fprintln(&buf, "package main")
|
|
|
|
|
for _, s := range ats.imports {
|
|
|
|
|
fmt.Fprintf(&buf, "import %q\n", s)
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-17 21:20:25 +02:00
|
|
|
for i, t := range ats.tests {
|
2017-08-28 09:55:18 -07:00
|
|
|
function := strings.Replace(t.fn, "func $", fmt.Sprintf("func f%d_%s", i, ats.arch), 1)
|
2017-08-17 21:20:25 +02:00
|
|
|
fmt.Fprintln(&buf, function)
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return buf.Bytes()
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-24 14:09:02 -07:00
|
|
|
// compile compiles the package pkg for architecture arch and
|
|
|
|
|
// returns the generated assembly. dir is a scratch directory.
|
2017-02-20 17:17:28 +01:00
|
|
|
func (ats *asmTests) compileToAsm(t *testing.T, dir string) string {
|
|
|
|
|
// create test directory
|
|
|
|
|
testDir := filepath.Join(dir, fmt.Sprintf("%s_%s", ats.arch, ats.os))
|
|
|
|
|
err := os.Mkdir(testDir, 0700)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("could not create directory: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-24 14:09:02 -07:00
|
|
|
// Create source.
|
2017-02-20 17:17:28 +01:00
|
|
|
src := filepath.Join(testDir, "test.go")
|
|
|
|
|
err = ioutil.WriteFile(src, ats.generateCode(), 0600)
|
2016-05-24 14:09:02 -07:00
|
|
|
if err != nil {
|
2017-02-20 17:17:28 +01:00
|
|
|
t.Fatalf("error writing code: %v", err)
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
|
|
|
|
|
2016-09-19 14:35:41 -07:00
|
|
|
// First, install any dependencies we need. This builds the required export data
|
|
|
|
|
// for any packages that are imported.
|
2017-02-20 17:17:28 +01:00
|
|
|
for _, i := range ats.imports {
|
|
|
|
|
out := filepath.Join(testDir, i+".a")
|
|
|
|
|
|
2017-02-24 21:40:57 -08:00
|
|
|
if s := ats.runGo(t, "build", "-o", out, "-gcflags=-dolinkobj=false", i); s != "" {
|
2017-02-20 17:17:28 +01:00
|
|
|
t.Fatalf("Stdout = %s\nWant empty", s)
|
|
|
|
|
}
|
2016-09-19 14:35:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now, compile the individual file for which we want to see the generated assembly.
|
2017-02-20 17:17:28 +01:00
|
|
|
asm := ats.runGo(t, "tool", "compile", "-I", testDir, "-S", "-o", filepath.Join(testDir, "out.o"), src)
|
|
|
|
|
return asm
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// runGo runs go command with the given args and returns stdout string.
|
|
|
|
|
// go is run with GOARCH and GOOS set as ats.arch and ats.os respectively
|
|
|
|
|
func (ats *asmTests) runGo(t *testing.T, args ...string) string {
|
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), args...)
|
2017-03-02 14:12:09 -08:00
|
|
|
cmd.Env = append(os.Environ(), "GOARCH="+ats.arch, "GOOS="+ats.os)
|
2016-05-24 14:09:02 -07:00
|
|
|
cmd.Stdout = &stdout
|
|
|
|
|
cmd.Stderr = &stderr
|
2017-02-20 17:17:28 +01:00
|
|
|
|
2016-05-24 14:09:02 -07:00
|
|
|
if err := cmd.Run(); err != nil {
|
2017-04-16 15:19:55 -07:00
|
|
|
t.Fatalf("error running cmd: %v\nstdout:\n%sstderr:\n%s\n", err, stdout.String(), stderr.String())
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
2017-02-20 17:17:28 +01:00
|
|
|
|
2016-05-24 14:09:02 -07:00
|
|
|
if s := stderr.String(); s != "" {
|
2017-02-20 17:17:28 +01:00
|
|
|
t.Fatalf("Stderr = %s\nWant empty", s)
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
2017-02-20 17:17:28 +01:00
|
|
|
|
2016-05-24 14:09:02 -07:00
|
|
|
return stdout.String()
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-20 17:17:28 +01:00
|
|
|
var allAsmTests = []*asmTests{
|
|
|
|
|
{
|
|
|
|
|
arch: "amd64",
|
|
|
|
|
os: "linux",
|
2018-03-06 20:10:35 +01:00
|
|
|
imports: []string{"unsafe", "runtime"},
|
2017-02-20 17:17:28 +01:00
|
|
|
tests: linuxAMD64Tests,
|
|
|
|
|
},
|
|
|
|
|
{
|
2018-03-03 19:17:20 +01:00
|
|
|
arch: "386",
|
|
|
|
|
os: "linux",
|
|
|
|
|
tests: linux386Tests,
|
2017-02-20 17:17:28 +01:00
|
|
|
},
|
|
|
|
|
{
|
2018-03-06 20:10:35 +01:00
|
|
|
arch: "s390x",
|
|
|
|
|
os: "linux",
|
|
|
|
|
tests: linuxS390XTests,
|
2017-02-20 17:17:28 +01:00
|
|
|
},
|
|
|
|
|
{
|
2017-03-16 14:08:31 -07:00
|
|
|
arch: "arm",
|
|
|
|
|
os: "linux",
|
2018-03-05 19:46:18 +01:00
|
|
|
imports: []string{"runtime"},
|
2017-03-16 14:08:31 -07:00
|
|
|
tests: linuxARMTests,
|
2017-02-20 17:17:28 +01:00
|
|
|
},
|
|
|
|
|
{
|
2018-03-06 20:10:35 +01:00
|
|
|
arch: "arm64",
|
|
|
|
|
os: "linux",
|
|
|
|
|
tests: linuxARM64Tests,
|
2017-02-20 17:17:28 +01:00
|
|
|
},
|
2017-03-16 14:08:31 -07:00
|
|
|
{
|
2018-03-05 19:46:18 +01:00
|
|
|
arch: "mips",
|
|
|
|
|
os: "linux",
|
|
|
|
|
tests: linuxMIPSTests,
|
2017-03-16 14:08:31 -07:00
|
|
|
},
|
2017-08-30 12:11:29 -04:00
|
|
|
{
|
2018-03-03 19:17:20 +01:00
|
|
|
arch: "mips64",
|
|
|
|
|
os: "linux",
|
|
|
|
|
tests: linuxMIPS64Tests,
|
2017-08-30 12:11:29 -04:00
|
|
|
},
|
2017-03-13 14:39:17 -04:00
|
|
|
{
|
2018-03-08 17:43:55 +01:00
|
|
|
arch: "ppc64le",
|
|
|
|
|
os: "linux",
|
|
|
|
|
tests: linuxPPC64LETests,
|
2017-03-13 14:39:17 -04:00
|
|
|
},
|
2017-08-26 23:05:36 +02:00
|
|
|
{
|
|
|
|
|
arch: "amd64",
|
|
|
|
|
os: "plan9",
|
|
|
|
|
tests: plan9AMD64Tests,
|
|
|
|
|
},
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
|
|
|
|
|
2017-02-20 17:17:28 +01:00
|
|
|
var linuxAMD64Tests = []*asmTest{
|
2017-10-05 16:05:03 +02:00
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(x int) int {
|
2017-02-20 17:17:28 +01:00
|
|
|
return x * 96
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
|
2016-05-24 14:09:02 -07:00
|
|
|
},
|
2017-02-20 17:17:28 +01:00
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-20 17:17:28 +01:00
|
|
|
func f33(m map[int]int) int {
|
|
|
|
|
return m[5]
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVQ\t[$]5,"},
|
2016-12-08 16:17:20 -08:00
|
|
|
},
|
2017-02-20 17:17:28 +01:00
|
|
|
// Direct use of constants in fast map access calls. Issue 19015.
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-20 17:17:28 +01:00
|
|
|
func f34(m map[int]int) bool {
|
|
|
|
|
_, ok := m[5]
|
|
|
|
|
return ok
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVQ\t[$]5,"},
|
2016-12-08 16:17:20 -08:00
|
|
|
},
|
2017-02-20 17:17:28 +01:00
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-20 17:17:28 +01:00
|
|
|
func f35(m map[string]int) int {
|
|
|
|
|
return m["abc"]
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\"abc\""},
|
2017-02-20 17:17:28 +01:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-20 17:17:28 +01:00
|
|
|
func f36(m map[string]int) bool {
|
|
|
|
|
_, ok := m["abc"]
|
|
|
|
|
return ok
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\"abc\""},
|
2017-02-20 17:17:28 +01:00
|
|
|
},
|
2017-02-06 10:55:39 -08:00
|
|
|
// Bit test ops on amd64, issue 18943.
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-06 10:55:39 -08:00
|
|
|
func f37(a, b uint64) int {
|
|
|
|
|
if a&(1<<(b&63)) != 0 {
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tBTQ\t"},
|
2017-02-06 10:55:39 -08:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-06 10:55:39 -08:00
|
|
|
func f38(a, b uint64) bool {
|
|
|
|
|
return a&(1<<(b&63)) != 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tBTQ\t"},
|
2017-02-06 10:55:39 -08:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-06 10:55:39 -08:00
|
|
|
func f39(a uint64) int {
|
|
|
|
|
if a&(1<<60) != 0 {
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tBTQ\t\\$60"},
|
2017-02-06 10:55:39 -08:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-02-06 10:55:39 -08:00
|
|
|
func f40(a uint64) bool {
|
|
|
|
|
return a&(1<<60) != 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tBTQ\t\\$60"},
|
2017-02-06 10:55:39 -08:00
|
|
|
},
|
2017-03-18 11:16:30 -07:00
|
|
|
// see issue 19595.
|
|
|
|
|
// We want to merge load+op in f58, but not in f59.
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-18 11:16:30 -07:00
|
|
|
func f58(p, q *int) {
|
|
|
|
|
x := *p
|
|
|
|
|
*q += x
|
|
|
|
|
}`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tADDQ\t\\("},
|
2017-03-18 11:16:30 -07:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-18 11:16:30 -07:00
|
|
|
func f59(p, q *int) {
|
|
|
|
|
x := *p
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
|
*q += x
|
|
|
|
|
}
|
|
|
|
|
}`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tADDQ\t[A-Z]"},
|
2017-03-18 11:16:30 -07:00
|
|
|
},
|
2017-03-28 15:30:31 -05:00
|
|
|
// Check that compare to constant string uses 2/4/8 byte compares
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-28 15:30:31 -05:00
|
|
|
func f65(a string) bool {
|
|
|
|
|
return a == "xx"
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{"\tCMPW\t\\(.*\\), [$]"},
|
2017-03-28 15:30:31 -05:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-28 15:30:31 -05:00
|
|
|
func f66(a string) bool {
|
|
|
|
|
return a == "xxxx"
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{"\tCMPL\t\\(.*\\), [$]"},
|
2017-03-28 15:30:31 -05:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-28 15:30:31 -05:00
|
|
|
func f67(a string) bool {
|
|
|
|
|
return a == "xxxxxxxx"
|
|
|
|
|
}`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tCMPQ\t[A-Z]"},
|
2017-03-28 15:30:31 -05:00
|
|
|
},
|
2017-04-14 13:53:40 -05:00
|
|
|
// Check that array compare uses 2/4/8 byte compares
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-04-14 13:53:40 -05:00
|
|
|
func f68(a,b [2]byte) bool {
|
|
|
|
|
return a == b
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{"\tCMPW\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]"},
|
2017-04-14 13:53:40 -05:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-04-14 13:53:40 -05:00
|
|
|
func f69(a,b [3]uint16) bool {
|
|
|
|
|
return a == b
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{
|
|
|
|
|
"\tCMPL\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
|
|
|
"\tCMPW\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
|
|
|
},
|
2017-04-14 13:53:40 -05:00
|
|
|
},
|
2018-02-06 09:44:34 -08:00
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a,b [3]int16) bool {
|
|
|
|
|
return a == b
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{
|
|
|
|
|
"\tCMPL\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
|
|
|
"\tCMPW\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
|
|
|
},
|
2018-02-06 09:44:34 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a,b [12]int8) bool {
|
|
|
|
|
return a == b
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{
|
|
|
|
|
"\tCMPQ\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
|
|
|
"\tCMPL\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
|
|
|
},
|
2018-02-06 09:44:34 -08:00
|
|
|
},
|
2017-04-14 13:53:40 -05:00
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-04-14 13:53:40 -05:00
|
|
|
func f70(a,b [15]byte) bool {
|
|
|
|
|
return a == b
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{"\tCMPQ\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]"},
|
2017-04-14 13:53:40 -05:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-04-14 13:53:40 -05:00
|
|
|
func f71(a,b unsafe.Pointer) bool { // This was a TODO in mapaccess1_faststr
|
|
|
|
|
return *((*[4]byte)(a)) != *((*[4]byte)(b))
|
|
|
|
|
}`,
|
2018-01-03 14:38:55 -08:00
|
|
|
pos: []string{"\tCMPL\t\\(.*\\), [A-Z]"},
|
2017-04-14 13:53:40 -05:00
|
|
|
},
|
2017-07-18 08:35:00 -04:00
|
|
|
{
|
|
|
|
|
// make sure assembly output has matching offset and base register.
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-07-18 08:35:00 -04:00
|
|
|
func f72(a, b int) int {
|
2017-10-24 21:57:51 -07:00
|
|
|
runtime.GC() // use some frame
|
2017-07-18 08:35:00 -04:00
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-10-24 21:57:51 -07:00
|
|
|
pos: []string{"b\\+24\\(SP\\)"},
|
2017-08-18 14:03:33 -05:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// check load combining
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-08-18 14:03:33 -05:00
|
|
|
func f73(a, b byte) (byte,byte) {
|
|
|
|
|
return f73(f73(a,b))
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVW\t"},
|
2017-08-18 14:03:33 -05:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-08-18 14:03:33 -05:00
|
|
|
func f74(a, b uint16) (uint16,uint16) {
|
|
|
|
|
return f74(f74(a,b))
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVL\t"},
|
2017-08-18 14:03:33 -05:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-08-18 14:03:33 -05:00
|
|
|
func f75(a, b uint32) (uint32,uint32) {
|
|
|
|
|
return f75(f75(a,b))
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVQ\t"},
|
2017-08-18 14:03:33 -05:00
|
|
|
},
|
|
|
|
|
// Make sure we don't put pointers in SSE registers across safe points.
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-08-18 14:03:33 -05:00
|
|
|
func $(p, q *[2]*int) {
|
|
|
|
|
a, b := p[0], p[1]
|
|
|
|
|
runtime.GC()
|
|
|
|
|
q[0], q[1] = a, b
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
neg: []string{"MOVUPS"},
|
2017-07-18 08:35:00 -04:00
|
|
|
},
|
2017-03-29 14:01:41 -04:00
|
|
|
{
|
|
|
|
|
// check that stack store is optimized away
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-29 14:01:41 -04:00
|
|
|
func $() int {
|
|
|
|
|
var x int
|
|
|
|
|
return *(&x)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"TEXT\t.*, [$]0-8"},
|
2017-03-29 14:01:41 -04:00
|
|
|
},
|
cmd/compile,math: improve code generation for math.Abs
Implement int reg <-> fp reg moves on amd64.
If we see a load to int reg followed by an int->fp move, then we can just
load to the fp reg instead. Same for stores.
math.Abs is now:
MOVQ "".x+8(SP), AX
SHLQ $1, AX
SHRQ $1, AX
MOVQ AX, "".~r1+16(SP)
math.Copysign is now:
MOVQ "".x+8(SP), AX
SHLQ $1, AX
SHRQ $1, AX
MOVQ "".y+16(SP), CX
SHRQ $63, CX
SHLQ $63, CX
ORQ CX, AX
MOVQ AX, "".~r2+24(SP)
math.Float64bits is now:
MOVSD "".x+8(SP), X0
MOVSD X0, "".~r1+16(SP)
(it would be nicer to use a non-SSE reg for this, nothing is perfect)
And due to the fix for #21440, the inlined version of these improve as well.
name old time/op new time/op delta
Abs 1.38ns ± 5% 0.89ns ±10% -35.54% (p=0.000 n=10+10)
Copysign 1.56ns ± 7% 1.35ns ± 6% -13.77% (p=0.000 n=9+10)
Fixes #13095
Change-Id: Ibd7f2792412a6668608780b0688a77062e1f1499
Reviewed-on: https://go-review.googlesource.com/58732
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Ilya Tocar <ilya.tocar@intel.com>
2017-08-24 13:19:40 -07:00
|
|
|
// int <-> fp moves
|
2017-10-03 14:12:00 -05:00
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(x uint32) bool {
|
|
|
|
|
return x > 4
|
|
|
|
|
}
|
|
|
|
|
`,
|
2018-02-18 20:02:17 +01:00
|
|
|
pos: []string{"\tSETHI\t.*\\(SP\\)"},
|
2017-10-03 14:12:00 -05:00
|
|
|
},
|
2018-01-03 14:38:55 -08:00
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(p int, q *int) bool {
|
|
|
|
|
return p < *q
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"CMPQ\t\\(.*\\), [A-Z]"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(p *int, q int) bool {
|
|
|
|
|
return *p < q
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"CMPQ\t\\(.*\\), [A-Z]"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(p *int) bool {
|
|
|
|
|
return *p < 7
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"CMPQ\t\\(.*\\), [$]7"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(p *int) bool {
|
|
|
|
|
return 7 < *p
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"CMPQ\t\\(.*\\), [$]7"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(p **int) {
|
|
|
|
|
*p = nil
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"CMPL\truntime.writeBarrier\\(SB\\), [$]0"},
|
|
|
|
|
},
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
2016-12-08 16:17:20 -08:00
|
|
|
|
2017-02-20 17:17:28 +01:00
|
|
|
var linux386Tests = []*asmTest{
|
2017-03-29 14:01:41 -04:00
|
|
|
{
|
|
|
|
|
// check that stack store is optimized away
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-29 14:01:41 -04:00
|
|
|
func $() int {
|
|
|
|
|
var x int
|
|
|
|
|
return *(&x)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"TEXT\t.*, [$]0-4"},
|
2017-03-29 14:01:41 -04:00
|
|
|
},
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var linuxS390XTests = []*asmTest{
|
2017-03-29 14:01:41 -04:00
|
|
|
{
|
|
|
|
|
// check that stack store is optimized away
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-29 14:01:41 -04:00
|
|
|
func $() int {
|
|
|
|
|
var x int
|
|
|
|
|
return *(&x)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"TEXT\t.*, [$]0-8"},
|
2017-03-29 14:01:41 -04:00
|
|
|
},
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
2016-12-08 16:17:20 -08:00
|
|
|
|
2017-02-20 17:17:28 +01:00
|
|
|
var linuxARMTests = []*asmTest{
|
2017-07-18 08:35:00 -04:00
|
|
|
{
|
|
|
|
|
// make sure assembly output has matching offset and base register.
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-07-18 08:35:00 -04:00
|
|
|
func f13(a, b int) int {
|
2017-10-24 21:57:51 -07:00
|
|
|
runtime.GC() // use some frame
|
2017-07-18 08:35:00 -04:00
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"b\\+4\\(FP\\)"},
|
2017-07-18 08:35:00 -04:00
|
|
|
},
|
2017-03-29 14:01:41 -04:00
|
|
|
{
|
|
|
|
|
// check that stack store is optimized away
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-29 14:01:41 -04:00
|
|
|
func $() int {
|
|
|
|
|
var x int
|
|
|
|
|
return *(&x)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"TEXT\t.*, [$]-4-4"},
|
2017-03-29 14:01:41 -04:00
|
|
|
},
|
2017-02-20 17:17:28 +01:00
|
|
|
}
|
2017-02-09 14:00:23 -08:00
|
|
|
|
2017-02-20 17:17:28 +01:00
|
|
|
var linuxARM64Tests = []*asmTest{
|
2018-02-25 09:10:54 +00:00
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(x, y uint32) uint32 {
|
|
|
|
|
return x &^ y
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"\tBIC\t"},
|
|
|
|
|
neg: []string{"\tAND\t"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(x, y uint32) uint32 {
|
|
|
|
|
return x ^ ^y
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"\tEON\t"},
|
|
|
|
|
neg: []string{"\tXOR\t"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(x, y uint32) uint32 {
|
|
|
|
|
return x | ^y
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"\tORN\t"},
|
|
|
|
|
neg: []string{"\tORR\t"},
|
|
|
|
|
},
|
2017-04-06 09:36:23 -04:00
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-04-06 09:36:23 -04:00
|
|
|
func f34(a uint64) uint64 {
|
|
|
|
|
return a & ((1<<63)-1)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tAND\t"},
|
2017-04-06 09:36:23 -04:00
|
|
|
},
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-04-06 09:36:23 -04:00
|
|
|
func f35(a uint64) uint64 {
|
|
|
|
|
return a & (1<<63)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tAND\t"},
|
2017-04-06 09:36:23 -04:00
|
|
|
},
|
cmd/internal/obj/arm64, cmd/compile: improve offset folding on ARM64
ARM64 assembler backend only accepts loads and stores with small
or aligned offset. The compiler therefore can only fold small or
aligned offsets into loads and stores. For locals and args, their
offsets to SP are not known until very late, and the compiler
makes conservative decision not folding some of them. However,
in most cases, the offset is indeed small or aligned, and can
be folded into load and store (but actually not).
This CL adds support of loads and stores with large and unaligned
offsets. When the offset doesn't fit into the instruction, it
uses two instructions and (for very large offset) the constant
pool. This way, the compiler doesn't need to be conservative,
and can simply fold the offset.
To make it work, the assembler's optab matching rules need to be
changed. Before, MOVD accepts C_UAUTO32K which matches multiple
of 8 between 0 and 32K, and also C_UAUTO16K, which may not be
multiple of 8 and does not fit into MOVD instruction. The
assembler errors in the latter case. This change makes it only
matches multiple of 8 (or offsets within ±256, which also fits
in instruction), and uses the large-or-unaligned-offset rule
for things doesn't fit (without error). Other sized move rules
are changed similarly.
Class C_UAUTO64K and C_UOREG64K are removed, as they are never
used.
In shared library, load/store of global is rewritten to using
GOT and temp register, which conflicts with the use of temp
register for assembling large offset. So the folding is disabled
for globals in shared library mode.
Reduce cmd/go binary size by 2%.
name old time/op new time/op delta
BinaryTree17-8 8.67s ± 0% 8.61s ± 0% -0.60% (p=0.000 n=9+10)
Fannkuch11-8 6.24s ± 0% 6.19s ± 0% -0.83% (p=0.000 n=10+9)
FmtFprintfEmpty-8 116ns ± 0% 116ns ± 0% ~ (all equal)
FmtFprintfString-8 196ns ± 0% 192ns ± 0% -1.89% (p=0.000 n=10+10)
FmtFprintfInt-8 199ns ± 0% 198ns ± 0% -0.35% (p=0.001 n=9+10)
FmtFprintfIntInt-8 294ns ± 0% 293ns ± 0% -0.34% (p=0.000 n=8+8)
FmtFprintfPrefixedInt-8 318ns ± 1% 318ns ± 1% ~ (p=1.000 n=10+10)
FmtFprintfFloat-8 537ns ± 0% 531ns ± 0% -1.17% (p=0.000 n=9+10)
FmtManyArgs-8 1.19µs ± 1% 1.18µs ± 1% -1.41% (p=0.001 n=10+10)
GobDecode-8 17.2ms ± 1% 17.3ms ± 2% ~ (p=0.165 n=10+10)
GobEncode-8 14.7ms ± 1% 14.7ms ± 2% ~ (p=0.631 n=10+10)
Gzip-8 837ms ± 0% 836ms ± 0% -0.14% (p=0.006 n=9+10)
Gunzip-8 141ms ± 0% 139ms ± 0% -1.24% (p=0.000 n=9+10)
HTTPClientServer-8 256µs ± 1% 253µs ± 1% -1.35% (p=0.000 n=10+10)
JSONEncode-8 40.1ms ± 1% 41.3ms ± 1% +3.06% (p=0.000 n=10+9)
JSONDecode-8 157ms ± 1% 156ms ± 1% -0.83% (p=0.001 n=9+8)
Mandelbrot200-8 8.94ms ± 0% 8.94ms ± 0% +0.02% (p=0.000 n=9+9)
GoParse-8 8.69ms ± 0% 8.54ms ± 1% -1.69% (p=0.000 n=8+10)
RegexpMatchEasy0_32-8 227ns ± 1% 228ns ± 1% +0.48% (p=0.016 n=10+9)
RegexpMatchEasy0_1K-8 1.92µs ± 0% 1.63µs ± 0% -15.08% (p=0.000 n=10+9)
RegexpMatchEasy1_32-8 256ns ± 0% 251ns ± 0% -2.19% (p=0.000 n=10+9)
RegexpMatchEasy1_1K-8 2.38µs ± 0% 2.09µs ± 0% -12.49% (p=0.000 n=10+9)
RegexpMatchMedium_32-8 352ns ± 0% 354ns ± 0% +0.39% (p=0.002 n=10+9)
RegexpMatchMedium_1K-8 106µs ± 0% 106µs ± 0% -0.05% (p=0.005 n=10+9)
RegexpMatchHard_32-8 5.92µs ± 0% 5.89µs ± 0% -0.40% (p=0.000 n=9+8)
RegexpMatchHard_1K-8 180µs ± 0% 179µs ± 0% -0.14% (p=0.000 n=10+9)
Revcomp-8 1.20s ± 0% 1.13s ± 0% -6.29% (p=0.000 n=9+8)
Template-8 159ms ± 1% 154ms ± 1% -3.14% (p=0.000 n=9+10)
TimeParse-8 800ns ± 3% 769ns ± 1% -3.91% (p=0.000 n=10+10)
TimeFormat-8 826ns ± 2% 817ns ± 2% -1.04% (p=0.050 n=10+10)
[Geo mean] 145µs 143µs -1.79%
Change-Id: I5fc42087cee9b54ea414f8ef6d6d020b80eb5985
Reviewed-on: https://go-review.googlesource.com/42172
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
2017-04-28 18:02:00 -04:00
|
|
|
{
|
|
|
|
|
// make sure offsets are folded into load and store.
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
cmd/internal/obj/arm64, cmd/compile: improve offset folding on ARM64
ARM64 assembler backend only accepts loads and stores with small
or aligned offset. The compiler therefore can only fold small or
aligned offsets into loads and stores. For locals and args, their
offsets to SP are not known until very late, and the compiler
makes conservative decision not folding some of them. However,
in most cases, the offset is indeed small or aligned, and can
be folded into load and store (but actually not).
This CL adds support of loads and stores with large and unaligned
offsets. When the offset doesn't fit into the instruction, it
uses two instructions and (for very large offset) the constant
pool. This way, the compiler doesn't need to be conservative,
and can simply fold the offset.
To make it work, the assembler's optab matching rules need to be
changed. Before, MOVD accepts C_UAUTO32K which matches multiple
of 8 between 0 and 32K, and also C_UAUTO16K, which may not be
multiple of 8 and does not fit into MOVD instruction. The
assembler errors in the latter case. This change makes it only
matches multiple of 8 (or offsets within ±256, which also fits
in instruction), and uses the large-or-unaligned-offset rule
for things doesn't fit (without error). Other sized move rules
are changed similarly.
Class C_UAUTO64K and C_UOREG64K are removed, as they are never
used.
In shared library, load/store of global is rewritten to using
GOT and temp register, which conflicts with the use of temp
register for assembling large offset. So the folding is disabled
for globals in shared library mode.
Reduce cmd/go binary size by 2%.
name old time/op new time/op delta
BinaryTree17-8 8.67s ± 0% 8.61s ± 0% -0.60% (p=0.000 n=9+10)
Fannkuch11-8 6.24s ± 0% 6.19s ± 0% -0.83% (p=0.000 n=10+9)
FmtFprintfEmpty-8 116ns ± 0% 116ns ± 0% ~ (all equal)
FmtFprintfString-8 196ns ± 0% 192ns ± 0% -1.89% (p=0.000 n=10+10)
FmtFprintfInt-8 199ns ± 0% 198ns ± 0% -0.35% (p=0.001 n=9+10)
FmtFprintfIntInt-8 294ns ± 0% 293ns ± 0% -0.34% (p=0.000 n=8+8)
FmtFprintfPrefixedInt-8 318ns ± 1% 318ns ± 1% ~ (p=1.000 n=10+10)
FmtFprintfFloat-8 537ns ± 0% 531ns ± 0% -1.17% (p=0.000 n=9+10)
FmtManyArgs-8 1.19µs ± 1% 1.18µs ± 1% -1.41% (p=0.001 n=10+10)
GobDecode-8 17.2ms ± 1% 17.3ms ± 2% ~ (p=0.165 n=10+10)
GobEncode-8 14.7ms ± 1% 14.7ms ± 2% ~ (p=0.631 n=10+10)
Gzip-8 837ms ± 0% 836ms ± 0% -0.14% (p=0.006 n=9+10)
Gunzip-8 141ms ± 0% 139ms ± 0% -1.24% (p=0.000 n=9+10)
HTTPClientServer-8 256µs ± 1% 253µs ± 1% -1.35% (p=0.000 n=10+10)
JSONEncode-8 40.1ms ± 1% 41.3ms ± 1% +3.06% (p=0.000 n=10+9)
JSONDecode-8 157ms ± 1% 156ms ± 1% -0.83% (p=0.001 n=9+8)
Mandelbrot200-8 8.94ms ± 0% 8.94ms ± 0% +0.02% (p=0.000 n=9+9)
GoParse-8 8.69ms ± 0% 8.54ms ± 1% -1.69% (p=0.000 n=8+10)
RegexpMatchEasy0_32-8 227ns ± 1% 228ns ± 1% +0.48% (p=0.016 n=10+9)
RegexpMatchEasy0_1K-8 1.92µs ± 0% 1.63µs ± 0% -15.08% (p=0.000 n=10+9)
RegexpMatchEasy1_32-8 256ns ± 0% 251ns ± 0% -2.19% (p=0.000 n=10+9)
RegexpMatchEasy1_1K-8 2.38µs ± 0% 2.09µs ± 0% -12.49% (p=0.000 n=10+9)
RegexpMatchMedium_32-8 352ns ± 0% 354ns ± 0% +0.39% (p=0.002 n=10+9)
RegexpMatchMedium_1K-8 106µs ± 0% 106µs ± 0% -0.05% (p=0.005 n=10+9)
RegexpMatchHard_32-8 5.92µs ± 0% 5.89µs ± 0% -0.40% (p=0.000 n=9+8)
RegexpMatchHard_1K-8 180µs ± 0% 179µs ± 0% -0.14% (p=0.000 n=10+9)
Revcomp-8 1.20s ± 0% 1.13s ± 0% -6.29% (p=0.000 n=9+8)
Template-8 159ms ± 1% 154ms ± 1% -3.14% (p=0.000 n=9+10)
TimeParse-8 800ns ± 3% 769ns ± 1% -3.91% (p=0.000 n=10+10)
TimeFormat-8 826ns ± 2% 817ns ± 2% -1.04% (p=0.050 n=10+10)
[Geo mean] 145µs 143µs -1.79%
Change-Id: I5fc42087cee9b54ea414f8ef6d6d020b80eb5985
Reviewed-on: https://go-review.googlesource.com/42172
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
2017-04-28 18:02:00 -04:00
|
|
|
func f36(_, a [20]byte) (b [20]byte) {
|
|
|
|
|
b = a
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVD\t\"\"\\.a\\+[0-9]+\\(FP\\), R[0-9]+", "\tMOVD\tR[0-9]+, \"\"\\.b\\+[0-9]+\\(FP\\)"},
|
cmd/internal/obj/arm64, cmd/compile: improve offset folding on ARM64
ARM64 assembler backend only accepts loads and stores with small
or aligned offset. The compiler therefore can only fold small or
aligned offsets into loads and stores. For locals and args, their
offsets to SP are not known until very late, and the compiler
makes conservative decision not folding some of them. However,
in most cases, the offset is indeed small or aligned, and can
be folded into load and store (but actually not).
This CL adds support of loads and stores with large and unaligned
offsets. When the offset doesn't fit into the instruction, it
uses two instructions and (for very large offset) the constant
pool. This way, the compiler doesn't need to be conservative,
and can simply fold the offset.
To make it work, the assembler's optab matching rules need to be
changed. Before, MOVD accepts C_UAUTO32K which matches multiple
of 8 between 0 and 32K, and also C_UAUTO16K, which may not be
multiple of 8 and does not fit into MOVD instruction. The
assembler errors in the latter case. This change makes it only
matches multiple of 8 (or offsets within ±256, which also fits
in instruction), and uses the large-or-unaligned-offset rule
for things doesn't fit (without error). Other sized move rules
are changed similarly.
Class C_UAUTO64K and C_UOREG64K are removed, as they are never
used.
In shared library, load/store of global is rewritten to using
GOT and temp register, which conflicts with the use of temp
register for assembling large offset. So the folding is disabled
for globals in shared library mode.
Reduce cmd/go binary size by 2%.
name old time/op new time/op delta
BinaryTree17-8 8.67s ± 0% 8.61s ± 0% -0.60% (p=0.000 n=9+10)
Fannkuch11-8 6.24s ± 0% 6.19s ± 0% -0.83% (p=0.000 n=10+9)
FmtFprintfEmpty-8 116ns ± 0% 116ns ± 0% ~ (all equal)
FmtFprintfString-8 196ns ± 0% 192ns ± 0% -1.89% (p=0.000 n=10+10)
FmtFprintfInt-8 199ns ± 0% 198ns ± 0% -0.35% (p=0.001 n=9+10)
FmtFprintfIntInt-8 294ns ± 0% 293ns ± 0% -0.34% (p=0.000 n=8+8)
FmtFprintfPrefixedInt-8 318ns ± 1% 318ns ± 1% ~ (p=1.000 n=10+10)
FmtFprintfFloat-8 537ns ± 0% 531ns ± 0% -1.17% (p=0.000 n=9+10)
FmtManyArgs-8 1.19µs ± 1% 1.18µs ± 1% -1.41% (p=0.001 n=10+10)
GobDecode-8 17.2ms ± 1% 17.3ms ± 2% ~ (p=0.165 n=10+10)
GobEncode-8 14.7ms ± 1% 14.7ms ± 2% ~ (p=0.631 n=10+10)
Gzip-8 837ms ± 0% 836ms ± 0% -0.14% (p=0.006 n=9+10)
Gunzip-8 141ms ± 0% 139ms ± 0% -1.24% (p=0.000 n=9+10)
HTTPClientServer-8 256µs ± 1% 253µs ± 1% -1.35% (p=0.000 n=10+10)
JSONEncode-8 40.1ms ± 1% 41.3ms ± 1% +3.06% (p=0.000 n=10+9)
JSONDecode-8 157ms ± 1% 156ms ± 1% -0.83% (p=0.001 n=9+8)
Mandelbrot200-8 8.94ms ± 0% 8.94ms ± 0% +0.02% (p=0.000 n=9+9)
GoParse-8 8.69ms ± 0% 8.54ms ± 1% -1.69% (p=0.000 n=8+10)
RegexpMatchEasy0_32-8 227ns ± 1% 228ns ± 1% +0.48% (p=0.016 n=10+9)
RegexpMatchEasy0_1K-8 1.92µs ± 0% 1.63µs ± 0% -15.08% (p=0.000 n=10+9)
RegexpMatchEasy1_32-8 256ns ± 0% 251ns ± 0% -2.19% (p=0.000 n=10+9)
RegexpMatchEasy1_1K-8 2.38µs ± 0% 2.09µs ± 0% -12.49% (p=0.000 n=10+9)
RegexpMatchMedium_32-8 352ns ± 0% 354ns ± 0% +0.39% (p=0.002 n=10+9)
RegexpMatchMedium_1K-8 106µs ± 0% 106µs ± 0% -0.05% (p=0.005 n=10+9)
RegexpMatchHard_32-8 5.92µs ± 0% 5.89µs ± 0% -0.40% (p=0.000 n=9+8)
RegexpMatchHard_1K-8 180µs ± 0% 179µs ± 0% -0.14% (p=0.000 n=10+9)
Revcomp-8 1.20s ± 0% 1.13s ± 0% -6.29% (p=0.000 n=9+8)
Template-8 159ms ± 1% 154ms ± 1% -3.14% (p=0.000 n=9+10)
TimeParse-8 800ns ± 3% 769ns ± 1% -3.91% (p=0.000 n=10+10)
TimeFormat-8 826ns ± 2% 817ns ± 2% -1.04% (p=0.050 n=10+10)
[Geo mean] 145µs 143µs -1.79%
Change-Id: I5fc42087cee9b54ea414f8ef6d6d020b80eb5985
Reviewed-on: https://go-review.googlesource.com/42172
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
2017-04-28 18:02:00 -04:00
|
|
|
},
|
2017-03-29 14:01:41 -04:00
|
|
|
{
|
|
|
|
|
// check that stack store is optimized away
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-29 14:01:41 -04:00
|
|
|
func $() int {
|
|
|
|
|
var x int
|
|
|
|
|
return *(&x)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"TEXT\t.*, [$]-8-8"},
|
2017-03-29 14:01:41 -04:00
|
|
|
},
|
2017-08-30 12:11:29 -04:00
|
|
|
{
|
|
|
|
|
// check that we don't emit comparisons for constant shift
|
|
|
|
|
fn: `
|
|
|
|
|
//go:nosplit
|
|
|
|
|
func $(x int) int {
|
|
|
|
|
return x << 17
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"LSL\t\\$17"},
|
|
|
|
|
neg: []string{"CMP"},
|
|
|
|
|
},
|
2018-02-07 15:37:33 -05:00
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a int32, ptr *int) {
|
|
|
|
|
if a >= 0 {
|
|
|
|
|
*ptr = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"TBNZ"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a int64, ptr *int) {
|
|
|
|
|
if a >= 0 {
|
|
|
|
|
*ptr = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"TBNZ"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a int32, ptr *int) {
|
|
|
|
|
if a < 0 {
|
|
|
|
|
*ptr = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"TBZ"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a int64, ptr *int) {
|
|
|
|
|
if a < 0 {
|
|
|
|
|
*ptr = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"TBZ"},
|
|
|
|
|
},
|
2018-02-15 14:49:03 -05:00
|
|
|
// Load-combining tests.
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(s []byte) uint16 {
|
|
|
|
|
return uint16(s[0]) | uint16(s[1]) << 8
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"\tMOVHU\t\\(R[0-9]+\\)"},
|
|
|
|
|
neg: []string{"ORR\tR[0-9]+<<8\t"},
|
|
|
|
|
},
|
2017-08-13 22:36:47 +00:00
|
|
|
{
|
|
|
|
|
// make sure that CSEL is emitted for conditional moves
|
|
|
|
|
fn: `
|
|
|
|
|
func f37(c int) int {
|
|
|
|
|
x := c + 4
|
|
|
|
|
if c < 0 {
|
|
|
|
|
x = 182
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"\tCSEL\t"},
|
|
|
|
|
},
|
2018-02-23 15:17:54 -05:00
|
|
|
// Check that zero stores are combine into larger stores
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(b []byte) {
|
|
|
|
|
_ = b[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
b[0] = 0
|
|
|
|
|
b[1] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVH\tZR"},
|
|
|
|
|
neg: []string{"MOVB"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(b []byte) {
|
|
|
|
|
_ = b[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
b[1] = 0
|
|
|
|
|
b[0] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVH\tZR"},
|
|
|
|
|
neg: []string{"MOVB"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(b []byte) {
|
|
|
|
|
_ = b[3] // early bounds check to guarantee safety of writes below
|
|
|
|
|
b[0] = 0
|
|
|
|
|
b[1] = 0
|
|
|
|
|
b[2] = 0
|
|
|
|
|
b[3] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVW\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(b []byte) {
|
|
|
|
|
_ = b[3] // early bounds check to guarantee safety of writes below
|
|
|
|
|
b[2] = 0
|
|
|
|
|
b[3] = 0
|
|
|
|
|
b[1] = 0
|
|
|
|
|
b[0] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVW\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(h []uint16) {
|
|
|
|
|
_ = h[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
h[0] = 0
|
|
|
|
|
h[1] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVW\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(h []uint16) {
|
|
|
|
|
_ = h[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
h[1] = 0
|
|
|
|
|
h[0] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVW\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(b []byte) {
|
|
|
|
|
_ = b[7] // early bounds check to guarantee safety of writes below
|
|
|
|
|
b[0] = 0
|
|
|
|
|
b[1] = 0
|
|
|
|
|
b[2] = 0
|
|
|
|
|
b[3] = 0
|
|
|
|
|
b[4] = 0
|
|
|
|
|
b[5] = 0
|
|
|
|
|
b[6] = 0
|
|
|
|
|
b[7] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVD\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(h []uint16) {
|
|
|
|
|
_ = h[3] // early bounds check to guarantee safety of writes below
|
|
|
|
|
h[0] = 0
|
|
|
|
|
h[1] = 0
|
|
|
|
|
h[2] = 0
|
|
|
|
|
h[3] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVD\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(h []uint16) {
|
|
|
|
|
_ = h[3] // early bounds check to guarantee safety of writes below
|
|
|
|
|
h[2] = 0
|
|
|
|
|
h[3] = 0
|
|
|
|
|
h[1] = 0
|
|
|
|
|
h[0] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVD\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(w []uint32) {
|
|
|
|
|
_ = w[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
w[0] = 0
|
|
|
|
|
w[1] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVD\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(w []uint32) {
|
|
|
|
|
_ = w[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
w[1] = 0
|
|
|
|
|
w[0] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVD\tZR"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(b []byte) {
|
|
|
|
|
_ = b[15] // early bounds check to guarantee safety of writes below
|
|
|
|
|
b[0] = 0
|
|
|
|
|
b[1] = 0
|
|
|
|
|
b[2] = 0
|
|
|
|
|
b[3] = 0
|
|
|
|
|
b[4] = 0
|
|
|
|
|
b[5] = 0
|
|
|
|
|
b[6] = 0
|
|
|
|
|
b[7] = 0
|
|
|
|
|
b[8] = 0
|
|
|
|
|
b[9] = 0
|
|
|
|
|
b[10] = 0
|
|
|
|
|
b[11] = 0
|
|
|
|
|
b[12] = 0
|
|
|
|
|
b[13] = 0
|
|
|
|
|
b[15] = 0
|
|
|
|
|
b[14] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"STP"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(h []uint16) {
|
|
|
|
|
_ = h[7] // early bounds check to guarantee safety of writes below
|
|
|
|
|
h[0] = 0
|
|
|
|
|
h[1] = 0
|
|
|
|
|
h[2] = 0
|
|
|
|
|
h[3] = 0
|
|
|
|
|
h[4] = 0
|
|
|
|
|
h[5] = 0
|
|
|
|
|
h[6] = 0
|
|
|
|
|
h[7] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"STP"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(w []uint32) {
|
|
|
|
|
_ = w[3] // early bounds check to guarantee safety of writes below
|
|
|
|
|
w[0] = 0
|
|
|
|
|
w[1] = 0
|
|
|
|
|
w[2] = 0
|
|
|
|
|
w[3] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"STP"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(w []uint32) {
|
|
|
|
|
_ = w[3] // early bounds check to guarantee safety of writes below
|
|
|
|
|
w[1] = 0
|
|
|
|
|
w[0] = 0
|
|
|
|
|
w[3] = 0
|
|
|
|
|
w[2] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"STP"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(d []uint64) {
|
|
|
|
|
_ = d[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
d[0] = 0
|
|
|
|
|
d[1] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"STP"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(d []uint64) {
|
|
|
|
|
_ = d[1] // early bounds check to guarantee safety of writes below
|
|
|
|
|
d[1] = 0
|
|
|
|
|
d[0] = 0
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"STP"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH"},
|
|
|
|
|
},
|
2018-02-23 13:28:48 -05:00
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a *[39]byte) {
|
|
|
|
|
*a = [39]byte{}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"MOVD"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fn: `
|
|
|
|
|
func $(a *[30]byte) {
|
|
|
|
|
*a = [30]byte{}
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"STP"},
|
|
|
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
|
|
|
},
|
2017-03-16 14:08:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var linuxMIPSTests = []*asmTest{
|
2017-03-29 14:01:41 -04:00
|
|
|
{
|
|
|
|
|
// check that stack store is optimized away
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-29 14:01:41 -04:00
|
|
|
func $() int {
|
|
|
|
|
var x int
|
|
|
|
|
return *(&x)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"TEXT\t.*, [$]-4-4"},
|
2017-03-29 14:01:41 -04:00
|
|
|
},
|
2016-05-24 14:09:02 -07:00
|
|
|
}
|
2016-06-01 15:13:55 +02:00
|
|
|
|
2017-08-30 12:11:29 -04:00
|
|
|
var linuxMIPS64Tests = []*asmTest{
|
|
|
|
|
{
|
|
|
|
|
// check that we don't emit comparisons for constant shift
|
|
|
|
|
fn: `
|
|
|
|
|
func $(x int) int {
|
|
|
|
|
return x << 17
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
pos: []string{"SLLV\t\\$17"},
|
|
|
|
|
neg: []string{"SGT"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-13 14:39:17 -04:00
|
|
|
var linuxPPC64LETests = []*asmTest{
|
2017-03-29 14:01:41 -04:00
|
|
|
{
|
|
|
|
|
// check that stack store is optimized away
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-03-29 14:01:41 -04:00
|
|
|
func $() int {
|
|
|
|
|
var x int
|
|
|
|
|
return *(&x)
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"TEXT\t.*, [$]0-8"},
|
2017-03-29 14:01:41 -04:00
|
|
|
},
|
2017-03-13 14:39:17 -04:00
|
|
|
}
|
|
|
|
|
|
2017-08-26 23:05:36 +02:00
|
|
|
var plan9AMD64Tests = []*asmTest{
|
|
|
|
|
// We should make sure that the compiler doesn't generate floating point
|
|
|
|
|
// instructions for non-float operations on Plan 9, because floating point
|
|
|
|
|
// operations are not allowed in the note handler.
|
|
|
|
|
// Array zeroing.
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-08-26 23:05:36 +02:00
|
|
|
func $() [16]byte {
|
|
|
|
|
var a [16]byte
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVQ\t\\$0, \"\""},
|
2017-08-26 23:05:36 +02:00
|
|
|
},
|
|
|
|
|
// Array copy.
|
|
|
|
|
{
|
2017-08-28 09:55:18 -07:00
|
|
|
fn: `
|
2017-08-26 23:05:36 +02:00
|
|
|
func $(a [16]byte) (b [16]byte) {
|
|
|
|
|
b = a
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
`,
|
2017-08-28 09:55:18 -07:00
|
|
|
pos: []string{"\tMOVQ\t\"\"\\.a\\+[0-9]+\\(SP\\), (AX|CX)", "\tMOVQ\t(AX|CX), \"\"\\.b\\+[0-9]+\\(SP\\)"},
|
2017-08-26 23:05:36 +02:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-30 06:36:31 -04:00
|
|
|
// TestLineNumber checks to make sure the generated assembly has line numbers
|
|
|
|
|
// see issue #16214
|
|
|
|
|
func TestLineNumber(t *testing.T) {
|
|
|
|
|
testenv.MustHaveGoBuild(t)
|
|
|
|
|
dir, err := ioutil.TempDir("", "TestLineNumber")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("could not create directory: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
|
|
src := filepath.Join(dir, "x.go")
|
|
|
|
|
err = ioutil.WriteFile(src, []byte(issue16214src), 0644)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("could not write file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 11:08:47 -07:00
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
|
2016-06-30 06:36:31 -04:00
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("fail to run go tool compile: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.Contains(string(out), "unknown line number") {
|
|
|
|
|
t.Errorf("line number missing in assembly:\n%s", out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var issue16214src = `
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
func Mod32(x uint32) uint32 {
|
2016-12-07 17:40:46 -08:00
|
|
|
return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has unknown Pos
|
2016-06-30 06:36:31 -04:00
|
|
|
}
|
2018-02-27 10:35:17 -05:00
|
|
|
`
|