mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Introduce a new SSA pass to generate CondSelect intstrutions, and add CondSelect lowering rules for arm64. In order to make the CSEL instruction easier to optimize, and to simplify the introduction of CSNEG, CSINC, and CSINV in the future, modify the CSEL instruction to accept a condition code in the aux field. Notably, this change makes the go1 Gzip benchmark more than 10% faster. Benchmarks on a Cavium ThunderX: name old time/op new time/op delta BinaryTree17-96 15.9s ± 6% 16.0s ± 4% ~ (p=0.968 n=10+9) Fannkuch11-96 7.17s ± 0% 7.00s ± 0% -2.43% (p=0.000 n=8+9) FmtFprintfEmpty-96 208ns ± 1% 207ns ± 0% ~ (p=0.152 n=10+8) FmtFprintfString-96 379ns ± 0% 375ns ± 0% -0.95% (p=0.000 n=10+9) FmtFprintfInt-96 385ns ± 0% 383ns ± 0% -0.52% (p=0.000 n=9+10) FmtFprintfIntInt-96 591ns ± 0% 586ns ± 0% -0.85% (p=0.006 n=7+9) FmtFprintfPrefixedInt-96 656ns ± 0% 667ns ± 0% +1.71% (p=0.000 n=10+10) FmtFprintfFloat-96 967ns ± 0% 984ns ± 0% +1.78% (p=0.000 n=10+10) FmtManyArgs-96 2.35µs ± 0% 2.25µs ± 0% -4.63% (p=0.000 n=9+8) GobDecode-96 31.0ms ± 0% 30.8ms ± 0% -0.36% (p=0.006 n=9+9) GobEncode-96 24.4ms ± 0% 24.5ms ± 0% +0.30% (p=0.000 n=9+9) Gzip-96 1.60s ± 0% 1.43s ± 0% -10.58% (p=0.000 n=9+10) Gunzip-96 167ms ± 0% 169ms ± 0% +0.83% (p=0.000 n=8+9) HTTPClientServer-96 311µs ± 1% 308µs ± 0% -0.75% (p=0.000 n=10+10) JSONEncode-96 65.0ms ± 0% 64.8ms ± 0% -0.25% (p=0.000 n=9+8) JSONDecode-96 262ms ± 1% 261ms ± 1% ~ (p=0.579 n=10+10) Mandelbrot200-96 18.0ms ± 0% 18.1ms ± 0% +0.17% (p=0.000 n=8+10) GoParse-96 14.0ms ± 0% 14.1ms ± 1% +0.42% (p=0.003 n=9+10) RegexpMatchEasy0_32-96 644ns ± 2% 645ns ± 2% ~ (p=0.836 n=10+10) RegexpMatchEasy0_1K-96 3.70µs ± 0% 3.49µs ± 0% -5.58% (p=0.000 n=10+10) RegexpMatchEasy1_32-96 662ns ± 2% 657ns ± 2% ~ (p=0.137 n=10+10) RegexpMatchEasy1_1K-96 4.47µs ± 0% 4.31µs ± 0% -3.48% (p=0.000 n=10+10) RegexpMatchMedium_32-96 844ns ± 2% 849ns ± 1% ~ (p=0.208 n=10+10) RegexpMatchMedium_1K-96 179µs ± 0% 182µs ± 0% +1.20% (p=0.000 n=10+10) RegexpMatchHard_32-96 10.0µs ± 0% 10.1µs ± 0% +0.48% (p=0.000 n=10+9) RegexpMatchHard_1K-96 297µs ± 0% 297µs ± 0% -0.14% (p=0.000 n=10+10) Revcomp-96 3.08s ± 0% 3.13s ± 0% +1.56% (p=0.000 n=9+9) Template-96 276ms ± 2% 275ms ± 1% ~ (p=0.393 n=10+10) TimeParse-96 1.37µs ± 0% 1.36µs ± 0% -0.53% (p=0.000 n=10+7) TimeFormat-96 1.40µs ± 0% 1.42µs ± 0% +0.97% (p=0.000 n=10+10) [Geo mean] 264µs 262µs -0.77% Change-Id: Ie54eee4b3092af53e6da3baa6d1755098f57f3a2 Reviewed-on: https://go-review.googlesource.com/55670 Run-TryBot: Philip Hofer <phofer@umich.edu> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Keith Randall <khr@golang.org>
3282 lines
52 KiB
Go
3282 lines
52 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"},
|
|
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"},
|
|
tests: linuxMIPSTests,
|
|
},
|
|
{
|
|
arch: "mips64",
|
|
os: "linux",
|
|
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 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"},
|
|
},
|
|
// 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[A-Z]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f66(a string) bool {
|
|
return a == "xxxx"
|
|
}`,
|
|
pos: []string{"\tCMPL\t[A-Z]"},
|
|
},
|
|
{
|
|
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-Z]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f69(a,b [3]uint16) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{"\tCMPL\t[A-Z]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a,b [3]int16) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{"\tCMPL\t[A-Z]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func $(a,b [12]int8) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{"\tCMPQ\t[A-Z]", "\tCMPL\t[A-Z]"},
|
|
},
|
|
{
|
|
fn: `
|
|
func f70(a,b [15]byte) bool {
|
|
return a == b
|
|
}`,
|
|
pos: []string{"\tCMPQ\t[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\\("},
|
|
},
|
|
// 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"},
|
|
},
|
|
}
|
|
|
|
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"},
|
|
},
|
|
}
|
|
|
|
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 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"},
|
|
},
|
|
}
|
|
|
|
var linuxMIPSTests = []*asmTest{
|
|
{
|
|
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"},
|
|
},
|
|
{
|
|
// 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"},
|
|
},
|
|
}
|
|
|
|
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
|
|
}
|
|
`
|