Skip to content
Snippets Groups Projects
asm_test.go 30.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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
    
    // 	  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
    //
    //   {
    
    // 	  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 {
    
    	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)
    	}
    
    
    		function := strings.Replace(t.fn, "func $", fmt.Sprintf("func f%d_%s", i, ats.arch), 1)
    
    // 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)
    	}
    
    
    	src := filepath.Join(testDir, "test.go")
    	err = ioutil.WriteFile(src, ats.generateCode(), 0600)
    
    		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)
    
    var allAsmTests = []*asmTests{
    	{
    		arch:    "amd64",
    		os:      "linux",
    
    		imports: []string{"unsafe", "runtime"},
    
    		tests:   linuxAMD64Tests,
    	},
    	{
    
    		arch:  "386",
    		os:    "linux",
    		tests: linux386Tests,
    
    		arch:  "s390x",
    		os:    "linux",
    		tests: linuxS390XTests,
    
    		imports: []string{"runtime"},
    
    		arch:  "arm64",
    		os:    "linux",
    		tests: linuxARM64Tests,
    
    		arch:  "mips",
    		os:    "linux",
    		tests: linuxMIPSTests,
    
    		arch:  "mips64",
    		os:    "linux",
    		tests: linuxMIPS64Tests,
    
    		arch:  "ppc64le",
    		os:    "linux",
    		tests: linuxPPC64LETests,
    
    	{
    		arch:  "amd64",
    		os:    "plan9",
    		tests: plan9AMD64Tests,
    	},
    
    var linuxAMD64Tests = []*asmTest{
    
    		pos: []string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
    
    	// Structure zeroing.  See issue #18370.
    
    		type T1 struct {
    			a, b, c int
    		}
    
    		pos: []string{"\tXORPS\tX., X", "\tMOVUPS\tX., \\(.*\\)", "\tMOVQ\t\\$0, 16\\(.*\\)"},
    
    	// SSA-able composite literal initialization. Issue 18872.
    	{
    
    		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).
    
    		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\\)"},
    
    		func f20(x uint64) uint64 {
    			return x<<7 | x>>57
    		}
    		`,
    
    		pos: []string{"\tROLQ\t[$]7,"},
    
    		func f21(x uint64) uint64 {
    			return x<<7 + x>>57
    		}
    		`,
    
    		pos: []string{"\tROLQ\t[$]7,"},
    
    		func f22(x uint64) uint64 {
    			return x<<7 ^ x>>57
    		}
    		`,
    
    		pos: []string{"\tROLQ\t[$]7,"},
    
    		func f23(x uint32) uint32 {
    			return x<<7 + x>>25
    		}
    		`,
    
    		pos: []string{"\tROLL\t[$]7,"},
    
    		func f24(x uint32) uint32 {
    			return x<<7 | x>>25
    		}
    		`,
    
    		pos: []string{"\tROLL\t[$]7,"},
    
    		func f25(x uint32) uint32 {
    			return x<<7 ^ x>>25
    		}
    		`,
    
    		pos: []string{"\tROLL\t[$]7,"},
    
    		func f26(x uint16) uint16 {
    			return x<<7 + x>>9
    		}
    		`,
    
    		pos: []string{"\tROLW\t[$]7,"},
    
    		func f27(x uint16) uint16 {
    			return x<<7 | x>>9
    		}
    		`,
    
    		pos: []string{"\tROLW\t[$]7,"},
    
    		func f28(x uint16) uint16 {
    			return x<<7 ^ x>>9
    		}
    		`,
    
    		pos: []string{"\tROLW\t[$]7,"},
    
    		func f29(x uint8) uint8 {
    			return x<<7 + x>>1
    		}
    		`,
    
    		pos: []string{"\tROLB\t[$]7,"},
    
    		func f30(x uint8) uint8 {
    			return x<<7 | x>>1
    		}
    		`,
    
    		pos: []string{"\tROLB\t[$]7,"},
    
    		func f31(x uint8) uint8 {
    			return x<<7 ^ x>>1
    		}
    		`,
    
    		pos: []string{"\tROLB\t[$]7,"},
    
    	// Rotate after inlining (see issue 18254).
    	{
    
    		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,"},
    
    		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.
    	{
    
    		func f34(m map[int]int) bool {
    			_, ok := m[5]
    			return ok
    		}
    		`,
    
    		pos: []string{"\tMOVQ\t[$]5,"},
    
    		func f35(m map[string]int) int {
    			return m["abc"]
    		}
    		`,
    
    		pos: []string{"\"abc\""},
    
    		func f36(m map[string]int) bool {
    			_, ok := m["abc"]
    			return ok
    		}
    		`,
    
    		pos: []string{"\"abc\""},
    
    	// Bit test ops on amd64, issue 18943.
    	{
    
    		func f37(a, b uint64) int {
    			if a&(1<<(b&63)) != 0 {
    				return 1
    			}
    			return -1
    		}
    		`,
    
    		pos: []string{"\tBTQ\t"},
    
    		func f38(a, b uint64) bool {
    			return a&(1<<(b&63)) != 0
    		}
    		`,
    
    		pos: []string{"\tBTQ\t"},
    
    		func f39(a uint64) int {
    			if a&(1<<60) != 0 {
    				return 1
    			}
    			return -1
    		}
    		`,
    
    		pos: []string{"\tBTQ\t\\$60"},
    
    		func f40(a uint64) bool {
    			return a&(1<<60) != 0
    		}
    		`,
    
    		pos: []string{"\tBTQ\t\\$60"},
    
    	// see issue 19595.
    	// We want to merge load+op in f58, but not in f59.
    	{
    
    		pos: []string{"\tADDQ\t\\("},
    
    		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
    	{
    
    		func f60(f float64) float64 {
    			return f * 2.0
    		}`,
    
    		pos: []string{"\tADDSD\t"},
    
    		func f62(f float64) float64 {
    			return f / 16.0
    		}`,
    
    		pos: []string{"\tMULSD\t"},
    
    		func f63(f float64) float64 {
    			return f / 0.125
    		}`,
    
    		pos: []string{"\tMULSD\t"},
    
    		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
    	{
    
    		func f65(a string) bool {
    		    return a == "xx"
    		}`,
    
    		pos: []string{"\tCMPW\t\\(.*\\), [$]"},
    
    		func f66(a string) bool {
    		    return a == "xxxx"
    		}`,
    
    		pos: []string{"\tCMPL\t\\(.*\\), [$]"},
    
    		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
    	{
    
    		func f68(a,b [2]byte) bool {
    		    return a == b
    		}`,
    
    		pos: []string{"\tCMPW\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]"},
    
    		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]",
    		},
    
    		func f70(a,b [15]byte) bool {
    		    return a == b
    		}`,
    
    		pos: []string{"\tCMPQ\t\"\"[.+_a-z0-9]+\\(SP\\), [A-Z]"},
    
    		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.
    
    			runtime.GC() // use some frame
    
    		pos: []string{"b\\+24\\(SP\\)"},
    
    		func f73(a, b byte) (byte,byte) {
    		    return f73(f73(a,b))
    		}
    		`,
    
    		pos: []string{"\tMOVW\t"},
    
    		func f74(a, b uint16) (uint16,uint16) {
    		    return f74(f74(a,b))
    		}
    		`,
    
    		pos: []string{"\tMOVL\t"},
    
    		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.
    	{
    
    		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
    
    		func $() int {
    			var x int
    			return *(&x)
    		}
    		`,
    
    		pos: []string{"TEXT\t.*, [$]0-8"},
    
    	{
    		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"},
    	},
    
    	{
    		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{
    
    	{
    		// check that stack store is optimized away
    
    		func $() int {
    			var x int
    			return *(&x)
    		}
    		`,
    
    		pos: []string{"TEXT\t.*, [$]0-4"},
    
    	// 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{
    	{
    
    		func f8(x uint64) uint64 {
    			return x<<7 + x>>57
    		}
    		`,
    
    		pos: []string{"\tRLLG\t[$]7,"},
    
    		func f9(x uint64) uint64 {
    			return x<<7 | x>>57
    		}
    		`,
    
    		pos: []string{"\tRLLG\t[$]7,"},
    
    		func f10(x uint64) uint64 {
    			return x<<7 ^ x>>57
    		}
    		`,
    
    		pos: []string{"\tRLLG\t[$]7,"},
    
    		func f11(x uint32) uint32 {
    			return x<<7 + x>>25
    		}
    		`,
    
    		pos: []string{"\tRLL\t[$]7,"},
    
    		func f12(x uint32) uint32 {
    			return x<<7 | x>>25
    		}
    		`,
    
    		pos: []string{"\tRLL\t[$]7,"},
    
    		func f13(x uint32) uint32 {
    			return x<<7 ^ x>>25