| // 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{"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"}, |
| }, |
| } |
| |
| 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 |
| } |
| ` |