mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
This optimization mirrors that which is already implemented for AMD64. The optimization specifically targets the binary.LittleEndian.PutUint* functions. encoding/binary results on Amberwing: name old time/op new time/op delta ReadSlice1000Int32s 9.67µs ± 1% 9.64µs ± 1% ~ (p=0.185 n=9+9) ReadStruct 5.24µs ± 2% 5.36µs ± 2% +2.24% (p=0.002 n=10+8) ReadInts 8.69µs ± 5% 8.88µs ± 5% ~ (p=0.083 n=10+10) WriteInts 3.90µs ±10% 3.71µs ± 9% ~ (p=0.077 n=10+10) WriteSlice1000Int32s 10.9µs ± 1% 10.9µs ± 1% ~ (p=0.701 n=9+9) PutUint16 572ns ±14% 505ns ±11% -11.75% (p=0.006 n=9+10) PutUint32 550ns ±18% 540ns ±11% ~ (p=0.692 n=10+10) PutUint64 565ns ±15% 540ns ±17% ~ (p=0.248 n=10+10) LittleEndianPutUint16 540ns ±11% 500ns ±10% ~ (p=0.094 n=10+10) LittleEndianPutUint32 520ns ±15% 480ns ±15% ~ (p=0.087 n=10+10) LittleEndianPutUint64 505ns ±29% 470ns ±17% ~ (p=0.208 n=10+10) PutUvarint32 700ns ±21% 635ns ±10% -9.29% (p=0.028 n=10+10) PutUvarint64 740ns ± 8% 740ns ± 8% ~ (p=0.713 n=10+10) [Geo mean] 1.53µs 1.47µs -3.93% name old speed new speed delta ReadSlice1000Int32s 414MB/s ± 1% 415MB/s ± 1% ~ (p=0.185 n=9+9) ReadStruct 14.3MB/s ± 2% 14.0MB/s ± 2% -2.21% (p=0.000 n=10+8) ReadInts 3.45MB/s ± 4% 3.38MB/s ± 6% ~ (p=0.085 n=10+10) WriteInts 7.71MB/s ± 9% 8.09MB/s ± 8% +4.93% (p=0.048 n=10+10) WriteSlice1000Int32s 367MB/s ± 1% 366MB/s ± 1% ~ (p=0.701 n=9+9) PutUint16 3.51MB/s ±14% 3.99MB/s ±11% +13.47% (p=0.009 n=9+10) PutUint32 7.35MB/s ±21% 7.44MB/s ±10% ~ (p=0.692 n=10+10) PutUint64 14.3MB/s ±14% 15.0MB/s ±19% ~ (p=0.248 n=10+10) LittleEndianPutUint16 3.72MB/s ±11% 4.03MB/s ±10% ~ (p=0.094 n=10+10) LittleEndianPutUint32 7.75MB/s ±15% 8.39MB/s ±13% ~ (p=0.087 n=10+10) LittleEndianPutUint64 16.1MB/s ±23% 17.2MB/s ±16% ~ (p=0.208 n=10+10) PutUvarint32 5.76MB/s ±18% 6.32MB/s ±10% +9.72% (p=0.028 n=10+10) PutUvarint64 10.8MB/s ± 8% 10.8MB/s ± 8% ~ (p=0.713 n=10+10) [Geo mean] 13.7MB/s 14.3MB/s +4.02% go1 results on Amberwing: name old time/op new time/op delta RegexpMatchEasy0_32 249ns ± 0% 249ns ± 0% ~ (p=0.087 n=10+10) RegexpMatchEasy0_1K 584ns ± 0% 584ns ± 0% ~ (all equal) RegexpMatchEasy1_32 246ns ± 0% 246ns ± 0% ~ (p=1.000 n=10+10) RegexpMatchEasy1_1K 806ns ± 0% 806ns ± 0% ~ (p=0.706 n=10+9) RegexpMatchMedium_32 314ns ± 0% 314ns ± 0% ~ (all equal) RegexpMatchMedium_1K 52.1µs ± 0% 52.1µs ± 0% ~ (p=0.245 n=10+8) RegexpMatchHard_32 2.75µs ± 1% 2.75µs ± 1% ~ (p=0.690 n=10+10) RegexpMatchHard_1K 78.9µs ± 0% 78.9µs ± 1% ~ (p=0.295 n=9+9) FmtFprintfEmpty 58.5ns ± 0% 58.5ns ± 0% ~ (all equal) FmtFprintfString 112ns ± 0% 112ns ± 0% ~ (all equal) FmtFprintfInt 117ns ± 0% 116ns ± 0% -0.85% (p=0.000 n=10+10) FmtFprintfIntInt 181ns ± 0% 181ns ± 0% ~ (all equal) FmtFprintfPrefixedInt 222ns ± 0% 224ns ± 0% +0.90% (p=0.000 n=9+10) FmtFprintfFloat 318ns ± 1% 322ns ± 0% ~ (p=0.059 n=10+8) FmtManyArgs 736ns ± 1% 735ns ± 0% ~ (p=0.206 n=9+9) Gzip 437ms ± 0% 436ms ± 0% -0.25% (p=0.000 n=10+10) HTTPClientServer 89.8µs ± 1% 90.2µs ± 2% ~ (p=0.393 n=10+10) JSONEncode 20.1ms ± 1% 20.2ms ± 1% ~ (p=0.065 n=9+10) JSONDecode 94.2ms ± 1% 93.9ms ± 1% -0.42% (p=0.043 n=10+10) GobDecode 12.7ms ± 1% 12.8ms ± 2% +0.94% (p=0.019 n=10+10) GobEncode 12.1ms ± 0% 12.1ms ± 0% ~ (p=0.052 n=10+10) Mandelbrot200 5.06ms ± 0% 5.05ms ± 0% -0.04% (p=0.000 n=9+10) TimeParse 450ns ± 3% 446ns ± 0% ~ (p=0.238 n=10+9) TimeFormat 485ns ± 1% 483ns ± 1% ~ (p=0.073 n=10+10) Template 90.4ms ± 0% 90.7ms ± 0% +0.29% (p=0.000 n=8+10) GoParse 6.01ms ± 0% 6.03ms ± 0% +0.35% (p=0.000 n=10+10) BinaryTree17 11.7s ± 0% 11.7s ± 0% ~ (p=0.481 n=10+10) Revcomp 669ms ± 0% 669ms ± 0% ~ (p=0.315 n=10+10) Fannkuch11 3.40s ± 0% 3.37s ± 0% -0.92% (p=0.000 n=10+10) [Geo mean] 67.9µs 67.9µs +0.02% name old speed new speed delta RegexpMatchEasy0_32 128MB/s ± 0% 128MB/s ± 0% -0.08% (p=0.003 n=8+10) RegexpMatchEasy0_1K 1.75GB/s ± 0% 1.75GB/s ± 0% ~ (p=0.642 n=8+10) RegexpMatchEasy1_32 130MB/s ± 0% 130MB/s ± 0% ~ (p=0.690 n=10+9) RegexpMatchEasy1_1K 1.27GB/s ± 0% 1.27GB/s ± 0% ~ (p=0.661 n=10+9) RegexpMatchMedium_32 3.18MB/s ± 0% 3.18MB/s ± 0% ~ (all equal) RegexpMatchMedium_1K 19.7MB/s ± 0% 19.6MB/s ± 0% ~ (p=0.190 n=10+9) RegexpMatchHard_32 11.6MB/s ± 0% 11.6MB/s ± 1% ~ (p=0.669 n=10+10) RegexpMatchHard_1K 13.0MB/s ± 0% 13.0MB/s ± 0% ~ (p=0.718 n=9+9) Gzip 44.4MB/s ± 0% 44.5MB/s ± 0% +0.24% (p=0.000 n=10+10) JSONEncode 96.5MB/s ± 1% 96.1MB/s ± 1% ~ (p=0.065 n=9+10) JSONDecode 20.6MB/s ± 1% 20.7MB/s ± 1% +0.42% (p=0.041 n=10+10) GobDecode 60.6MB/s ± 1% 60.0MB/s ± 2% -0.92% (p=0.016 n=10+10) GobEncode 63.4MB/s ± 0% 63.6MB/s ± 0% ~ (p=0.055 n=10+10) Template 21.5MB/s ± 0% 21.4MB/s ± 0% -0.30% (p=0.000 n=9+10) GoParse 9.64MB/s ± 0% 9.61MB/s ± 0% -0.36% (p=0.000 n=10+10) Revcomp 380MB/s ± 0% 380MB/s ± 0% ~ (p=0.323 n=10+10) [Geo mean] 56.0MB/s 55.9MB/s -0.07% Change-Id: I79a4978d42d01a5f72ed5ceec07f5e78ac6b3859 Reviewed-on: https://go-review.googlesource.com/97175 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
3716 lines
60 KiB
Go
3716 lines
60 KiB
Go
// 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"
|
|
)
|
|
|
|
// 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.
|
|
//
|
|
// Each asmTest consists of a function to compile, an array of
|
|
// positive regexps that must match the generated assembly and
|
|
// an array of negative regexps that must not match generated assembly.
|
|
// For example, the following amd64 test
|
|
//
|
|
// {
|
|
// fn: `
|
|
// func f0(x int) int {
|
|
// return x * 64
|
|
// }
|
|
// `,
|
|
// pos: []string{"\tSHLQ\t[$]6,"},
|
|
// neg: []string{"MULQ"}
|
|
// }
|
|
//
|
|
// verifies that the code the compiler generates for a multiplication
|
|
// by 64 contains a 'SHLQ' instruction and does not contain a MULQ.
|
|
//
|
|
// 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
|
|
//
|
|
// {
|
|
// fn: `
|
|
// func $(x int) int {
|
|
// return x * 64
|
|
// }
|
|
// `,
|
|
// pos: []string{"\tSHLQ\t[$]6,"},
|
|
// neg: []string{"MULQ"}
|
|
// }
|
|
//
|
|
// 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
|
|
// array; the named functions will retain their original names.
|
|
|
|
// 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)
|
|
|
|
nameRegexp := regexp.MustCompile("func \\w+")
|
|
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)
|
|
|
|
for i, at := range ats.tests {
|
|
var funcName string
|
|
if strings.Contains(at.fn, "func $") {
|
|
funcName = fmt.Sprintf("f%d_%s", i, ats.arch)
|
|
} else {
|
|
funcName = nameRegexp.FindString(at.fn)[len("func "):]
|
|
}
|
|
fa := funcAsm(tt, asm, funcName)
|
|
if fa != "" {
|
|
at.verifyAsm(tt, fa)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
var nextTextRegexp = regexp.MustCompile(`\n\S`)
|
|
|
|
// funcAsm returns the assembly listing for the given function name.
|
|
func funcAsm(t *testing.T, asm string, funcName string) string {
|
|
if i := strings.Index(asm, fmt.Sprintf("TEXT\t\"\".%s(SB)", funcName)); i >= 0 {
|
|
asm = asm[i:]
|
|
} else {
|
|
t.Errorf("could not find assembly for function %v", funcName)
|
|
return ""
|
|
}
|
|
|
|
// Find the next line that doesn't begin with whitespace.
|
|
loc := nextTextRegexp.FindStringIndex(asm)
|
|
if loc != nil {
|
|
asm = asm[:loc[0]]
|
|
}
|
|
|
|
return asm
|
|
}
|
|
|
|
type asmTest struct {
|
|
// function to compile
|
|
fn string
|
|
// regular expressions that must match the generated assembly
|
|
pos []string
|
|
// regular expressions that must not match the generated assembly
|
|
neg []string
|
|
}
|
|
|
|
func (at asmTest) verifyAsm(t *testing.T, fa string) {
|
|
for _, r := range at.pos {
|
|
if b, err := regexp.MatchString(r, fa); !b || err != nil {
|
|
t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, at.fn, fa)
|
|
}
|
|
}
|
|
for _, r := range at.neg {
|
|
if b, err := regexp.MatchString(r, fa); b || err != nil {
|
|
t.Errorf("not expected:%s\ngo:%s\nasm:%s\n", r, at.fn, fa)
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
for i, t := range ats.tests {
|
|
function := strings.Replace(t.fn, "func $", fmt.Sprintf("func f%d_%s", i, ats.arch), 1)
|
|
fmt.Fprintln(&buf, function)
|
|
}
|
|
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// compile compiles the package pkg for architecture arch and
|
|
// returns the generated assembly. dir is a scratch directory.
|
|
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)
|
|
}
|
|
|
|
// Create source.
|
|
src := filepath.Join(testDir, "test.go")
|
|
err = ioutil.WriteFile(src, ats.generateCode(), 0600)
|
|
if err != nil {
|
|
t.Fatalf("error writing code: %v", err)
|
|
}
|
|
|
|
// First, install any dependencies we need. This builds the required export data
|
|
// for any packages that are imported.
|
|
for _, i := range ats.imports {
|
|
out := filepath.Join(testDir, i+".a")
|
|
|
|
if s := ats.runGo(t, "build", "-o", out, "-gcflags=-dolinkobj=false", i); s != "" {
|
|
t.Fatalf("Stdout = %s\nWant empty", s)
|
|
}
|
|
}
|
|
|
|
// Now, compile the individual file for which we want to see the generated assembly.
|
|
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...)
|
|
cmd.Env = append(os.Environ(), "GOARCH="+ats.arch, "GOOS="+ats.os)
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
t.Fatalf("error running cmd: %v\nstdout:\n%sstderr:\n%s\n", err, stdout.String(), stderr.String())
|
|
}
|
|
|
|
if s := stderr.String(); s != "" {
|
|
t.Fatalf("Stderr = %s\nWant empty", s)
|
|
}
|
|
|
|
return stdout.String()
|
|
}
|
|
|
|
var allAsmTests = []*asmTests{
|
|
{
|
|
arch: "amd64",
|
|
os: "linux",
|
|
imports: []string{"encoding/binary", "math", "math/bits", "unsafe", "runtime"},
|
|
tests: linuxAMD64Tests,
|
|
},
|
|
{
|
|
arch: "386",
|
|
os: "linux",
|
|
imports: []string{"encoding/binary", "math"},
|
|
tests: linux386Tests,
|
|
},
|
|
{
|
|
arch: "s390x",
|
|
os: "linux",
|
|
imports: []string{"encoding/binary", "math", "math/bits"},
|
|
tests: linuxS390XTests,
|
|
},
|
|
{
|
|
arch: "arm",
|
|
os: "linux",
|
|
imports: []string{"math/bits", "runtime"},
|
|
tests: linuxARMTests,
|
|
},
|
|
{
|
|
arch: "arm64",
|
|
os: "linux",
|
|
imports: []string{"encoding/binary", "math", "math/bits"},
|
|
tests: linuxARM64Tests,
|
|
},
|
|
{
|
|
arch: "mips",
|
|
os: "linux",
|
|
imports: []string{"math/bits", "math"},
|
|
tests: linuxMIPSTests,
|
|
},
|
|
{
|
|
arch: "mips64",
|
|
os: "linux",
|
|
imports: []string{"math"},
|
|
tests: linuxMIPS64Tests,
|
|
},
|
|
{
|
|
arch: "ppc64le",
|
|
os: "linux",
|
|
imports: []string{"encoding/binary", "math", "math/bits"},
|
|
tests: linuxPPC64LETests,
|
|
},
|
|
{
|
|
arch: "amd64",
|
|
os: "plan9",
|
|
tests: plan9AMD64Tests,
|
|
},
|
|
}
|
|
|
|
var linuxAMD64Tests = []*asmTest{
|
|
// multiplication by powers of two
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return n * 64
|
|
}
|
|
`,
|
|
pos: []string{"\tSHLQ\t\\$6,"},
|
|
neg: []string{"IMULQ"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return -128*n
|
|
}
|
|
`,
|
|
pos: []string{"SHLQ"},
|
|
neg: []string{"IMULQ"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func $(x int) int {
|
|
return x * 96
|
|
}
|
|
`,
|
|
pos: []string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
|
|
},
|
|
// Load-combining tests.
|
|
{
|
|
fn: `
|
|
func f2(b []byte) uint64 {
|
|
return binary.LittleEndian.Uint64(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t\\(.*\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f3(b []byte, i int) uint64 {
|
|
return binary.LittleEndian.Uint64(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t\\(.*\\)\\(.*\\*1\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f4(b []byte) uint32 {
|
|
return binary.LittleEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVL\t\\(.*\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f5(b []byte, i int) uint32 {
|
|
return binary.LittleEndian.Uint32(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, v uint64) {
|
|
binary.LittleEndian.PutUint64(b, v)
|
|
}
|
|
`,
|
|
neg: []string{"SHRQ"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, i int, v uint64) {
|
|
binary.LittleEndian.PutUint64(b[i:], v)
|
|
}
|
|
`,
|
|
neg: []string{"SHRQ"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, v uint32) {
|
|
binary.LittleEndian.PutUint32(b, v)
|
|
}
|
|
`,
|
|
neg: []string{"SHRL", "SHRQ"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, i int, v uint32) {
|
|
binary.LittleEndian.PutUint32(b[i:], v)
|
|
}
|
|
`,
|
|
neg: []string{"SHRL", "SHRQ"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, v uint16) {
|
|
binary.LittleEndian.PutUint16(b, v)
|
|
}
|
|
`,
|
|
neg: []string{"SHRW", "SHRL", "SHRQ"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, i int, v uint16) {
|
|
binary.LittleEndian.PutUint16(b[i:], v)
|
|
}
|
|
`,
|
|
neg: []string{"SHRW", "SHRL", "SHRQ"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f6(b []byte) uint64 {
|
|
return binary.BigEndian.Uint64(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f7(b []byte, i int) uint64 {
|
|
return binary.BigEndian.Uint64(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f8(b []byte, v uint64) {
|
|
binary.BigEndian.PutUint64(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f9(b []byte, i int, v uint64) {
|
|
binary.BigEndian.PutUint64(b[i:], v)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f10(b []byte) uint32 {
|
|
return binary.BigEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f11(b []byte, i int) uint32 {
|
|
return binary.BigEndian.Uint32(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f12(b []byte, v uint32) {
|
|
binary.BigEndian.PutUint32(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f13(b []byte, i int, v uint32) {
|
|
binary.BigEndian.PutUint32(b[i:], v)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f14(b []byte) uint16 {
|
|
return binary.BigEndian.Uint16(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t\\$8,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f15(b []byte, i int) uint16 {
|
|
return binary.BigEndian.Uint16(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t\\$8,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f16(b []byte, v uint16) {
|
|
binary.BigEndian.PutUint16(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t\\$8,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f17(b []byte, i int, v uint16) {
|
|
binary.BigEndian.PutUint16(b[i:], v)
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t\\$8,"},
|
|
},
|
|
// Structure zeroing. See issue #18370.
|
|
{
|
|
fn: `
|
|
type T1 struct {
|
|
a, b, c int
|
|
}
|
|
func $(t *T1) {
|
|
*t = T1{}
|
|
}
|
|
`,
|
|
pos: []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)"},
|
|
},
|
|
// SSA-able composite literal initialization. Issue 18872.
|
|
{
|
|
fn: `
|
|
type T18872 struct {
|
|
a, b, c, d int
|
|
}
|
|
|
|
func f18872(p *T18872) {
|
|
*p = T18872{1, 2, 3, 4}
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t[$]1", "\tMOVQ\t[$]2", "\tMOVQ\t[$]3", "\tMOVQ\t[$]4"},
|
|
},
|
|
// Also test struct containing pointers (this was special because of write barriers).
|
|
{
|
|
fn: `
|
|
type T2 struct {
|
|
a, b, c *int
|
|
}
|
|
func f19(t *T2) {
|
|
*t = T2{}
|
|
}
|
|
`,
|
|
pos: []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)", "\tCALL\truntime\\.gcWriteBarrier\\(SB\\)"},
|
|
},
|
|
// Rotate tests
|
|
{
|
|
fn: `
|
|
func f20(x uint64) uint64 {
|
|
return x<<7 | x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROLQ\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f21(x uint64) uint64 {
|
|
return x<<7 + x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROLQ\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f22(x uint64) uint64 {
|
|
return x<<7 ^ x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROLQ\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f23(x uint32) uint32 {
|
|
return x<<7 + x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tROLL\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f24(x uint32) uint32 {
|
|
return x<<7 | x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tROLL\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f25(x uint32) uint32 {
|
|
return x<<7 ^ x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tROLL\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f26(x uint16) uint16 {
|
|
return x<<7 + x>>9
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f27(x uint16) uint16 {
|
|
return x<<7 | x>>9
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f28(x uint16) uint16 {
|
|
return x<<7 ^ x>>9
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f29(x uint8) uint8 {
|
|
return x<<7 + x>>1
|
|
}
|
|
`,
|
|
pos: []string{"\tROLB\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f30(x uint8) uint8 {
|
|
return x<<7 | x>>1
|
|
}
|
|
`,
|
|
pos: []string{"\tROLB\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f31(x uint8) uint8 {
|
|
return x<<7 ^ x>>1
|
|
}
|
|
`,
|
|
pos: []string{"\tROLB\t[$]7,"},
|
|
},
|
|
// Rotate after inlining (see issue 18254).
|
|
{
|
|
fn: `
|
|
func f32(x uint32) uint32 {
|
|
return g(x, 7)
|
|
}
|
|
func g(x uint32, k uint) uint32 {
|
|
return x<<k | x>>(32-k)
|
|
}
|
|
`,
|
|
pos: []string{"\tROLL\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f33(m map[int]int) int {
|
|
return m[5]
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t[$]5,"},
|
|
},
|
|
// Direct use of constants in fast map access calls. Issue 19015.
|
|
{
|
|
fn: `
|
|
func f34(m map[int]int) bool {
|
|
_, ok := m[5]
|
|
return ok
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t[$]5,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f35(m map[string]int) int {
|
|
return m["abc"]
|
|
}
|
|
`,
|
|
pos: []string{"\"abc\""},
|
|
},
|
|
{
|
|
fn: `
|
|
func f36(m map[string]int) bool {
|
|
_, ok := m["abc"]
|
|
return ok
|
|
}
|
|
`,
|
|
pos: []string{"\"abc\""},
|
|
},
|
|
// Bit test ops on amd64, issue 18943.
|
|
{
|
|
fn: `
|
|
func f37(a, b uint64) int {
|
|
if a&(1<<(b&63)) != 0 {
|
|
return 1
|
|
}
|
|
return -1
|
|
}
|
|
`,
|
|
pos: []string{"\tBTQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f38(a, b uint64) bool {
|
|
return a&(1<<(b&63)) != 0
|
|
}
|
|
`,
|
|
pos: []string{"\tBTQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f39(a uint64) int {
|
|
if a&(1<<60) != 0 {
|
|
return 1
|
|
}
|
|
return -1
|
|
}
|
|
`,
|
|
pos: []string{"\tBTQ\t\\$60"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f40(a uint64) bool {
|
|
return a&(1<<60) != 0
|
|
}
|
|
`,
|
|
pos: []string{"\tBTQ\t\\$60"},
|
|
},
|
|
// Intrinsic tests for math/bits
|
|
{
|
|
fn: `
|
|
func f41(a uint64) int {
|
|
return bits.TrailingZeros64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSFQ\t", "\tMOVL\t\\$64,", "\tCMOVQEQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f42(a uint32) int {
|
|
return bits.TrailingZeros32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSFQ\t", "\tORQ\t[^$]", "\tMOVQ\t\\$4294967296,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f43(a uint16) int {
|
|
return bits.TrailingZeros16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSFQ\t", "\tORQ\t\\$65536,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f44(a uint8) int {
|
|
return bits.TrailingZeros8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSFQ\t", "\tORQ\t\\$256,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f45(a uint64) uint64 {
|
|
return bits.ReverseBytes64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f46(a uint32) uint32 {
|
|
return bits.ReverseBytes32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSWAPL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f47(a uint16) uint16 {
|
|
return bits.ReverseBytes16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tROLW\t\\$8,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f48(a uint64) int {
|
|
return bits.Len64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f49(a uint32) int {
|
|
return bits.Len32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f50(a uint16) int {
|
|
return bits.Len16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
/* see ssa.go
|
|
{
|
|
fn:`
|
|
func f51(a uint8) int {
|
|
return bits.Len8(a)
|
|
}
|
|
`,
|
|
pos:[]string{"\tBSRQ\t"},
|
|
},
|
|
*/
|
|
{
|
|
fn: `
|
|
func f52(a uint) int {
|
|
return bits.Len(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f53(a uint64) int {
|
|
return bits.LeadingZeros64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f54(a uint32) int {
|
|
return bits.LeadingZeros32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f55(a uint16) int {
|
|
return bits.LeadingZeros16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
/* see ssa.go
|
|
{
|
|
fn:`
|
|
func f56(a uint8) int {
|
|
return bits.LeadingZeros8(a)
|
|
}
|
|
`,
|
|
pos:[]string{"\tBSRQ\t"},
|
|
},
|
|
*/
|
|
{
|
|
fn: `
|
|
func f57(a uint) int {
|
|
return bits.LeadingZeros(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tBSRQ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func pop1(x uint64) int {
|
|
return bits.OnesCount64(x)
|
|
}`,
|
|
pos: []string{"\tPOPCNTQ\t", "support_popcnt"},
|
|
},
|
|
{
|
|
fn: `
|
|
func pop2(x uint32) int {
|
|
return bits.OnesCount32(x)
|
|
}`,
|
|
pos: []string{"\tPOPCNTL\t", "support_popcnt"},
|
|
},
|
|
{
|
|
fn: `
|
|
func pop3(x uint16) int {
|
|
return bits.OnesCount16(x)
|
|
}`,
|
|
pos: []string{"\tPOPCNTL\t", "support_popcnt"},
|
|
},
|
|
{
|
|
fn: `
|
|
func pop4(x uint) int {
|
|
return bits.OnesCount(x)
|
|
}`,
|
|
pos: []string{"\tPOPCNTQ\t", "support_popcnt"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Sqrt(x)
|
|
}
|
|
`,
|
|
pos: []string{"SQRTSD"},
|
|
},
|
|
// multiplication merging tests
|
|
{
|
|
fn: `
|
|
func mul1(n int) int {
|
|
return 15*n + 31*n
|
|
}`,
|
|
pos: []string{"\tIMULQ\t[$]46"}, // 46*n
|
|
},
|
|
{
|
|
fn: `
|
|
func mul2(n int) int {
|
|
return 5*n + 7*(n+1) + 11*(n+2)
|
|
}`,
|
|
pos: []string{"\tIMULQ\t[$]23", "\tADDQ\t[$]29"}, // 23*n + 29
|
|
},
|
|
{
|
|
fn: `
|
|
func mul3(a, n int) int {
|
|
return a*n + 19*n
|
|
}`,
|
|
pos: []string{"\tADDQ\t[$]19", "\tIMULQ"}, // (a+19)*n
|
|
},
|
|
{
|
|
fn: `
|
|
func mul4(n int) int {
|
|
return 23*n - 9*n
|
|
}`,
|
|
pos: []string{"\tIMULQ\t[$]14"}, // 14*n
|
|
},
|
|
{
|
|
fn: `
|
|
func mul5(a, n int) int {
|
|
return a*n - 19*n
|
|
}`,
|
|
pos: []string{"\tADDQ\t[$]-19", "\tIMULQ"}, // (a-19)*n
|
|
},
|
|
|
|
// see issue 19595.
|
|
// We want to merge load+op in f58, but not in f59.
|
|
{
|
|
fn: `
|
|
func f58(p, q *int) {
|
|
x := *p
|
|
*q += x
|
|
}`,
|
|
pos: []string{"\tADDQ\t\\("},
|
|
},
|
|
{
|
|
fn: `
|
|
func f59(p, q *int) {
|
|
x := *p
|
|
for i := 0; i < 10; i++ {
|
|
*q += x
|
|
}
|
|
}`,
|
|
pos: []string{"\tADDQ\t[A-Z]"},
|
|
},
|
|
// Floating-point strength reduction
|
|
{
|
|
fn: `
|
|
func f60(f float64) float64 {
|
|
return f * 2.0
|
|
}`,
|
|
pos: []string{"\tADDSD\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f62(f float64) float64 {
|
|
return f / 16.0
|
|
}`,
|
|
pos: []string{"\tMULSD\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f63(f float64) float64 {
|
|
return f / 0.125
|
|
}`,
|
|
pos: []string{"\tMULSD\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f64(f float64) float64 {
|
|
return f / 0.5
|
|
}`,
|
|
pos: []string{"\tADDSD\t"},
|
|
},
|
|
// Check that compare to constant string uses 2/4/8 byte compares
|
|
{
|
|
fn: `
|
|
func f65(a string) bool {
|
|
return a == "xx"
|
|
}`,
|
|
pos: []string{"\tCMPW\t\\(.*\\), [$]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f66(a string) bool {
|
|
return a == "xxxx"
|
|
}`,
|
|
pos: []string{"\tCMPL\t\\(.*\\), [$]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f67(a string) bool {
|
|
return a == "xxxxxxxx"
|
|
}`,
|
|
pos: []string{"\tCMPQ\t[A-Z]"},
|
|
},
|
|
// Non-constant rotate
|
|
{
|
|
fn: `func rot64l(x uint64, y int) uint64 {
|
|
z := uint(y & 63)
|
|
return x << z | x >> (64-z)
|
|
}`,
|
|
pos: []string{"\tROLQ\t"},
|
|
},
|
|
{
|
|
fn: `func rot64r(x uint64, y int) uint64 {
|
|
z := uint(y & 63)
|
|
return x >> z | x << (64-z)
|
|
}`,
|
|
pos: []string{"\tRORQ\t"},
|
|
},
|
|
{
|
|
fn: `func rot32l(x uint32, y int) uint32 {
|
|
z := uint(y & 31)
|
|
return x << z | x >> (32-z)
|
|
}`,
|
|
pos: []string{"\tROLL\t"},
|
|
},
|
|
{
|
|
fn: `func rot32r(x uint32, y int) uint32 {
|
|
z := uint(y & 31)
|
|
return x >> z | x << (32-z)
|
|
}`,
|
|
pos: []string{"\tRORL\t"},
|
|
},
|
|
{
|
|
fn: `func rot16l(x uint16, y int) uint16 {
|
|
z := uint(y & 15)
|
|
return x << z | x >> (16-z)
|
|
}`,
|
|
pos: []string{"\tROLW\t"},
|
|
},
|
|
{
|
|
fn: `func rot16r(x uint16, y int) uint16 {
|
|
z := uint(y & 15)
|
|
return x >> z | x << (16-z)
|
|
}`,
|
|
pos: []string{"\tRORW\t"},
|
|
},
|
|
{
|
|
fn: `func rot8l(x uint8, y int) uint8 {
|
|
z := uint(y & 7)
|
|
return x << z | x >> (8-z)
|
|
}`,
|
|
pos: []string{"\tROLB\t"},
|
|
},
|
|
{
|
|
fn: `func rot8r(x uint8, y int) uint8 {
|
|
z := uint(y & 7)
|
|
return x >> z | x << (8-z)
|
|
}`,
|
|
pos: []string{"\tRORB\t"},
|
|
},
|
|
// Check that array compare uses 2/4/8 byte compares
|
|
{
|
|
fn: `
|
|
func f68(a,b [2]byte) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{"\tCMPW\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f69(a,b [3]uint16) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{
|
|
"\tCMPL\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
"\tCMPW\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a,b [3]int16) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{
|
|
"\tCMPL\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
"\tCMPW\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a,b [12]int8) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{
|
|
"\tCMPQ\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
"\tCMPL\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]",
|
|
},
|
|
},
|
|
{
|
|
fn: `
|
|
func f70(a,b [15]byte) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{"\tCMPQ\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f71(a,b unsafe.Pointer) bool { // This was a TODO in mapaccess1_faststr
|
|
return *((*[4]byte)(a)) != *((*[4]byte)(b))
|
|
}`,
|
|
pos: []string{"\tCMPL\t\\(.*\\), [A-Z]"},
|
|
},
|
|
{
|
|
// make sure assembly output has matching offset and base register.
|
|
fn: `
|
|
func f72(a, b int) int {
|
|
runtime.GC() // use some frame
|
|
return b
|
|
}
|
|
`,
|
|
pos: []string{"b\\+24\\(SP\\)"},
|
|
},
|
|
{
|
|
// check load combining
|
|
fn: `
|
|
func f73(a, b byte) (byte,byte) {
|
|
return f73(f73(a,b))
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVW\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f74(a, b uint16) (uint16,uint16) {
|
|
return f74(f74(a,b))
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f75(a, b uint32) (uint32,uint32) {
|
|
return f75(f75(a,b))
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t"},
|
|
},
|
|
// Make sure we don't put pointers in SSE registers across safe points.
|
|
{
|
|
fn: `
|
|
func $(p, q *[2]*int) {
|
|
a, b := p[0], p[1]
|
|
runtime.GC()
|
|
q[0], q[1] = a, b
|
|
}
|
|
`,
|
|
neg: []string{"MOVUPS"},
|
|
},
|
|
{
|
|
// check that stack store is optimized away
|
|
fn: `
|
|
func $() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
`,
|
|
pos: []string{"TEXT\t.*, [$]0-8"},
|
|
},
|
|
// math.Abs using integer registers
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Abs(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,"},
|
|
},
|
|
// math.Copysign using integer registers
|
|
{
|
|
fn: `
|
|
func $(x, y float64) float64 {
|
|
return math.Copysign(x, y)
|
|
}
|
|
`,
|
|
pos: []string{"\tSHLQ\t[$]1,", "\tSHRQ\t[$]1,", "\tSHRQ\t[$]63,", "\tSHLQ\t[$]63,", "\tORQ\t"},
|
|
},
|
|
// int <-> fp moves
|
|
{
|
|
fn: `
|
|
func $(x float64) uint64 {
|
|
return math.Float64bits(x+1) + 1
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\tX.*, [^X].*"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x float32) uint32 {
|
|
return math.Float32bits(x+1) + 1
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVL\tX.*, [^X].*"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x uint64) float64 {
|
|
return math.Float64frombits(x+1) + 1
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t[^X].*, X.*"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x uint32) float32 {
|
|
return math.Float32frombits(x+1) + 1
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVL\t[^X].*, X.*"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x uint32) bool {
|
|
return x > 4
|
|
}
|
|
`,
|
|
pos: []string{"\tSETHI\t.*\\(SP\\)"},
|
|
},
|
|
// Check that len() and cap() div by a constant power of two
|
|
// are compiled into SHRQ.
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return len(a) / 1024
|
|
}
|
|
`,
|
|
pos: []string{"\tSHRQ\t\\$10,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(s string) int {
|
|
return len(s) / (4097 >> 1)
|
|
}
|
|
`,
|
|
pos: []string{"\tSHRQ\t\\$11,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return cap(a) / ((1 << 11) + 2048)
|
|
}
|
|
`,
|
|
pos: []string{"\tSHRQ\t\\$12,"},
|
|
},
|
|
// Check that len() and cap() mod by a constant power of two
|
|
// are compiled into ANDQ.
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return len(a) % 1024
|
|
}
|
|
`,
|
|
pos: []string{"\tANDQ\t\\$1023,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(s string) int {
|
|
return len(s) % (4097 >> 1)
|
|
}
|
|
`,
|
|
pos: []string{"\tANDQ\t\\$2047,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return cap(a) % ((1 << 11) + 2048)
|
|
}
|
|
`,
|
|
pos: []string{"\tANDQ\t\\$4095,"},
|
|
},
|
|
{
|
|
// Test that small memmove was replaced with direct movs
|
|
fn: `
|
|
func $() {
|
|
x := [...]byte{1, 2, 3, 4, 5, 6, 7}
|
|
copy(x[1:], x[:])
|
|
}
|
|
`,
|
|
neg: []string{"memmove"},
|
|
},
|
|
{
|
|
// Same as above but with different size
|
|
fn: `
|
|
func $() {
|
|
x := [...]byte{1, 2, 3, 4}
|
|
copy(x[1:], x[:])
|
|
}
|
|
`,
|
|
neg: []string{"memmove"},
|
|
},
|
|
{
|
|
// Same as above but with different size
|
|
fn: `
|
|
func $() {
|
|
x := [...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
|
copy(x[1:], x[:])
|
|
}
|
|
`,
|
|
neg: []string{"memmove"},
|
|
},
|
|
// Nil checks before calling interface methods
|
|
{
|
|
fn: `
|
|
type I interface {
|
|
foo000()
|
|
foo001()
|
|
foo002()
|
|
foo003()
|
|
foo004()
|
|
foo005()
|
|
foo006()
|
|
foo007()
|
|
foo008()
|
|
foo009()
|
|
foo010()
|
|
foo011()
|
|
foo012()
|
|
foo013()
|
|
foo014()
|
|
foo015()
|
|
foo016()
|
|
foo017()
|
|
foo018()
|
|
foo019()
|
|
foo020()
|
|
foo021()
|
|
foo022()
|
|
foo023()
|
|
foo024()
|
|
foo025()
|
|
foo026()
|
|
foo027()
|
|
foo028()
|
|
foo029()
|
|
foo030()
|
|
foo031()
|
|
foo032()
|
|
foo033()
|
|
foo034()
|
|
foo035()
|
|
foo036()
|
|
foo037()
|
|
foo038()
|
|
foo039()
|
|
foo040()
|
|
foo041()
|
|
foo042()
|
|
foo043()
|
|
foo044()
|
|
foo045()
|
|
foo046()
|
|
foo047()
|
|
foo048()
|
|
foo049()
|
|
foo050()
|
|
foo051()
|
|
foo052()
|
|
foo053()
|
|
foo054()
|
|
foo055()
|
|
foo056()
|
|
foo057()
|
|
foo058()
|
|
foo059()
|
|
foo060()
|
|
foo061()
|
|
foo062()
|
|
foo063()
|
|
foo064()
|
|
foo065()
|
|
foo066()
|
|
foo067()
|
|
foo068()
|
|
foo069()
|
|
foo070()
|
|
foo071()
|
|
foo072()
|
|
foo073()
|
|
foo074()
|
|
foo075()
|
|
foo076()
|
|
foo077()
|
|
foo078()
|
|
foo079()
|
|
foo080()
|
|
foo081()
|
|
foo082()
|
|
foo083()
|
|
foo084()
|
|
foo085()
|
|
foo086()
|
|
foo087()
|
|
foo088()
|
|
foo089()
|
|
foo090()
|
|
foo091()
|
|
foo092()
|
|
foo093()
|
|
foo094()
|
|
foo095()
|
|
foo096()
|
|
foo097()
|
|
foo098()
|
|
foo099()
|
|
foo100()
|
|
foo101()
|
|
foo102()
|
|
foo103()
|
|
foo104()
|
|
foo105()
|
|
foo106()
|
|
foo107()
|
|
foo108()
|
|
foo109()
|
|
foo110()
|
|
foo111()
|
|
foo112()
|
|
foo113()
|
|
foo114()
|
|
foo115()
|
|
foo116()
|
|
foo117()
|
|
foo118()
|
|
foo119()
|
|
foo120()
|
|
foo121()
|
|
foo122()
|
|
foo123()
|
|
foo124()
|
|
foo125()
|
|
foo126()
|
|
foo127()
|
|
foo128()
|
|
foo129()
|
|
foo130()
|
|
foo131()
|
|
foo132()
|
|
foo133()
|
|
foo134()
|
|
foo135()
|
|
foo136()
|
|
foo137()
|
|
foo138()
|
|
foo139()
|
|
foo140()
|
|
foo141()
|
|
foo142()
|
|
foo143()
|
|
foo144()
|
|
foo145()
|
|
foo146()
|
|
foo147()
|
|
foo148()
|
|
foo149()
|
|
foo150()
|
|
foo151()
|
|
foo152()
|
|
foo153()
|
|
foo154()
|
|
foo155()
|
|
foo156()
|
|
foo157()
|
|
foo158()
|
|
foo159()
|
|
foo160()
|
|
foo161()
|
|
foo162()
|
|
foo163()
|
|
foo164()
|
|
foo165()
|
|
foo166()
|
|
foo167()
|
|
foo168()
|
|
foo169()
|
|
foo170()
|
|
foo171()
|
|
foo172()
|
|
foo173()
|
|
foo174()
|
|
foo175()
|
|
foo176()
|
|
foo177()
|
|
foo178()
|
|
foo179()
|
|
foo180()
|
|
foo181()
|
|
foo182()
|
|
foo183()
|
|
foo184()
|
|
foo185()
|
|
foo186()
|
|
foo187()
|
|
foo188()
|
|
foo189()
|
|
foo190()
|
|
foo191()
|
|
foo192()
|
|
foo193()
|
|
foo194()
|
|
foo195()
|
|
foo196()
|
|
foo197()
|
|
foo198()
|
|
foo199()
|
|
foo200()
|
|
foo201()
|
|
foo202()
|
|
foo203()
|
|
foo204()
|
|
foo205()
|
|
foo206()
|
|
foo207()
|
|
foo208()
|
|
foo209()
|
|
foo210()
|
|
foo211()
|
|
foo212()
|
|
foo213()
|
|
foo214()
|
|
foo215()
|
|
foo216()
|
|
foo217()
|
|
foo218()
|
|
foo219()
|
|
foo220()
|
|
foo221()
|
|
foo222()
|
|
foo223()
|
|
foo224()
|
|
foo225()
|
|
foo226()
|
|
foo227()
|
|
foo228()
|
|
foo229()
|
|
foo230()
|
|
foo231()
|
|
foo232()
|
|
foo233()
|
|
foo234()
|
|
foo235()
|
|
foo236()
|
|
foo237()
|
|
foo238()
|
|
foo239()
|
|
foo240()
|
|
foo241()
|
|
foo242()
|
|
foo243()
|
|
foo244()
|
|
foo245()
|
|
foo246()
|
|
foo247()
|
|
foo248()
|
|
foo249()
|
|
foo250()
|
|
foo251()
|
|
foo252()
|
|
foo253()
|
|
foo254()
|
|
foo255()
|
|
foo256()
|
|
foo257()
|
|
foo258()
|
|
foo259()
|
|
foo260()
|
|
foo261()
|
|
foo262()
|
|
foo263()
|
|
foo264()
|
|
foo265()
|
|
foo266()
|
|
foo267()
|
|
foo268()
|
|
foo269()
|
|
foo270()
|
|
foo271()
|
|
foo272()
|
|
foo273()
|
|
foo274()
|
|
foo275()
|
|
foo276()
|
|
foo277()
|
|
foo278()
|
|
foo279()
|
|
foo280()
|
|
foo281()
|
|
foo282()
|
|
foo283()
|
|
foo284()
|
|
foo285()
|
|
foo286()
|
|
foo287()
|
|
foo288()
|
|
foo289()
|
|
foo290()
|
|
foo291()
|
|
foo292()
|
|
foo293()
|
|
foo294()
|
|
foo295()
|
|
foo296()
|
|
foo297()
|
|
foo298()
|
|
foo299()
|
|
foo300()
|
|
foo301()
|
|
foo302()
|
|
foo303()
|
|
foo304()
|
|
foo305()
|
|
foo306()
|
|
foo307()
|
|
foo308()
|
|
foo309()
|
|
foo310()
|
|
foo311()
|
|
foo312()
|
|
foo313()
|
|
foo314()
|
|
foo315()
|
|
foo316()
|
|
foo317()
|
|
foo318()
|
|
foo319()
|
|
foo320()
|
|
foo321()
|
|
foo322()
|
|
foo323()
|
|
foo324()
|
|
foo325()
|
|
foo326()
|
|
foo327()
|
|
foo328()
|
|
foo329()
|
|
foo330()
|
|
foo331()
|
|
foo332()
|
|
foo333()
|
|
foo334()
|
|
foo335()
|
|
foo336()
|
|
foo337()
|
|
foo338()
|
|
foo339()
|
|
foo340()
|
|
foo341()
|
|
foo342()
|
|
foo343()
|
|
foo344()
|
|
foo345()
|
|
foo346()
|
|
foo347()
|
|
foo348()
|
|
foo349()
|
|
foo350()
|
|
foo351()
|
|
foo352()
|
|
foo353()
|
|
foo354()
|
|
foo355()
|
|
foo356()
|
|
foo357()
|
|
foo358()
|
|
foo359()
|
|
foo360()
|
|
foo361()
|
|
foo362()
|
|
foo363()
|
|
foo364()
|
|
foo365()
|
|
foo366()
|
|
foo367()
|
|
foo368()
|
|
foo369()
|
|
foo370()
|
|
foo371()
|
|
foo372()
|
|
foo373()
|
|
foo374()
|
|
foo375()
|
|
foo376()
|
|
foo377()
|
|
foo378()
|
|
foo379()
|
|
foo380()
|
|
foo381()
|
|
foo382()
|
|
foo383()
|
|
foo384()
|
|
foo385()
|
|
foo386()
|
|
foo387()
|
|
foo388()
|
|
foo389()
|
|
foo390()
|
|
foo391()
|
|
foo392()
|
|
foo393()
|
|
foo394()
|
|
foo395()
|
|
foo396()
|
|
foo397()
|
|
foo398()
|
|
foo399()
|
|
foo400()
|
|
foo401()
|
|
foo402()
|
|
foo403()
|
|
foo404()
|
|
foo405()
|
|
foo406()
|
|
foo407()
|
|
foo408()
|
|
foo409()
|
|
foo410()
|
|
foo411()
|
|
foo412()
|
|
foo413()
|
|
foo414()
|
|
foo415()
|
|
foo416()
|
|
foo417()
|
|
foo418()
|
|
foo419()
|
|
foo420()
|
|
foo421()
|
|
foo422()
|
|
foo423()
|
|
foo424()
|
|
foo425()
|
|
foo426()
|
|
foo427()
|
|
foo428()
|
|
foo429()
|
|
foo430()
|
|
foo431()
|
|
foo432()
|
|
foo433()
|
|
foo434()
|
|
foo435()
|
|
foo436()
|
|
foo437()
|
|
foo438()
|
|
foo439()
|
|
foo440()
|
|
foo441()
|
|
foo442()
|
|
foo443()
|
|
foo444()
|
|
foo445()
|
|
foo446()
|
|
foo447()
|
|
foo448()
|
|
foo449()
|
|
foo450()
|
|
foo451()
|
|
foo452()
|
|
foo453()
|
|
foo454()
|
|
foo455()
|
|
foo456()
|
|
foo457()
|
|
foo458()
|
|
foo459()
|
|
foo460()
|
|
foo461()
|
|
foo462()
|
|
foo463()
|
|
foo464()
|
|
foo465()
|
|
foo466()
|
|
foo467()
|
|
foo468()
|
|
foo469()
|
|
foo470()
|
|
foo471()
|
|
foo472()
|
|
foo473()
|
|
foo474()
|
|
foo475()
|
|
foo476()
|
|
foo477()
|
|
foo478()
|
|
foo479()
|
|
foo480()
|
|
foo481()
|
|
foo482()
|
|
foo483()
|
|
foo484()
|
|
foo485()
|
|
foo486()
|
|
foo487()
|
|
foo488()
|
|
foo489()
|
|
foo490()
|
|
foo491()
|
|
foo492()
|
|
foo493()
|
|
foo494()
|
|
foo495()
|
|
foo496()
|
|
foo497()
|
|
foo498()
|
|
foo499()
|
|
foo500()
|
|
foo501()
|
|
foo502()
|
|
foo503()
|
|
foo504()
|
|
foo505()
|
|
foo506()
|
|
foo507()
|
|
foo508()
|
|
foo509()
|
|
foo510()
|
|
foo511()
|
|
}
|
|
func $(i I) {
|
|
i.foo511()
|
|
}
|
|
`,
|
|
pos: []string{"TESTB"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(i I) {
|
|
i.foo001()
|
|
}
|
|
`,
|
|
neg: []string{"TESTB"},
|
|
},
|
|
{
|
|
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"},
|
|
},
|
|
}
|
|
|
|
var linux386Tests = []*asmTest{
|
|
{
|
|
fn: `
|
|
func f0(b []byte) uint32 {
|
|
return binary.LittleEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVL\t\\(.*\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f1(b []byte, i int) uint32 {
|
|
return binary.LittleEndian.Uint32(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"},
|
|
},
|
|
|
|
// multiplication by powers of two
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return 32*n
|
|
}
|
|
`,
|
|
pos: []string{"SHLL"},
|
|
neg: []string{"IMULL"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return -64*n
|
|
}
|
|
`,
|
|
pos: []string{"SHLL"},
|
|
neg: []string{"IMULL"},
|
|
},
|
|
|
|
// multiplication merging tests
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return 9*n + 14*n
|
|
}`,
|
|
pos: []string{"\tIMULL\t[$]23"}, // 23*n
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a, n int) int {
|
|
return 19*a + a*n
|
|
}`,
|
|
pos: []string{"\tADDL\t[$]19", "\tIMULL"}, // (n+19)*a
|
|
},
|
|
{
|
|
// check that stack store is optimized away
|
|
fn: `
|
|
func $() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
`,
|
|
pos: []string{"TEXT\t.*, [$]0-4"},
|
|
},
|
|
{
|
|
fn: `
|
|
func mul3(n int) int {
|
|
return 23*n - 9*n
|
|
}`,
|
|
pos: []string{"\tIMULL\t[$]14"}, // 14*n
|
|
},
|
|
{
|
|
fn: `
|
|
func mul4(a, n int) int {
|
|
return n*a - a*19
|
|
}`,
|
|
pos: []string{"\tADDL\t[$]-19", "\tIMULL"}, // (n-19)*a
|
|
},
|
|
// Check that len() and cap() div by a constant power of two
|
|
// are compiled into SHRL.
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return len(a) / 1024
|
|
}
|
|
`,
|
|
pos: []string{"\tSHRL\t\\$10,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(s string) int {
|
|
return len(s) / (4097 >> 1)
|
|
}
|
|
`,
|
|
pos: []string{"\tSHRL\t\\$11,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return cap(a) / ((1 << 11) + 2048)
|
|
}
|
|
`,
|
|
pos: []string{"\tSHRL\t\\$12,"},
|
|
},
|
|
// Check that len() and cap() mod by a constant power of two
|
|
// are compiled into ANDL.
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return len(a) % 1024
|
|
}
|
|
`,
|
|
pos: []string{"\tANDL\t\\$1023,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(s string) int {
|
|
return len(s) % (4097 >> 1)
|
|
}
|
|
`,
|
|
pos: []string{"\tANDL\t\\$2047,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a []int) int {
|
|
return cap(a) % ((1 << 11) + 2048)
|
|
}
|
|
`,
|
|
pos: []string{"\tANDL\t\\$4095,"},
|
|
},
|
|
{
|
|
// Test that small memmove was replaced with direct movs
|
|
fn: `
|
|
func $() {
|
|
x := [...]byte{1, 2, 3, 4, 5, 6, 7}
|
|
copy(x[1:], x[:])
|
|
}
|
|
`,
|
|
neg: []string{"memmove"},
|
|
},
|
|
{
|
|
// Same as above but with different size
|
|
fn: `
|
|
func $() {
|
|
x := [...]byte{1, 2, 3, 4}
|
|
copy(x[1:], x[:])
|
|
}
|
|
`,
|
|
neg: []string{"memmove"},
|
|
},
|
|
|
|
// Intrinsic tests for math
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Sqrt(x)
|
|
}
|
|
`,
|
|
pos: []string{"FSQRT|SQRTSD"}, // 387|sse2
|
|
},
|
|
}
|
|
|
|
var linuxS390XTests = []*asmTest{
|
|
{
|
|
fn: `
|
|
func f0(b []byte) uint32 {
|
|
return binary.LittleEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWBR\t\\(.*\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f1(b []byte, i int) uint32 {
|
|
return binary.LittleEndian.Uint32(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWBR\t\\(.*\\)\\(.*\\*1\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f2(b []byte) uint64 {
|
|
return binary.LittleEndian.Uint64(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVDBR\t\\(.*\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f3(b []byte, i int) uint64 {
|
|
return binary.LittleEndian.Uint64(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVDBR\t\\(.*\\)\\(.*\\*1\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f4(b []byte) uint32 {
|
|
return binary.BigEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWZ\t\\(.*\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f5(b []byte, i int) uint32 {
|
|
return binary.BigEndian.Uint32(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWZ\t\\(.*\\)\\(.*\\*1\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f6(b []byte) uint64 {
|
|
return binary.BigEndian.Uint64(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVD\t\\(.*\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f7(b []byte, i int) uint64 {
|
|
return binary.BigEndian.Uint64(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVD\t\\(.*\\)\\(.*\\*1\\),"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f8(x uint64) uint64 {
|
|
return x<<7 + x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tRLLG\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f9(x uint64) uint64 {
|
|
return x<<7 | x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tRLLG\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f10(x uint64) uint64 {
|
|
return x<<7 ^ x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tRLLG\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f11(x uint32) uint32 {
|
|
return x<<7 + x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tRLL\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f12(x uint32) uint32 {
|
|
return x<<7 | x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tRLL\t[$]7,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f13(x uint32) uint32 {
|
|
return x<<7 ^ x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tRLL\t[$]7,"},
|
|
},
|
|
// Fused multiply-add/sub instructions.
|
|
{
|
|
fn: `
|
|
func f14(x, y, z float64) float64 {
|
|
return x * y + z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMADD\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f15(x, y, z float64) float64 {
|
|
return x * y - z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMSUB\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f16(x, y, z float32) float32 {
|
|
return x * y + z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMADDS\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f17(x, y, z float32) float32 {
|
|
return x * y - z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMSUBS\t"},
|
|
},
|
|
// Intrinsic tests for math/bits
|
|
{
|
|
fn: `
|
|
func f18(a uint64) int {
|
|
return bits.TrailingZeros64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f19(a uint32) int {
|
|
return bits.TrailingZeros32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t", "\tMOVWZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f20(a uint16) int {
|
|
return bits.TrailingZeros16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t", "\tOR\t\\$65536,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f21(a uint8) int {
|
|
return bits.TrailingZeros8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t", "\tOR\t\\$256,"},
|
|
},
|
|
// Intrinsic tests for math/bits
|
|
{
|
|
fn: `
|
|
func f22(a uint64) uint64 {
|
|
return bits.ReverseBytes64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVDBR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f23(a uint32) uint32 {
|
|
return bits.ReverseBytes32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWBR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f24(a uint64) int {
|
|
return bits.Len64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f25(a uint32) int {
|
|
return bits.Len32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f26(a uint16) int {
|
|
return bits.Len16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f27(a uint8) int {
|
|
return bits.Len8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f28(a uint) int {
|
|
return bits.Len(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f29(a uint64) int {
|
|
return bits.LeadingZeros64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f30(a uint32) int {
|
|
return bits.LeadingZeros32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f31(a uint16) int {
|
|
return bits.LeadingZeros16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f32(a uint8) int {
|
|
return bits.LeadingZeros8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f33(a uint) int {
|
|
return bits.LeadingZeros(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFLOGR\t"},
|
|
},
|
|
// Intrinsic tests for math.
|
|
{
|
|
fn: `
|
|
func ceil(x float64) float64 {
|
|
return math.Ceil(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tFIDBR\t[$]6"},
|
|
},
|
|
{
|
|
fn: `
|
|
func floor(x float64) float64 {
|
|
return math.Floor(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tFIDBR\t[$]7"},
|
|
},
|
|
{
|
|
fn: `
|
|
func round(x float64) float64 {
|
|
return math.Round(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tFIDBR\t[$]1"},
|
|
},
|
|
{
|
|
fn: `
|
|
func trunc(x float64) float64 {
|
|
return math.Trunc(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tFIDBR\t[$]5"},
|
|
},
|
|
{
|
|
fn: `
|
|
func roundToEven(x float64) float64 {
|
|
return math.RoundToEven(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tFIDBR\t[$]4"},
|
|
},
|
|
{
|
|
// check that stack store is optimized away
|
|
fn: `
|
|
func $() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
`,
|
|
pos: []string{"TEXT\t.*, [$]0-8"},
|
|
},
|
|
// Constant propagation through raw bits conversions.
|
|
{
|
|
// uint32 constant converted to float32 constant
|
|
fn: `
|
|
func $(x float32) float32 {
|
|
if x > math.Float32frombits(0x3f800000) {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
`,
|
|
pos: []string{"\tFMOVS\t[$]f32.3f800000\\(SB\\)"},
|
|
},
|
|
{
|
|
// float32 constant converted to uint32 constant
|
|
fn: `
|
|
func $(x uint32) uint32 {
|
|
if x > math.Float32bits(1) {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
`,
|
|
neg: []string{"\tFMOVS\t"},
|
|
},
|
|
// Constant propagation through float comparisons.
|
|
{
|
|
fn: `
|
|
func $() bool {
|
|
return 0.5 == float64(uint32(1)) ||
|
|
1.5 > float64(uint64(1<<63)) ||
|
|
math.NaN() == math.NaN()
|
|
}
|
|
`,
|
|
pos: []string{"\tMOV(B|BZ|D)\t[$]0,"},
|
|
neg: []string{"\tFCMPU\t", "\tMOV(B|BZ|D)\t[$]1,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $() bool {
|
|
return float32(0.5) <= float32(int64(1)) &&
|
|
float32(1.5) >= float32(int32(-1<<31)) &&
|
|
float32(math.NaN()) != float32(math.NaN())
|
|
}
|
|
`,
|
|
pos: []string{"\tMOV(B|BZ|D)\t[$]1,"},
|
|
neg: []string{"\tCEBR\t", "\tMOV(B|BZ|D)\t[$]0,"},
|
|
},
|
|
// math tests
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Abs(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tLPDFR\t"},
|
|
neg: []string{"\tMOVD\t"}, // no integer loads/stores
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x float32) float32 {
|
|
return float32(math.Abs(float64(x)))
|
|
}
|
|
`,
|
|
pos: []string{"\tLPDFR\t"},
|
|
neg: []string{"\tLDEBR\t", "\tLEDBR\t"}, // no float64 conversion
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Float64frombits(math.Float64bits(x)|1<<63)
|
|
}
|
|
`,
|
|
pos: []string{"\tLNDFR\t"},
|
|
neg: []string{"\tMOVD\t"}, // no integer loads/stores
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return -math.Abs(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tLNDFR\t"},
|
|
neg: []string{"\tMOVD\t"}, // no integer loads/stores
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x, y float64) float64 {
|
|
return math.Copysign(x, y)
|
|
}
|
|
`,
|
|
pos: []string{"\tCPSDR\t"},
|
|
neg: []string{"\tMOVD\t"}, // no integer loads/stores
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Copysign(x, -1)
|
|
}
|
|
`,
|
|
pos: []string{"\tLNDFR\t"},
|
|
neg: []string{"\tMOVD\t"}, // no integer loads/stores
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Copysign(-1, x)
|
|
}
|
|
`,
|
|
pos: []string{"\tCPSDR\t"},
|
|
neg: []string{"\tMOVD\t"}, // no integer loads/stores
|
|
},
|
|
}
|
|
|
|
var linuxARMTests = []*asmTest{
|
|
// multiplication by powers of two
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return 16*n
|
|
}
|
|
`,
|
|
pos: []string{"\tSLL\t[$]4"},
|
|
neg: []string{"\tMUL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return -32*n
|
|
}
|
|
`,
|
|
pos: []string{"\tSLL\t[$]5"},
|
|
neg: []string{"\tMUL\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f0(x uint32) uint32 {
|
|
return x<<7 + x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVW\tR[0-9]+@>25,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f1(x uint32) uint32 {
|
|
return x<<7 | x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVW\tR[0-9]+@>25,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f2(x uint32) uint32 {
|
|
return x<<7 ^ x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVW\tR[0-9]+@>25,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f3(a uint64) int {
|
|
return bits.Len64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f4(a uint32) int {
|
|
return bits.Len32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f5(a uint16) int {
|
|
return bits.Len16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f6(a uint8) int {
|
|
return bits.Len8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f7(a uint) int {
|
|
return bits.Len(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f8(a uint64) int {
|
|
return bits.LeadingZeros64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f9(a uint32) int {
|
|
return bits.LeadingZeros32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f10(a uint16) int {
|
|
return bits.LeadingZeros16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f11(a uint8) int {
|
|
return bits.LeadingZeros8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f12(a uint) int {
|
|
return bits.LeadingZeros(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
// make sure assembly output has matching offset and base register.
|
|
fn: `
|
|
func f13(a, b int) int {
|
|
runtime.GC() // use some frame
|
|
return b
|
|
}
|
|
`,
|
|
pos: []string{"b\\+4\\(FP\\)"},
|
|
},
|
|
{
|
|
// check that stack store is optimized away
|
|
fn: `
|
|
func $() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
`,
|
|
pos: []string{"TEXT\t.*, [$]-4-4"},
|
|
},
|
|
}
|
|
|
|
var linuxARM64Tests = []*asmTest{
|
|
// multiplication by powers of two
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return 64*n
|
|
}
|
|
`,
|
|
pos: []string{"\tLSL\t[$]6"},
|
|
neg: []string{"\tMUL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(n int) int {
|
|
return -128*n
|
|
}
|
|
`,
|
|
pos: []string{"\tLSL\t[$]7"},
|
|
neg: []string{"\tMUL\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f0(x uint64) uint64 {
|
|
return x<<7 + x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROR\t[$]57,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f1(x uint64) uint64 {
|
|
return x<<7 | x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROR\t[$]57,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f2(x uint64) uint64 {
|
|
return x<<7 ^ x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROR\t[$]57,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f3(x uint32) uint32 {
|
|
return x<<7 + x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tRORW\t[$]25,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f4(x uint32) uint32 {
|
|
return x<<7 | x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tRORW\t[$]25,"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f5(x uint32) uint32 {
|
|
return x<<7 ^ x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tRORW\t[$]25,"},
|
|
},
|
|
{
|
|
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"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f22(a uint64) uint64 {
|
|
return bits.ReverseBytes64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tREV\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f23(a uint32) uint32 {
|
|
return bits.ReverseBytes32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tREVW\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f24(a uint64) int {
|
|
return bits.Len64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f25(a uint32) int {
|
|
return bits.Len32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f26(a uint16) int {
|
|
return bits.Len16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f27(a uint8) int {
|
|
return bits.Len8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f28(a uint) int {
|
|
return bits.Len(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f29(a uint64) int {
|
|
return bits.LeadingZeros64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f30(a uint32) int {
|
|
return bits.LeadingZeros32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f31(a uint16) int {
|
|
return bits.LeadingZeros16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f32(a uint8) int {
|
|
return bits.LeadingZeros8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f33(a uint) int {
|
|
return bits.LeadingZeros(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f34(a uint64) uint64 {
|
|
return a & ((1<<63)-1)
|
|
}
|
|
`,
|
|
pos: []string{"\tAND\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f35(a uint64) uint64 {
|
|
return a & (1<<63)
|
|
}
|
|
`,
|
|
pos: []string{"\tAND\t"},
|
|
},
|
|
{
|
|
// make sure offsets are folded into load and store.
|
|
fn: `
|
|
func f36(_, a [20]byte) (b [20]byte) {
|
|
b = a
|
|
return
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVD\t\"\"\\.a\\+[0-9]+\\(FP\\), R[0-9]+", "\tMOVD\tR[0-9]+, \"\"\\.b\\+[0-9]+\\(FP\\)"},
|
|
},
|
|
{
|
|
// check that stack store is optimized away
|
|
fn: `
|
|
func $() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
`,
|
|
pos: []string{"TEXT\t.*, [$]-8-8"},
|
|
},
|
|
{
|
|
// 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"},
|
|
},
|
|
{
|
|
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"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x uint64) int {
|
|
return bits.OnesCount64(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tVCNT\t", "\tVUADDLV\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x uint32) int {
|
|
return bits.OnesCount32(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tVCNT\t", "\tVUADDLV\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(x uint16) int {
|
|
return bits.OnesCount16(x)
|
|
}
|
|
`,
|
|
pos: []string{"\tVCNT\t", "\tVUADDLV\t"},
|
|
},
|
|
// Load-combining tests.
|
|
{
|
|
fn: `
|
|
func $(b []byte) uint64 {
|
|
return binary.LittleEndian.Uint64(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVD\t\\(R[0-9]+\\)"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, i int) uint64 {
|
|
return binary.LittleEndian.Uint64(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVD\t\\(R[0-9]+\\)"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte) uint32 {
|
|
return binary.LittleEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWU\t\\(R[0-9]+\\)"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, i int) uint32 {
|
|
return binary.LittleEndian.Uint32(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWU\t\\(R[0-9]+\\)"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte) uint64 {
|
|
return binary.BigEndian.Uint64(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tREV\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, i int) uint64 {
|
|
return binary.BigEndian.Uint64(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tREV\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte) uint32 {
|
|
return binary.BigEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tREVW\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, i int) uint32 {
|
|
return binary.BigEndian.Uint32(b[i:])
|
|
}
|
|
`,
|
|
pos: []string{"\tREVW\t"},
|
|
},
|
|
{
|
|
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"},
|
|
},
|
|
// Intrinsic tests for math.
|
|
{
|
|
fn: `
|
|
func sqrt(x float64) float64 {
|
|
return math.Sqrt(x)
|
|
}
|
|
`,
|
|
pos: []string{"FSQRTD"},
|
|
},
|
|
{
|
|
fn: `
|
|
func ceil(x float64) float64 {
|
|
return math.Ceil(x)
|
|
}
|
|
`,
|
|
pos: []string{"FRINTPD"},
|
|
},
|
|
{
|
|
fn: `
|
|
func floor(x float64) float64 {
|
|
return math.Floor(x)
|
|
}
|
|
`,
|
|
pos: []string{"FRINTMD"},
|
|
},
|
|
{
|
|
fn: `
|
|
func round(x float64) float64 {
|
|
return math.Round(x)
|
|
}
|
|
`,
|
|
pos: []string{"FRINTAD"},
|
|
},
|
|
{
|
|
fn: `
|
|
func trunc(x float64) float64 {
|
|
return math.Trunc(x)
|
|
}
|
|
`,
|
|
pos: []string{"FRINTZD"},
|
|
},
|
|
{
|
|
// 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"},
|
|
},
|
|
// 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"},
|
|
},
|
|
{
|
|
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"},
|
|
},
|
|
// Check that stores are combine into larger stores
|
|
{
|
|
fn: `
|
|
func $(b []byte, v uint16) {
|
|
binary.LittleEndian.PutUint16(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"MOVH"},
|
|
neg: []string{"MOVB"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, v uint32) {
|
|
binary.LittleEndian.PutUint32(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"MOVW"},
|
|
neg: []string{"MOVB", "MOVH"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(b []byte, v uint64) {
|
|
binary.LittleEndian.PutUint64(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"MOVD"},
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
},
|
|
}
|
|
|
|
var linuxMIPSTests = []*asmTest{
|
|
// Intrinsic tests for math/bits
|
|
{
|
|
fn: `
|
|
func f0(a uint64) int {
|
|
return bits.Len64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f1(a uint32) int {
|
|
return bits.Len32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f2(a uint16) int {
|
|
return bits.Len16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f3(a uint8) int {
|
|
return bits.Len8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f4(a uint) int {
|
|
return bits.Len(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f5(a uint64) int {
|
|
return bits.LeadingZeros64(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f6(a uint32) int {
|
|
return bits.LeadingZeros32(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f7(a uint16) int {
|
|
return bits.LeadingZeros16(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f8(a uint8) int {
|
|
return bits.LeadingZeros8(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f9(a uint) int {
|
|
return bits.LeadingZeros(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tCLZ\t"},
|
|
},
|
|
// Intrinsic tests for math.
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Sqrt(x)
|
|
}
|
|
`,
|
|
pos: []string{"SQRTD"},
|
|
},
|
|
{
|
|
// check that stack store is optimized away
|
|
fn: `
|
|
func $() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
`,
|
|
pos: []string{"TEXT\t.*, [$]-4-4"},
|
|
},
|
|
}
|
|
|
|
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"},
|
|
},
|
|
// Intrinsic tests for math.
|
|
{
|
|
fn: `
|
|
func $(x float64) float64 {
|
|
return math.Sqrt(x)
|
|
}
|
|
`,
|
|
pos: []string{"SQRTD"},
|
|
},
|
|
}
|
|
|
|
var linuxPPC64LETests = []*asmTest{
|
|
// Fused multiply-add/sub instructions.
|
|
{
|
|
fn: `
|
|
func f0(x, y, z float64) float64 {
|
|
return x * y + z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMADD\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f1(x, y, z float64) float64 {
|
|
return x * y - z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMSUB\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f2(x, y, z float32) float32 {
|
|
return x * y + z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMADDS\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f3(x, y, z float32) float32 {
|
|
return x * y - z
|
|
}
|
|
`,
|
|
pos: []string{"\tFMSUBS\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f4(x uint32) uint32 {
|
|
return x<<7 | x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tROTLW\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f5(x uint32) uint32 {
|
|
return x<<7 + x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tROTLW\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f6(x uint32) uint32 {
|
|
return x<<7 ^ x>>25
|
|
}
|
|
`,
|
|
pos: []string{"\tROTLW\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f7(x uint64) uint64 {
|
|
return x<<7 | x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROTL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f8(x uint64) uint64 {
|
|
return x<<7 + x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROTL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f9(x uint64) uint64 {
|
|
return x<<7 ^ x>>57
|
|
}
|
|
`,
|
|
pos: []string{"\tROTL\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f10(a uint32) uint32 {
|
|
return bits.RotateLeft32(a, 9)
|
|
}
|
|
`,
|
|
pos: []string{"\tROTLW\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f11(a uint64) uint64 {
|
|
return bits.RotateLeft64(a, 37)
|
|
}
|
|
`,
|
|
pos: []string{"\tROTL\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f12(a, b float64) float64 {
|
|
return math.Copysign(a, b)
|
|
}
|
|
`,
|
|
pos: []string{"\tFCPSGN\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f13(a float64) float64 {
|
|
return math.Abs(a)
|
|
}
|
|
`,
|
|
pos: []string{"\tFABS\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f14(b []byte) uint16 {
|
|
return binary.LittleEndian.Uint16(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVHZ\t"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f15(b []byte) uint32 {
|
|
return binary.LittleEndian.Uint32(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVWZ\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f16(b []byte) uint64 {
|
|
return binary.LittleEndian.Uint64(b)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVD\t"},
|
|
neg: []string{"MOVBZ", "MOVHZ", "MOVWZ"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f17(b []byte, v uint16) {
|
|
binary.LittleEndian.PutUint16(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVH\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f18(b []byte, v uint32) {
|
|
binary.LittleEndian.PutUint32(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVW\t"},
|
|
},
|
|
|
|
{
|
|
fn: `
|
|
func f19(b []byte, v uint64) {
|
|
binary.LittleEndian.PutUint64(b, v)
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVD\t"},
|
|
neg: []string{"MOVB", "MOVH", "MOVW"},
|
|
},
|
|
|
|
{
|
|
// check that stack store is optimized away
|
|
fn: `
|
|
func $() int {
|
|
var x int
|
|
return *(&x)
|
|
}
|
|
`,
|
|
pos: []string{"TEXT\t.*, [$]0-8"},
|
|
},
|
|
// Constant propagation through raw bits conversions.
|
|
{
|
|
// uint32 constant converted to float32 constant
|
|
fn: `
|
|
func $(x float32) float32 {
|
|
if x > math.Float32frombits(0x3f800000) {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
`,
|
|
pos: []string{"\tFMOVS\t[$]f32.3f800000\\(SB\\)"},
|
|
},
|
|
{
|
|
// float32 constant converted to uint32 constant
|
|
fn: `
|
|
func $(x uint32) uint32 {
|
|
if x > math.Float32bits(1) {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
`,
|
|
neg: []string{"\tFMOVS\t"},
|
|
},
|
|
}
|
|
|
|
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.
|
|
{
|
|
fn: `
|
|
func $() [16]byte {
|
|
var a [16]byte
|
|
return a
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t\\$0, \"\""},
|
|
},
|
|
// Array copy.
|
|
{
|
|
fn: `
|
|
func $(a [16]byte) (b [16]byte) {
|
|
b = a
|
|
return
|
|
}
|
|
`,
|
|
pos: []string{"\tMOVQ\t\"\"\\.a\\+[0-9]+\\(SP\\), (AX|CX)", "\tMOVQ\t(AX|CX), \"\"\\.b\\+[0-9]+\\(SP\\)"},
|
|
},
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
|
|
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 {
|
|
return x % 3 // frontend rewrites it as HMUL with 2863311531, the LITERAL node has unknown Pos
|
|
}
|
|
`
|