go source for verification 2026-05-22

This commit is contained in:
2026-05-22 16:44:48 +08:00
commit 3ae432d492
14992 changed files with 3674967 additions and 0 deletions

159
test/codegen/README Normal file
View File

@@ -0,0 +1,159 @@
// Copyright 2018 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.
The codegen directory contains code generation tests for the gc
compiler.
- Introduction
The test harness compiles Go code inside files in this directory and
matches the generated assembly (the output of `go tool compile -S`)
against a set of regexps to be specified in comments that follow a
special syntax (described below). The test driver is implemented as
an action within the GOROOT/test test suite, called "asmcheck".
The codegen harness is part of the all.bash test suite, but for
performance reasons only the codegen tests for the host machine's
GOARCH are enabled by default, and only on GOOS=linux.
To perform comprehensive tests for all the supported architectures
(even on a non-Linux system), one can run the following command:
$ ../../bin/go test cmd/internal/testdir -run='Test/codegen' -all_codegen -v
This is recommended after any change that affect the compiler's code.
The test harness compiles the tests with the same go toolchain that is
used to run the test. After writing tests for a newly added codegen
transformation, it can be useful to first run the test harness with a
toolchain from a released Go version (and verify that the new tests
fail), and then re-running the tests using the devel toolchain.
- Regexps comments syntax
Instructions to match are specified inside plain comments that start
with an architecture tag, followed by a colon and a quoted Go-style
regexp to be matched. For example, the following test:
func Sqrt(x float64) float64 {
// amd64:"SQRTSD"
// arm64:"FSQRTD"
return math.Sqrt(x)
}
verifies that math.Sqrt calls are intrinsified to a SQRTSD instruction
on amd64, and to a FSQRTD instruction on arm64.
It is possible to put multiple architectures checks into the same
line, as:
// amd64:"SQRTSD" arm64:"FSQRTD"
although this form should be avoided when doing so would make the
regexps line excessively long and difficult to read.
Comments that are on their own line will be matched against the first
subsequent non-comment line. Inline comments are also supported; the
regexp will be matched against the code found on the same line:
func Sqrt(x float64) float64 {
return math.Sqrt(x) // arm:"SQRTD"
}
It's possible to specify a comma-separated list of regexps to be
matched. For example, the following test:
func TZ8(n uint8) int {
// amd64:"BSFQ","ORQ\t\\$256"
return bits.TrailingZeros8(n)
}
verifies that the code generated for a bits.TrailingZeros8 call on
amd64 contains both a "BSFQ" instruction and an "ORQ $256".
Note how the ORQ regex includes a tab char (\t). In the Go assembly
syntax, operands are separated from opcodes by a tabulation.
Regexps can be quoted using either " or `. Special characters must be
escaped accordingly. Both of these are accepted, and equivalent:
// amd64:"ADDQ\t\\$3"
// amd64:`ADDQ\t\$3`
and they'll match this assembly line:
ADDQ $3
Negative matches can be specified using a - before the quoted regexp.
For example:
func MoveSmall() {
x := [...]byte{1, 2, 3, 4, 5, 6, 7}
copy(x[1:], x[:]) // arm64:-".*memmove"
}
verifies that NO memmove call is present in the assembly generated for
the copy() line.
The expected number of matches for the regexp can be specified using a
positive number:
func fb(a [4]int) (r [4]int) {
// amd64:2`MOVUPS[^,]+, X0$`,2`MOVUPS\sX0,[^\n]+$`
return a
}
- Architecture specifiers
There are three different ways to specify on which architecture a test
should be run:
* Specify only the architecture (eg: "amd64"). This indicates that the
check should be run on all the supported architecture variants. For
instance, arm checks will be run against all supported GOARM
variations (5,6,7).
* Specify both the architecture and a variant, separated by a slash
(eg: "arm/7"). This means that the check will be run only on that
specific variant.
* Specify the operating system, the architecture and the variant,
separated by slashes (eg: "plan9/386/sse2", "plan9/amd64/"). This is
needed in the rare case that you need to do a codegen test affected
by a specific operating system; by default, tests are compiled only
targeting linux.
- Remarks, and Caveats
-- Write small test functions
As a general guideline, test functions should be small, to avoid
possible interactions between unrelated lines of code that may be
introduced, for example, by the compiler's optimization passes.
Any given line of Go code could get assigned more instructions than it
may appear from reading the source. In particular, matching all MOV
instructions should be avoided; the compiler may add them for
unrelated reasons and this may render the test ineffective.
-- Line matching logic
Regexps are always matched from the start of the instructions line.
This means, for example, that the "MULQ" regexp is equivalent to
"^MULQ" (^ representing the start of the line), and it will NOT match
the following assembly line:
IMULQ $99, AX
To force a match at any point of the line, ".*MULQ" should be used.
For the same reason, a negative regexp like -"memmove" is not enough
to make sure that no memmove call is included in the assembly. A
memmove call looks like this:
CALL runtime.memmove(SB)
To make sure that the "memmove" symbol does not appear anywhere in the
assembly, the negative regexp to be used is -".*memmove".

14
test/codegen/addrcalc.go Normal file
View File

@@ -0,0 +1,14 @@
// asmcheck
// Copyright 2019 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 codegen
// Make sure we use ADDQ instead of LEAQ when we can.
func f(p *[4][2]int, x int) *int {
// amd64:"ADDQ" -"LEAQ"
return &p[x][0]
}

37
test/codegen/alloc.go Normal file
View File

@@ -0,0 +1,37 @@
// asmcheck
// Copyright 2018 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.
// These tests check that allocating a 0-size object does not
// introduce a call to runtime.newobject.
package codegen
func zeroAllocNew1() *struct{} {
// 386:-`CALL runtime\.newobject`, `LEAL runtime.zerobase`
// amd64:-`CALL runtime\.newobject`, `LEAQ runtime.zerobase`
// arm:-`CALL runtime\.newobject`, `MOVW [$]runtime.zerobase`
// arm64:-`CALL runtime\.newobject`, `MOVD [$]runtime.zerobase`
// riscv64:-`CALL runtime\.newobject`, `MOV [$]runtime.zerobase`
return new(struct{})
}
func zeroAllocNew2() *[0]int {
// 386:-`CALL runtime\.newobject`, `LEAL runtime.zerobase`
// amd64:-`CALL runtime\.newobject`, `LEAQ runtime.zerobase`
// arm:-`CALL runtime\.newobject`, `MOVW [$]runtime.zerobase`
// arm64:-`CALL runtime\.newobject`, `MOVD [$]runtime.zerobase`
// riscv64:-`CALL runtime\.newobject`, `MOV [$]runtime.zerobase`
return new([0]int)
}
func zeroAllocSliceLit() []int {
// 386:-`CALL runtime\.newobject`, `LEAL runtime.zerobase`
// amd64:-`CALL runtime\.newobject`, `LEAQ runtime.zerobase`
// arm:-`CALL runtime\.newobject`, `MOVW [$]runtime.zerobase`
// arm64:-`CALL runtime\.newobject`, `MOVD [$]runtime.zerobase`
// riscv64:-`CALL runtime\.newobject`, `MOV [$]runtime.zerobase`
return []int{}
}

245
test/codegen/append.go Normal file
View File

@@ -0,0 +1,245 @@
// asmcheck
//go:build !goexperiment.runtimefreegc
// Copyright 2025 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 codegen
func Append1(n int) []int {
var r []int
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append2(n int) (r []int) {
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return
}
func Append3(n int) (r []int) {
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append4(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceBuf\b`
r = append(r, i)
}
println(cap(r))
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append5(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceBuf\b`
r = append(r, i)
}
useSlice(r)
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append5b(n int) []int {
var r []int
useSlice(r)
for i := range n {
// amd64:`.*growsliceBuf\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append6(n int) []*int {
var r []*int
for i := range n {
// amd64:`.*growslice\b`
r = append(r, new(i))
}
// amd64:`.*moveSliceNoCap\b`
return r
}
func Append7(n int) []*int {
var r []*int
for i := range n {
// amd64:`.*growsliceBuf\b`
r = append(r, new(i))
}
println(cap(r))
// amd64:`.*moveSlice\b`
return r
}
func Append8(n int, p *[]int) {
var r []int
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
*p = r
}
func Append8b(n int, p *[]int) {
var r []int
// amd64:`.*moveSliceNoCapNoScan\b`
*p = r
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
}
func Append9(n int) []int {
var r []int
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
println(len(r))
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append10(n int) []int {
var r []int
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
println(r[3])
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append11(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceBuf\b`
r = append(r, i)
}
r = r[3:5]
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append12(n int) []int {
var r []int
r = nil
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append13(n int) []int {
var r []int
r, r = nil, nil
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append14(n int) []int {
var r []int
r = []int{3, 4, 5}
for i := range n {
// amd64:`.*growsliceBuf\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append15(n int) []int {
r := []int{3, 4, 5}
for i := range n {
// amd64:`.*growsliceBuf\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append16(r []int, n int) []int {
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append17(n int) []int {
var r []int
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
for i, x := range r {
println(i, x)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append18(n int, p *[]int) {
var r []int
for i := range n {
// amd64:-`.*moveSliceNoCapNoScan`
*p = r
// amd64:`.*growslice`
r = append(r, i)
}
}
func Append19(n int, p [][]int) {
for j := range p {
var r []int
for i := range n {
// amd64:`.*growslice`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan`
p[j] = r
}
}
func Append20(n int, p [][]int) {
for j := range p {
var r []int
// amd64:`.*growslice`
r = append(r, 0)
// amd64:-`.*moveSliceNoCapNoScan`
p[j] = r
}
}
//go:noinline
func useSlice(s []int) {
}

View File

@@ -0,0 +1,217 @@
// asmcheck
//go:build goexperiment.runtimefreegc
// Copyright 2025 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 codegen
func Append1(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceNoAlias\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append2(n int) (r []int) {
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return
}
func Append3(n int) (r []int) {
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append4(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceBufNoAlias\b`
r = append(r, i)
}
println(cap(r))
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append5(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceBufNoAlias\b`
r = append(r, i)
}
useSlice(r)
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append5b(n int) []int {
var r []int
useSlice(r)
for i := range n {
// amd64:`.*growsliceBuf\b` -`.*growsliceBufNoAlias\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append6(n int) []*int {
var r []*int
for i := range n {
// TODO(thepudds): for now, the compiler only uses the NoAlias version
// for element types without pointers.
// amd64:`.*growslice\b`
r = append(r, new(i))
}
// amd64:`.*moveSliceNoCap\b`
return r
}
func Append7(n int) []*int {
var r []*int
for i := range n {
// TODO(thepudds): for now, the compiler only uses the NoAlias version
// for element types without pointers.
// amd64:`.*growsliceBuf\b`
r = append(r, new(i))
}
println(cap(r))
// amd64:`.*moveSlice\b`
return r
}
func Append8(n int, p *[]int) {
var r []int
for i := range n {
// amd64:`.*growsliceNoAlias\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
*p = r
}
func Append8b(n int, p *[]int) {
var r []int
// amd64:`.*moveSliceNoCapNoScan\b`
*p = r
for i := range n {
// amd64:`.*growslice\b` -`.*growsliceNoAlias\b`
r = append(r, i)
}
}
func Append9(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceNoAlias\b`
r = append(r, i)
}
println(len(r))
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append10(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceNoAlias\b`
r = append(r, i)
}
println(r[3])
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append11(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceBufNoAlias\b`
r = append(r, i)
}
r = r[3:5]
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append12(n int) []int {
var r []int
r = nil
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append13(n int) []int {
var r []int
r, r = nil, nil
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append14(n int) []int {
var r []int
r = []int{3, 4, 5}
for i := range n {
// amd64:`.*growsliceBufNoAlias\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append15(n int) []int {
r := []int{3, 4, 5}
for i := range n {
// amd64:`.*growsliceBufNoAlias\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoScan\b`
return r
}
func Append16(r []int, n int) []int {
for i := range n {
// amd64:`.*growslice\b`
r = append(r, i)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
func Append17(n int) []int {
var r []int
for i := range n {
// amd64:`.*growsliceNoAlias\b`
r = append(r, i)
}
for i, x := range r {
println(i, x)
}
// amd64:`.*moveSliceNoCapNoScan\b`
return r
}
//go:noinline
func useSlice(s []int) {
}

791
test/codegen/arithmetic.go Normal file
View File

@@ -0,0 +1,791 @@
// asmcheck
// Copyright 2018 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 codegen
// This file contains codegen tests related to arithmetic
// simplifications and optimizations on integer types.
// For codegen tests on float types, see floats.go.
// Addition
func AddLargeConst(a uint64, out []uint64) {
// ppc64x/power10:"ADD [$]4294967296,"
// ppc64x/power9:"MOVD [$]1", "SLD [$]32" "ADD R[0-9]*"
// ppc64x/power8:"MOVD [$]1", "SLD [$]32" "ADD R[0-9]*"
out[0] = a + 0x100000000
// ppc64x/power10:"ADD [$]-8589934592,"
// ppc64x/power9:"MOVD [$]-1", "SLD [$]33" "ADD R[0-9]*"
// ppc64x/power8:"MOVD [$]-1", "SLD [$]33" "ADD R[0-9]*"
out[1] = a + 0xFFFFFFFE00000000
// ppc64x/power10:"ADD [$]1234567,"
// ppc64x/power9:"ADDIS [$]19,", "ADD [$]-10617,"
// ppc64x/power8:"ADDIS [$]19,", "ADD [$]-10617,"
out[2] = a + 1234567
// ppc64x/power10:"ADD [$]-1234567,"
// ppc64x/power9:"ADDIS [$]-19,", "ADD [$]10617,"
// ppc64x/power8:"ADDIS [$]-19,", "ADD [$]10617,"
out[3] = a - 1234567
// ppc64x/power10:"ADD [$]2147450879,"
// ppc64x/power9:"ADDIS [$]32767,", "ADD [$]32767,"
// ppc64x/power8:"ADDIS [$]32767,", "ADD [$]32767,"
out[4] = a + 0x7FFF7FFF
// ppc64x/power10:"ADD [$]-2147483647,"
// ppc64x/power9:"ADDIS [$]-32768,", "ADD [$]1,"
// ppc64x/power8:"ADDIS [$]-32768,", "ADD [$]1,"
out[5] = a - 2147483647
// ppc64x:"ADDIS [$]-32768,", ^"ADD "
out[6] = a - 2147483648
// ppc64x:"ADD [$]2147450880,", ^"ADDIS "
out[7] = a + 0x7FFF8000
// ppc64x:"ADD [$]-32768,", ^"ADDIS "
out[8] = a - 32768
// ppc64x/power10:"ADD [$]-32769,"
// ppc64x/power9:"ADDIS [$]-1,", "ADD [$]32767,"
// ppc64x/power8:"ADDIS [$]-1,", "ADD [$]32767,"
out[9] = a - 32769
}
func AddLargeConst2(a int, out []int) {
// loong64: -"ADDVU" "ADDV16"
out[0] = a + 0x10000
}
// Subtraction
var ef int
func SubMem(arr []int, b, c, d int) int {
// 386:`SUBL\s[A-Z]+,\s8\([A-Z]+\)`
// amd64:`SUBQ\s[A-Z]+,\s16\([A-Z]+\)`
arr[2] -= b
// 386:`SUBL\s[A-Z]+,\s12\([A-Z]+\)`
// amd64:`SUBQ\s[A-Z]+,\s24\([A-Z]+\)`
arr[3] -= b
// 386:`DECL\s16\([A-Z]+\)`
arr[4]--
// 386:`ADDL\s[$]-20,\s20\([A-Z]+\)`
arr[5] -= 20
// 386:`SUBL\s\([A-Z]+\)\([A-Z]+\*4\),\s[A-Z]+`
ef -= arr[b]
// 386:`SUBL\s[A-Z]+,\s\([A-Z]+\)\([A-Z]+\*4\)`
arr[c] -= b
// 386:`ADDL\s[$]-15,\s\([A-Z]+\)\([A-Z]+\*4\)`
arr[d] -= 15
// 386:`DECL\s\([A-Z]+\)\([A-Z]+\*4\)`
arr[b]--
// amd64:`DECQ\s64\([A-Z]+\)`
arr[8]--
// 386:"SUBL 4"
// amd64:"SUBQ 8"
return arr[0] - arr[1]
}
func SubFromConst(a int) int {
// ppc64x: `SUBC R[0-9]+,\s[$]40,\sR`
// riscv64: "ADDI [$]-40" "NEG"
b := 40 - a
return b
}
func SubFromConstNeg(a int) int {
// arm64: "ADD [$]40"
// loong64: "ADDV[U] [$]40"
// mips: "ADD[U] [$]40"
// mips64: "ADDV[U] [$]40"
// ppc64x: `ADD [$]40,\sR[0-9]+,\sR`
// riscv64: "ADDI [$]40" -"NEG"
c := 40 - (-a)
return c
}
func SubSubFromConst(a int) int {
// arm64: "ADD [$]20"
// loong64: "ADDV[U] [$]20"
// mips: "ADD[U] [$]20"
// mips64: "ADDV[U] [$]20"
// ppc64x: `ADD [$]20,\sR[0-9]+,\sR`
// riscv64: "ADDI [$]20" -"NEG"
c := 40 - (20 - a)
return c
}
func AddSubFromConst(a int) int {
// ppc64x: `SUBC R[0-9]+,\s[$]60,\sR`
// riscv64: "ADDI [$]-60" "NEG"
c := 40 + (20 - a)
return c
}
func NegSubFromConst(a int) int {
// arm64: "SUB [$]20"
// loong64: "ADDV[U] [$]-20"
// mips: "ADD[U] [$]-20"
// mips64: "ADDV[U] [$]-20"
// ppc64x: `ADD [$]-20,\sR[0-9]+,\sR`
// riscv64: "ADDI [$]-20"
c := -(20 - a)
return c
}
func NegAddFromConstNeg(a int) int {
// arm64: "SUB [$]40" "NEG"
// loong64: "ADDV[U] [$]-40" "SUBV"
// mips: "ADD[U] [$]-40" "SUB"
// mips64: "ADDV[U] [$]-40" "SUBV"
// ppc64x: `SUBC R[0-9]+,\s[$]40,\sR`
// riscv64: "ADDI [$]-40" "NEG"
c := -(-40 + a)
return c
}
func SubSubNegSimplify(a, b int) int {
// amd64:"NEGQ"
// arm64:"NEG"
// loong64:"SUBV"
// mips:"SUB"
// mips64:"SUBV"
// ppc64x:"NEG"
// riscv64:"NEG" -"SUB"
r := (a - b) - a
return r
}
func SubAddSimplify(a, b int) int {
// amd64:-"SUBQ" -"ADDQ"
// arm64:-"SUB" -"ADD"
// loong64:-"SUBV" -"ADDV"
// mips:-"SUB" -"ADD"
// mips64:-"SUBV" -"ADDV"
// ppc64x:-"SUB" -"ADD"
// riscv64:-"SUB" -"ADD"
r := a + (b - a)
return r
}
func SubAddSimplify2(a, b, c int) (int, int, int, int, int, int) {
// amd64:-"ADDQ"
// arm64:-"ADD"
// mips:"SUB" -"ADD"
// mips64:"SUBV" -"ADDV"
// loong64:"SUBV" -"ADDV"
// riscv64:-"ADD"
r := (a + b) - (a + c)
// amd64:-"ADDQ"
// riscv64:-"ADD"
r1 := (a + b) - (c + a)
// amd64:-"ADDQ"
// riscv64:-"ADD"
r2 := (b + a) - (a + c)
// amd64:-"ADDQ"
// riscv64:-"ADD"
r3 := (b + a) - (c + a)
// amd64:-"SUBQ"
// arm64:-"SUB"
// mips:"ADD" -"SUB"
// mips64:"ADDV" -"SUBV"
// loong64:"ADDV" -"SUBV"
// riscv64:-"SUB"
r4 := (a - c) + (c + b)
// amd64:-"SUBQ"
// riscv64:-"SUB"
r5 := (a - c) + (b + c)
return r, r1, r2, r3, r4, r5
}
func SubAddNegSimplify(a, b int) int {
// amd64:"NEGQ" -"ADDQ" -"SUBQ"
// arm64:"NEG" -"ADD" -"SUB"
// loong64:"SUBV" -"ADDV"
// mips:"SUB" -"ADD"
// mips64:"SUBV" -"ADDV"
// ppc64x:"NEG" -"ADD" -"SUB"
// riscv64:"NEG" -"ADD" -"SUB"
r := a - (b + a)
return r
}
func AddAddSubSimplify(a, b, c int) int {
// amd64:-"SUBQ"
// arm64:"ADD" -"SUB"
// loong64:"ADDV" -"SUBV"
// mips:"ADD" -"SUB"
// mips64:"ADDV" -"SUBV"
// ppc64x:-"SUB"
// riscv64:"ADD" "ADD" -"SUB"
r := a + (b + (c - a))
return r
}
func NegToInt32(a int) int {
// riscv64: "NEGW" -"MOVW"
r := int(int32(-a))
return r
}
// -------------------- //
// Multiplication //
// -------------------- //
func Pow2Muls(n1, n2 int) (int, int) {
// amd64:"SHLQ [$]5" -"IMULQ"
// 386:"SHLL [$]5" -"IMULL"
// arm:"SLL [$]5" -"MUL"
// arm64:"LSL [$]5" -"MUL"
// loong64:"SLLV [$]5" -"MULV"
// ppc64x:"SLD [$]5" -"MUL"
a := n1 * 32
// amd64:"SHLQ [$]6" -"IMULQ"
// 386:"SHLL [$]6" -"IMULL"
// arm:"SLL [$]6" -"MUL"
// arm64:`NEG\sR[0-9]+<<6,\sR[0-9]+`,-`LSL`,-`MUL`
// loong64:"SLLV [$]6" -"MULV"
// ppc64x:"SLD [$]6" "NEG\\sR[0-9]+,\\sR[0-9]+" -"MUL"
b := -64 * n2
return a, b
}
func Mul_2(n1 int32, n2 int64) (int32, int64) {
// amd64:"ADDL", -"SHLL"
a := n1 * 2
// amd64:"ADDQ", -"SHLQ"
b := n2 * 2
return a, b
}
func Mul_96(n int) int {
// amd64:`SHLQ [$]5`,`LEAQ \(.*\)\(.*\*2\),`,-`IMULQ`
// 386:`SHLL [$]5`,`LEAL \(.*\)\(.*\*2\),`,-`IMULL`
// arm64:`LSL [$]5`,`ADD\sR[0-9]+<<1,\sR[0-9]+`,-`MUL`
// arm:`SLL [$]5`,`ADD\sR[0-9]+<<1,\sR[0-9]+`,-`MUL`
// loong64:"SLLV [$]5" "ALSLV [$]1,"
// s390x:`SLD [$]5`,`SLD [$]6`,-`MULLD`
return n * 96
}
func Mul_n120(n int) int {
// loong64:"SLLV [$]3" "SLLV [$]7" "SUBVU" -"MULV"
// s390x:`SLD [$]3`,`SLD [$]7`,-`MULLD`
return n * -120
}
func MulMemSrc(a []uint32, b []float32) {
// 386:`IMULL\s4\([A-Z]+\),\s[A-Z]+`
a[0] *= a[1]
// 386/sse2:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
// amd64:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
b[0] *= b[1]
}
// Multiplications merging tests
func MergeMuls1(n int) int {
// amd64:"IMUL3Q [$]46"
// 386:"IMUL3L [$]46"
// ppc64x:"MULLD [$]46"
return 15*n + 31*n // 46n
}
func MergeMuls2(n int) int {
// amd64:"IMUL3Q [$]23" "(ADDQ [$]29)|(LEAQ 29)"
// 386:"IMUL3L [$]23" "ADDL [$]29"
// ppc64x/power9:"MADDLD" -"MULLD [$]23" -"ADD [$]29"
// ppc64x/power8:"MULLD [$]23" "ADD [$]29"
return 5*n + 7*(n+1) + 11*(n+2) // 23n + 29
}
func MergeMuls3(a, n int) int {
// amd64:"ADDQ [$]19" -"IMULQ [$]19"
// 386:"ADDL [$]19" -"IMULL [$]19"
// ppc64x:"ADD [$]19" -"MULLD [$]19"
return a*n + 19*n // (a+19)n
}
func MergeMuls4(n int) int {
// amd64:"IMUL3Q [$]14"
// 386:"IMUL3L [$]14"
// ppc64x:"MULLD [$]14"
return 23*n - 9*n // 14n
}
func MergeMuls5(a, n int) int {
// amd64:"ADDQ [$]-19" -"IMULQ [$]19"
// 386:"ADDL [$]-19" -"IMULL [$]19"
// ppc64x:"ADD [$]-19" -"MULLD [$]19"
return a*n - 19*n // (a-19)n
}
// Multiplications folded negation
func FoldNegMul(a int) int {
// amd64:"IMUL3Q [$]-11" -"NEGQ"
// arm64:"MOVD [$]-11" "MUL" -"NEG"
// loong64:"ALSLV [$]2" "SUBVU" "ALSLV [$]4"
// riscv64:"MOV [$]-11" "MUL" -"NEG"
return -a * 11
}
func Fold2NegMul(a, b int) int {
// amd64:"IMULQ" -"NEGQ"
// arm64:"MUL" -"NEG"
// loong64:"MULV" -"SUBVU R[0-9], R0,"
// riscv64:"MUL" -"NEG"
return -a * -b
}
func Mul32(a, b int32) int64 {
// arm64:"SMULL" -"MOVW"
// loong64:"MULWVW" -"MOVW"
return int64(a) * int64(b)
}
func Mul32U(a, b uint32) uint64 {
// arm64:"UMULL" -"MOVWU"
// loong64:"MULWVWU" -"MOVWU"
return uint64(a) * uint64(b)
}
func SimplifyNegMulConst(a int) int {
// amd64:-"NEGQ"
// arm64:"MOVD [$]11" "MUL" -"NEG"
// riscv64:"MOV [$]11" "MUL" -"NEG"
return -(a * -11)
}
func SimplifyNegMul(a, b int) int {
// amd64:-"NEGQ"
// arm64:"MUL" -"NEG"
// riscv64:"MUL" -"NEG"
return -(-a * b)
}
// -------------- //
// Division //
// -------------- //
func DivMemSrc(a []float64) {
// 386/sse2:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
// amd64:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
a[0] /= a[1]
}
func Pow2Divs(n1 uint, n2 int) (uint, int) {
// 386:"SHRL [$]5" -"DIVL"
// amd64:"SHRQ [$]5" -"DIVQ"
// arm:"SRL [$]5" -".*udiv"
// arm64:"LSR [$]5" -"UDIV"
// ppc64x:"SRD"
a := n1 / 32 // unsigned
// amd64:"SARQ [$]6" -"IDIVQ"
// 386:"SARL [$]6" -"IDIVL"
// arm:"SRA [$]6" -".*udiv"
// arm64:"ASR [$]6" -"SDIV"
// ppc64x:"SRAD"
b := n2 / 64 // signed
return a, b
}
// Check that constant divisions get turned into MULs
func ConstDivs(n1 uint, n2 int) (uint, int) {
// amd64: "MOVQ [$]-1085102592571150095" "MULQ" -"DIVQ"
// 386: "MOVL [$]-252645135" "MULL" -"DIVL"
// arm64: `MOVD`,`UMULH`,-`DIV`
// arm: `MOVW`,`MUL`,-`.*udiv`
a := n1 / 17 // unsigned
// amd64: "MOVQ [$]-1085102592571150095" "IMULQ" -"IDIVQ"
// 386: "IMULL" "SARL [$]4," "SARL [$]31," "SUBL" -".*DIV"
// arm64: `SMULH` -`DIV`
// arm: `MOVW` `MUL` -`.*udiv`
b := n2 / 17 // signed
return a, b
}
func FloatDivs(a []float32) float32 {
// amd64:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
// 386/sse2:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
return a[1] / a[2]
}
func Pow2Mods(n1 uint, n2 int) (uint, int) {
// 386:"ANDL [$]31" -"DIVL"
// amd64:"ANDL [$]31" -"DIVQ"
// arm:"AND [$]31" -".*udiv"
// arm64:"AND [$]31" -"UDIV"
// ppc64x:"RLDICL"
a := n1 % 32 // unsigned
// 386:"SHRL" -"IDIVL"
// amd64:"SHRQ" -"IDIVQ"
// arm:"SRA" -".*udiv"
// arm64:"ASR" -"REM"
// ppc64x:"SRAD"
b := n2 % 64 // signed
return a, b
}
// Check that signed divisibility checks get converted to AND on low bits
func Pow2DivisibleSigned(n1, n2 int) (bool, bool) {
// 386:"TESTL [$]63" -"DIVL" -"SHRL"
// amd64:"TESTQ [$]63" -"DIVQ" -"SHRQ"
// arm:"AND [$]63" -".*udiv" -"SRA"
// arm64:"TST [$]63" -"UDIV" -"ASR" -"AND"
// ppc64x:"ANDCC" -"RLDICL" -"SRAD" -"CMP"
a := n1%64 == 0 // signed divisible
// 386:"TESTL [$]63" -"DIVL" -"SHRL"
// amd64:"TESTQ [$]63" -"DIVQ" -"SHRQ"
// arm:"AND [$]63" -".*udiv" -"SRA"
// arm64:"TST [$]63" -"UDIV" -"ASR" -"AND"
// ppc64x:"ANDCC" -"RLDICL" -"SRAD" -"CMP"
b := n2%64 != 0 // signed indivisible
return a, b
}
// Check that constant modulo divs get turned into MULs
func ConstMods(n1 uint, n2 int) (uint, int) {
// amd64: "MOVQ [$]-1085102592571150095" "MULQ" -"DIVQ"
// 386: "MOVL [$]-252645135" "MULL" -".*DIVL"
// arm64: `MOVD` `UMULH` -`DIV`
// arm: `MOVW` `MUL` -`.*udiv`
a := n1 % 17 // unsigned
// amd64: "MOVQ [$]-1085102592571150095" "IMULQ" -"IDIVQ"
// 386: "IMULL" "SARL [$]4," "SARL [$]31," "SUBL" "SHLL [$]4," "SUBL" -".*DIV"
// arm64: `SMULH` -`DIV`
// arm: `MOVW` `MUL` -`.*udiv`
b := n2 % 17 // signed
return a, b
}
// Check that divisibility checks x%c==0 are converted to MULs and rotates
func DivisibleU(n uint) (bool, bool) {
// amd64:"MOVQ [$]-6148914691236517205" "IMULQ" "ROLQ [$]63" -"DIVQ"
// 386:"IMUL3L [$]-1431655765" "ROLL [$]31" -"DIVQ"
// arm64:"MOVD [$]-6148914691236517205" "MOVD [$]3074457345618258602" "MUL" "ROR" -"DIV"
// arm:"MUL" "CMP [$]715827882" -".*udiv"
// ppc64x:"MULLD" "ROTL [$]63"
even := n%6 == 0
// amd64:"MOVQ [$]-8737931403336103397" "IMULQ" -"ROLQ" -"DIVQ"
// 386:"IMUL3L [$]678152731" -"ROLL" -"DIVQ"
// arm64:"MOVD [$]-8737931403336103397" "MUL" -"ROR" -"DIV"
// arm:"MUL" "CMP [$]226050910" -".*udiv"
// ppc64x:"MULLD" -"ROTL"
odd := n%19 == 0
return even, odd
}
func Divisible(n int) (bool, bool) {
// amd64:"IMULQ" "ADD" "ROLQ [$]63" -"DIVQ"
// 386:"IMUL3L [$]-1431655765" "ADDL [$]715827882" "ROLL [$]31" -"DIVQ"
// arm64:"MOVD [$]-6148914691236517205" "MOVD [$]3074457345618258602" "MUL" "ADD R" "ROR" -"DIV"
// arm:"MUL" "ADD [$]715827882" -".*udiv"
// ppc64x/power8:"MULLD" "ADD" "ROTL [$]63"
// ppc64x/power9:"MADDLD" "ROTL [$]63"
even := n%6 == 0
// amd64:"IMULQ" "ADD" -"ROLQ" -"DIVQ"
// 386:"IMUL3L [$]678152731" "ADDL [$]113025455" -"ROLL" -"DIVQ"
// arm64:"MUL" "MOVD [$]485440633518672410" "ADD" -"ROR" -"DIV"
// arm:"MUL" "ADD [$]113025455" -".*udiv"
// ppc64x/power8:"MULLD" "ADD" -"ROTL"
// ppc64x/power9:"MADDLD" -"ROTL"
odd := n%19 == 0
return even, odd
}
// Check that fix-up code is not generated for divisions where it has been proven that
// that the divisor is not -1 or that the dividend is > MinIntNN.
func NoFix64A(divr int64) (int64, int64) {
var d int64 = 42
var e int64 = 84
if divr > 5 {
d /= divr // amd64:-"JMP"
e %= divr // amd64:-"JMP"
// The following statement is to avoid conflict between the above check
// and the normal JMP generated at the end of the block.
d += e
}
return d, e
}
func NoFix64B(divd int64) (int64, int64) {
var d int64
var e int64
var divr int64 = -1
if divd > -9223372036854775808 {
d = divd / divr // amd64:-"JMP"
e = divd % divr // amd64:-"JMP"
d += e
}
return d, e
}
func NoFix32A(divr int32) (int32, int32) {
var d int32 = 42
var e int32 = 84
if divr > 5 {
// amd64:-"JMP"
// 386:-"JMP"
d /= divr
// amd64:-"JMP"
// 386:-"JMP"
e %= divr
d += e
}
return d, e
}
func NoFix32B(divd int32) (int32, int32) {
var d int32
var e int32
var divr int32 = -1
if divd > -2147483648 {
// amd64:-"JMP"
// 386:-"JMP"
d = divd / divr
// amd64:-"JMP"
// 386:-"JMP"
e = divd % divr
d += e
}
return d, e
}
func NoFix16A(divr int16) (int16, int16) {
var d int16 = 42
var e int16 = 84
if divr > 5 {
// amd64:-"JMP"
// 386:-"JMP"
d /= divr
// amd64:-"JMP"
// 386:-"JMP"
e %= divr
d += e
}
return d, e
}
func NoFix16B(divd int16) (int16, int16) {
var d int16
var e int16
var divr int16 = -1
if divd > -32768 {
// amd64:-"JMP"
// 386:-"JMP"
d = divd / divr
// amd64:-"JMP"
// 386:-"JMP"
e = divd % divr
d += e
}
return d, e
}
// Check that len() and cap() calls divided by powers of two are
// optimized into shifts and ands
func LenDiv1(a []int) int {
// 386:"SHRL [$]10"
// amd64:"SHRQ [$]10"
// arm64:"LSR [$]10" -"SDIV"
// arm:"SRL [$]10" -".*udiv"
// ppc64x:"SRD" [$]10"
return len(a) / 1024
}
func LenDiv2(s string) int {
// 386:"SHRL [$]11"
// amd64:"SHRQ [$]11"
// arm64:"LSR [$]11" -"SDIV"
// arm:"SRL [$]11" -".*udiv"
// ppc64x:"SRD [$]11"
return len(s) / (4097 >> 1)
}
func LenMod1(a []int) int {
// 386:"ANDL [$]1023"
// amd64:"ANDL [$]1023"
// arm64:"AND [$]1023" -"SDIV"
// arm/6:"AND" -".*udiv"
// arm/7:"BFC" -".*udiv" -"AND"
// ppc64x:"RLDICL"
return len(a) % 1024
}
func LenMod2(s string) int {
// 386:"ANDL [$]2047"
// amd64:"ANDL [$]2047"
// arm64:"AND [$]2047" -"SDIV"
// arm/6:"AND" -".*udiv"
// arm/7:"BFC" -".*udiv" -"AND"
// ppc64x:"RLDICL"
return len(s) % (4097 >> 1)
}
func CapDiv(a []int) int {
// 386:"SHRL [$]12"
// amd64:"SHRQ [$]12"
// arm64:"LSR [$]12" -"SDIV"
// arm:"SRL [$]12" -".*udiv"
// ppc64x:"SRD [$]12"
return cap(a) / ((1 << 11) + 2048)
}
func CapMod(a []int) int {
// 386:"ANDL [$]4095"
// amd64:"ANDL [$]4095"
// arm64:"AND [$]4095" -"SDIV"
// arm/6:"AND" -".*udiv"
// arm/7:"BFC" -".*udiv" -"AND"
// ppc64x:"RLDICL"
return cap(a) % ((1 << 11) + 2048)
}
func AddMul(x int) int {
// amd64:"LEAQ 1"
return 2*x + 1
}
func AddShift(a, b int) int {
// loong64: "ALSLV"
return a + (b << 4)
}
func MULA(a, b, c uint32) (uint32, uint32, uint32) {
// arm:`MULA`,-`MUL\s`
// arm64:`MADDW`,-`MULW`
r0 := a*b + c
// arm:`MULA`,-`MUL\s`
// arm64:`MADDW`,-`MULW`
r1 := c*79 + a
// arm:`ADD`,-`MULA`,-`MUL\s`
// arm64:`ADD`,-`MADD`,-`MULW`
// ppc64x:`ADD`,-`MULLD`
r2 := b*64 + c
return r0, r1, r2
}
func MULS(a, b, c uint32) (uint32, uint32, uint32) {
// arm/7:`MULS`,-`MUL\s`
// arm/6:`SUB`,`MUL\s`,-`MULS`
// arm64:`MSUBW`,-`MULW`
r0 := c - a*b
// arm/7:`MULS`,-`MUL\s`
// arm/6:`SUB`,`MUL\s`,-`MULS`
// arm64:`MSUBW`,-`MULW`
r1 := a - c*79
// arm/7:`SUB`,-`MULS`,-`MUL\s`
// arm64:`SUB`,-`MSUBW`,-`MULW`
// ppc64x:`SUB`,-`MULLD`
r2 := c - b*64
return r0, r1, r2
}
func addSpecial(a, b, c uint32) (uint32, uint32, uint32) {
// amd64:`INCL`
a++
// amd64:`DECL`
b--
// amd64:`SUBL.*-128`
c += 128
return a, b, c
}
// Divide -> shift rules usually require fixup for negative inputs.
// If the input is non-negative, make sure the unsigned form is generated.
func divInt(v int64) int64 {
if v < 0 {
// amd64:`SARQ.*63,`, `SHRQ.*56,`, `SARQ.*8,`
return v / 256
}
// amd64:-`.*SARQ`, `SHRQ.*9,`
return v / 512
}
// The reassociate rules "x - (z + C) -> (x - z) - C" and
// "(z + C) -x -> C + (z - x)" can optimize the following cases.
func constantFold1(i0, j0, i1, j1, i2, j2, i3, j3 int) (int, int, int, int) {
// arm64:"SUB" "ADD [$]2"
// ppc64x:"SUB" "ADD [$]2"
r0 := (i0 + 3) - (j0 + 1)
// arm64:"SUB" "SUB [$]4"
// ppc64x:"SUB" "ADD [$]-4"
r1 := (i1 - 3) - (j1 + 1)
// arm64:"SUB" "ADD [$]4"
// ppc64x:"SUB" "ADD [$]4"
r2 := (i2 + 3) - (j2 - 1)
// arm64:"SUB" "SUB [$]2"
// ppc64x:"SUB" "ADD [$]-2"
r3 := (i3 - 3) - (j3 - 1)
return r0, r1, r2, r3
}
// The reassociate rules "x - (z + C) -> (x - z) - C" and
// "(C - z) - x -> C - (z + x)" can optimize the following cases.
func constantFold2(i0, j0, i1, j1 int) (int, int) {
// arm64:"ADD" "MOVD [$]2" "SUB"
// ppc64x: `SUBC R[0-9]+,\s[$]2,\sR`
r0 := (3 - i0) - (j0 + 1)
// arm64:"ADD" "MOVD [$]4" "SUB"
// ppc64x: `SUBC R[0-9]+,\s[$]4,\sR`
r1 := (3 - i1) - (j1 - 1)
return r0, r1
}
func constantFold3(i, j int) int {
// arm64: "LSL [$]5," "SUB R[0-9]+<<1," -"ADD"
// ppc64x:"MULLD [$]30" "MULLD"
r := (5 * i) * (6 * j)
return r
}
// Integer Min/Max
func Int64Min(a, b int64) int64 {
// amd64: "CMPQ" "CMOVQLT"
// arm64: "CMP" "CSEL"
// riscv64/rva20u64:"BLT "
// riscv64/rva22u64,riscv64/rva23u64:"MIN "
return min(a, b)
}
func Int64Max(a, b int64) int64 {
// amd64: "CMPQ" "CMOVQGT"
// arm64: "CMP" "CSEL"
// riscv64/rva20u64:"BLT "
// riscv64/rva22u64,riscv64/rva23u64:"MAX "
return max(a, b)
}
func Uint64Min(a, b uint64) uint64 {
// amd64: "CMPQ" "CMOVQCS"
// arm64: "CMP" "CSEL"
// riscv64/rva20u64:"BLTU"
// riscv64/rva22u64,riscv64/rva23u64:"MINU"
return min(a, b)
}
func Uint64Max(a, b uint64) uint64 {
// amd64: "CMPQ" "CMOVQHI"
// arm64: "CMP" "CSEL"
// riscv64/rva20u64:"BLTU"
// riscv64/rva22u64,riscv64/rva23u64:"MAXU"
return max(a, b)
}

95
test/codegen/atomics.go Normal file
View File

@@ -0,0 +1,95 @@
// asmcheck
// Copyright 2024 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.
// These tests check that atomic instructions without dynamic checks are
// generated for architectures that support them
package codegen
import "sync/atomic"
type Counter struct {
count int32
}
func (c *Counter) Increment() {
// Check that ARm64 v8.0 has both atomic instruction (LDADDALW) and a dynamic check
// (for arm64HasATOMICS), while ARM64 v8.1 has only atomic and no dynamic check.
// arm64/v8.0:"LDADDALW"
// arm64/v8.1:"LDADDALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK" -"CMPXCHG"
atomic.AddInt32(&c.count, 1)
}
func atomicLogical64(x *atomic.Uint64) uint64 {
var r uint64
// arm64/v8.0:"LDCLRALD"
// arm64/v8.1:"LDCLRALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+AND instead of CMPXCHG when we don't use the result.
// amd64:"LOCK" -"CMPXCHGQ"
x.And(11)
// arm64/v8.0:"LDCLRALD"
// arm64/v8.1:"LDCLRALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK" "CMPXCHGQ"
r += x.And(22)
// arm64/v8.0:"LDORALD"
// arm64/v8.1:"LDORALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+OR instead of CMPXCHG when we don't use the result.
// amd64:"LOCK" -"CMPXCHGQ"
x.Or(33)
// arm64/v8.0:"LDORALD"
// arm64/v8.1:"LDORALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK" "CMPXCHGQ"
r += x.Or(44)
return r
}
func atomicLogical32(x *atomic.Uint32) uint32 {
var r uint32
// arm64/v8.0:"LDCLRALW"
// arm64/v8.1:"LDCLRALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+AND instead of CMPXCHG when we don't use the result.
// amd64:"LOCK" -"CMPXCHGL"
x.And(11)
// arm64/v8.0:"LDCLRALW"
// arm64/v8.1:"LDCLRALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK" "CMPXCHGL"
r += x.And(22)
// arm64/v8.0:"LDORALW"
// arm64/v8.1:"LDORALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+OR instead of CMPXCHG when we don't use the result.
// amd64:"LOCK" -"CMPXCHGL"
x.Or(33)
// arm64/v8.0:"LDORALW"
// arm64/v8.1:"LDORALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK" "CMPXCHGL"
r += x.Or(44)
return r
}

378
test/codegen/bitfield.go Normal file
View File

@@ -0,0 +1,378 @@
// asmcheck
// Copyright 2018 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 codegen
// This file contains codegen tests related to bit field
// insertion/extraction simplifications/optimizations.
func extr1(x, x2 uint64) uint64 {
return x<<7 + x2>>57 // arm64:"EXTR [$]57,"
}
func extr2(x, x2 uint64) uint64 {
return x<<7 | x2>>57 // arm64:"EXTR [$]57,"
}
func extr3(x, x2 uint64) uint64 {
return x<<7 ^ x2>>57 // arm64:"EXTR [$]57,"
}
func extr4(x, x2 uint32) uint32 {
return x<<7 + x2>>25 // arm64:"EXTRW [$]25,"
}
func extr5(x, x2 uint32) uint32 {
return x<<7 | x2>>25 // arm64:"EXTRW [$]25,"
}
func extr6(x, x2 uint32) uint32 {
return x<<7 ^ x2>>25 // arm64:"EXTRW [$]25,"
}
// check 32-bit shift masking
func mask32(x uint32) uint32 {
return (x << 29) >> 29 // arm64:"AND [$]7, R[0-9]+" -"LSR" -"LSL"
}
// check 16-bit shift masking
func mask16(x uint16) uint16 {
return (x << 14) >> 14 // arm64:"AND [$]3, R[0-9]+" -"LSR" -"LSL"
}
// check 8-bit shift masking
func mask8(x uint8) uint8 {
return (x << 7) >> 7 // arm64:"AND [$]1, R[0-9]+" -"LSR" -"LSL"
}
func maskshift(x uint64) uint64 {
// arm64:"AND [$]4095, R[0-9]+" -"LSL" -"LSR" -"UBFIZ" -"UBFX"
return ((x << 5) & (0xfff << 5)) >> 5
}
// bitfield ops
// bfi
func bfi1(x, y uint64) uint64 {
// arm64:"BFI [$]4, R[0-9]+, [$]12" -"LSL" -"LSR" -"AND"
return ((x & 0xfff) << 4) | (y & 0xffffffffffff000f)
}
func bfi2(x, y uint64) uint64 {
// arm64:"BFI [$]12, R[0-9]+, [$]40" -"LSL" -"LSR" -"AND"
return (x << 24 >> 12) | (y & 0xfff0000000000fff)
}
// bfxil
func bfxil1(x, y uint64) uint64 {
// arm64:"BFXIL [$]5, R[0-9]+, [$]12" -"LSL" -"LSR" -"AND"
return ((x >> 5) & 0xfff) | (y & 0xfffffffffffff000)
}
func bfxil2(x, y uint64) uint64 {
// arm64:"BFXIL [$]12, R[0-9]+, [$]40" -"LSL" -"LSR" -"AND"
return (x << 12 >> 24) | (y & 0xffffff0000000000)
}
// sbfiz
// merge shifts into sbfiz: (x << lc) >> rc && lc > rc.
func sbfiz1(x int64) int64 {
// arm64:"SBFIZ [$]1, R[0-9]+, [$]60" -"LSL" -"ASR"
return (x << 4) >> 3
}
// merge shift and sign-extension into sbfiz.
func sbfiz2(x int32) int64 {
return int64(x << 3) // arm64:"SBFIZ [$]3, R[0-9]+, [$]29" -"LSL"
}
func sbfiz3(x int16) int64 {
return int64(x << 3) // arm64:"SBFIZ [$]3, R[0-9]+, [$]13" -"LSL"
}
func sbfiz4(x int8) int64 {
return int64(x << 3) // arm64:"SBFIZ [$]3, R[0-9]+, [$]5" -"LSL"
}
// sbfiz combinations.
// merge shift with sbfiz into sbfiz.
func sbfiz5(x int32) int32 {
// arm64:"SBFIZ [$]1, R[0-9]+, [$]28" -"LSL" -"ASR"
return (x << 4) >> 3
}
func sbfiz6(x int16) int64 {
return int64(x+1) << 3 // arm64:"SBFIZ [$]3, R[0-9]+, [$]16" -"LSL"
}
func sbfiz7(x int8) int64 {
return int64(x+1) << 62 // arm64:"SBFIZ [$]62, R[0-9]+, [$]2" -"LSL"
}
func sbfiz8(x int32) int64 {
return int64(x+1) << 40 // arm64:"SBFIZ [$]40, R[0-9]+, [$]24" -"LSL"
}
// sbfx
// merge shifts into sbfx: (x << lc) >> rc && lc <= rc.
func sbfx1(x int64) int64 {
return (x << 3) >> 4 // arm64:"SBFX [$]1, R[0-9]+, [$]60" -"LSL" -"ASR"
}
func sbfx2(x int64) int64 {
return (x << 60) >> 60 // arm64:"SBFX [$]0, R[0-9]+, [$]4" -"LSL" -"ASR"
}
// merge shift and sign-extension into sbfx.
func sbfx3(x int32) int64 {
return int64(x) >> 3 // arm64:"SBFX [$]3, R[0-9]+, [$]29" -"ASR"
}
func sbfx4(x int16) int64 {
return int64(x) >> 3 // arm64:"SBFX [$]3, R[0-9]+, [$]13" -"ASR"
}
func sbfx5(x int8) int64 {
return int64(x) >> 3 // arm64:"SBFX [$]3, R[0-9]+, [$]5" -"ASR"
}
func sbfx6(x int32) int64 {
return int64(x >> 30) // arm64:"SBFX [$]30, R[0-9]+, [$]2"
}
func sbfx7(x int16) int64 {
return int64(x >> 10) // arm64:"SBFX [$]10, R[0-9]+, [$]6"
}
func sbfx8(x int8) int64 {
return int64(x >> 5) // arm64:"SBFX [$]5, R[0-9]+, [$]3"
}
// sbfx combinations.
// merge shifts with sbfiz into sbfx.
func sbfx9(x int32) int32 {
return (x << 3) >> 4 // arm64:"SBFX [$]1, R[0-9]+, [$]28" -"LSL" -"ASR"
}
// merge sbfx and sign-extension into sbfx.
func sbfx10(x int32) int64 {
c := x + 5
return int64(c >> 20) // arm64"SBFX [$]20, R[0-9]+, [$]12" -"MOVW R[0-9]+, R[0-9]+"
}
// ubfiz
// merge shifts into ubfiz: (x<<lc)>>rc && lc>rc
func ubfiz1(x uint64) uint64 {
// arm64:"UBFIZ [$]1, R[0-9]+, [$]60" -"LSL" -"LSR"
// s390x:"RISBGZ [$]3, [$]62, [$]1, " -"SLD" -"SRD"
return (x << 4) >> 3
}
// merge shift and zero-extension into ubfiz.
func ubfiz2(x uint32) uint64 {
return uint64(x+1) << 3 // arm64:"UBFIZ [$]3, R[0-9]+, [$]32" -"LSL"
}
func ubfiz3(x uint16) uint64 {
return uint64(x+1) << 3 // arm64:"UBFIZ [$]3, R[0-9]+, [$]16" -"LSL"
}
func ubfiz4(x uint8) uint64 {
return uint64(x+1) << 3 // arm64:"UBFIZ [$]3, R[0-9]+, [$]8" -"LSL"
}
func ubfiz5(x uint8) uint64 {
return uint64(x) << 60 // arm64:"UBFIZ [$]60, R[0-9]+, [$]4" -"LSL"
}
func ubfiz6(x uint32) uint64 {
return uint64(x << 30) // arm64:"UBFIZ [$]30, R[0-9]+, [$]2",
}
func ubfiz7(x uint16) uint64 {
return uint64(x << 10) // arm64:"UBFIZ [$]10, R[0-9]+, [$]6",
}
func ubfiz8(x uint8) uint64 {
return uint64(x << 7) // arm64:"UBFIZ [$]7, R[0-9]+, [$]1",
}
// merge ANDconst into ubfiz.
func ubfiz9(x uint64) uint64 {
// arm64:"UBFIZ [$]3, R[0-9]+, [$]12" -"LSL" -"AND"
// s390x:"RISBGZ [$]49, [$]60, [$]3," -"SLD" -"AND"
return (x & 0xfff) << 3
}
func ubfiz10(x uint64) uint64 {
// arm64:"UBFIZ [$]4, R[0-9]+, [$]12" -"LSL" -"AND"
// s390x:"RISBGZ [$]48, [$]59, [$]4," -"SLD" -"AND"
return (x << 4) & 0xfff0
}
// ubfiz combinations
func ubfiz11(x uint32) uint32 {
// arm64:"UBFIZ [$]1, R[0-9]+, [$]28" -"LSL" -"LSR"
return (x << 4) >> 3
}
func ubfiz12(x uint64) uint64 {
// arm64:"UBFIZ [$]1, R[0-9]+, [$]20" -"LSL" -"LSR"
// s390x:"RISBGZ [$]43, [$]62, [$]1, " -"SLD" -"SRD" -"AND"
return ((x & 0xfffff) << 4) >> 3
}
func ubfiz13(x uint64) uint64 {
// arm64:"UBFIZ [$]5, R[0-9]+, [$]13" -"LSL" -"LSR" -"AND"
return ((x << 3) & 0xffff) << 2
}
func ubfiz14(x uint64) uint64 {
// arm64:"UBFIZ [$]7, R[0-9]+, [$]12" -"LSL" -"LSR" -"AND"
// s390x:"RISBGZ [$]45, [$]56, [$]7, " -"SLD" -"SRD" -"AND"
return ((x << 5) & (0xfff << 5)) << 2
}
// ubfx
// merge shifts into ubfx: (x<<lc)>>rc && lc<rc
func ubfx1(x uint64) uint64 {
// arm64:"UBFX [$]1, R[0-9]+, [$]62" -"LSL" -"LSR"
// s390x:"RISBGZ [$]2, [$]63, [$]63," -"SLD" -"SRD"
return (x << 1) >> 2
}
// merge shift and zero-extension into ubfx.
func ubfx2(x uint32) uint64 {
return uint64(x >> 15) // arm64:"UBFX [$]15, R[0-9]+, [$]17" -"LSR"
}
func ubfx3(x uint16) uint64 {
return uint64(x >> 9) // arm64:"UBFX [$]9, R[0-9]+, [$]7" -"LSR"
}
func ubfx4(x uint8) uint64 {
return uint64(x >> 3) // arm64:"UBFX [$]3, R[0-9]+, [$]5" -"LSR"
}
func ubfx5(x uint32) uint64 {
return uint64(x) >> 30 // arm64:"UBFX [$]30, R[0-9]+, [$]2"
}
func ubfx6(x uint16) uint64 {
return uint64(x) >> 10 // arm64:"UBFX [$]10, R[0-9]+, [$]6"
}
func ubfx7(x uint8) uint64 {
return uint64(x) >> 3 // arm64:"UBFX [$]3, R[0-9]+, [$]5"
}
// merge ANDconst into ubfx.
func ubfx8(x uint64) uint64 {
// arm64:"UBFX [$]25, R[0-9]+, [$]10" -"LSR" -"AND"
// s390x:"RISBGZ [$]54, [$]63, [$]39, " -"SRD" -"AND"
return (x >> 25) & 1023
}
func ubfx9(x uint64) uint64 {
// arm64:"UBFX [$]4, R[0-9]+, [$]8" -"LSR" -"AND"
// s390x:"RISBGZ [$]56, [$]63, [$]60, " -"SRD" -"AND"
return (x & 0x0ff0) >> 4
}
// ubfx combinations.
func ubfx10(x uint32) uint32 {
// arm64:"UBFX [$]1, R[0-9]+, [$]30" -"LSL" -"LSR"
return (x << 1) >> 2
}
func ubfx11(x uint64) uint64 {
// arm64:"UBFX [$]1, R[0-9]+, [$]12" -"LSL" -"LSR" -"AND"
// s390x:"RISBGZ [$]52, [$]63, [$]63," -"SLD" -"SRD" -"AND"
return ((x << 1) >> 2) & 0xfff
}
func ubfx12(x uint64) uint64 {
// arm64:"UBFX [$]4, R[0-9]+, [$]11" -"LSL" -"LSR" -"AND"
// s390x:"RISBGZ [$]53, [$]63, [$]60, " -"SLD" -"SRD" -"AND"
return ((x >> 3) & 0xfff) >> 1
}
func ubfx13(x uint64) uint64 {
// arm64:"UBFX [$]5, R[0-9]+, [$]56" -"LSL" -"LSR"
// s390x:"RISBGZ [$]8, [$]63, [$]59, " -"SLD" -"SRD"
return ((x >> 2) << 5) >> 8
}
func ubfx14(x uint64) uint64 {
// arm64:"UBFX [$]1, R[0-9]+, [$]19" -"LSL" -"LSR"
// s390x:"RISBGZ [$]45, [$]63, [$]63, " -"SLD" -"SRD" -"AND"
return ((x & 0xfffff) << 3) >> 4
}
// merge ubfx and zero-extension into ubfx.
func ubfx15(x uint64) bool {
midr := x + 10
part_num := uint16((midr >> 4) & 0xfff)
if part_num == 0xd0c { // arm64:"UBFX [$]4, R[0-9]+, [$]12" -"MOVHU R[0-9]+, R[0-9]+"
return true
}
return false
}
// merge ANDconst and ubfx into ubfx
func ubfx16(x uint64) uint64 {
// arm64:"UBFX [$]4, R[0-9]+, [$]6" -"AND [$]63"
return ((x >> 3) & 0xfff) >> 1 & 0x3f
}
// Check that we don't emit comparisons for constant shifts.
//
//go:nosplit
func shift_no_cmp(x int) int {
// arm64:`LSL [$]17`,-`CMP`
// mips64:`SLLV [$]17`,-`SGT`
return x << 17
}
func rev16(c uint64) (uint64, uint64, uint64) {
// arm64:`REV16`,-`AND`,-`LSR`,-`AND`,-`ORR R[0-9]+<<8`
// loong64:`REVB4H`,-`MOVV`,-`AND`,-`SRLV`,-`AND`,-`SLLV`,-`OR`
b1 := ((c & 0xff00ff00ff00ff00) >> 8) | ((c & 0x00ff00ff00ff00ff) << 8)
// arm64:-`ADD R[0-9]+<<8`
// loong64:-`ADDV`
b2 := ((c & 0xff00ff00ff00ff00) >> 8) + ((c & 0x00ff00ff00ff00ff) << 8)
// arm64:-`EOR R[0-9]+<<8`
// loong64:-`XOR`
b3 := ((c & 0xff00ff00ff00ff00) >> 8) ^ ((c & 0x00ff00ff00ff00ff) << 8)
return b1, b2, b3
}
func rev16w(c uint32) (uint32, uint32, uint32) {
// arm64:`REV16W`,-`AND`,-`UBFX`,-`AND`,-`ORR R[0-9]+<<8`
// loong64:`REVB2H`,-`AND`,-`SRL`,-`AND`,-`SLL`,-`OR`
b1 := ((c & 0xff00ff00) >> 8) | ((c & 0x00ff00ff) << 8)
// arm64:-`ADD R[0-9]+<<8`
// loong64:-`ADDV`
b2 := ((c & 0xff00ff00) >> 8) + ((c & 0x00ff00ff) << 8)
// arm64:-`EOR R[0-9]+<<8`
// loong64:-`XOR`
b3 := ((c & 0xff00ff00) >> 8) ^ ((c & 0x00ff00ff) << 8)
return b1, b2, b3
}
func shift(x uint32, y uint16, z uint8) uint64 {
// arm64:-`MOVWU`,-`LSR [$]32`
// loong64:-`MOVWU`,-`SRLV [$]32`
a := uint64(x) >> 32
// arm64:-`MOVHU
// loong64:-`MOVHU`,-`SRLV [$]16`
b := uint64(y) >> 16
// arm64:-`MOVBU`
// loong64:-`MOVBU`,-`SRLV [$]8`
c := uint64(z) >> 8
// arm64:`MOVD ZR`,-`ADD R[0-9]+>>16`,-`ADD R[0-9]+>>8`,
// loong64:`MOVV R0`,-`ADDVU`
return a + b + c
}

565
test/codegen/bits.go Normal file
View File

@@ -0,0 +1,565 @@
// asmcheck
// Copyright 2018 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 codegen
import "math/bits"
//
// 64 bit instructions
//
func bitsCheckConstLeftShiftU64(a uint64) (n int) {
// amd64:"BTQ [$]63,"
// arm64:"TBNZ [$]63,"
// riscv64:"MOV [$]" "AND" "BNEZ"
if a&(1<<63) != 0 {
return 1
}
// amd64:"BTQ [$]60,"
// arm64:"TBNZ [$]60,"
// riscv64:"MOV [$]" "AND" "BNEZ"
if a&(1<<60) != 0 {
return 1
}
// amd64:"BTL [$]0,"
// arm64:"TBZ [$]0,"
// riscv64:"ANDI" "BEQZ"
if a&(1<<0) != 0 {
return 1
}
return 0
}
func bitsCheckConstRightShiftU64(a [8]uint64) (n int) {
// amd64:"BTQ [$]63,"
// arm64:"LSR [$]63," "TBNZ [$]0,"
// riscv64:"SRLI" "ANDI" "BNEZ"
if (a[0]>>63)&1 != 0 {
return 1
}
// amd64:"BTQ [$]63,"
// arm64:"LSR [$]63," "CBNZ"
// riscv64:"SRLI" "BNEZ"
if a[1]>>63 != 0 {
return 1
}
// amd64:"BTQ [$]63,"
// arm64:"LSR [$]63," "CBZ"
// riscv64:"SRLI" "BEQZ"
if a[2]>>63 == 0 {
return 1
}
// amd64:"BTQ [$]60,"
// arm64:"LSR [$]60," "TBZ [$]0,"
// riscv64:"SRLI", "ANDI" "BEQZ"
if (a[3]>>60)&1 == 0 {
return 1
}
// amd64:"BTL [$]1,"
// arm64:"LSR [$]1," "TBZ [$]0,"
// riscv64:"SRLI" "ANDI" "BEQZ"
if (a[4]>>1)&1 == 0 {
return 1
}
// amd64:"BTL [$]0,"
// arm64:"TBZ [$]0," -"LSR"
// riscv64:"ANDI" "BEQZ" -"SRLI"
if (a[5]>>0)&1 == 0 {
return 1
}
// amd64:"BTL [$]7,"
// arm64:"LSR [$]5," "TBNZ [$]2,"
// riscv64:"SRLI" "ANDI" "BNEZ"
if (a[6]>>5)&4 == 0 {
return 1
}
return 0
}
func bitsCheckVarU64(a, b uint64) (n int) {
// amd64:"BTQ"
// arm64:"MOVD [$]1," "LSL" "TST"
// riscv64:"ANDI [$]63," "SLL " "AND "
if a&(1<<(b&63)) != 0 {
return 1
}
// amd64:"BTQ" -"BT. [$]0,"
// arm64:"LSR" "TBZ [$]0,"
// riscv64:"ANDI [$]63," "SRL" "ANDI [$]1,"
if (b>>(a&63))&1 != 0 {
return 1
}
return 0
}
func bitsCheckMaskU64(a uint64) (n int) {
// amd64:"BTQ [$]63,"
// arm64:"TBNZ [$]63,"
// riscv64:"MOV [$]" "AND" "BNEZ"
if a&0x8000000000000000 != 0 {
return 1
}
// amd64:"BTQ [$]59,"
// arm64:"TBNZ [$]59,"
// riscv64:"MOV [$]" "AND" "BNEZ"
if a&0x800000000000000 != 0 {
return 1
}
// amd64:"BTL [$]0,"
// arm64:"TBZ [$]0,"
// riscv64:"ANDI" "BEQZ"
if a&0x1 != 0 {
return 1
}
return 0
}
func bitsSetU64(a, b uint64) (n uint64) {
// amd64:"BTSQ"
// arm64:"MOVD [$]1," "LSL" "ORR"
// riscv64:"ANDI" "SLL" "OR"
n += b | (1 << (a & 63))
// amd64:"BTSQ [$]63,"
// arm64:"ORR [$]-9223372036854775808,"
// riscv64:"MOV [$]" "OR "
n += a | (1 << 63)
// amd64:"BTSQ [$]60,"
// arm64:"ORR [$]1152921504606846976,"
// riscv64:"MOV [$]" "OR "
n += a | (1 << 60)
// amd64:"ORQ [$]1,"
// arm64:"ORR [$]1,"
// riscv64:"ORI"
n += a | (1 << 0)
return n
}
func bitsClearU64(a, b uint64) (n uint64) {
// amd64:"BTRQ"
// arm64:"MOVD [$]1," "LSL" "BIC"
// riscv64:"ANDI" "SLL" "ANDN"
n += b &^ (1 << (a & 63))
// amd64:"BTRQ [$]63,"
// arm64:"AND [$]9223372036854775807,"
// riscv64:"MOV [$]" "AND "
n += a &^ (1 << 63)
// amd64:"BTRQ [$]60,"
// arm64:"AND [$]-1152921504606846977,"
// riscv64:"MOV [$]" "AND "
n += a &^ (1 << 60)
// amd64:"ANDQ [$]-2"
// arm64:"AND [$]-2"
// riscv64:"ANDI [$]-2"
n += a &^ (1 << 0)
return n
}
func bitsClearLowest(x int64, y int32) (int64, int32) {
// amd64:"ANDQ [$]-2,"
// arm64:"AND [$]-2,"
// riscv64:"ANDI [$]-2,"
a := (x >> 1) << 1
// amd64:"ANDL [$]-2,"
// arm64:"AND [$]-2,"
// riscv64:"ANDI [$]-2,"
b := (y >> 1) << 1
return a, b
}
func bitsFlipU64(a, b uint64) (n uint64) {
// amd64:"BTCQ"
// arm64:"MOVD [$]1," "LSL" "EOR"
// riscv64:"ANDI" "SLL" "XOR "
n += b ^ (1 << (a & 63))
// amd64:"BTCQ [$]63,"
// arm64:"EOR [$]-9223372036854775808,"
// riscv64:"MOV [$]" "XOR "
n += a ^ (1 << 63)
// amd64:"BTCQ [$]60,"
// arm64:"EOR [$]1152921504606846976,"
// riscv64:"MOV [$]" "XOR "
n += a ^ (1 << 60)
// amd64:"XORQ [$]1,"
// arm64:"EOR [$]1,"
// riscv64:"XORI [$]1,"
n += a ^ (1 << 0)
return n
}
//
// 32 bit instructions
//
func bitsCheckConstShiftLeftU32(a uint32) (n int) {
// amd64:"BTL [$]31,"
// arm64:"TBNZ [$]31,"
// riscv64:"MOV [$]" "AND" "BNEZ"
if a&(1<<31) != 0 {
return 1
}
// amd64:"BTL [$]28,"
// arm64:"TBNZ [$]28,"
// riscv64:"ANDI" "BNEZ"
if a&(1<<28) != 0 {
return 1
}
// amd64:"BTL [$]0,"
// arm64:"TBZ [$]0,"
// riscv64:"ANDI" "BEQZ"
if a&(1<<0) != 0 {
return 1
}
return 0
}
func bitsCheckConstRightShiftU32(a [8]uint32) (n int) {
// amd64:"BTL [$]31,"
// arm64:"UBFX [$]31," "CBNZW"
// riscv64:"SRLI" "ANDI" "BNEZ"
if (a[0]>>31)&1 != 0 {
return 1
}
// amd64:"BTL [$]31,"
// arm64:"UBFX [$]31," "CBNZW"
// riscv64:"SRLI" "BNEZ"
if a[1]>>31 != 0 {
return 1
}
// amd64:"BTL [$]31,"
// arm64:"UBFX [$]31," "CBZW"
// riscv64:"SRLI" "BEQZ"
if a[2]>>31 == 0 {
return 1
}
// amd64:"BTL [$]28,"
// arm64:"UBFX [$]28," "TBZ"
// riscv64:"SRLI" "ANDI" "BEQZ"
if (a[3]>>28)&1 == 0 {
return 1
}
// amd64:"BTL [$]1,"
// arm64:"UBFX [$]1," "TBZ"
// riscv64:"SRLI" "ANDI" "BEQZ"
if (a[4]>>1)&1 == 0 {
return 1
}
// amd64:"BTL [$]0,"
// arm64:"TBZ" -"UBFX" -"SRL"
// riscv64:"ANDI" "BEQZ" -"SRLI "
if (a[5]>>0)&1 == 0 {
return 1
}
// amd64:"BTL [$]7,"
// arm64:"UBFX [$]5," "TBNZ"
// riscv64:"SRLI" "ANDI" "BNEZ"
if (a[6]>>5)&4 == 0 {
return 1
}
return 0
}
func bitsCheckVarU32(a, b uint32) (n int) {
// amd64:"BTL"
// arm64:"AND [$]31," "MOVD [$]1," "LSL" "TSTW"
// riscv64:"ANDI [$]31," "SLL " "AND "
if a&(1<<(b&31)) != 0 {
return 1
}
// amd64:"BTL" -"BT. [$]0"
// arm64:"AND [$]31," "LSR" "TBZ"
// riscv64:"ANDI [$]31," "SRLW " "ANDI [$]1,"
if (b>>(a&31))&1 != 0 {
return 1
}
return 0
}
func bitsCheckMaskU32(a uint32) (n int) {
// amd64:"BTL [$]31,"
// arm64:"TBNZ [$]31,"
// riscv64:"MOV [$]" "AND" "BNEZ"
if a&0x80000000 != 0 {
return 1
}
// amd64:"BTL [$]27,"
// arm64:"TBNZ [$]27,"
// riscv64:"ANDI" "BNEZ"
if a&0x8000000 != 0 {
return 1
}
// amd64:"BTL [$]0,"
// arm64:"TBZ [$]0,"
// riscv64:"ANDI" "BEQZ"
if a&0x1 != 0 {
return 1
}
return 0
}
func bitsSetU32(a, b uint32) (n uint32) {
// amd64:"BTSL"
// arm64:"AND [$]31," "MOVD [$]1," "LSL" "ORR"
// riscv64:"ANDI" "SLL" "OR"
n += b | (1 << (a & 31))
// amd64:"ORL [$]-2147483648,"
// arm64:"ORR [$]-2147483648,"
// riscv64:"ORI [$]-2147483648,"
n += a | (1 << 31)
// amd64:"ORL [$]268435456,"
// arm64:"ORR [$]268435456,"
// riscv64:"ORI [$]268435456,"
n += a | (1 << 28)
// amd64:"ORL [$]1,"
// arm64:"ORR [$]1,"
// riscv64:"ORI [$]1,"
n += a | (1 << 0)
return n
}
func bitsClearU32(a, b uint32) (n uint32) {
// amd64:"BTRL"
// arm64:"AND [$]31," "MOVD [$]1," "LSL" "BIC"
// riscv64:"ANDI" "SLL" "ANDN"
n += b &^ (1 << (a & 31))
// amd64:"ANDL [$]2147483647,"
// arm64:"AND [$]2147483647,"
// riscv64:"ANDI [$]2147483647,"
n += a &^ (1 << 31)
// amd64:"ANDL [$]-268435457,"
// arm64:"AND [$]-268435457,"
// riscv64:"ANDI [$]-268435457,"
n += a &^ (1 << 28)
// amd64:"ANDL [$]-2,"
// arm64:"AND [$]-2,"
// riscv64:"ANDI [$]-2,"
n += a &^ (1 << 0)
return n
}
func bitsFlipU32(a, b uint32) (n uint32) {
// amd64:"BTCL"
// arm64:"AND [$]31," "MOVD [$]1," "LSL" "EOR"
// riscv64:"ANDI" "SLL" "XOR "
n += b ^ (1 << (a & 31))
// amd64:"XORL [$]-2147483648,"
// arm64:"EOR [$]-2147483648,"
// riscv64:"XORI [$]-2147483648,"
n += a ^ (1 << 31)
// amd64:"XORL [$]268435456,"
// arm64:"EOR [$]268435456,"
// riscv64:"XORI [$]268435456,"
n += a ^ (1 << 28)
// amd64:"XORL [$]1,"
// arm64:"EOR [$]1,"
// riscv64:"XORI [$]1,"
n += a ^ (1 << 0)
return n
}
func bitsOpOnMem(a []uint32, b, c, d uint32) {
// check direct operation on memory with constant
// amd64:`ANDL\s[$]200,\s\([A-Z][A-Z0-9]+\)`
a[0] &= 200
// amd64:`ORL\s[$]220,\s4\([A-Z][A-Z0-9]+\)`
a[1] |= 220
// amd64:`XORL\s[$]240,\s8\([A-Z][A-Z0-9]+\)`
a[2] ^= 240
}
func bitsCheckMostNegative(b uint8) bool {
// amd64:"TESTB"
// arm64:"TSTW" "CSET"
// riscv64:"ANDI [$]128," "SNEZ" -"ADDI"
return b&0x80 == 0x80
}
func bitsIssue19857a(a uint64) uint64 {
// arm64:`AND `
return a & ((1 << 63) - 1)
}
func bitsIssue19857b(a uint64) uint64 {
// arm64:`AND `
return a & (1 << 63)
}
func bitsIssue19857c(a, b uint32) (uint32, uint32) {
// arm/7:`BIC`,-`AND`
a &= 0xffffaaaa
// arm/7:`BFC`,-`AND`,-`BIC`
b &= 0xffc003ff
return a, b
}
func bitsAndNot(x, y uint32) uint32 {
// arm64:`BIC `,-`AND`
// loong64:"ANDN " -"AND "
// riscv64:"ANDN" -"AND "
return x &^ y
}
func bitsXorNot(x, y, z uint32, a []uint32, n, m uint64) uint64 {
// arm64:`EON `,-`EOR`,-`MVN`
// riscv64:"XNOR " -"MOV [$]" -"XOR"
a[0] = x ^ (y ^ 0xffffffff)
// arm64:`EON `,-`EOR`,-`MVN`
// riscv64:"XNOR" -"XOR"
a[1] = ^(y ^ z)
// arm64:`EON `,-`XOR`
// riscv64:"XNOR" -"XOR" -"NOT"
a[2] = x ^ ^z
// arm64:`EON `,-`EOR`,-`MVN`
// riscv64:"XNOR" -"MOV [$]" -"XOR"
return n ^ (m ^ 0xffffffffffffffff)
}
func bitsOrNot(x, y uint32) uint32 {
// arm64:"ORN " -"ORR"
// loong64:"ORN" -"OR "
// riscv64:"ORN" -"OR "
return x | ^y
}
func bitsNotOr(x int64, a []int64) {
// loong64: "MOVV [$]0" "NOR R"
a[0] = ^(0x1234 | x)
// loong64:"NOR" -"XOR"
a[1] = (-1) ^ x
// loong64: "MOVV [$]-55" -"OR" -"NOR"
a[2] = ^(0x12 | 0x34)
}
func bitsSetPowerOf2Test(x int) bool {
// amd64:"BTL [$]3"
// riscv64:"ANDI [$]8," "SNEZ" -"ADDI"
return x&8 == 8
}
func bitsSetTest(x int) bool {
// amd64:"ANDL [$]9, AX"
// amd64:"CMPQ AX, [$]9"
// riscv64:"ANDI [$]9," "ADDI [$]-9," "SEQZ"
return x&9 == 9
}
func bitsMaskContiguousOnes64U(x uint64) uint64 {
// s390x:"RISBGZ [$]16, [$]47, [$]0,"
return x & 0x0000ffffffff0000
}
func bitsMaskContiguousZeroes64U(x uint64) uint64 {
// s390x:"RISBGZ [$]48, [$]15, [$]0,"
return x & 0xffff00000000ffff
}
func bitsIssue44228a(a []int64, i int) bool {
// amd64: "BTQ", -"SHL"
return a[i>>6]&(1<<(i&63)) != 0
}
func bitsIssue44228b(a []int32, i int) bool {
// amd64: "BTL", -"SHL"
return a[i>>5]&(1<<(i&31)) != 0
}
func bitsIssue48467(x, y uint64) uint64 {
// arm64: -"NEG"
d, borrow := bits.Sub64(x, y, 0)
return x - d&(-borrow)
}
func bitsFoldConst(x, y uint64) uint64 {
// arm64: "ADDS [$]7" -"MOVD [$]7"
// ppc64x: "ADDC [$]7,"
d, b := bits.Add64(x, 7, 0)
return b & d
}
func bitsFoldConstOutOfRange(a uint64) uint64 {
// arm64: "MOVD [$]19088744" -"ADD [$]19088744"
return a + 0x1234568
}
func bitsSignExtendAndMask8to64U(a int8) (s, z uint64) {
// Verify sign-extended values are not zero-extended under a bit mask (#61297)
// ppc64x: "MOVB", "ANDCC [$]1015,"
s = uint64(a) & 0x3F7
// ppc64x: -"MOVB", "ANDCC [$]247,"
z = uint64(uint8(a)) & 0x3F7
return
}
func bitsZeroExtendAndMask8toU64(a int8, b int16) (x, y uint64) {
// Verify zero-extended values are not sign-extended under a bit mask (#61297)
// ppc64x: -"MOVB ", -"ANDCC", "MOVBZ"
x = uint64(a) & 0xFF
// ppc64x: -"MOVH ", -"ANDCC", "MOVHZ"
y = uint64(b) & 0xFFFF
return
}
func bitsRotateAndMask(io64 [8]uint64, io32 [4]uint32, io16 [4]uint16, io8 [4]uint8) {
// Verify rotate and mask instructions, and further simplified instructions for small types
// ppc64x: "RLDICR [$]0, R[0-9]*, [$]47, R"
io64[0] = io64[0] & 0xFFFFFFFFFFFF0000
// ppc64x: "RLDICL [$]0, R[0-9]*, [$]16, R"
io64[1] = io64[1] & 0x0000FFFFFFFFFFFF
// ppc64x: -"SRD", -"AND", "RLDICL [$]60, R[0-9]*, [$]16, R"
io64[2] = (io64[2] >> 4) & 0x0000FFFFFFFFFFFF
// ppc64x: -"SRD", -"AND", "RLDICL [$]36, R[0-9]*, [$]28, R"
io64[3] = (io64[3] >> 28) & 0x0000FFFFFFFFFFFF
// ppc64x: "MOVWZ", "RLWNM [$]1, R[0-9]*, [$]28, [$]3, R"
io64[4] = uint64(bits.RotateLeft32(io32[0], 1) & 0xF000000F)
// ppc64x: "RLWNM [$]0, R[0-9]*, [$]4, [$]19, R"
io32[0] = io32[0] & 0x0FFFF000
// ppc64x: "RLWNM [$]0, R[0-9]*, [$]20, [$]3, R"
io32[1] = io32[1] & 0xF0000FFF
// ppc64x: -"RLWNM", MOVD, AND
io32[2] = io32[2] & 0xFFFF0002
var bigc uint32 = 0x12345678
// ppc64x: "ANDCC [$]22136"
io16[0] = io16[0] & uint16(bigc)
// ppc64x: "ANDCC [$]120"
io8[0] = io8[0] & uint8(bigc)
}

209
test/codegen/bmi.go Normal file
View File

@@ -0,0 +1,209 @@
// asmcheck
// Copyright 2021 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 codegen
func andn64(x, y int64) int64 {
// amd64/v3:"ANDNQ"
return x &^ y
}
func andn32(x, y int32) int32 {
// amd64/v3:"ANDNL"
return x &^ y
}
func blsi64(x int64) int64 {
// amd64/v3:"BLSIQ"
return x & -x
}
func blsi32(x int32) int32 {
// amd64/v3:"BLSIL"
return x & -x
}
func blsmsk64(x int64) int64 {
// amd64/v3:"BLSMSKQ"
return x ^ (x - 1)
}
func blsmsk32(x int32) int32 {
// amd64/v3:"BLSMSKL"
return x ^ (x - 1)
}
func blsr64(x int64) int64 {
// amd64/v3:"BLSRQ"
return x & (x - 1)
}
func blsr32(x int32) int32 {
// amd64/v3:"BLSRL"
return x & (x - 1)
}
func isPowerOfTwo64(x int64) bool {
// amd64/v3:"BLSRQ" -"TESTQ" -"CALL"
return blsr64(x) == 0
}
func isPowerOfTwo32(x int32) bool {
// amd64/v3:"BLSRL" -"TESTL" -"CALL"
return blsr32(x) == 0
}
func isPowerOfTwoSelect64(x, a, b int64) int64 {
var r int64
// amd64/v3:"BLSRQ" -"TESTQ" -"CALL"
if isPowerOfTwo64(x) {
r = a
} else {
r = b
}
// amd64/v3:"CMOVQEQ" -"TESTQ" -"CALL"
return r * 2 // force return blocks joining
}
func isPowerOfTwoSelect32(x, a, b int32) int32 {
var r int32
// amd64/v3:"BLSRL" -"TESTL" -"CALL"
if isPowerOfTwo32(x) {
r = a
} else {
r = b
}
// amd64/v3:"CMOVLEQ" -"TESTL" -"CALL"
return r * 2 // force return blocks joining
}
func isPowerOfTwoBranch64(x int64, a func(bool), b func(string)) {
// amd64/v3:"BLSRQ" -"TESTQ" -"CALL"
if isPowerOfTwo64(x) {
a(true)
} else {
b("false")
}
}
func isPowerOfTwoBranch32(x int32, a func(bool), b func(string)) {
// amd64/v3:"BLSRL" -"TESTL" -"CALL"
if isPowerOfTwo32(x) {
a(true)
} else {
b("false")
}
}
func isNotPowerOfTwo64(x int64) bool {
// amd64/v3:"BLSRQ" -"TESTQ" -"CALL"
return blsr64(x) != 0
}
func isNotPowerOfTwo32(x int32) bool {
// amd64/v3:"BLSRL" -"TESTL" -"CALL"
return blsr32(x) != 0
}
func isNotPowerOfTwoSelect64(x, a, b int64) int64 {
var r int64
// amd64/v3:"BLSRQ" -"TESTQ" -"CALL"
if isNotPowerOfTwo64(x) {
r = a
} else {
r = b
}
// amd64/v3:"CMOVQNE" -"TESTQ" -"CALL"
return r * 2 // force return blocks joining
}
func isNotPowerOfTwoSelect32(x, a, b int32) int32 {
var r int32
// amd64/v3:"BLSRL" -"TESTL" -"CALL"
if isNotPowerOfTwo32(x) {
r = a
} else {
r = b
}
// amd64/v3:"CMOVLNE" -"TESTL" -"CALL"
return r * 2 // force return blocks joining
}
func isNotPowerOfTwoBranch64(x int64, a func(bool), b func(string)) {
// amd64/v3:"BLSRQ" -"TESTQ" -"CALL"
if isNotPowerOfTwo64(x) {
a(true)
} else {
b("false")
}
}
func isNotPowerOfTwoBranch32(x int32, a func(bool), b func(string)) {
// amd64/v3:"BLSRL" -"TESTL" -"CALL"
if isNotPowerOfTwo32(x) {
a(true)
} else {
b("false")
}
}
func sarx64(x, y int64) int64 {
// amd64/v3:"SARXQ"
return x >> y
}
func sarx32(x, y int32) int32 {
// amd64/v3:"SARXL"
return x >> y
}
func sarx64_load(x []int64, i int) int64 {
// amd64/v3: `SARXQ [A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s := x[i] >> (i & 63)
// amd64/v3: `SARXQ [A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s = x[i+1] >> (s & 63)
return s
}
func sarx32_load(x []int32, i int) int32 {
// amd64/v3: `SARXL [A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s := x[i] >> (i & 63)
// amd64/v3: `SARXL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s = x[i+1] >> (s & 63)
return s
}
func shlrx64(x, y uint64) uint64 {
// amd64/v3:"SHRXQ"
s := x >> y
// amd64/v3:"SHLXQ"
s = s << y
return s
}
func shlrx32(x, y uint32) uint32 {
// amd64/v3:"SHRXL"
s := x >> y
// amd64/v3:"SHLXL"
s = s << y
return s
}
func shlrx64_load(x []uint64, i int, s uint64) uint64 {
// amd64/v3: `SHRXQ [A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s = x[i] >> i
// amd64/v3: `SHLXQ [A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s = x[i+1] << s
return s
}
func shlrx32_load(x []uint32, i int, s uint32) uint32 {
// amd64/v3: `SHRXL [A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s = x[i] >> i
// amd64/v3: `SHLXL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s = x[i+1] << s
return s
}

330
test/codegen/bool.go Normal file
View File

@@ -0,0 +1,330 @@
// asmcheck
// Copyright 2020 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 codegen
import (
"math/bits"
)
// This file contains codegen tests related to boolean simplifications/optimizations.
func convertNeq0B(x uint8, c bool) bool {
// amd64:"ANDL [$]1" -"SETNE"
// ppc64x:"RLDICL" -"CMPW" -"ISEL"
b := x&1 != 0
return c && b
}
func convertNeq0W(x uint16, c bool) bool {
// amd64:"ANDL [$]1" -"SETNE"
// ppc64x:"RLDICL" -"CMPW" -"ISEL"
b := x&1 != 0
return c && b
}
func convertNeq0L(x uint32, c bool) bool {
// amd64:"ANDL [$]1" -"SETB"
// ppc64x:"RLDICL" -"CMPW" -"ISEL"
b := x&1 != 0
return c && b
}
func convertNeq0Q(x uint64, c bool) bool {
// amd64:"ANDL [$]1" -"SETB"
// ppc64x:"RLDICL" -"CMP" -"ISEL"
b := x&1 != 0
return c && b
}
func convertNeqBool32(x uint32) bool {
// ppc64x:"RLDICL" -"CMPW" -"ISEL"
return x&1 != 0
}
func convertEqBool32(x uint32) bool {
// ppc64x:"RLDICL" -"CMPW" "XOR" -"ISEL"
// amd64:"ANDL" "XORL" -"BTL" -"SETCC"
return x&1 == 0
}
func convertNeqBool64(x uint64) bool {
// ppc64x:"RLDICL" -"CMP" -"ISEL"
return x&1 != 0
}
func convertEqBool64(x uint64) bool {
// ppc64x:"RLDICL" "XOR" -"CMP" -"ISEL"
// amd64:"ANDL" "XORL" -"BTL" -"SETCC"
return x&1 == 0
}
func phiAnd(a, b bool) bool {
var x bool
// amd64:-"TESTB"
if a {
x = b
} else {
x = a
}
// amd64:"ANDL"
return x
}
func phiOr(a, b bool) bool {
var x bool
// amd64:-"TESTB"
if a {
x = a
} else {
x = b
}
// amd64:"ORL"
return x
}
func TestSetEq64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBC CR0EQ" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBC CR0EQ"
// ppc64x/power8:"CMP" "ISEL" -"SETBC CR0EQ"
b := x == y
return b
}
func TestSetNeq64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBCR CR0EQ" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBCR CR0EQ"
// ppc64x/power8:"CMP" "ISEL" -"SETBCR CR0EQ"
b := x != y
return b
}
func TestSetLt64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBC CR0GT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBC CR0GT"
// ppc64x/power8:"CMP" "ISEL" -"SETBC CR0GT"
b := x < y
return b
}
func TestSetLe64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBCR CR0LT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBCR CR0LT"
// ppc64x/power8:"CMP" "ISEL" -"SETBCR CR0LT"
b := x <= y
return b
}
func TestSetGt64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBC CR0LT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBC CR0LT"
// ppc64x/power8:"CMP" "ISEL" -"SETBC CR0LT"
b := x > y
return b
}
func TestSetGe64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBCR CR0GT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBCR CR0GT"
// ppc64x/power8:"CMP" "ISEL" -"SETBCR CR0GT"
b := x >= y
return b
}
func TestSetLtFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBC CR0LT" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBC CR0LT"
// ppc64x/power8:"FCMP" "ISEL" -"SETBC CR0LT"
b := x < y
return b
}
func TestSetLeFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBC CR0LT" "SETBC CR0EQ" "OR" -"ISEL" -"ISEL"
// ppc64x/power9:"ISEL" "ISEL" -"SETBC CR0LT" -"SETBC CR0EQ" "OR"
// ppc64x/power8:"ISEL" "ISEL" -"SETBC CR0LT" -"SETBC CR0EQ" "OR"
b := x <= y
return b
}
func TestSetGtFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBC CR0LT" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBC CR0LT"
// ppc64x/power8:"FCMP" "ISEL" -"SETBC CR0LT"
b := x > y
return b
}
func TestSetGeFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBC CR0LT" "SETBC CR0EQ" "OR" -"ISEL" -"ISEL"
// ppc64x/power9:"ISEL" "ISEL" -"SETBC CR0LT" -"SETBC CR0EQ" "OR"
// ppc64x/power8:"ISEL" "ISEL" -"SETBC CR0LT" -"SETBC CR0EQ" "OR"
b := x >= y
return b
}
func TestSetInvEq64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBCR CR0EQ" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBCR CR0EQ"
// ppc64x/power8:"CMP" "ISEL" -"SETBCR CR0EQ"
b := !(x == y)
return b
}
func TestSetInvNeq64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBC CR0EQ" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBC CR0EQ"
// ppc64x/power8:"CMP" "ISEL" -"SETBC CR0EQ"
b := !(x != y)
return b
}
func TestSetInvLt64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBCR CR0GT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBCR CR0GT"
// ppc64x/power8:"CMP" "ISEL" -"SETBCR CR0GT"
b := !(x < y)
return b
}
func TestSetInvLe64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBC CR0LT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBC CR0LT"
// ppc64x/power8:"CMP" "ISEL" -"SETBC CR0LT"
b := !(x <= y)
return b
}
func TestSetInvGt64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBCR CR0LT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBCR CR0LT"
// ppc64x/power8:"CMP" "ISEL" -"SETBCR CR0LT"
b := !(x > y)
return b
}
func TestSetInvGe64(x uint64, y uint64) bool {
// ppc64x/power10:"SETBC CR0GT" -"ISEL"
// ppc64x/power9:"CMP" "ISEL" -"SETBC CR0GT"
// ppc64x/power8:"CMP" "ISEL" -"SETBC CR0GT"
b := !(x >= y)
return b
}
func TestSetInvEqFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBCR CR0EQ" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBCR CR0EQ"
// ppc64x/power8:"FCMP" "ISEL" -"SETBCR CR0EQ"
b := !(x == y)
return b
}
func TestSetInvNeqFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBC CR0EQ" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBC CR0EQ"
// ppc64x/power8:"FCMP" "ISEL" -"SETBC CR0EQ"
b := !(x != y)
return b
}
func TestSetInvLtFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBCR CR0LT" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBCR CR0LT"
// ppc64x/power8:"FCMP" "ISEL" -"SETBCR CR0LT"
b := !(x < y)
return b
}
func TestSetInvLeFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBC CR0LT" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBC CR0LT"
// ppc64x/power8:"FCMP" "ISEL" -"SETBC CR0LT"
b := !(x <= y)
return b
}
func TestSetInvGtFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBCR CR0LT" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBCR CR0LT"
// ppc64x/power8:"FCMP" "ISEL" -"SETBCR CR0LT"
b := !(x > y)
return b
}
func TestSetInvGeFp64(x float64, y float64) bool {
// ppc64x/power10:"SETBC CR0LT" -"ISEL"
// ppc64x/power9:"FCMP" "ISEL" -"SETBC CR0LT"
// ppc64x/power8:"FCMP" "ISEL" -"SETBC CR0LT"
b := !(x >= y)
return b
}
func TestLogicalCompareZero(x *[64]uint64) {
// ppc64x:"ANDCC",^"AND"
b := x[0] & 3
if b != 0 {
x[0] = b
}
// ppc64x:"ANDCC",^"AND"
b = x[1] & x[2]
if b != 0 {
x[1] = b
}
// ppc64x:"ANDNCC",^"ANDN"
b = x[1] &^ x[2]
if b != 0 {
x[1] = b
}
// ppc64x:"ORCC",^"OR"
b = x[3] | x[4]
if b != 0 {
x[3] = b
}
// ppc64x:"SUBCC",^"SUB"
b = x[5] - x[6]
if b != 0 {
x[5] = b
}
// ppc64x:"NORCC",^"NOR"
b = ^(x[5] | x[6])
if b != 0 {
x[5] = b
}
// ppc64x:"XORCC",^"XOR"
b = x[7] ^ x[8]
if b != 0 {
x[7] = b
}
// ppc64x:"ADDCC",^"ADD"
b = x[9] + x[10]
if b != 0 {
x[9] = b
}
// ppc64x:"NEGCC",^"NEG"
b = -x[11]
if b != 0 {
x[11] = b
}
// ppc64x:"CNTLZDCC",^"CNTLZD"
b = uint64(bits.LeadingZeros64(x[12]))
if b != 0 {
x[12] = b
}
// ppc64x:"ADDCCC [$]4,"
c := int64(x[12]) + 4
if c <= 0 {
x[12] = uint64(c)
}
// ppc64x:"MULHDUCC",^"MULHDU"
hi, _ := bits.Mul64(x[13], x[14])
if hi != 0 {
x[14] = hi
}
}
func constantWrite(b bool, p *bool) {
if b {
// amd64:`MOVB [$]1, \(`
*p = b
}
}
func boolCompare1(p, q *bool) int {
// arm64:-"EOR [$]1"
if *p == *q {
return 5
}
return 7
}
func boolCompare2(p, q *bool) int {
// arm64:-"EOR [$]1"
if *p != *q {
return 5
}
return 7
}

View File

@@ -0,0 +1,35 @@
// asmcheck -gcflags=-clobberdead
//go:build amd64 || arm64
// Copyright 2021 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 codegen
type T [2]*int // contain pointer, not SSA-able (so locals are not registerized)
var p1, p2, p3 T
func F() {
// 3735936685 is 0xdeaddead. On ARM64 R27 is REGTMP.
// clobber x, y at entry. not clobber z (stack object).
// amd64:`MOVL \$3735936685, command-line-arguments\.x`, `MOVL \$3735936685, command-line-arguments\.y`, -`MOVL \$3735936685, command-line-arguments\.z`
// arm64:`MOVW R27, command-line-arguments\.x`, `MOVW R27, command-line-arguments\.y`, -`MOVW R27, command-line-arguments\.z`
x, y, z := p1, p2, p3
addrTaken(&z)
// x is dead at the call (the value of x is loaded before the CALL), y is not
// amd64:`MOVL \$3735936685, command-line-arguments\.x`, -`MOVL \$3735936685, command-line-arguments\.y`
// arm64:`MOVW R27, command-line-arguments\.x`, -`MOVW R27, command-line-arguments\.y`
use(x)
// amd64:`MOVL \$3735936685, command-line-arguments\.x`, `MOVL \$3735936685, command-line-arguments\.y`
// arm64:`MOVW R27, command-line-arguments\.x`, `MOVW R27, command-line-arguments\.y`
use(y)
}
//go:noinline
func use(T) {}
//go:noinline
func addrTaken(*T) {}

View File

@@ -0,0 +1,33 @@
// asmcheck -gcflags=-clobberdeadreg
//go:build amd64
// Copyright 2021 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 codegen
type S struct {
a, b, c, d, e, f int
}
func F(a, b, c int, d S) {
// -2401018187971961171 is 0xdeaddeaddeaddead
// amd64:`MOVQ \$-2401018187971961171, AX`, `MOVQ \$-2401018187971961171, BX`, `MOVQ \$-2401018187971961171, CX`
// amd64:`MOVQ \$-2401018187971961171, DX`, `MOVQ \$-2401018187971961171, SI`, `MOVQ \$-2401018187971961171, DI`
// amd64:`MOVQ \$-2401018187971961171, R8`, `MOVQ \$-2401018187971961171, R9`, `MOVQ \$-2401018187971961171, R10`
// amd64:`MOVQ \$-2401018187971961171, R11`, `MOVQ \$-2401018187971961171, R12`, `MOVQ \$-2401018187971961171, R13`
// amd64:-`MOVQ \$-2401018187971961171, BP` // frame pointer is not clobbered
StackArgsCall([10]int{a, b, c})
// amd64:`MOVQ \$-2401018187971961171, R12`, `MOVQ \$-2401018187971961171, R13`, `MOVQ \$-2401018187971961171, DX`
// amd64:-`MOVQ \$-2401018187971961171, AX`, -`MOVQ \$-2401018187971961171, R11` // register args are not clobbered
RegArgsCall(a, b, c, d)
}
//go:noinline
func StackArgsCall([10]int) {}
//go:noinline
//go:registerparams
func RegArgsCall(int, int, int, S) {}

View File

@@ -0,0 +1,254 @@
// asmcheck
// Copyright 2019 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 codegen
//go:noinline
func dummy() {}
// Signed 64-bit compare-and-branch.
func si64(x, y chan int64) {
// s390x:"CGRJ [$](2|4), R[0-9]+, R[0-9]+, "
for <-x < <-y {
dummy()
}
// s390x:"CL?GRJ [$]8, R[0-9]+, R[0-9]+, "
for <-x == <-y {
dummy()
}
}
// Signed 64-bit compare-and-branch with 8-bit immediate.
func si64x8(doNotOptimize int64) {
// take in doNotOptimize as an argument to avoid the loops being rewritten to count down
// s390x:"CGIJ [$]12, R[0-9]+, [$]127, "
for i := doNotOptimize; i < 128; i++ {
dummy()
}
// s390x:"CGIJ [$]10, R[0-9]+, [$]-128, "
for i := doNotOptimize; i > -129; i-- {
dummy()
}
// s390x:"CGIJ [$]2, R[0-9]+, [$]127, "
for i := doNotOptimize; i >= 128; i++ {
dummy()
}
// s390x:"CGIJ [$]4, R[0-9]+, [$]-128, "
for i := doNotOptimize; i <= -129; i-- {
dummy()
}
}
// Unsigned 64-bit compare-and-branch.
func ui64(x, y chan uint64) {
// s390x:"CLGRJ [$](2|4), R[0-9]+, R[0-9]+, "
for <-x > <-y {
dummy()
}
// s390x:"CL?GRJ [$]6, R[0-9]+, R[0-9]+, "
for <-x != <-y {
dummy()
}
}
// Unsigned 64-bit comparison with 8-bit immediate.
func ui64x8() {
// s390x:"CLGIJ [$]4, R[0-9]+, [$]128, "
for i := uint64(0); i < 128; i++ {
dummy()
}
// s390x:"CLGIJ [$]12, R[0-9]+, [$]255, "
for i := uint64(0); i < 256; i++ {
dummy()
}
// s390x:"CLGIJ [$]2, R[0-9]+, [$]255, "
for i := uint64(257); i >= 256; i-- {
dummy()
}
// s390x:"CLGIJ [$]2, R[0-9]+, [$]0, "
for i := uint64(1024); i > 0; i-- {
dummy()
}
}
// Signed 32-bit compare-and-branch.
func si32(x, y chan int32) {
// s390x:"CRJ [$](2|4), R[0-9]+, R[0-9]+, "
for <-x < <-y {
dummy()
}
// s390x:"CL?RJ [$]8, R[0-9]+, R[0-9]+, "
for <-x == <-y {
dummy()
}
}
// Signed 32-bit compare-and-branch with 8-bit immediate.
func si32x8(doNotOptimize int32) {
// take in doNotOptimize as an argument to avoid the loops being rewritten to count down
// s390x:"CIJ [$]12, R[0-9]+, [$]127, "
for i := doNotOptimize; i < 128; i++ {
dummy()
}
// s390x:"CIJ [$]10, R[0-9]+, [$]-128, "
for i := doNotOptimize; i > -129; i-- {
dummy()
}
// s390x:"CIJ [$]2, R[0-9]+, [$]127, "
for i := doNotOptimize; i >= 128; i++ {
dummy()
}
// s390x:"CIJ [$]4, R[0-9]+, [$]-128, "
for i := doNotOptimize; i <= -129; i-- {
dummy()
}
}
// Unsigned 32-bit compare-and-branch.
func ui32(x, y chan uint32) {
// s390x:"CLRJ [$](2|4), R[0-9]+, R[0-9]+, "
for <-x > <-y {
dummy()
}
// s390x:"CL?RJ [$]6, R[0-9]+, R[0-9]+, "
for <-x != <-y {
dummy()
}
}
// Unsigned 32-bit comparison with 8-bit immediate.
func ui32x8() {
// s390x:"CLIJ [$]4, R[0-9]+, [$]128, "
for i := uint32(0); i < 128; i++ {
dummy()
}
// s390x:"CLIJ [$]12, R[0-9]+, [$]255, "
for i := uint32(0); i < 256; i++ {
dummy()
}
// s390x:"CLIJ [$]2, R[0-9]+, [$]255, "
for i := uint32(257); i >= 256; i-- {
dummy()
}
// s390x:"CLIJ [$]2, R[0-9]+, [$]0, "
for i := uint32(1024); i > 0; i-- {
dummy()
}
}
// Signed 64-bit comparison with unsigned 8-bit immediate.
func si64xu8(x chan int64) {
// s390x:"CLGIJ [$]8, R[0-9]+, [$]128, "
for <-x == 128 {
dummy()
}
// s390x:"CLGIJ [$]6, R[0-9]+, [$]255, "
for <-x != 255 {
dummy()
}
}
// Signed 32-bit comparison with unsigned 8-bit immediate.
func si32xu8(x chan int32) {
// s390x:"CLIJ [$]8, R[0-9]+, [$]255, "
for <-x == 255 {
dummy()
}
// s390x:"CLIJ [$]6, R[0-9]+, [$]128, "
for <-x != 128 {
dummy()
}
}
// Unsigned 64-bit comparison with signed 8-bit immediate.
func ui64xu8(x chan uint64) {
// s390x:"CGIJ [$]8, R[0-9]+, [$]-1, "
for <-x == ^uint64(0) {
dummy()
}
// s390x:"CGIJ [$]6, R[0-9]+, [$]-128, "
for <-x != ^uint64(127) {
dummy()
}
}
// Unsigned 32-bit comparison with signed 8-bit immediate.
func ui32xu8(x chan uint32) {
// s390x:"CIJ [$]8, R[0-9]+, [$]-128, "
for <-x == ^uint32(127) {
dummy()
}
// s390x:"CIJ [$]6, R[0-9]+, [$]-1, "
for <-x != ^uint32(0) {
dummy()
}
}
// Signed 64-bit comparison with 1/-1 to comparison with 0.
func si64x0(x chan int64) {
// riscv64:"BGTZ"
for <-x >= 1 {
dummy()
}
// riscv64:"BLEZ"
for <-x < 1 {
dummy()
}
// riscv64:"BLTZ"
for <-x <= -1 {
dummy()
}
// riscv64:"BGEZ"
for <-x > -1 {
dummy()
}
}
// Unsigned 64-bit comparison with 1 to comparison with 0.
func ui64x0(x chan uint64) {
// riscv64:"BNEZ"
for <-x >= 1 {
dummy()
}
// riscv64:"BEQZ"
for <-x < 1 {
dummy()
}
// riscv64:"BNEZ"
for 0 < <-x {
dummy()
}
// riscv64:"BEQZ"
for 0 >= <-x {
dummy()
}
}

886
test/codegen/comparisons.go Normal file
View File

@@ -0,0 +1,886 @@
// asmcheck
// Copyright 2018 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 codegen
import (
"cmp"
"unsafe"
)
// This file contains code generation tests related to the comparison
// operators.
// -------------- //
// Equality //
// -------------- //
// Check that compare to constant string use 2/4/8 byte compares
func CompareString1(s string) bool {
// amd64:`CMPW \(.*\), [$]`
// arm64:`MOVHU \(.*\), [R]`,`MOVD [$]`,`CMPW R`
// ppc64le:`MOVHZ \(.*\), [R]`,`CMPW .*, [$]`
// s390x:`MOVHBR \(.*\), [R]`,`CMPW .*, [$]`
return s == "xx"
}
func CompareString2(s string) bool {
// amd64:`CMPL \(.*\), [$]`
// arm64:`MOVWU \(.*\), [R]`,`CMPW .*, [R]`
// ppc64le:`MOVWZ \(.*\), [R]`,`CMPW .*, [R]`
// s390x:`MOVWBR \(.*\), [R]`,`CMPW .*, [$]`
return s == "xxxx"
}
func CompareString3(s string) bool {
// amd64:`CMPQ \(.*\), [A-Z]`
// arm64:-`CMPW `
// ppc64x:-`CMPW `
// s390x:-`CMPW `
return s == "xxxxxxxx"
}
// Check that arrays compare use 2/4/8 byte compares
func CompareArray1(a, b [2]byte) bool {
// amd64:`CMPW command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// arm64:-`MOVBU `
// ppc64le:-`MOVBZ `
// s390x:-`MOVBZ `
return a == b
}
func CompareArray2(a, b [3]uint16) bool {
// amd64:`CMPL command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// amd64:`CMPW command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
return a == b
}
func CompareArray3(a, b [3]int16) bool {
// amd64:`CMPL command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// amd64:`CMPW command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
return a == b
}
func CompareArray4(a, b [12]int8) bool {
// amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// amd64:`CMPL command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
return a == b
}
func CompareArray5(a, b [15]byte) bool {
// amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
return a == b
}
// This was a TODO in mapaccess1_faststr
func CompareArray6(a, b unsafe.Pointer) bool {
// amd64:`CMPL \(.*\), [A-Z]`
// arm64:`MOVWU \(.*\), [R]`,`CMPW .*, [R]`
// ppc64le:`MOVWZ \(.*\), [R]`,`CMPW .*, [R]`
// s390x:`MOVWBR \(.*\), [R]`,`CMPW .*, [R]`
return *((*[4]byte)(a)) != *((*[4]byte)(b))
}
// Check that some structs generate 2/4/8 byte compares.
type T1 struct {
a [8]byte
}
func CompareStruct1(s1, s2 T1) bool {
// amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// amd64:-`CALL`
return s1 == s2
}
type T2 struct {
a [16]byte
}
func CompareStruct2(s1, s2 T2) bool {
// amd64:`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// amd64:-`CALL`
return s1 == s2
}
// Assert that a memequal call is still generated when
// inlining would increase binary size too much.
type T3 struct {
a [24]byte
}
func CompareStruct3(s1, s2 T3) bool {
// amd64:-`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// amd64:`CALL`
return s1 == s2
}
type T4 struct {
a [32]byte
}
func CompareStruct4(s1, s2 T4) bool {
// amd64:-`CMPQ command-line-arguments[.+_a-z0-9]+\(SP\), [A-Z]`
// amd64:`CALL`
return s1 == s2
}
// -------------- //
// Ordering //
// -------------- //
// Test that LEAQ/ADDQconst are folded into SETx ops
var r bool
func CmpFold(x uint32) {
// amd64:`SETHI .*\(SB\)`
r = x > 4
}
// Test that direct comparisons with memory are generated when
// possible
func CmpMem1(p int, q *int) bool {
// amd64:`CMPQ \(.*\), [A-Z]`
return p < *q
}
func CmpMem2(p *int, q int) bool {
// amd64:`CMPQ \(.*\), [A-Z]`
return *p < q
}
func CmpMem3(p *int) bool {
// amd64:`CMPQ \(.*\), [$]7`
return *p < 7
}
func CmpMem4(p *int) bool {
// amd64:`CMPQ \(.*\), [$]7`
return 7 < *p
}
func CmpMem5(p **int) {
// amd64:`CMPL runtime.writeBarrier\(SB\), [$]0`
*p = nil
}
func CmpMem6(a []int) int {
// 386:`CMPL\s8\([A-Z]+\),`
// amd64:`CMPQ\s16\([A-Z]+\),`
if a[1] > a[2] {
return 1
} else {
return 2
}
}
// Check tbz/tbnz are generated when comparing against zero on arm64
func CmpZero1(a int32, ptr *int) {
if a < 0 { // arm64:"TBZ"
*ptr = 0
}
}
func CmpZero2(a int64, ptr *int) {
if a < 0 { // arm64:"TBZ"
*ptr = 0
}
}
func CmpZero3(a int32, ptr *int) {
if a >= 0 { // arm64:"TBNZ"
*ptr = 0
}
}
func CmpZero4(a int64, ptr *int) {
if a >= 0 { // arm64:"TBNZ"
*ptr = 0
}
}
func CmpToZero(a, b, d int32, e, f int64, deOptC0, deOptC1 bool) int32 {
// arm:`TST`,-`AND`
// arm64:`TSTW`,-`AND`
// 386:`TESTL`,-`ANDL`
// amd64:`TESTL`,-`ANDL`
c0 := a&b < 0
// arm:`CMN`,-`ADD`
// arm64:`CMNW`,-`ADD`
c1 := a+b < 0
// arm:`TEQ`,-`XOR`
c2 := a^b < 0
// arm64:`TST`,-`AND`
// amd64:`TESTQ`,-`ANDQ`
c3 := e&f < 0
// arm64:`CMN`,-`ADD`
c4 := e+f < 0
// not optimized to single CMNW/CMN due to further use of b+d
// arm64:`ADD`,-`CMNW`
// arm:`ADD`,-`CMN`
c5 := b+d == 0
// not optimized to single TSTW/TST due to further use of a&d
// arm64:`AND`,-`TSTW`
// arm:`AND`,-`TST`
// 386:`ANDL`
c6 := a&d >= 0
// For arm64, could be TST+BGE or AND+TBZ
c7 := e&(f<<3) < 0
// For arm64, could be CMN+BPL or ADD+TBZ
c8 := e+(f<<3) < 0
// arm64:`TST\sR[0-9],\sR[0-9]+`
c9 := e&(-19) < 0
if c0 {
return 1
} else if c1 {
return 2
} else if c2 {
return 3
} else if c3 {
return 4
} else if c4 {
return 5
} else if c5 {
return 6
} else if c6 {
return 7
} else if c7 {
return 9
} else if c8 {
return 10
} else if c9 {
return 11
} else if deOptC0 {
return b + d
} else if deOptC1 {
return a & d
} else {
return 0
}
}
func CmpLogicalToZero(a, b, c uint32, d, e, f, g uint64) uint64 {
// ppc64x:"ANDCC" -"CMPW"
// wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64"
if a&63 == 0 {
return 1
}
// ppc64x:"ANDCC" -"CMP"
// wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64"
if d&255 == 0 {
return 1
}
// ppc64x:"ANDCC" -"CMP"
// wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64"
if d&e == 0 {
return 1
}
// ppc64x:"ORCC" -"CMP"
// wasm:"I64Eqz" -"I32Eqz" -"I64ExtendI32U" -"I32WrapI64"
if f|g == 0 {
return 1
}
// ppc64x:"XORCC" -"CMP"
// wasm:"I64Eqz" "I32Eqz" -"I64ExtendI32U" -"I32WrapI64"
if e^d == 0 {
return 1
}
return 0
}
// The following CmpToZero_ex* check that cmp|cmn with bmi|bpl are generated for
// 'comparing to zero' expressions
// var + const
// 'x-const' might be canonicalized to 'x+(-const)', so we check both
// CMN and CMP for subtraction expressions to make the pattern robust.
func CmpToZero_ex1(a int64, e int32) int {
// arm64:`CMN`,-`ADD`,`(BMI|BPL)`
if a+3 < 0 {
return 1
}
// arm64:`CMN`,-`ADD`,`BEQ`,`(BMI|BPL)`
if a+5 <= 0 {
return 1
}
// arm64:`CMN`,-`ADD`,`(BMI|BPL)`
if a+13 >= 0 {
return 2
}
// arm64:`CMP|CMN`,-`(ADD|SUB)`,`(BMI|BPL)`
if a-7 < 0 {
return 3
}
// arm64:`SUB`,`TBZ`
if a-11 >= 0 {
return 4
}
// arm64:`SUB`,`CMP`,`BGT`
if a-19 > 0 {
return 4
}
// arm64:`CMNW`,-`ADDW`,`(BMI|BPL)`
// arm:`CMN`,-`ADD`,`(BMI|BPL)`
if e+3 < 0 {
return 5
}
// arm64:`CMNW`,-`ADDW`,`(BMI|BPL)`
// arm:`CMN`,-`ADD`,`(BMI|BPL)`
if e+13 >= 0 {
return 6
}
// arm64:`CMPW|CMNW`,`(BMI|BPL)`
// arm:`CMP|CMN`, -`(ADD|SUB)`, `(BMI|BPL)`
if e-7 < 0 {
return 7
}
// arm64:`SUB`,`TBNZ`
// arm:`CMP|CMN`, -`(ADD|SUB)`, `(BMI|BPL)`
if e-11 >= 0 {
return 8
}
return 0
}
// var + var
// TODO: optimize 'var - var'
func CmpToZero_ex2(a, b, c int64, e, f, g int32) int {
// arm64:`CMN`,-`ADD`,`(BMI|BPL)`
if a+b < 0 {
return 1
}
// arm64:`CMN`,-`ADD`,`BEQ`,`(BMI|BPL)`
if a+c <= 0 {
return 1
}
// arm64:`CMN`,-`ADD`,`(BMI|BPL)`
if b+c >= 0 {
return 2
}
// arm64:`CMNW`,-`ADDW`,`(BMI|BPL)`
// arm:`CMN`,-`ADD`,`(BMI|BPL)`
if e+f < 0 {
return 5
}
// arm64:`CMNW`,-`ADDW`,`(BMI|BPL)`
// arm:`CMN`,-`ADD`,`(BMI|BPL)`
if f+g >= 0 {
return 6
}
return 0
}
// var + var*var
func CmpToZero_ex3(a, b, c, d int64, e, f, g, h int32) int {
// arm64:`CMN`,-`MADD`,`MUL`,`(BMI|BPL)`
if a+b*c < 0 {
return 1
}
// arm64:`CMN`,-`MADD`,`MUL`,`(BMI|BPL)`
if b+c*d >= 0 {
return 2
}
// arm64:`CMNW`,-`MADDW`,`MULW`,`BEQ`,`(BMI|BPL)`
// arm:`CMN`,-`MULA`,`MUL`,`BEQ`,`(BMI|BPL)`
if e+f*g > 0 {
return 5
}
// arm64:`CMNW`,-`MADDW`,`MULW`,`BEQ`,`(BMI|BPL)`
// arm:`CMN`,-`MULA`,`MUL`,`BEQ`,`(BMI|BPL)`
if f+g*h <= 0 {
return 6
}
return 0
}
// var - var*var
func CmpToZero_ex4(a, b, c, d int64, e, f, g, h int32) int {
// arm64:`CMP`,-`MSUB`,`MUL`,`BEQ`,`(BMI|BPL)`
if a-b*c > 0 {
return 1
}
// arm64:`CMP`,-`MSUB`,`MUL`,`(BMI|BPL)`
if b-c*d >= 0 {
return 2
}
// arm64:`CMPW`,-`MSUBW`,`MULW`,`(BMI|BPL)`
if e-f*g < 0 {
return 5
}
// arm64:`CMPW`,-`MSUBW`,`MULW`,`(BMI|BPL)`
if f-g*h >= 0 {
return 6
}
return 0
}
func CmpToZero_ex5(e, f int32, u uint32) int {
// arm:`CMN`,-`ADD`,`BEQ`,`(BMI|BPL)`
if e+f<<1 > 0 {
return 1
}
// arm:`CMP`,-`SUB`,`(BMI|BPL)`
if f-int32(u>>2) >= 0 {
return 2
}
return 0
}
func UintLtZero(a uint8, b uint16, c uint32, d uint64) int {
// amd64: -`(TESTB|TESTW|TESTL|TESTQ|JCC|JCS)`
// arm64: -`(CMPW|CMP|BHS|BLO)`
if a < 0 || b < 0 || c < 0 || d < 0 {
return 1
}
return 0
}
func UintGeqZero(a uint8, b uint16, c uint32, d uint64) int {
// amd64: -`(TESTB|TESTW|TESTL|TESTQ|JCS|JCC)`
// arm64: -`(CMPW|CMP|BLO|BHS)`
if a >= 0 || b >= 0 || c >= 0 || d >= 0 {
return 1
}
return 0
}
func UintGtZero(a uint8, b uint16, c uint32, d uint64) int {
// arm64: `(CBN?ZW)`, `(CBN?Z[^W])`, -`(CMPW|CMP|BLS|BHI)`
if a > 0 || b > 0 || c > 0 || d > 0 {
return 1
}
return 0
}
func UintLeqZero(a uint8, b uint16, c uint32, d uint64) int {
// arm64: `(CBN?ZW)`, `(CBN?Z[^W])`, -`(CMPW|CMP|BHI|BLS)`
if a <= 0 || b <= 0 || c <= 0 || d <= 0 {
return 1
}
return 0
}
func UintLtOne(a uint8, b uint16, c uint32, d uint64) int {
// arm64: `(CBN?ZW)`, `(CBN?Z[^W])`, -`(CMPW|CMP|BHS|BLO)`
if a < 1 || b < 1 || c < 1 || d < 1 {
return 1
}
return 0
}
func UintGeqOne(a uint8, b uint16, c uint32, d uint64) int {
// arm64: `(CBN?ZW)`, `(CBN?Z[^W])`, -`(CMPW|CMP|BLO|BHS)`
if a >= 1 || b >= 1 || c >= 1 || d >= 1 {
return 1
}
return 0
}
func CmpToZeroU_ex1(a uint8, b uint16, c uint32, d uint64) int {
// wasm:"I64Eqz"-"I64LtU"
if 0 < a {
return 1
}
// wasm:"I64Eqz"-"I64LtU"
if 0 < b {
return 1
}
// wasm:"I64Eqz"-"I64LtU"
if 0 < c {
return 1
}
// wasm:"I64Eqz"-"I64LtU"
if 0 < d {
return 1
}
return 0
}
func CmpToZeroU_ex2(a uint8, b uint16, c uint32, d uint64) int {
// wasm:"I64Eqz"-"I64LeU"
if a <= 0 {
return 1
}
// wasm:"I64Eqz"-"I64LeU"
if b <= 0 {
return 1
}
// wasm:"I64Eqz"-"I64LeU"
if c <= 0 {
return 1
}
// wasm:"I64Eqz"-"I64LeU"
if d <= 0 {
return 1
}
return 0
}
func CmpToOneU_ex1(a uint8, b uint16, c uint32, d uint64) int {
// wasm:"I64Eqz"-"I64LtU"
if a < 1 {
return 1
}
// wasm:"I64Eqz"-"I64LtU"
if b < 1 {
return 1
}
// wasm:"I64Eqz"-"I64LtU"
if c < 1 {
return 1
}
// wasm:"I64Eqz"-"I64LtU"
if d < 1 {
return 1
}
return 0
}
func CmpToOneU_ex2(a uint8, b uint16, c uint32, d uint64) int {
// wasm:"I64Eqz"-"I64LeU"
if 1 <= a {
return 1
}
// wasm:"I64Eqz"-"I64LeU"
if 1 <= b {
return 1
}
// wasm:"I64Eqz"-"I64LeU"
if 1 <= c {
return 1
}
// wasm:"I64Eqz"-"I64LeU"
if 1 <= d {
return 1
}
return 0
}
// Check that small memequals are replaced with eq instructions
func equalConstString1() bool {
a := string("A")
b := string("Z")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a == b
}
func equalVarString1(a string) bool {
b := string("Z")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a[:1] == b
}
func equalConstString2() bool {
a := string("AA")
b := string("ZZ")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a == b
}
func equalVarString2(a string) bool {
b := string("ZZ")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a[:2] == b
}
func equalConstString4() bool {
a := string("AAAA")
b := string("ZZZZ")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a == b
}
func equalVarString4(a string) bool {
b := string("ZZZZ")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a[:4] == b
}
func equalConstString8() bool {
a := string("AAAAAAAA")
b := string("ZZZZZZZZ")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a == b
}
func equalVarString8(a string) bool {
b := string("ZZZZZZZZ")
// amd64:-".*memequal"
// arm64:-".*memequal"
// ppc64x:-".*memequal"
return a[:8] == b
}
func equalVarStringNoSpill(a, b string) bool {
s := string("ZZZZZZZZZ")
// arm64:".*memequal"
memeq1 := a[:9] == s
// arm64:-".*"
memeq2 := s == a[:9]
// arm64:-"MOVB R0,.*SP",".*memequal"
memeq3 := s == b[:9]
return memeq1 && memeq2 && memeq3
}
func cmpToCmn(a, b, c, d int) int {
var c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11 int
// arm64:`CMN`,-`CMP`
if a < -8 {
c1 = 1
}
// arm64:`CMN`,-`CMP`
if a+1 == 0 {
c2 = 1
}
// arm64:`CMN`,-`CMP`
if a+3 != 0 {
c3 = 1
}
// arm64:`CMN`,-`CMP`
if a+b == 0 {
c4 = 1
}
// arm64:`CMN`,-`CMP`
if b+c != 0 {
c5 = 1
}
// arm64:`CMN`,-`CMP`
if a == -c {
c6 = 1
}
// arm64:`CMN`,-`CMP`
if b != -d {
c7 = 1
}
// arm64:`CMN`,-`CMP`
if a*b+c == 0 {
c8 = 1
}
// arm64:`CMN`,-`CMP`
if a*c+b != 0 {
c9 = 1
}
// arm64:`CMP`,-`CMN`
if b*c-a == 0 {
c10 = 1
}
// arm64:`CMP`,-`CMN`
if a*d-b != 0 {
c11 = 1
}
return c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11
}
func cmpToCmnLessThan(a, b, c, d int) int {
var c1, c2, c3, c4 int
// arm64:`CMN`,`CSET MI`,-`CMP`
if a+1 < 0 {
c1 = 1
}
// arm64:`CMN`,`CSET MI`,-`CMP`
if a+b < 0 {
c2 = 1
}
// arm64:`CMN`,`CSET MI`,-`CMP`
if a*b+c < 0 {
c3 = 1
}
// arm64:`CMP`,`CSET MI`,-`CMN`
if a-b*c < 0 {
c4 = 1
}
return c1 + c2 + c3 + c4
}
func less128Signed32(x int32) bool {
// amd64:`CMPL.*127`
// amd64:`SETLE`
return x < 128
}
func less128Signed64(x int64) bool {
// amd64:`CMPQ.*127`
// amd64:`SETLE`
return x < 128
}
func less128Unsigned32(x uint32) bool {
// amd64:`CMPL.*127`
// amd64:`SETLS`
return x < 128
}
func less128Unsigned64(x uint64) bool {
// amd64:`CMPQ.*127`
// amd64:`SETLS`
return x < 128
}
func ge128Unsigned32(x uint32) bool {
// amd64:`CMPL.*127`
// amd64:`SETHI`
return x >= 128
}
func ge128Unsigned64(x uint64) bool {
// amd64:`CMPQ.*127`
// amd64:`SETHI`
return x >= 128
}
func ge128Signed32(x int32) bool {
// amd64:`CMPL.*127`
// amd64:`SETGT`
return x >= 128
}
func ge128Signed64(x int64) bool {
// amd64:`CMPQ.*127`
// amd64:`SETGT`
return x >= 128
}
func cmpToCmnGreaterThanEqual(a, b, c, d int) int {
var c1, c2, c3, c4 int
// arm64:`CMN`,`CSET PL`,-`CMP`
if a+1 >= 0 {
c1 = 1
}
// arm64:`CMN`,`CSET PL`,-`CMP`
if a+b >= 0 {
c2 = 1
}
// arm64:`CMN`,`CSET PL`,-`CMP`
if a*b+c >= 0 {
c3 = 1
}
// arm64:`CMP`,`CSET PL`,-`CMN`
if a-b*c >= 0 {
c4 = 1
}
return c1 + c2 + c3 + c4
}
func cmp1(val string) bool {
var z string
// amd64:-".*memequal"
return z == val
}
func cmp2(val string) bool {
var z string
// amd64:-".*memequal"
return val == z
}
func cmp3(val string) bool {
z := "food"
// amd64:-".*memequal"
return z == val
}
func cmp4(val string) bool {
z := "food"
// amd64:-".*memequal"
return val == z
}
func cmp5[T comparable](val T) bool {
var z T
// amd64:-".*memequal"
return z == val
}
func cmp6[T comparable](val T) bool {
var z T
// amd64:-".*memequal"
return val == z
}
func cmp7() {
cmp5[string]("") // force instantiation
cmp6[string]("") // force instantiation
}
type Point struct {
X, Y int
}
// invertLessThanNoov checks (LessThanNoov (InvertFlags x)) is lowered as
// CMP, CSET, CSEL instruction sequence. InvertFlags are only generated under
// certain conditions, see canonLessThan, so if the code below does not
// generate an InvertFlags OP, this check may fail.
func invertLessThanNoov(p1, p2, p3 Point) bool {
// arm64:`CMP`,`CSET`,`CSEL`
return (p1.X-p3.X)*(p2.Y-p3.Y)-(p2.X-p3.X)*(p1.Y-p3.Y) < 0
}
func cmpstring1(x, y string) int {
// amd64:".*cmpstring"
if x < y {
return -1
}
// amd64:-".*cmpstring"
if x > y {
return +1
}
return 0
}
func cmpstring2(x, y string) int {
// We want to fail if there are two calls to cmpstring.
// They will both have the same line number, so a test
// like in cmpstring1 will not work. Instead, we
// look for spill/restore instructions, which only
// need to exist if there are 2 calls.
//amd64:-`MOVQ .*\(SP\)`
return cmp.Compare(x, y)
}

529
test/codegen/condmove.go Normal file
View File

@@ -0,0 +1,529 @@
// asmcheck
// Copyright 2018 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 codegen
func cmovint(c int) int {
x := c + 4
if x < 0 {
x = 182
}
// amd64:"CMOVQLT"
// arm64:"CSEL LT"
// ppc64x:"ISEL [$]0"
// wasm:"Select"
return x
}
func cmovchan(x, y chan int) chan int {
if x != y {
x = y
}
// amd64:"CMOVQNE"
// arm64:"CSEL NE"
// ppc64x:"ISEL [$]2"
// wasm:"Select"
return x
}
func cmovuintptr(x, y uintptr) uintptr {
if x < y {
x = -y
}
// amd64:"CMOVQ(HI|CS)"
// arm64:"CSNEG LS"
// ppc64x:"ISEL [$]1"
// wasm:"Select"
return x
}
func cmov32bit(x, y uint32) uint32 {
if x < y {
x = -y
}
// amd64:"CMOVL(HI|CS)"
// arm64:"CSNEG (LS|HS)"
// ppc64x:"ISEL [$]1"
// wasm:"Select"
return x
}
func cmov16bit(x, y uint16) uint16 {
if x < y {
x = -y
}
// amd64:"CMOVW(HI|CS)"
// arm64:"CSNEG (LS|HS)"
// ppc64x:"ISEL [$][01]"
// wasm:"Select"
return x
}
// Floating point comparison. For EQ/NE, we must
// generate special code to handle NaNs.
func cmovfloateq(x, y float64) int {
a := 128
if x == y {
a = 256
}
// amd64:"CMOVQNE" "CMOVQPC"
// arm64:"CSEL EQ"
// ppc64x:"ISEL [$]2"
// wasm:"Select"
return a
}
func cmovfloatne(x, y float64) int {
a := 128
if x != y {
a = 256
}
// amd64:"CMOVQNE" "CMOVQPS"
// arm64:"CSEL NE"
// ppc64x:"ISEL [$]2"
// wasm:"Select"
return a
}
//go:noinline
func frexp(f float64) (frac float64, exp int) {
return 1.0, 4
}
//go:noinline
func ldexp(frac float64, exp int) float64 {
return 1.0
}
// Generate a CMOV with a floating comparison and integer move.
func cmovfloatint2(x, y float64) float64 {
yfr, yexp := 4.0, 5
r := x
for r >= y {
rfr, rexp := frexp(r)
if rfr < yfr {
rexp = rexp - 42
}
// amd64:"CMOVQHI"
// arm64:"CSEL MI"
// ppc64x:"ISEL [$]0"
// wasm:"Select"
r = r - ldexp(y, rexp-yexp)
}
return r
}
func cmovloaded(x [4]int, y int) int {
if x[2] != 0 {
y = x[2]
} else {
y = y >> 2
}
// amd64:"CMOVQNE"
// arm64:"CSEL NE"
// ppc64x:"ISEL [$]2"
// wasm:"Select"
return y
}
func cmovuintptr2(x, y uintptr) uintptr {
a := x * 2
if a == 0 {
a = 256
}
// amd64:"CMOVQEQ"
// arm64:"CSEL EQ"
// ppc64x:"ISEL [$]2"
// wasm:"Select"
return a
}
// Floating point CMOVs are not supported by amd64/arm64/ppc64x
func cmovfloatmove(x, y int) float64 {
a := 1.0
if x <= y {
a = 2.0
}
// amd64:-"CMOV"
// arm64:-"CSEL"
// ppc64x:-"ISEL"
// wasm:-"Select"
return a
}
// On amd64, the following patterns trigger comparison inversion.
// Test that we correctly invert the CMOV condition
var gsink int64
var gusink uint64
func cmovinvert1(x, y int64) int64 {
if x < gsink {
y = -y
}
// amd64:"CMOVQGT"
return y
}
func cmovinvert2(x, y int64) int64 {
if x <= gsink {
y = -y
}
// amd64:"CMOVQGE"
return y
}
func cmovinvert3(x, y int64) int64 {
if x == gsink {
y = -y
}
// amd64:"CMOVQEQ"
return y
}
func cmovinvert4(x, y int64) int64 {
if x != gsink {
y = -y
}
// amd64:"CMOVQNE"
return y
}
func cmovinvert5(x, y uint64) uint64 {
if x > gusink {
y = -y
}
// amd64:"CMOVQCS"
return y
}
func cmovinvert6(x, y uint64) uint64 {
if x >= gusink {
y = -y
}
// amd64:"CMOVQLS"
return y
}
func cmovload(a []int, i int, b bool) int {
if b {
i += 42
}
// See issue 26306
// amd64:-"CMOVQNE"
return a[i]
}
func cmovstore(a []int, i int, b bool) {
if b {
i += 42
}
// amd64:"CMOVQNE"
a[i] = 7
}
var r0, r1, r2, r3, r4, r5 int
func cmovinc(cond bool, a, b, c int) {
var x0, x1 int
if cond {
x0 = a
} else {
x0 = b + 1
}
// arm64:"CSINC NE", -"CSEL"
r0 = x0
if cond {
x1 = b + 1
} else {
x1 = a
}
// arm64:"CSINC EQ", -"CSEL"
r1 = x1
if cond {
c++
}
// arm64:"CSINC EQ", -"CSEL"
r2 = c
}
func cmovinv(cond bool, a, b int) {
var x0, x1 int
if cond {
x0 = a
} else {
x0 = ^b
}
// arm64:"CSINV NE", -"CSEL"
r0 = x0
if cond {
x1 = ^b
} else {
x1 = a
}
// arm64:"CSINV EQ", -"CSEL"
r1 = x1
}
func cmovneg(cond bool, a, b, c int) {
var x0, x1 int
if cond {
x0 = a
} else {
x0 = -b
}
// arm64:"CSNEG NE", -"CSEL"
r0 = x0
if cond {
x1 = -b
} else {
x1 = a
}
// arm64:"CSNEG EQ", -"CSEL"
r1 = x1
}
func cmovsetm(cond bool, x int) {
var x0, x1 int
if cond {
x0 = -1
} else {
x0 = 0
}
// arm64:"CSETM NE", -"CSEL"
r0 = x0
if cond {
x1 = 0
} else {
x1 = -1
}
// arm64:"CSETM EQ", -"CSEL"
r1 = x1
}
func cmovFcmp0(s, t float64, a, b int) {
var x0, x1, x2, x3, x4, x5 int
if s < t {
x0 = a
} else {
x0 = b + 1
}
// arm64:"CSINC MI", -"CSEL"
r0 = x0
if s <= t {
x1 = a
} else {
x1 = ^b
}
// arm64:"CSINV LS", -"CSEL"
r1 = x1
if s > t {
x2 = a
} else {
x2 = -b
}
// arm64:"CSNEG MI", -"CSEL"
r2 = x2
if s >= t {
x3 = -1
} else {
x3 = 0
}
// arm64:"CSETM LS", -"CSEL"
r3 = x3
if s == t {
x4 = a
} else {
x4 = b + 1
}
// arm64:"CSINC EQ", -"CSEL"
r4 = x4
if s != t {
x5 = a
} else {
x5 = b + 1
}
// arm64:"CSINC NE", -"CSEL"
r5 = x5
}
func cmovFcmp1(s, t float64, a, b int) {
var x0, x1, x2, x3, x4, x5 int
if s < t {
x0 = b + 1
} else {
x0 = a
}
// arm64:"CSINC PL", -"CSEL"
r0 = x0
if s <= t {
x1 = ^b
} else {
x1 = a
}
// arm64:"CSINV HI", -"CSEL"
r1 = x1
if s > t {
x2 = -b
} else {
x2 = a
}
// arm64:"CSNEG PL", -"CSEL"
r2 = x2
if s >= t {
x3 = 0
} else {
x3 = -1
}
// arm64:"CSETM HI", -"CSEL"
r3 = x3
if s == t {
x4 = b + 1
} else {
x4 = a
}
// arm64:"CSINC NE", -"CSEL"
r4 = x4
if s != t {
x5 = b + 1
} else {
x5 = a
}
// arm64:"CSINC EQ", -"CSEL"
r5 = x5
}
func cmovzero1(c bool) int {
var x int
if c {
x = 182
}
// loong64:"MASKEQZ", -"MASKNEZ"
return x
}
func cmovzero2(c bool) int {
var x int
if !c {
x = 182
}
// loong64:"MASKNEZ", -"MASKEQZ"
return x
}
// Conditionally selecting between a value or 0 can be done without
// an extra load of 0 to a register on PPC64 by using R0 (which always
// holds the value $0) instead. Verify both cases where either arg1
// or arg2 is zero.
func cmovzeroreg0(a, b int) int {
x := 0
if a == b {
x = a
}
// ppc64x:"ISEL [$]2, R[0-9]+, R0, R[0-9]+"
return x
}
func cmovzeroreg1(a, b int) int {
x := a
if a == b {
x = 0
}
// ppc64x:"ISEL [$]2, R0, R[0-9]+, R[0-9]+"
return x
}
func cmovmathadd(a uint, b bool) uint {
if b {
a++
}
// amd64:"ADDQ", -"CMOV"
// arm64:"CSINC", -"CSEL"
// ppc64x:"ADD", -"ISEL"
// wasm:"I64Add", -"Select"
return a
}
func cmovmathsub(a uint, b bool) uint {
if b {
a--
}
// amd64:"SUBQ", -"CMOV"
// arm64:"SUB", -"CSEL"
// ppc64x:"SUB", -"ISEL"
// wasm:"I64Sub", -"Select"
return a
}
func cmovmathdouble(a uint, b bool) uint {
if b {
a *= 2
}
// amd64:"SHL", -"CMOV"
// amd64/v3:"SHL", -"CMOV", -"MOV"
// arm64:"LSL", -"CSEL"
// wasm:"I64Shl", -"Select"
return a
}
func cmovmathhalvei(a int, b bool) int {
if b {
// For some reason the compiler attributes the shift to inside this block rather than where the Phi node is.
// arm64:"ASR", -"CSEL"
// wasm:"I64ShrS", -"Select"
a /= 2
}
// arm64:-"CSEL"
// wasm:-"Select"
return a
}
func cmovmathhalveu(a uint, b bool) uint {
if b {
a /= 2
}
// amd64:"SHR", -"CMOV"
// amd64/v3:"SHR", -"CMOV", -"MOV"
// arm64:"LSR", -"CSEL"
// wasm:"I64ShrU", -"Select"
return a
}
func branchlessBoolToUint8(b bool) (r uint8) {
if b {
r = 1
}
return
}
func cmovFromMulFromFlags64(x uint64, b bool) uint64 {
// amd64:-"MOVB.ZX"
r := uint64(branchlessBoolToUint8(b))
// amd64:"CMOV",-"MOVB.ZX",-"MUL"
return x * r
}
func cmovFromMulFromFlags64sext(x int64, b bool) int64 {
// amd64:-"MOVB.ZX"
r := int64(int8(branchlessBoolToUint8(b)))
// amd64:"CMOV",-"MOVB.ZX",-"MUL"
return x * r
}

35
test/codegen/constants.go Normal file
View File

@@ -0,0 +1,35 @@
// asmcheck
// Copyright 2023 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 codegen
// A uint16 or sint16 constant shifted left.
func shifted16BitConstants() (out [64]uint64) {
// ppc64x: "MOVD [$]8193,", "SLD [$]27,"
out[0] = 0x0000010008000000
// ppc64x: "MOVD [$]-32767", "SLD [$]26,"
out[1] = 0xFFFFFE0004000000
// ppc64x: "MOVD [$]-1", "SLD [$]48,"
out[2] = 0xFFFF000000000000
// ppc64x: "MOVD [$]65535", "SLD [$]44,"
out[3] = 0x0FFFF00000000000
return
}
// A contiguous set of 1 bits, potentially wrapping.
func contiguousMaskConstants() (out [64]uint64) {
// ppc64x: "MOVD [$]-1", "RLDC R[0-9]+, [$]44, [$]63,"
out[0] = 0xFFFFF00000000001
// ppc64x: "MOVD [$]-1", "RLDC R[0-9]+, [$]43, [$]63,"
out[1] = 0xFFFFF80000000001
// ppc64x: "MOVD [$]-1", "RLDC R[0-9]+, [$]43, [$]4,"
out[2] = 0x0FFFF80000000000
// ppc64x/power8: "MOVD [$]-1", "RLDC R[0-9]+, [$]33, [$]63,"
// ppc64x/power9: "MOVD [$]-1", "RLDC R[0-9]+, [$]33, [$]63,"
// ppc64x/power10: "MOVD [$]-8589934591,"
out[3] = 0xFFFFFFFE00000001
return
}

162
test/codegen/copy.go Normal file
View File

@@ -0,0 +1,162 @@
// asmcheck
// Copyright 2018 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 codegen
import "runtime"
// Check small copies are replaced with moves.
func movesmall4() {
x := [...]byte{1, 2, 3, 4}
// 386:-".*memmove"
// amd64:-".*memmove"
// arm:-".*memmove"
// arm64:-".*memmove"
// ppc64x:-".*memmove"
copy(x[1:], x[:])
}
func movesmall7() {
x := [...]byte{1, 2, 3, 4, 5, 6, 7}
// 386:-".*memmove"
// amd64:-".*memmove"
// arm64:-".*memmove"
// ppc64x:-".*memmove"
copy(x[1:], x[:])
}
func movesmall16() {
x := [...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
// amd64:-".*memmove"
// ppc64x:".*memmove"
copy(x[1:], x[:])
}
var x [256]byte
// Check that large disjoint copies are replaced with moves.
func moveDisjointStack32() {
var s [32]byte
// ppc64x:-".*memmove"
// ppc64x/power8:"LXVD2X" -"ADD" -"BC"
// ppc64x/power9:"LXV" -"LXVD2X" -"ADD" -"BC"
copy(s[:], x[:32])
runtime.KeepAlive(&s)
}
func moveDisjointStack64() {
var s [96]byte
// ppc64x:-".*memmove"
// ppc64x/power8:"LXVD2X" "ADD" "BC"
// ppc64x/power9:"LXV" -"LXVD2X" -"ADD" -"BC"
copy(s[:], x[:96])
runtime.KeepAlive(&s)
}
func moveDisjointStack() {
var s [256]byte
// s390x:-".*memmove"
// amd64:-".*memmove"
// ppc64x:-".*memmove"
// ppc64x/power8:"LXVD2X"
// ppc64x/power9:"LXV" -"LXVD2X"
copy(s[:], x[:])
runtime.KeepAlive(&s)
}
func moveDisjointArg(b *[256]byte) {
var s [256]byte
// s390x:-".*memmove"
// amd64:-".*memmove"
// ppc64x:-".*memmove"
// ppc64x/power8:"LXVD2X"
// ppc64x/power9:"LXV" -"LXVD2X"
copy(s[:], b[:])
runtime.KeepAlive(&s)
}
func moveDisjointNoOverlap(a *[256]byte) {
// s390x:-".*memmove"
// amd64:-".*memmove"
// ppc64x:-".*memmove"
// ppc64x/power8:"LXVD2X"
// ppc64x/power9:"LXV" -"LXVD2X"
copy(a[:], a[128:])
}
// Check arch-specific memmove lowering. See issue 41662 fot details
func moveArchLowering1(b []byte, x *[1]byte) {
_ = b[1]
// amd64:-".*memmove"
// arm64:-".*memmove"
// loong64:-".*memmove"
// ppc64x:-".*memmove"
copy(b, x[:])
}
func moveArchLowering2(b []byte, x *[2]byte) {
_ = b[2]
// amd64:-".*memmove"
// arm64:-".*memmove"
// loong64:-".*memmove"
// ppc64x:-".*memmove"
copy(b, x[:])
}
func moveArchLowering4(b []byte, x *[4]byte) {
_ = b[4]
// amd64:-".*memmove"
// arm64:-".*memmove"
// loong64:-".*memmove"
// ppc64x:-".*memmove"
copy(b, x[:])
}
func moveArchLowering8(b []byte, x *[8]byte) {
_ = b[8]
// amd64:-".*memmove"
// arm64:-".*memmove"
// ppc64x:-".*memmove"
copy(b, x[:])
}
func moveArchLowering16(b []byte, x *[16]byte) {
_ = b[16]
// amd64:-".*memmove"
copy(b, x[:])
}
// Check that no branches are generated when the pointers are [not] equal.
func ptrEqual() {
// amd64:-"JEQ" -"JNE"
// ppc64x:-"BEQ" -"BNE"
// s390x:-"BEQ" -"BNE"
copy(x[:], x[:])
}
func ptrOneOffset() {
// amd64:-"JEQ" -"JNE"
// ppc64x:-"BEQ" -"BNE"
// s390x:-"BEQ" -"BNE"
copy(x[1:], x[:])
}
func ptrBothOffset() {
// amd64:-"JEQ" -"JNE"
// ppc64x:-"BEQ" -"BNE"
// s390x:-"BEQ" -"BNE"
copy(x[1:], x[2:])
}
// Verify #62698 on PPC64.
func noMaskOnCopy(a []int, s string, x int) int {
// ppc64x:-"MOVD [$]-1", -"AND"
return a[x&^copy([]byte{}, s)]
}

1200
test/codegen/divmod.go Normal file

File diff suppressed because it is too large Load Diff

315
test/codegen/floats.go Normal file
View File

@@ -0,0 +1,315 @@
// asmcheck
// Copyright 2018 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 codegen
import "math"
// This file contains codegen tests related to arithmetic
// simplifications and optimizations on float types.
// For codegen tests on integer types, see arithmetic.go.
// --------------------- //
// Strength-reduce //
// --------------------- //
func Mul2(f float64) float64 {
// 386/sse2:"ADDSD" -"MULSD"
// amd64:"ADDSD" -"MULSD"
// arm/7:"ADDD" -"MULD"
// arm64:"FADDD" -"FMULD"
// loong64:"ADDD" -"MULD"
// ppc64x:"FADD" -"FMUL"
// riscv64:"FADDD" -"FMULD"
return f * 2.0
}
func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
// 386/sse2:"MULSD" -"DIVSD"
// amd64:"MULSD" -"DIVSD"
// arm/7:"MULD" -"DIVD"
// arm64:"FMULD" -"FDIVD"
// loong64:"MULD" -"DIVD"
// ppc64x:"FMUL" -"FDIV"
// riscv64:"FMULD" -"FDIVD"
x := f1 / 16.0
// 386/sse2:"MULSD" -"DIVSD"
// amd64:"MULSD" -"DIVSD"
// arm/7:"MULD" -"DIVD"
// arm64:"FMULD" -"FDIVD"
// loong64:"MULD" -"DIVD"
// ppc64x:"FMUL" -"FDIVD"
// riscv64:"FMULD" -"FDIVD"
y := f2 / 0.125
// 386/sse2:"ADDSD" -"DIVSD" -"MULSD"
// amd64:"ADDSD" -"DIVSD" -"MULSD"
// arm/7:"ADDD" -"MULD" -"DIVD"
// arm64:"FADDD" -"FMULD" -"FDIVD"
// loong64:"ADDD" -"MULD" -"DIVD"
// ppc64x:"FADD" -"FMUL" -"FDIV"
// riscv64:"FADDD" -"FMULD" -"FDIVD"
z := f3 / 0.5
return x, y, z
}
func indexLoad(b0 []float32, b1 float32, idx int) float32 {
// arm64:`FMOVS\s\(R[0-9]+\)\(R[0-9]+<<2\),\sF[0-9]+`
// loong64:`MOVF\s\(R[0-9]+\)\(R[0-9]+\),\sF[0-9]+`
return b0[idx] * b1
}
func indexStore(b0 []float64, b1 float64, idx int) {
// arm64:`FMOVD\sF[0-9]+,\s\(R[0-9]+\)\(R[0-9]+<<3\)`
// loong64:`MOVD\sF[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`
b0[idx] = b1
}
// ----------- //
// Fused //
// ----------- //
func FusedAdd32(x, y, z float32) float32 {
// s390x:"FMADDS "
// ppc64x:"FMADDS "
// arm64:"FMADDS"
// loong64:"FMADDF "
// riscv64:"FMADDS "
// amd64/v3:"VFMADD231SS "
return x*y + z
}
func FusedSub32_a(x, y, z float32) float32 {
// s390x:"FMSUBS "
// ppc64x:"FMSUBS "
// riscv64:"FMSUBS "
// loong64:"FMSUBF "
return x*y - z
}
func FusedSub32_b(x, y, z float32) float32 {
// arm64:"FMSUBS"
// loong64:"FNMSUBF "
// riscv64:"FNMSUBS "
return z - x*y
}
func FusedAdd64(x, y, z float64) float64 {
// s390x:"FMADD "
// ppc64x:"FMADD "
// arm64:"FMADDD"
// loong64:"FMADDD "
// riscv64:"FMADDD "
// amd64/v3:"VFMADD231SD "
return x*y + z
}
func FusedSub64_a(x, y, z float64) float64 {
// s390x:"FMSUB "
// ppc64x:"FMSUB "
// riscv64:"FMSUBD "
// loong64:"FMSUBD "
return x*y - z
}
func FusedSub64_b(x, y, z float64) float64 {
// arm64:"FMSUBD"
// loong64:"FNMSUBD "
// riscv64:"FNMSUBD "
return z - x*y
}
func Cmp(f float64) bool {
// arm64:"FCMPD" "(BGT|BLE|BMI|BPL)" -"CSET GT" -"CBZ"
return f > 4 || f < -4
}
func CmpZero64(f float64) bool {
// s390x:"LTDBR" -"FCMPU"
return f <= 0
}
func CmpZero32(f float32) bool {
// s390x:"LTEBR" -"CEBR"
return f <= 0
}
func CmpWithSub(a float64, b float64) bool {
f := a - b
// s390x:-"LTDBR"
return f <= 0
}
func CmpWithAdd(a float64, b float64) bool {
f := a + b
// s390x:-"LTDBR"
return f <= 0
}
// ---------------- //
// Non-floats //
// ---------------- //
func ArrayZero() [16]byte {
// amd64:"MOVUPS"
var a [16]byte
return a
}
func ArrayCopy(a [16]byte) (b [16]byte) {
// amd64:"MOVUPS"
b = a
return
}
// ---------------- //
// Float Min/Max //
// ---------------- //
func Float64Min(a, b float64) float64 {
// amd64:"MINSD"
// arm64:"FMIND"
// loong64:"FMIND"
// riscv64:"FMIN"
// ppc64/power9:"XSMINJDP"
// ppc64/power10:"XSMINJDP"
// s390x: "WFMINDB"
return min(a, b)
}
func Float64Max(a, b float64) float64 {
// amd64:"MINSD"
// arm64:"FMAXD"
// loong64:"FMAXD"
// riscv64:"FMAX"
// ppc64/power9:"XSMAXJDP"
// ppc64/power10:"XSMAXJDP"
// s390x: "WFMAXDB"
return max(a, b)
}
func Float32Min(a, b float32) float32 {
// amd64:"MINSS"
// arm64:"FMINS"
// loong64:"FMINF"
// riscv64:"FMINS"
// ppc64/power9:"XSMINJDP"
// ppc64/power10:"XSMINJDP"
// s390x: "WFMINSB"
return min(a, b)
}
func Float32Max(a, b float32) float32 {
// amd64:"MINSS"
// arm64:"FMAXS"
// loong64:"FMAXF"
// riscv64:"FMAXS"
// ppc64/power9:"XSMAXJDP"
// ppc64/power10:"XSMAXJDP"
// s390x: "WFMAXSB"
return max(a, b)
}
// ------------------------ //
// Constant Optimizations //
// ------------------------ //
func Float32ConstantZero() float32 {
// arm64:"FMOVS ZR,"
return 0.0
}
func Float32ConstantChipFloat() float32 {
// arm64:"FMOVS [$]\\(2\\.25\\),"
return 2.25
}
func Float32Constant() float32 {
// arm64:"FMOVS [$]f32\\.42440000\\(SB\\)"
// ppc64x/power8:"FMOVS [$]f32\\.42440000\\(SB\\)"
// ppc64x/power9:"FMOVS [$]f32\\.42440000\\(SB\\)"
// ppc64x/power10:"XXSPLTIDP [$]1111752704,"
return 49.0
}
func Float64ConstantZero() float64 {
// arm64:"FMOVD ZR,"
return 0.0
}
func Float64ConstantChipFloat() float64 {
// arm64:"FMOVD [$]\\(2\\.25\\),"
return 2.25
}
func Float64Constant() float64 {
// arm64:"FMOVD [$]f64\\.4048800000000000\\(SB\\)"
// ppc64x/power8:"FMOVD [$]f64\\.4048800000000000\\(SB\\)"
// ppc64x/power9:"FMOVD [$]f64\\.4048800000000000\\(SB\\)"
// ppc64x/power10:"XXSPLTIDP [$]1111752704,"
return 49.0
}
func Float32DenormalConstant() float32 {
// ppc64x:"FMOVS [$]f32\\.00400000\\(SB\\)"
return 0x1p-127
}
// A float64 constant which can be exactly represented as a
// denormal float32 value. On ppc64x, denormal values cannot
// be used with XXSPLTIDP.
func Float64DenormalFloat32Constant() float64 {
// ppc64x:"FMOVD [$]f64\\.3800000000000000\\(SB\\)"
return 0x1p-127
}
func Float32ConstantStore(p *float32) {
// amd64:"MOVL [$]1085133554"
// riscv64: "MOVF [$]f32.40add2f2"
*p = 5.432
}
func Float64ConstantStore(p *float64) {
// amd64: "MOVQ [$]4617801906721357038"
// riscv64: "MOVD [$]f64.4015ba5e353f7cee"
*p = 5.432
}
// ------------------------ //
// Subnormal tests //
// ------------------------ //
func isSubnormal(x float64) bool {
// riscv64:"FCLASSD" -"FABSD"
return math.Abs(x) < 2.2250738585072014e-308
}
func isNormal(x float64) bool {
// riscv64:"FCLASSD" -"FABSD"
return math.Abs(x) >= 0x1p-1022
}
func isPosSubnormal(x float64) bool {
// riscv64:"FCLASSD"
return x > 0 && x < 2.2250738585072014e-308
}
func isNegSubnormal(x float64) bool {
// riscv64:"FCLASSD"
return x < 0 && x > -0x1p-1022
}
func isPosNormal(x float64) bool {
// riscv64:"FCLASSD"
return x >= 2.2250738585072014e-308
}
func isNegNormal(x float64) bool {
// riscv64:"FCLASSD"
return x <= -2.2250738585072014e-308
}

418
test/codegen/fuse.go Normal file
View File

@@ -0,0 +1,418 @@
// asmcheck
// Copyright 2019 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 codegen
import "math"
// Notes:
// - these examples use channels to provide a source of
// unknown values that cannot be optimized away
// - these examples use for loops to force branches
// backward (predicted taken)
// ---------------------------------- //
// signed integer range (conjunction) //
// ---------------------------------- //
func si1c(c <-chan int64) {
// amd64:"CMPQ .+, [$]256"
// s390x:"CLGIJ [$]12, R[0-9]+, [$]255"
for x := <-c; x >= 0 && x < 256; x = <-c {
}
}
func si2c(c <-chan int32) {
// amd64:"CMPL .+, [$]256"
// s390x:"CLIJ [$]12, R[0-9]+, [$]255"
for x := <-c; x >= 0 && x < 256; x = <-c {
}
}
func si3c(c <-chan int16) {
// amd64:"CMPW .+, [$]256"
// s390x:"CLIJ [$]12, R[0-9]+, [$]255"
for x := <-c; x >= 0 && x < 256; x = <-c {
}
}
func si4c(c <-chan int8) {
// amd64:"CMPB .+, [$]10"
// s390x:"CLIJ [$]4, R[0-9]+, [$]10"
for x := <-c; x >= 0 && x < 10; x = <-c {
}
}
func si5c(c <-chan int64) {
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
// s390x:"CLGIJ [$]4, R[0-9]+, [$]251" "ADD [$]-5,"
for x := <-c; x < 256 && x > 4; x = <-c {
}
}
func si6c(c <-chan int32) {
// amd64:"CMPL .+, [$]255" "DECL "
// s390x:"CLIJ [$]12, R[0-9]+, [$]255" "ADDW [$]-1,"
for x := <-c; x > 0 && x <= 256; x = <-c {
}
}
func si7c(c <-chan int16) {
// amd64:"CMPW .+, [$]60" "ADDL [$]10,"
// s390x:"CLIJ [$]12, R[0-9]+, [$]60" "ADDW [$]10,"
for x := <-c; x >= -10 && x <= 50; x = <-c {
}
}
func si8c(c <-chan int8) {
// amd64:"CMPB .+, [$]126" "ADDL [$]126,"
// s390x:"CLIJ [$]4, R[0-9]+, [$]126" "ADDW [$]126,"
for x := <-c; x >= -126 && x < 0; x = <-c {
}
}
// ---------------------------------- //
// signed integer range (disjunction) //
// ---------------------------------- //
func si1d(c <-chan int64) {
// amd64:"CMPQ .+, [$]256"
// s390x:"CLGIJ [$]2, R[0-9]+, [$]255"
for x := <-c; x < 0 || x >= 256; x = <-c {
}
}
func si2d(c <-chan int32) {
// amd64:"CMPL .+, [$]256"
// s390x:"CLIJ [$]2, R[0-9]+, [$]255"
for x := <-c; x < 0 || x >= 256; x = <-c {
}
}
func si3d(c <-chan int16) {
// amd64:"CMPW .+, [$]256"
// s390x:"CLIJ [$]2, R[0-9]+, [$]255"
for x := <-c; x < 0 || x >= 256; x = <-c {
}
}
func si4d(c <-chan int8) {
// amd64:"CMPB .+, [$]10"
// s390x:"CLIJ [$]10, R[0-9]+, [$]10"
for x := <-c; x < 0 || x >= 10; x = <-c {
}
}
func si5d(c <-chan int64) {
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
// s390x:"CLGIJ [$]10, R[0-9]+, [$]251" "ADD [$]-5,"
for x := <-c; x >= 256 || x <= 4; x = <-c {
}
}
func si6d(c <-chan int32) {
// amd64:"CMPL .+, [$]255" "DECL "
// s390x:"CLIJ [$]2, R[0-9]+, [$]255" "ADDW [$]-1,"
for x := <-c; x <= 0 || x > 256; x = <-c {
}
}
func si7d(c <-chan int16) {
// amd64:"CMPW .+, [$]60" "ADDL [$]10,"
// s390x:"CLIJ [$]2, R[0-9]+, [$]60" "ADDW [$]10,"
for x := <-c; x < -10 || x > 50; x = <-c {
}
}
func si8d(c <-chan int8) {
// amd64:"CMPB .+, [$]126" "ADDL [$]126,"
// s390x:"CLIJ [$]10, R[0-9]+, [$]126" "ADDW [$]126,"
for x := <-c; x < -126 || x >= 0; x = <-c {
}
}
// ------------------------------------ //
// unsigned integer range (conjunction) //
// ------------------------------------ //
func ui1c(c <-chan uint64) {
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
// s390x:"CLGIJ [$]4, R[0-9]+, [$]251" "ADD [$]-5,"
for x := <-c; x < 256 && x > 4; x = <-c {
}
}
func ui2c(c <-chan uint32) {
// amd64:"CMPL .+, [$]255" "DECL "
// s390x:"CLIJ [$]12, R[0-9]+, [$]255" "ADDW [$]-1,"
for x := <-c; x > 0 && x <= 256; x = <-c {
}
}
func ui3c(c <-chan uint16) {
// amd64:"CMPW .+, [$]40" "ADDL [$]-10,"
// s390x:"CLIJ [$]12, R[0-9]+, [$]40" "ADDW [$]-10,"
for x := <-c; x >= 10 && x <= 50; x = <-c {
}
}
func ui4c(c <-chan uint8) {
// amd64:"CMPB .+, [$]2" "ADDL [$]-126,"
// s390x:"CLIJ [$]4, R[0-9]+, [$]2" "ADDW [$]-126,"
for x := <-c; x >= 126 && x < 128; x = <-c {
}
}
// ------------------------------------ //
// unsigned integer range (disjunction) //
// ------------------------------------ //
func ui1d(c <-chan uint64) {
// amd64:"CMPQ .+, [$]251" "ADDQ [$]-5,"
// s390x:"CLGIJ [$]10, R[0-9]+, [$]251" "ADD [$]-5,"
for x := <-c; x >= 256 || x <= 4; x = <-c {
}
}
func ui2d(c <-chan uint32) {
// amd64:"CMPL .+, [$]254" "ADDL [$]-2,"
// s390x:"CLIJ [$]2, R[0-9]+, [$]254" "ADDW [$]-2,"
for x := <-c; x <= 1 || x > 256; x = <-c {
}
}
func ui3d(c <-chan uint16) {
// amd64:"CMPW .+, [$]40" "ADDL [$]-10,"
// s390x:"CLIJ [$]2, R[0-9]+, [$]40" "ADDW [$]-10,"
for x := <-c; x < 10 || x > 50; x = <-c {
}
}
func ui4d(c <-chan uint8) {
// amd64:"CMPB .+, [$]2" "ADDL [$]-126,"
// s390x:"CLIJ [$]10, R[0-9]+, [$]2" "ADDW [$]-126,"
for x := <-c; x < 126 || x >= 128; x = <-c {
}
}
// ------------------------------------ //
// single bit difference (conjunction) //
// ------------------------------------ //
func sisbc64(c <-chan int64) {
// amd64: "ORQ [$]2,"
// riscv64: "ORI [$]2,"
for x := <-c; x != 4 && x != 6; x = <-c {
}
}
func sisbc32(c <-chan int32) {
// amd64: "ORL [$]4,"
// riscv64: "ORI [$]4,"
for x := <-c; x != -1 && x != -5; x = <-c {
}
}
func sisbc16(c <-chan int16) {
// amd64: "ORL [$]32,"
// riscv64: "ORI [$]32,"
for x := <-c; x != 16 && x != 48; x = <-c {
}
}
func sisbc8(c <-chan int8) {
// amd64: "ORL [$]16,"
// riscv64: "ORI [$]16,"
for x := <-c; x != -15 && x != -31; x = <-c {
}
}
func uisbc64(c <-chan uint64) {
// amd64: "ORQ [$]4,"
// riscv64: "ORI [$]4,"
for x := <-c; x != 1 && x != 5; x = <-c {
}
}
func uisbc32(c <-chan uint32) {
// amd64: "ORL [$]4,"
// riscv64: "ORI [$]4,"
for x := <-c; x != 2 && x != 6; x = <-c {
}
}
func uisbc16(c <-chan uint16) {
// amd64: "ORL [$]32,"
// riscv64: "ORI [$]32,"
for x := <-c; x != 16 && x != 48; x = <-c {
}
}
func uisbc8(c <-chan uint8) {
// amd64: "ORL [$]64,"
// riscv64: "ORI [$]64,"
for x := <-c; x != 64 && x != 0; x = <-c {
}
}
// ------------------------------------ //
// single bit difference (disjunction) //
// ------------------------------------ //
func sisbd64(c <-chan int64) {
// amd64: "ORQ [$]2,"
// riscv64: "ORI [$]2,"
for x := <-c; x == 4 || x == 6; x = <-c {
}
}
func sisbd32(c <-chan int32) {
// amd64: "ORL [$]4,"
// riscv64: "ORI [$]4,"
for x := <-c; x == -1 || x == -5; x = <-c {
}
}
func sisbd16(c <-chan int16) {
// amd64: "ORL [$]32,"
// riscv64: "ORI [$]32,"
for x := <-c; x == 16 || x == 48; x = <-c {
}
}
func sisbd8(c <-chan int8) {
// amd64: "ORL [$]16,"
// riscv64: "ORI [$]16,"
for x := <-c; x == -15 || x == -31; x = <-c {
}
}
func uisbd64(c <-chan uint64) {
// amd64: "ORQ [$]4,"
// riscv64: "ORI [$]4,"
for x := <-c; x == 1 || x == 5; x = <-c {
}
}
func uisbd32(c <-chan uint32) {
// amd64: "ORL [$]4,"
// riscv64: "ORI [$]4,"
for x := <-c; x == 2 || x == 6; x = <-c {
}
}
func uisbd16(c <-chan uint16) {
// amd64: "ORL [$]32,"
// riscv64: "ORI [$]32,"
for x := <-c; x == 16 || x == 48; x = <-c {
}
}
func uisbd8(c <-chan uint8) {
// amd64: "ORL [$]64,"
// riscv64: "ORI [$]64,"
for x := <-c; x == 64 || x == 0; x = <-c {
}
}
// -------------------------------------//
// merge NaN checks //
// ------------------------------------ //
func f64NaNOrPosInf(c <-chan float64) {
// This test assumes IsInf(x, 1) is implemented as x > MaxFloat rather than x == Inf(1).
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
// riscv64:"FCLASSD" -"FLED" -"FLTD" -"FNED" -"FEQD"
for x := <-c; math.IsNaN(x) || math.IsInf(x, 1); x = <-c {
}
}
func f64NaNOrNegInf(c <-chan float64) {
// This test assumes IsInf(x, -1) is implemented as x < -MaxFloat rather than x == Inf(-1).
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
// riscv64:"FCLASSD" -"FLED" -"FLTD" -"FNED" -"FEQD"
for x := <-c; math.IsNaN(x) || math.IsInf(x, -1); x = <-c {
}
}
func f64NaNOrLtOne(c <-chan float64) {
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLED" -"FLTD" -"FNED" -"FEQD"
for x := <-c; math.IsNaN(x) || x < 1; x = <-c {
}
}
func f64NaNOrLteOne(c <-chan float64) {
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLTD" -"FLED" -"FNED" -"FEQD"
for x := <-c; x <= 1 || math.IsNaN(x); x = <-c {
}
}
func f64NaNOrGtOne(c <-chan float64) {
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLED" -"FLTD" -"FNED" -"FEQD"
for x := <-c; math.IsNaN(x) || x > 1; x = <-c {
}
}
func f64NaNOrGteOne(c <-chan float64) {
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLTD" -"FLED" -"FNED" -"FEQD"
for x := <-c; x >= 1 || math.IsNaN(x); x = <-c {
}
}
func f32NaNOrLtOne(c <-chan float32) {
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLES" -"FLTS" -"FNES" -"FEQS"
for x := <-c; x < 1 || x != x; x = <-c {
}
}
func f32NaNOrLteOne(c <-chan float32) {
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLTS" -"FLES" -"FNES" -"FEQS"
for x := <-c; x != x || x <= 1; x = <-c {
}
}
func f32NaNOrGtOne(c <-chan float32) {
// amd64:"JCS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLES" -"FLTS" -"FNES" -"FEQS"
for x := <-c; x > 1 || x != x; x = <-c {
}
}
func f32NaNOrGteOne(c <-chan float32) {
// amd64:"JLS" -"JNE" -"JPS" -"JPC"
// riscv64:"FLTS" -"FLES" -"FNES" -"FEQS"
for x := <-c; x != x || x >= 1; x = <-c {
}
}
// ------------------------------------ //
// regressions //
// ------------------------------------ //
func gte4(x uint64) bool {
return x >= 4
}
func lt20(x uint64) bool {
return x < 20
}
func issue74915(c <-chan uint64) {
// Check that the optimization is not blocked by function inlining.
// amd64:"CMPQ .+, [$]16" "ADDQ [$]-4,"
// s390x:"CLGIJ [$]4, R[0-9]+, [$]16" "ADD [$]-4,"
for x := <-c; gte4(x) && lt20(x); x = <-c {
}
}

40
test/codegen/generics.go Normal file
View File

@@ -0,0 +1,40 @@
// asmcheck
// Copyright 2025 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 codegen
import "cmp"
func isNaN[T cmp.Ordered](x T) bool {
return x != x
}
func compare[T cmp.Ordered](x, y T) int {
// amd64:-"TESTB"
// arm64:-"MOVB"
xNaN := isNaN(x)
yNaN := isNaN(y)
if xNaN {
if yNaN {
return 0
}
return -1
}
if yNaN {
return +1
}
if x < y {
return -1
}
if x > y {
return +1
}
return 0
}
func usesCompare(a, b int) int {
return compare(a, b)
}

63
test/codegen/ifaces.go Normal file
View File

@@ -0,0 +1,63 @@
// asmcheck
// Copyright 2022 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 codegen
type I interface{ M() }
func NopConvertIface(x I) I {
// amd64:-`.*runtime.convI2I`
return I(x)
}
func NopConvertGeneric[T any](x T) T {
// amd64:-`.*runtime.convI2I`
return T(x)
}
var NopConvertGenericIface = NopConvertGeneric[I]
func ConvToM(x any) I {
// amd64:`CALL runtime.typeAssert`,`MOVL 16\(.*\)`,`MOVQ 8\(.*\)(.*\*1)`
// arm64:`CALL runtime.typeAssert`,`LDAR`,`MOVWU`,`MOVD \(R.*\)\(R.*\)`
return x.(I)
}
func e1(x any, y *int) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET EQ`
return x == y
}
func e2(x any, y *int) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET EQ`
return y == x
}
type E *int
func e3(x any, y E) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET EQ`
return x == y
}
type T int
func (t *T) M() {}
func i1(x I, y *T) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET EQ`
return x == y
}
func i2(x I, y *T) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET EQ`
return y == x
}

535
test/codegen/issue22703.go Normal file
View File

@@ -0,0 +1,535 @@
// asmcheck
// Copyright 2018 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 codegen
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()
}
// Nil checks before calling interface methods.
// We need it only when the offset is large.
func callMethodSmallOffset(i I) {
// amd64:-"TESTB"
i.foo001()
}
func callMethodLargeOffset(i I) {
// amd64:"TESTB"
i.foo511()
}

View File

@@ -0,0 +1,22 @@
// asmcheck
// Copyright 2018 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 codegen
var wsp = [256]bool{
' ': true,
'\t': true,
'\n': true,
'\r': true,
}
func zeroExtArgByte(ch [2]byte) bool {
return wsp[ch[0]] // amd64:-"MOVBLZX ..,.."
}
func zeroExtArgUint16(ch [2]uint16) bool {
return wsp[ch[0]] // amd64:-"MOVWLZX ..,.."
}

View File

@@ -0,0 +1,22 @@
// asmcheck
// Copyright 2019 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 codegen
// Make sure we remove both inline marks in the following code.
// Both +5 and +6 should map to real instructions, which can
// be used as inline marks instead of explicit nops.
func f(x int) int {
// amd64:-"XCHGL"
x = g(x) + 5
// amd64:-"XCHGL"
x = g(x) + 6
return x
}
func g(x int) int {
return x >> 3
}

View File

@@ -0,0 +1,25 @@
// asmcheck
// Copyright 2019 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.
// Make sure we reuse large constant loads, if we can.
// See issue 33580.
package codegen
const (
A = 7777777777777777
B = 8888888888888888
)
func f(x, y uint64) uint64 {
p := x & A
q := y & A
r := x & B
// amd64:-"MOVQ.*8888888888888888"
s := y & B
return p * q * r * s
}

View File

@@ -0,0 +1,15 @@
// asmcheck
// Copyright 2020 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.
// Test that we are zeroing directly instead of
// copying a large zero value. Issue 38554.
package codegen
func retlarge() [256]byte {
// amd64:-"DUFFCOPY"
return [256]byte{}
}

View File

@@ -0,0 +1,28 @@
// asmcheck
// Copyright 2020 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.
// Don't allow 0 masks in shift lowering rules on ppc64x.
// See issue 42610.
package codegen
func f32(a []int32, i uint32) {
g := func(p int32) int32 {
i = uint32(p) * (uint32(p) & (i & 1))
return 1
}
// ppc64x: -"RLWNIM"
a[0] = g(8) >> 1
}
func f(a []int, i uint) {
g := func(p int) int {
i = uint(p) * (uint(p) & (i & 1))
return 1
}
// ppc64x: -"RLDIC"
a[0] = g(8) >> 1
}

View File

@@ -0,0 +1,31 @@
// asmcheck
// Copyright 2021 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 codegen
func a(n string) bool {
// arm64:"CBZ"
if len(n) > 0 {
return true
}
return false
}
func a2(n []int) bool {
// arm64:"CBZ"
if len(n) > 0 {
return true
}
return false
}
func a3(n []int) bool {
// amd64:"TESTQ"
if len(n) < 1 {
return true
}
return false
}

View File

@@ -0,0 +1,53 @@
// asmcheck
// Copyright 2022 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.
// Test that optimized range memclr works with pointers to arrays.
// The clears get inlined, see https://github.com/golang/go/issues/56997
package codegen
type T struct {
a *[10]int
b [10]int
s []int
}
func (t *T) f() {
// amd64:-".*runtime.memclrNoHeapPointers"
// amd64:`MOVUPS X15,`
for i := range t.a {
t.a[i] = 0
}
// amd64:-".*runtime.memclrNoHeapPointers"
// amd64:`MOVUPS X15,`
for i := range *t.a {
t.a[i] = 0
}
// amd64:-".*runtime.memclrNoHeapPointers"
// amd64:`MOVUPS X15,`
for i := range t.a {
(*t.a)[i] = 0
}
// amd64:-".*runtime.memclrNoHeapPointers"
// amd64:`MOVUPS X15,`
for i := range *t.a {
(*t.a)[i] = 0
}
// amd64:-".*runtime.memclrNoHeapPointers"
// amd64:`MOVUPS X15,`
for i := range t.b {
t.b[i] = 0
}
// amd64:".*runtime.memclrNoHeapPointers"
for i := range t.s {
t.s[i] = 0
}
}

View File

@@ -0,0 +1,59 @@
// asmcheck
// Copyright 2022 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 codegen
func f1(x *[4]int, y *[4]int) {
// amd64:".*memmove"
*x = *y
}
func f2(x *[4]int, y [4]int) {
// amd64:-".*memmove"
*x = y
}
func f3(x *[4]int, y *[4]int) {
// amd64:-".*memmove"
t := *y
// amd64:-".*memmove"
*x = t
}
func f4(x *[4]int, y [4]int) {
// amd64:-".*memmove"
t := y
// amd64:-".*memmove"
*x = t
}
type T struct {
a [4]int
}
func f5(x, y *T) {
// amd64:-".*memmove"
x.a = y.a
}
func f6(x *T, y T) {
// amd64:-".*memmove"
x.a = y.a
}
func f7(x *T, y *[4]int) {
// amd64:-".*memmove"
x.a = *y
}
func f8(x *[4]int, y *T) {
// amd64:-".*memmove"
*x = y.a
}
func f9(x [][4]int, y [][4]int, i, j int) {
// amd64:-".*memmove"
x[i] = y[j]
}
func f10() []byte {
// amd64:-".*memmove"
return []byte("aReasonablyBigTestString")
}

View File

@@ -0,0 +1,34 @@
// asmcheck
// Copyright 2022 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.
// Check to make sure that we recognize when the length of an append
// is constant. We check this by making sure that the constant length
// is folded into a load offset.
package codegen
func f(x []int) int {
s := make([]int, 3)
s = append(s, 4, 5)
// amd64:`MOVQ 40\(.*\),`
return x[len(s)]
}
func g(x []int, p *bool) int {
s := make([]int, 3)
for {
s = s[:3]
if cap(s) < 5 {
s = make([]int, 3, 5)
}
s = append(s, 4, 5)
if *p {
// amd64:`MOVQ 40\(.*\),`
return x[len(s)]
}
}
return 0
}

View File

@@ -0,0 +1,23 @@
// asmcheck
// Copyright 2023 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 codegen
func dgemmSerialNotNot(m, n, k int, a []float64, lda int, b []float64, ldb int, c []float64, ldc int, alpha float64) {
for i := 0; i < m; i++ {
ctmp := c[i*ldc : i*ldc+n]
for l, v := range a[i*lda : i*lda+k] {
tmp := alpha * v
if tmp != 0 {
x := b[l*ldb : l*ldb+n]
// amd64:"INCQ"
for i, v := range x {
ctmp[i] += tmp * v
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
// asmcheck
// Copyright 2023 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 codegen
func f(x, y int, p *int) {
// amd64:`MOVQ\sAX, BX`
h(8, x)
*p = y
}
//go:noinline
func h(a, b int) {
}

View File

@@ -0,0 +1,36 @@
// asmcheck
// Copyright 2023 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 codegen
func main() {
// amd64:"LEAQ command-line-arguments\\.main\\.f\\.g\\.h\\.func3"
f(1)()
// amd64:"LEAQ command-line-arguments\\.main\\.g\\.h\\.func2"
g(2)()
// amd64:"LEAQ command-line-arguments\\.main\\.h\\.func1"
h(3)()
// amd64:"LEAQ command-line-arguments\\.main\\.f\\.g\\.h\\.func4"
f(4)()
}
func f(x int) func() {
// amd64:"LEAQ command-line-arguments\\.f\\.g\\.h\\.func1"
return g(x)
}
func g(x int) func() {
// amd64:"LEAQ command-line-arguments\\.g\\.h\\.func1"
return h(x)
}
func h(x int) func() {
// amd64:"LEAQ command-line-arguments\\.h\\.func1"
return func() { recover() }
}

View File

@@ -0,0 +1,18 @@
// asmcheck
// Copyright 2023 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 codegen
//go:noinline
func f(x int32) {
}
func g(p *int32) {
// argument marshaling code should live at line 17, not line 15.
x := *p
// 386: `MOVL\s[A-Z]+,\s\(SP\)`
f(x)
}

View File

@@ -0,0 +1,55 @@
// asmcheck
// Copyright 2023 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.
// Make sure this code doesn't generate spill/restore.
package codegen
func pack20(in *[20]uint64) uint64 {
var out uint64
out |= 4
// amd64:-`.*SP.*`
out |= in[0] << 4
// amd64:-`.*SP.*`
out |= in[1] << 7
// amd64:-`.*SP.*`
out |= in[2] << 10
// amd64:-`.*SP.*`
out |= in[3] << 13
// amd64:-`.*SP.*`
out |= in[4] << 16
// amd64:-`.*SP.*`
out |= in[5] << 19
// amd64:-`.*SP.*`
out |= in[6] << 22
// amd64:-`.*SP.*`
out |= in[7] << 25
// amd64:-`.*SP.*`
out |= in[8] << 28
// amd64:-`.*SP.*`
out |= in[9] << 31
// amd64:-`.*SP.*`
out |= in[10] << 34
// amd64:-`.*SP.*`
out |= in[11] << 37
// amd64:-`.*SP.*`
out |= in[12] << 40
// amd64:-`.*SP.*`
out |= in[13] << 43
// amd64:-`.*SP.*`
out |= in[14] << 46
// amd64:-`.*SP.*`
out |= in[15] << 49
// amd64:-`.*SP.*`
out |= in[16] << 52
// amd64:-`.*SP.*`
out |= in[17] << 55
// amd64:-`.*SP.*`
out |= in[18] << 58
// amd64:-`.*SP.*`
out |= in[19] << 61
return out
}

View File

@@ -0,0 +1,14 @@
// asmcheck
// Copyright 2023 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 codegen
func issue63332(c chan int) {
x := 0
// amd64:-`MOVQ`
x += 2
c <- x
}

View File

@@ -0,0 +1,24 @@
// asmcheck
// Copyright 2024 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 codegen
var x = func() int {
n := 0
f(&n)
return n
}()
func f(p *int) {
*p = 1
}
var y = 1
// z can be static initialized.
//
// amd64:-"MOVQ"
var z = y

View File

@@ -0,0 +1,52 @@
// asmcheck
// Copyright 2024 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 codegen
type T1 struct {
x string
}
func f1() *T1 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T1{}
}
type T2 struct {
x, y string
}
func f2() *T2 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T2{}
}
type T3 struct {
x complex128
}
func f3() *T3 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T3{}
}
type T4 struct {
x []byte
}
func f4() *T4 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T4{}
}
type T5 struct {
x any
}
func f5() *T5 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T5{}
}

View File

@@ -0,0 +1,13 @@
// asmcheck
// Copyright 2024 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 codegen
func calc(a uint64) uint64 {
v := a >> 20 & 0x7f
// amd64: `SHRQ\s\$17, AX$`, `ANDL\s\$1016, AX$`
return v << 3
}

View File

@@ -0,0 +1,20 @@
// asmcheck -gcflags=-d=ssa/check/on
// Copyright 2024 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 codegen
// amd64:-"MOVQ"
func foo(v uint64) (b [8]byte) {
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
return b
}

View File

@@ -0,0 +1,50 @@
// asmcheck
// Copyright 2025 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 codegen
type tile1 struct {
a uint16
b uint16
c uint32
}
func store_tile1(t *tile1) {
// amd64:`MOVQ`
t.a, t.b, t.c = 1, 1, 1
}
type tile2 struct {
a, b, c, d, e int8
}
func store_tile2(t *tile2) {
// amd64:`MOVW`
t.a, t.b = 1, 1
// amd64:`MOVW`
t.d, t.e = 1, 1
}
type tile3 struct {
a, b uint8
c uint16
}
func store_shifted(t *tile3, x uint32) {
// amd64:`MOVL`
// ppc64:`MOVHBR`
t.a = uint8(x)
t.b = uint8(x >> 8)
t.c = uint16(x >> 16)
}
func store_const(t *tile3) {
// 0x00030201
// amd64:`MOVL\s\$197121`
// 0x01020003
// ppc64:`MOVD\s\$16908291`
t.a, t.b, t.c = 1, 2, 3
}

View File

@@ -0,0 +1,47 @@
// asmcheck
// Copyright 2025 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 codegen
func divUint64(b uint64) uint64 {
// amd64:"SHRQ [$]63, AX"
return b / 9223372036854775808
}
func divUint32(b uint32) uint32 {
// amd64:"SHRL [$]31, AX"
return b / 2147483648
}
func divUint16(b uint16) uint16 {
// amd64:"SHRW [$]15, AX"
return b / 32768
}
func divUint8(b uint8) uint8 {
// amd64:"SHRB [$]7, AL"
return b / 128
}
func modUint64(b uint64) uint64 {
// amd64:"BTRQ [$]63, AX"
return b % 9223372036854775808
}
func modUint32(b uint32) uint32 {
// amd64:"ANDL [$]2147483647, AX"
return b % 2147483648
}
func modUint16(b uint16) uint16 {
// amd64:"ANDL [$]32767, AX"
return b % 32768
}
func modUint8(b uint8) uint8 {
// amd64:"ANDL [$]127, AX"
return b % 128
}

View File

@@ -0,0 +1,17 @@
// asmcheck
// Copyright 2025 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 codegen
func fa(a [2]int) (r [2]int) {
// amd64:1`MOVUPS[^,]+, X[0-9]+$`,1`MOVUPS\sX[0-9]+,[^\n]+$`
return a
}
func fb(a [4]int) (r [4]int) {
// amd64:2`MOVUPS[^,]+, X[0-9]+$`,2`MOVUPS\sX[0-9]+,[^\n]+$`
return a
}

View File

@@ -0,0 +1,22 @@
// asmcheck
// Copyright 2025 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 codegen
import "reflect"
func f() reflect.Type {
// amd64:`LEAQ\stype:\*int\(SB\)`
// arm64:`MOVD\s\$type:\*int\(SB\)`
return reflect.TypeFor[*int]()
}
func g() reflect.Type {
// amd64:`LEAQ\stype:int\(SB\)`
// arm64:`MOVD\s\$type:int\(SB\)`
return reflect.TypeFor[int]()
}

View File

@@ -0,0 +1,24 @@
// asmcheck
// Copyright 2025 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.
// This test makes sure that we statically load a type from an itab, instead
// of doing a indirect load from thet itab.
package codegen
type M interface{ M() }
type A interface{ A() }
type Impl struct{}
func (*Impl) M() {}
func (*Impl) A() {}
func main() {
var a M = &Impl{}
// amd64:`LEAQ type:.*Impl`
a.(A).A()
}

41
test/codegen/logic.go Normal file
View File

@@ -0,0 +1,41 @@
// asmcheck
// Copyright 2018 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 codegen
// Test to make sure that (CMPQ (ANDQ x y) [0]) does not get rewritten to
// (TESTQ x y) if the ANDQ has other uses. If that rewrite happens, then one
// of the args of the ANDQ needs to be saved so it can be used as the arg to TESTQ.
func andWithUse(x, y int) int {
z := x & y
// amd64:`TESTQ\s(AX, AX|BX, BX|CX, CX|DX, DX|SI, SI|DI, DI|R8, R8|R9, R9|R10, R10|R11, R11|R12, R12|R13, R13|R15, R15)`
if z == 0 {
return 77
}
// use z by returning it
return z
}
// Verify (OR x (NOT y)) rewrites to (ORN x y) where supported
func ornot(x, y int) int {
// ppc64x:"ORN"
z := x | ^y
return z
}
// Verify that (OR (NOT x) (NOT y)) rewrites to (NOT (AND x y))
func orDemorgans(x, y int) int {
// amd64:"AND" -"OR"
z := ^x | ^y
return z
}
// Verify that (AND (NOT x) (NOT y)) rewrites to (NOT (OR x y))
func andDemorgans(x, y int) int {
// amd64:"OR" -"AND"
z := ^x & ^y
return z
}

484
test/codegen/mapaccess.go Normal file
View File

@@ -0,0 +1,484 @@
// asmcheck
// Copyright 2018 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 codegen
// These tests check that mapaccess calls are not used.
// Issues #23661 and #24364.
func mapCompoundAssignmentInt8() {
m := make(map[int8]int8, 0)
var k int8 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] += 67
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] -= 123
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] *= 45
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] |= 78
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] ^= 89
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] <<= 9
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] >>= 10
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]++
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]--
}
func mapCompoundAssignmentInt32() {
m := make(map[int32]int32, 0)
var k int32 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] += 67890
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] -= 123
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] *= 456
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] |= 78
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] ^= 89
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] <<= 9
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] >>= 10
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]++
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]--
}
func mapCompoundAssignmentInt64() {
m := make(map[int64]int64, 0)
var k int64 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] += 67890
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] -= 123
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] *= 456
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] |= 78
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] ^= 89
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] <<= 9
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] >>= 10
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]++
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]--
}
func mapCompoundAssignmentComplex128() {
m := make(map[complex128]complex128, 0)
var k complex128 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] += 67890
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] -= 123
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] *= 456
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]++
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k]--
}
func mapCompoundAssignmentString() {
m := make(map[string]string, 0)
var k string = "key"
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] += "value"
}
var sinkAppend bool
func mapAppendAssignmentInt8() {
m := make(map[int8][]int8, 0)
var k int8 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1, 2, 3)
a := []int8{7, 8, 9, 0}
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], a...)
// Exceptions
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(a, m[k]...)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
sinkAppend, m[k] = !sinkAppend, append(m[k], 99)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(m[k+1], 100)
}
func mapAppendAssignmentInt32() {
m := make(map[int32][]int32, 0)
var k int32 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1, 2, 3)
a := []int32{7, 8, 9, 0}
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], a...)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k+1] = append(m[k+1], a...)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[-k] = append(m[-k], a...)
// Exceptions
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(a, m[k]...)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
sinkAppend, m[k] = !sinkAppend, append(m[k], 99)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(m[k+1], 100)
}
func mapAppendAssignmentInt64() {
m := make(map[int64][]int64, 0)
var k int64 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1, 2, 3)
a := []int64{7, 8, 9, 0}
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], a...)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k+1] = append(m[k+1], a...)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[-k] = append(m[-k], a...)
// Exceptions
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(a, m[k]...)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
sinkAppend, m[k] = !sinkAppend, append(m[k], 99)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(m[k+1], 100)
}
func mapAppendAssignmentComplex128() {
m := make(map[complex128][]complex128, 0)
var k complex128 = 0
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1)
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], 1, 2, 3)
a := []complex128{7, 8, 9, 0}
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], a...)
// Exceptions
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(a, m[k]...)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
sinkAppend, m[k] = !sinkAppend, append(m[k], 99)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(m[k+1], 100)
}
func mapAppendAssignmentString() {
m := make(map[string][]string, 0)
var k string = "key"
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], "1")
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], "1", "2", "3")
a := []string{"7", "8", "9", "0"}
// 386:-".*mapaccess"
// amd64:-".*mapaccess"
// arm:-".*mapaccess"
// arm64:-".*mapaccess"
m[k] = append(m[k], a...)
// Exceptions
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(a, m[k]...)
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
sinkAppend, m[k] = !sinkAppend, append(m[k], "99")
// 386:".*mapaccess"
// amd64:".*mapaccess"
// arm:".*mapaccess"
// arm64:".*mapaccess"
m[k] = append(m[k+"1"], "100")
}

245
test/codegen/maps.go Normal file
View File

@@ -0,0 +1,245 @@
// asmcheck
// Copyright 2018 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 codegen
// This file contains code generation tests related to the handling of
// map types.
// ------------------- //
// Access Const //
// ------------------- //
// Direct use of constants in fast map access calls (Issue #19015).
func AccessInt1(m map[int]int) int {
// amd64:"MOV[LQ] [$]5"
return m[5]
}
func AccessInt2(m map[int]int) bool {
// amd64:"MOV[LQ] [$]5"
_, ok := m[5]
return ok
}
func AccessString1(m map[string]int) int {
// amd64:`.*"abc"`
return m["abc"]
}
func AccessString2(m map[string]int) bool {
// amd64:`.*"abc"`
_, ok := m["abc"]
return ok
}
func AccessStringIntArray2(m map[string][16]int, k string) bool {
// amd64:-"MOVUPS"
_, ok := m[k]
return ok
}
type Struct struct {
A, B, C, D, E, F, G, H, I, J int
}
func AccessStringStruct2(m map[string]Struct, k string) bool {
// amd64:-"MOVUPS"
_, ok := m[k]
return ok
}
func AccessIntArrayLarge2(m map[int][512]int, k int) bool {
// amd64:-"REP",-"MOVSQ"
_, ok := m[k]
return ok
}
// ------------------- //
// String Conversion //
// ------------------- //
func LookupStringConversionSimple(m map[string]int, bytes []byte) int {
// amd64:-`.*runtime\.slicebytetostring\(`
return m[string(bytes)]
}
func LookupStringConversionStructLit(m map[struct{ string }]int, bytes []byte) int {
// amd64:-`.*runtime\.slicebytetostring\(`
return m[struct{ string }{string(bytes)}]
}
func LookupStringConversionArrayLit(m map[[2]string]int, bytes []byte) int {
// amd64:-`.*runtime\.slicebytetostring\(`
return m[[2]string{string(bytes), string(bytes)}]
}
func LookupStringConversionNestedLit(m map[[1]struct{ s [1]string }]int, bytes []byte) int {
// amd64:-`.*runtime\.slicebytetostring\(`
return m[[1]struct{ s [1]string }{struct{ s [1]string }{s: [1]string{string(bytes)}}}]
}
func LookupStringConversionKeyedArrayLit(m map[[2]string]int, bytes []byte) int {
// amd64:-`.*runtime\.slicebytetostring\(`
return m[[2]string{0: string(bytes)}]
}
func LookupStringConversion1(m map[string]int, bytes []byte) int {
// amd64:-`.*runtime\.slicebytetostring\(`
s := string(bytes)
return m[s]
}
func LookupStringConversion2(m *map[string]int, bytes []byte) int {
// amd64:-`.*runtime\.slicebytetostring\(`
s := string(bytes)
return (*m)[s]
}
func LookupStringConversion3(m map[string]int, bytes []byte) (int, bool) {
// amd64:-`.*runtime\.slicebytetostring\(`
s := string(bytes)
r, ok := m[s]
return r, ok
}
func DeleteStringConversion(m map[string]int, bytes []byte) {
// amd64:-`.*runtime\.slicebytetostring\(`
s := string(bytes)
delete(m, s)
}
// ------------------- //
// Map Clear //
// ------------------- //
// Optimization of map clear idiom (Issue #20138).
func MapClearReflexive(m map[int]int) {
// amd64:`.*runtime\.mapclear`
// amd64:-`.*runtime\.(mapiterinit|mapIterStart)`
for k := range m {
delete(m, k)
}
}
func MapClearIndirect(m map[int]int) {
s := struct{ m map[int]int }{m: m}
// amd64:`.*runtime\.mapclear`
// amd64:-`.*runtime\.(mapiterinit|mapIterStart)`
for k := range s.m {
delete(s.m, k)
}
}
func MapClearPointer(m map[*byte]int) {
// amd64:`.*runtime\.mapclear`
// amd64:-`.*runtime\.(mapiterinit|mapIterStart)`
for k := range m {
delete(m, k)
}
}
func MapClearNotReflexive(m map[float64]int) {
// amd64:`.*runtime\.(mapiterinit|mapIterStart)`
// amd64:-`.*runtime\.mapclear`
for k := range m {
delete(m, k)
}
}
func MapClearInterface(m map[interface{}]int) {
// amd64:`.*runtime\.(mapiterinit|mapIterStart)`
// amd64:-`.*runtime\.mapclear`
for k := range m {
delete(m, k)
}
}
func MapClearSideEffect(m map[int]int) int {
k := 0
// amd64:`.*runtime\.(mapiterinit|mapIterStart)`
// amd64:-`.*runtime\.mapclear`
for k = range m {
delete(m, k)
}
return k
}
func MapLiteralSizing(x int) (map[int]int, map[int]int) {
// This is tested for internal/abi/maps.go:MapBucketCountBits={3,4,5}
// amd64:"MOVL [$]33,"
m := map[int]int{
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
10: 10,
11: 11,
12: 12,
13: 13,
14: 14,
15: 15,
16: 16,
17: 17,
18: 18,
19: 19,
20: 20,
21: 21,
22: 22,
23: 23,
24: 24,
25: 25,
26: 26,
27: 27,
28: 28,
29: 29,
30: 30,
31: 32,
32: 32,
}
// amd64:"MOVL [$]33,"
n := map[int]int{
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
10: 10,
11: 11,
12: 12,
13: 13,
14: 14,
15: 15,
16: 16,
17: 17,
18: 18,
19: 19,
20: 20,
21: 21,
22: 22,
23: 23,
24: 24,
25: 25,
26: 26,
27: 27,
28: 28,
29: 29,
30: 30,
31: 32,
32: 32,
}
return m, n
}

377
test/codegen/math.go Normal file
View File

@@ -0,0 +1,377 @@
// asmcheck -gcflags=-d=converthash=qy
// Copyright 2018 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 codegen
import "math"
var sink64 [8]float64
func approx(x float64) {
// amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41"
// amd64:"ROUNDSD [$]2"
// s390x:"FIDBR [$]6"
// arm64:"FRINTPD"
// ppc64x:"FRIP"
// wasm:"F64Ceil"
sink64[0] = math.Ceil(x)
// amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41"
// amd64:"ROUNDSD [$]1"
// s390x:"FIDBR [$]7"
// arm64:"FRINTMD"
// ppc64x:"FRIM"
// wasm:"F64Floor"
sink64[1] = math.Floor(x)
// s390x:"FIDBR [$]1"
// arm64:"FRINTAD"
// ppc64x:"FRIN"
sink64[2] = math.Round(x)
// amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41"
// amd64:"ROUNDSD [$]3"
// s390x:"FIDBR [$]5"
// arm64:"FRINTZD"
// ppc64x:"FRIZ"
// wasm:"F64Trunc"
sink64[3] = math.Trunc(x)
// amd64/v2:-".*x86HasSSE41" amd64/v3:-".*x86HasSSE41"
// amd64:"ROUNDSD [$]0"
// s390x:"FIDBR [$]4"
// arm64:"FRINTND"
// wasm:"F64Nearest"
sink64[4] = math.RoundToEven(x)
}
func sqrt(x float64) float64 {
// amd64:"SQRTSD"
// 386/sse2:"SQRTSD" 386/softfloat:-"SQRTD"
// arm64:"FSQRTD"
// arm/7:"SQRTD"
// loong64:"SQRTD"
// mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD"
// mips64/hardfloat:"SQRTD" mips64/softfloat:-"SQRTD"
// wasm:"F64Sqrt"
// ppc64x:"FSQRT"
// riscv64: "FSQRTD"
return math.Sqrt(x)
}
func sqrt32(x float32) float32 {
// amd64:"SQRTSS"
// 386/sse2:"SQRTSS" 386/softfloat:-"SQRTS"
// arm64:"FSQRTS"
// arm/7:"SQRTF"
// loong64:"SQRTF"
// mips/hardfloat:"SQRTF" mips/softfloat:-"SQRTF"
// mips64/hardfloat:"SQRTF" mips64/softfloat:-"SQRTF"
// wasm:"F32Sqrt"
// ppc64x:"FSQRTS"
// riscv64: "FSQRTS"
return float32(math.Sqrt(float64(x)))
}
// Check that it's using integer registers
func abs(x, y float64) {
// amd64:"BTRQ [$]63"
// arm64:"FABSD "
// loong64:"ABSD "
// s390x:"LPDFR " -"MOVD " (no integer load/store)
// ppc64x:"FABS "
// riscv64:"FABSD "
// wasm:"F64Abs"
// arm/6:"ABSD "
// mips64/hardfloat:"ABSD "
// mips/hardfloat:"ABSD "
sink64[0] = math.Abs(x)
// amd64:"BTRQ [$]63" "PXOR" (TODO: this should be BTSQ)
// s390x:"LNDFR " -"MOVD " (no integer load/store)
// ppc64x:"FNABS "
sink64[1] = -math.Abs(y)
}
// Check that it's using integer registers
func abs32(x float32) float32 {
// s390x:"LPDFR" -"LDEBR" -"LEDBR" (no float64 conversion)
return float32(math.Abs(float64(x)))
}
// Check that it's using integer registers
func copysign(a, b, c float64) {
// amd64:"BTRQ [$]63" "ANDQ" "ORQ"
// loong64:"FCOPYSGD"
// s390x:"CPSDR" -"MOVD" (no integer load/store)
// ppc64x:"FCPSGN"
// riscv64:"FSGNJD"
// wasm:"F64Copysign"
sink64[0] = math.Copysign(a, b)
// amd64:"BTSQ [$]63"
// loong64:"FCOPYSGD"
// s390x:"LNDFR " -"MOVD " (no integer load/store)
// ppc64x:"FCPSGN"
// riscv64:"FSGNJD"
// arm64:"ORR", -"AND"
sink64[1] = math.Copysign(c, -1)
// Like math.Copysign(c, -1), but with integer operations. Useful
// for platforms that have a copysign opcode to see if it's detected.
// s390x:"LNDFR " -"MOVD " (no integer load/store)
sink64[2] = math.Float64frombits(math.Float64bits(a) | 1<<63)
// amd64:"ANDQ" "ORQ"
// loong64:"FCOPYSGD"
// s390x:"CPSDR " -"MOVD " (no integer load/store)
// ppc64x:"FCPSGN"
// riscv64:"FSGNJD"
sink64[3] = math.Copysign(-1, c)
}
func fma(x, y, z float64) float64 {
// amd64/v3:-".*x86HasFMA"
// amd64:"VFMADD231SD"
// arm/6:"FMULAD"
// arm64:"FMADDD"
// loong64:"FMADDD"
// s390x:"FMADD"
// ppc64x:"FMADD"
// riscv64:"FMADDD"
return math.FMA(x, y, z)
}
func fms(x, y, z float64) float64 {
// riscv64:"FMSUBD"
return math.FMA(x, y, -z)
}
func fnms(x, y, z float64) float64 {
// riscv64:"FNMSUBD" -"FNMADDD"
return math.FMA(-x, y, z)
}
func fnma(x, y, z float64) float64 {
// riscv64:"FNMADDD" -"FNMSUBD"
return math.FMA(x, -y, -z)
}
func isPosInf(x float64) bool {
// riscv64:"FCLASSD"
return math.IsInf(x, 1)
}
func isPosInfEq(x float64) bool {
// riscv64:"FCLASSD"
return x == math.Inf(1)
}
func isPosInfCmp(x float64) bool {
// riscv64:"FCLASSD"
return x > math.MaxFloat64
}
func isNotPosInf(x float64) bool {
// riscv64:"FCLASSD"
return !math.IsInf(x, 1)
}
func isNotPosInfEq(x float64) bool {
// riscv64:"FCLASSD"
return x != math.Inf(1)
}
func isNotPosInfCmp(x float64) bool {
// riscv64:"FCLASSD"
return x <= math.MaxFloat64
}
func isNegInf(x float64) bool {
// riscv64:"FCLASSD"
return math.IsInf(x, -1)
}
func isNegInfEq(x float64) bool {
// riscv64:"FCLASSD"
return x == math.Inf(-1)
}
func isNegInfCmp(x float64) bool {
// riscv64:"FCLASSD"
return x < -math.MaxFloat64
}
func isNotNegInf(x float64) bool {
// riscv64:"FCLASSD"
return !math.IsInf(x, -1)
}
func isNotNegInfEq(x float64) bool {
// riscv64:"FCLASSD"
return x != math.Inf(-1)
}
func isNotNegInfCmp(x float64) bool {
// riscv64:"FCLASSD"
return x >= -math.MaxFloat64
}
func fromFloat64(f64 float64) uint64 {
// amd64:"MOVQ X.*, [^X].*"
// arm64:"FMOVD F.*, R.*"
// loong64:"MOVV F.*, R.*"
// ppc64x:"MFVSRD"
// mips64/hardfloat:"MOVV F.*, R.*"
// riscv64:"FMVXD"
return math.Float64bits(f64+1) + 1
}
func fromFloat32(f32 float32) uint32 {
// amd64:"MOVL X.*, [^X].*"
// arm64:"FMOVS F.*, R.*"
// loong64:"MOVW F.*, R.*"
// mips64/hardfloat:"MOVW F.*, R.*"
// riscv64:"FMVXW"
return math.Float32bits(f32+1) + 1
}
func toFloat64(u64 uint64) float64 {
// amd64:"MOVQ [^X].*, X.*"
// arm64:"FMOVD R.*, F.*"
// loong64:"MOVV R.*, F.*"
// ppc64x:"MTVSRD"
// mips64/hardfloat:"MOVV R.*, F.*"
// riscv64:"FMVDX"
return math.Float64frombits(u64+1) + 1
}
func toFloat32(u32 uint32) float32 {
// amd64:"MOVL [^X].*, X.*"
// arm64:"FMOVS R.*, F.*"
// loong64:"MOVW R.*, F.*"
// mips64/hardfloat:"MOVW R.*, F.*"
// riscv64:"FMVWX"
return math.Float32frombits(u32+1) + 1
}
// Test that comparisons with constants converted to float
// are evaluated at compile-time
func constantCheck64() bool {
// amd64:"(MOVB [$]0)|(XORL [A-Z][A-Z0-9]+, [A-Z][A-Z0-9]+)" -"FCMP" -"MOVB [$]1"
// s390x:"MOV(B|BZ|D) [$]0," -"FCMPU" -"MOV(B|BZ|D) [$]1,"
return 0.5 == float64(uint32(1)) || 1.5 > float64(uint64(1<<63))
}
func constantCheck32() bool {
// amd64:"MOV(B|L) [$]1" -"FCMP" -"MOV(B|L) [$]0"
// s390x:"MOV(B|BZ|D) [$]1," -"FCMPU" -"MOV(B|BZ|D) [$]0,"
return float32(0.5) <= float32(int64(1)) && float32(1.5) >= float32(int32(-1<<31))
}
// Test that integer constants are converted to floating point constants
// at compile-time
func constantConvert32(x float32) float32 {
// amd64:"MOVSS [$]f32.3f800000\\(SB\\)"
// s390x:"FMOVS [$]f32.3f800000\\(SB\\)"
// ppc64x/power8:"FMOVS [$]f32.3f800000\\(SB\\)"
// ppc64x/power9:"FMOVS [$]f32.3f800000\\(SB\\)"
// ppc64x/power10:"XXSPLTIDP [$]1065353216, VS0"
// arm64:"FMOVS [$]\\(1.0\\)"
if x > math.Float32frombits(0x3f800000) {
return -x
}
return x
}
func constantConvertInt32(x uint32) uint32 {
// amd64:-"MOVSS"
// s390x:-"FMOVS"
// ppc64x:-"FMOVS"
// arm64:-"FMOVS"
if x > math.Float32bits(1) {
return -x
}
return x
}
func nanGenerate64() float64 {
// Test to make sure we don't generate a NaN while constant propagating.
// See issue 36400.
zero := 0.0
// amd64:-"DIVSD"
inf := 1 / zero // +inf. We can constant propagate this one.
negone := -1.0
// amd64:"DIVSD"
z0 := zero / zero
// amd64/v1,amd64/v2:"MULSD"
z1 := zero * inf
// amd64:"SQRTSD"
z2 := math.Sqrt(negone)
// amd64/v3:"VFMADD231SD"
return z0 + z1 + z2
}
func nanGenerate32() float32 {
zero := float32(0.0)
// amd64:-"DIVSS"
inf := 1 / zero // +inf. We can constant propagate this one.
// amd64:"DIVSS"
z0 := zero / zero
// amd64/v1,amd64/v2:"MULSS"
z1 := zero * inf
// amd64/v3:"VFMADD231SS"
return z0 + z1
}
func outOfBoundsConv(i32 *[2]int32, u32 *[2]uint32, i64 *[2]int64, u64 *[2]uint64) {
// arm64: "FCVTZSDW"
// amd64: "CVTTSD2SL", "CVTSD2SS"
i32[0] = int32(two40())
// arm64: "FCVTZSDW"
// amd64: "CVTTSD2SL", "CVTSD2SS"
i32[1] = int32(-two40())
// arm64: "FCVTZSDW"
// amd64: "CVTTSD2SL", "CVTSD2SS"
u32[0] = uint32(two41())
// on arm64, this uses an explicit <0 comparison, so it constant folds.
// on amd64, this uses an explicit <0 comparison, so it constant folds.
// amd64: "MOVL [$]0,"
u32[1] = uint32(minus1())
// arm64: "FCVTZSD"
// amd64: "CVTTSD2SQ"
i64[0] = int64(two80())
// arm64: "FCVTZSD"
// amd64: "CVTTSD2SQ"
i64[1] = int64(-two80())
// arm64: "FCVTZUD"
// amd64: "CVTTSD2SQ"
u64[0] = uint64(two81())
// arm64: "FCVTZUD"
// on amd64, this uses an explicit <0 comparison, so it constant folds.
// amd64: "MOVQ [$]0,"
u64[1] = uint64(minus1())
}
func two40() float64 {
return 1 << 40
}
func two41() float64 {
return 1 << 41
}
func two80() float64 {
return 1 << 80
}
func two81() float64 {
return 1 << 81
}
func minus1() float64 {
return -1
}

1011
test/codegen/mathbits.go Normal file

File diff suppressed because it is too large Load Diff

1089
test/codegen/memcombine.go Normal file

File diff suppressed because it is too large Load Diff

17
test/codegen/memcse.go Normal file
View File

@@ -0,0 +1,17 @@
// asmcheck
// Copyright 2025 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.
// Test common subexpression elimination of loads around other operations.
package codegen
func loadsAroundMemEqual(p *int, s1, s2 string) (int, bool) {
x := *p
eq := s1 == s2
y := *p
// arm64:"MOVD ZR, R0"
return x - y, eq
}

403
test/codegen/memops.go Normal file
View File

@@ -0,0 +1,403 @@
// asmcheck
// Copyright 2018 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 codegen
var x [2]bool
var x8 [2]uint8
var x16 [2]uint16
var x32 [2]uint32
var x64 [2]uint64
func compMem1() int {
// amd64:`CMPB command-line-arguments.x\+1\(SB\), [$]0`
if x[1] {
return 1
}
// amd64:`CMPB command-line-arguments.x8\+1\(SB\), [$]7`
if x8[1] == 7 {
return 1
}
// amd64:`CMPW command-line-arguments.x16\+2\(SB\), [$]7`
if x16[1] == 7 {
return 1
}
// amd64:`CMPL command-line-arguments.x32\+4\(SB\), [$]7`
if x32[1] == 7 {
return 1
}
// amd64:`CMPQ command-line-arguments.x64\+8\(SB\), [$]7`
if x64[1] == 7 {
return 1
}
return 0
}
type T struct {
x bool
x8 uint8
x16 uint16
x32 uint32
x64 uint64
a [2]int // force it passed in memory
}
func compMem2(t T) int {
// amd64:`CMPB .*\(SP\), [$]0`
if t.x {
return 1
}
// amd64:`CMPB .*\(SP\), [$]7`
if t.x8 == 7 {
return 1
}
// amd64:`CMPW .*\(SP\), [$]7`
if t.x16 == 7 {
return 1
}
// amd64:`CMPL .*\(SP\), [$]7`
if t.x32 == 7 {
return 1
}
// amd64:`CMPQ .*\(SP\), [$]7`
if t.x64 == 7 {
return 1
}
return 0
}
func compMem3(x, y *int) (int, bool) {
// We can do comparisons of a register with memory even if
// the register is used subsequently.
r := *x
// amd64:`CMPQ \(`
// 386:`CMPL \(`
return r, r < *y
}
// The following functions test that indexed load/store operations get generated.
func idxInt8(x, y []int8, i int) {
var t int8
// amd64: `MOVBL[SZ]X 1\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*`
// 386: `MOVBL[SZ]X 1\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*`
t = x[i+1]
// amd64: `MOVB [A-Z]+[0-9]*, 1\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
// 386: `MOVB [A-Z]+[0-9]*, 1\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
y[i+1] = t
// amd64: `MOVB [$]77, 1\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
// 386: `MOVB [$]77, 1\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
x[i+1] = 77
}
func idxInt16(x, y []int16, i int) {
var t int16
// amd64: `MOVWL[SZ]X 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\), [A-Z]+[0-9]*`
// 386: `MOVWL[SZ]X 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\), [A-Z]+[0-9]*`
t = x[i+1]
// amd64: `MOVW [A-Z]+[0-9]*, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\)`
// 386: `MOVW [A-Z]+[0-9]*, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\)`
y[i+1] = t
// amd64: `MOVWL[SZ]X 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\), [A-Z]+[0-9]*`
// 386: `MOVWL[SZ]X 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\), [A-Z]+[0-9]*`
t = x[16*i+1]
// amd64: `MOVW [A-Z]+[0-9]*, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\)`
// 386: `MOVW [A-Z]+[0-9]*, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\)`
y[16*i+1] = t
// amd64: `MOVW [$]77, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\)`
// 386: `MOVW [$]77, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\)`
x[i+1] = 77
// amd64: `MOVW [$]77, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\)`
// 386: `MOVW [$]77, 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\)`
x[16*i+1] = 77
}
func idxInt32(x, y []int32, i int) {
var t int32
// amd64: `MOVL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
// 386: `MOVL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
t = x[i+1]
// amd64: `MOVL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
// 386: `MOVL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
y[i+1] = t
// amd64: `MOVL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
t = x[2*i+1]
// amd64: `MOVL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
y[2*i+1] = t
// amd64: `MOVL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), [A-Z]+[0-9]*`
// 386: `MOVL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), [A-Z]+[0-9]*`
t = x[16*i+1]
// amd64: `MOVL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
// 386: `MOVL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
y[16*i+1] = t
// amd64: `MOVL [$]77, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
// 386: `MOVL [$]77, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+1] = 77
// amd64: `MOVL [$]77, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
// 386: `MOVL [$]77, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
x[16*i+1] = 77
}
func idxInt64(x, y []int64, i int) {
var t int64
// amd64: `MOVQ 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
t = x[i+1]
// amd64: `MOVQ [A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
y[i+1] = t
// amd64: `MOVQ 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), [A-Z]+[0-9]*`
t = x[16*i+1]
// amd64: `MOVQ [A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
y[16*i+1] = t
// amd64: `MOVQ [$]77, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+1] = 77
// amd64: `MOVQ [$]77, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
x[16*i+1] = 77
}
func idxFloat32(x, y []float32, i int) {
var t float32
// amd64: `MOVSS 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
// 386/sse2: `MOVSS 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
// arm64: `FMOVS \(R[0-9]*\)\(R[0-9]*<<2\), F[0-9]+`
t = x[i+1]
// amd64: `MOVSS X[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
// 386/sse2: `MOVSS X[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
// arm64: `FMOVS F[0-9]+, \(R[0-9]*\)\(R[0-9]*<<2\)`
y[i+1] = t
// amd64: `MOVSS 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
// 386/sse2: `MOVSS 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
t = x[16*i+1]
// amd64: `MOVSS X[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
// 386/sse2: `MOVSS X[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
y[16*i+1] = t
}
func idxFloat64(x, y []float64, i int) {
var t float64
// amd64: `MOVSD 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
// 386/sse2: `MOVSD 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
// arm64: `FMOVD \(R[0-9]*\)\(R[0-9]*<<3\), F[0-9]+`
t = x[i+1]
// amd64: `MOVSD X[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
// 386/sse2: `MOVSD X[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
// arm64: `FMOVD F[0-9]+, \(R[0-9]*\)\(R[0-9]*<<3\)`
y[i+1] = t
// amd64: `MOVSD 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
// 386/sse2: `MOVSD 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
t = x[16*i+1]
// amd64: `MOVSD X[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
// 386/sse2: `MOVSD X[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
y[16*i+1] = t
}
func idxLoadPlusOp32(x []int32, i int) int32 {
s := x[0]
// 386: `ADDL 4\([A-Z]+\)\([A-Z]+\*4\), [A-Z]+`
// amd64: `ADDL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s += x[i+1]
// 386: `SUBL 8\([A-Z]+\)\([A-Z]+\*4\), [A-Z]+`
// amd64: `SUBL 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s -= x[i+2]
// 386: `IMULL 12\([A-Z]+\)\([A-Z]+\*4\), [A-Z]+`
s *= x[i+3]
// 386: `ANDL 16\([A-Z]+\)\([A-Z]+\*4\), [A-Z]+`
// amd64: `ANDL 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s &= x[i+4]
// 386: `ORL 20\([A-Z]+\)\([A-Z]+\*4\), [A-Z]+`
// amd64: `ORL 20\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s |= x[i+5]
// 386: `XORL 24\([A-Z]+\)\([A-Z]+\*4\), [A-Z]+`
// amd64: `XORL 24\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s ^= x[i+6]
return s
}
func idxLoadPlusOp64(x []int64, i int) int64 {
s := x[0]
// amd64: `ADDQ 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s += x[i+1]
// amd64: `SUBQ 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s -= x[i+2]
// amd64: `ANDQ 24\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s &= x[i+3]
// amd64: `ORQ 32\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s |= x[i+4]
// amd64: `XORQ 40\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s ^= x[i+5]
return s
}
func idxStorePlusOp32(x []int32, i int, v int32) {
// 386: `ADDL [A-Z]+, 4\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `ADDL [A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+1] += v
// 386: `SUBL [A-Z]+, 8\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `SUBL [A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+2] -= v
// 386: `ANDL [A-Z]+, 12\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `ANDL [A-Z]+[0-9]*, 12\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+3] &= v
// 386: `ORL [A-Z]+, 16\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `ORL [A-Z]+[0-9]*, 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+4] |= v
// 386: `XORL [A-Z]+, 20\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `XORL [A-Z]+[0-9]*, 20\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+5] ^= v
// 386: `ADDL [$]77, 24\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `ADDL [$]77, 24\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+6] += 77
// 386: `ANDL [$]77, 28\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `ANDL [$]77, 28\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+7] &= 77
// 386: `ORL [$]77, 32\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `ORL [$]77, 32\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+8] |= 77
// 386: `XORL [$]77, 36\([A-Z]+\)\([A-Z]+\*4\)`
// amd64: `XORL [$]77, 36\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
x[i+9] ^= 77
}
func idxStorePlusOp64(x []int64, i int, v int64) {
// amd64: `ADDQ [A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+1] += v
// amd64: `SUBQ [A-Z]+[0-9]*, 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+2] -= v
// amd64: `ANDQ [A-Z]+[0-9]*, 24\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+3] &= v
// amd64: `ORQ [A-Z]+[0-9]*, 32\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+4] |= v
// amd64: `XORQ [A-Z]+[0-9]*, 40\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+5] ^= v
// amd64: `ADDQ [$]77, 48\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+6] += 77
// amd64: `ANDQ [$]77, 56\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+7] &= 77
// amd64: `ORQ [$]77, 64\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+8] |= 77
// amd64: `XORQ [$]77, 72\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
x[i+9] ^= 77
}
func idxCompare(i int) int {
// amd64: `MOVBLZX 1\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*`
if x8[i+1] < x8[0] {
return 0
}
// amd64: `MOVWLZX 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\), [A-Z]+[0-9]*`
if x16[i+1] < x16[0] {
return 0
}
// amd64: `MOVWLZX 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\), [A-Z]+[0-9]*`
if x16[16*i+1] < x16[0] {
return 0
}
// amd64: `MOVL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
if x32[i+1] < x32[0] {
return 0
}
// amd64: `MOVL 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), [A-Z]+[0-9]*`
if x32[16*i+1] < x32[0] {
return 0
}
// amd64: `MOVQ 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
if x64[i+1] < x64[0] {
return 0
}
// amd64: `MOVQ 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), [A-Z]+[0-9]*`
if x64[16*i+1] < x64[0] {
return 0
}
// amd64: `MOVBLZX 2\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*`
if x8[i+2] < 77 {
return 0
}
// amd64: `MOVWLZX 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*2\), [A-Z]+[0-9]*`
if x16[i+2] < 77 {
return 0
}
// amd64: `MOVWLZX 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[12]\), [A-Z]+[0-9]*`
if x16[16*i+2] < 77 {
return 0
}
// amd64: `MOVL 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
if x32[i+2] < 77 {
return 0
}
// amd64: `MOVL 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), [A-Z]+[0-9]*`
if x32[16*i+2] < 77 {
return 0
}
// amd64: `MOVQ 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
if x64[i+2] < 77 {
return 0
}
// amd64: `MOVQ 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), [A-Z]+[0-9]*`
if x64[16*i+2] < 77 {
return 0
}
return 1
}
func idxFloatOps(a []float64, b []float32, i int) (float64, float32) {
c := float64(7)
// amd64: `ADDSD 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
c += a[i+1]
// amd64: `SUBSD 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
c -= a[i+2]
// amd64: `MULSD 24\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
c *= a[i+3]
// amd64: `DIVSD 32\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
c /= a[i+4]
d := float32(8)
// amd64: `ADDSS 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
d += b[i+1]
// amd64: `SUBSS 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
d -= b[i+2]
// amd64: `MULSS 12\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
d *= b[i+3]
// amd64: `DIVSS 16\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
d /= b[i+4]
return c, d
}
func storeTest(a []bool, v int, i int) {
// amd64: `BTL \$0,`,`SETCS 4\([A-Z]+[0-9]*\)`
a[4] = v&1 != 0
// amd64: `BTL \$1,`,`SETCS 3\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
a[3+i] = v&2 != 0
}
func bitOps(p *[12]uint64) {
// amd64: `ORQ \$8, \(AX\)`
p[0] |= 8
// amd64: `ORQ \$1073741824, 8\(AX\)`
p[1] |= 1 << 30
// amd64: `BTSQ \$31, 16\(AX\)`
p[2] |= 1 << 31
// amd64: `BTSQ \$63, 24\(AX\)`
p[3] |= 1 << 63
// amd64: `ANDQ \$-9, 32\(AX\)`
p[4] &^= 8
// amd64: `ANDQ \$-1073741825, 40\(AX\)`
p[5] &^= 1 << 30
// amd64: `BTRQ \$31, 48\(AX\)`
p[6] &^= 1 << 31
// amd64: `BTRQ \$63, 56\(AX\)`
p[7] &^= 1 << 63
// amd64: `XORQ \$8, 64\(AX\)`
p[8] ^= 8
// amd64: `XORQ \$1073741824, 72\(AX\)`
p[9] ^= 1 << 30
// amd64: `BTCQ \$31, 80\(AX\)`
p[10] ^= 1 << 31
// amd64: `BTCQ \$63, 88\(AX\)`
p[11] ^= 1 << 63
}

View File

@@ -0,0 +1,71 @@
// asmcheck
// Copyright 2023 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 codegen
type big1 struct {
w [1<<30 - 1]uint32
}
type big2 struct {
d [1<<29 - 1]uint64
}
func loadLargeOffset(sw *big1, sd *big2) (uint32, uint64) {
// ppc64x:`MOVWZ\s+[0-9]+\(R[0-9]+\)`,-`ADD`
a3 := sw.w[1<<10]
// ppc64le/power10:`MOVWZ\s+[0-9]+\(R[0-9]+\),\sR[0-9]+`,-`ADD`
// ppc64x/power9:`ADD`,`MOVWZ\s+\(R[0-9]+\),\sR[0-9]+`
// ppc64x/power8:`ADD`,`MOVWZ\s+\(R[0-9]+\),\sR[0-9]+`
b3 := sw.w[1<<16]
// ppc64le/power10:`MOVWZ\s+[0-9]+\(R[0-9]+\),\sR[0-9]+`,-`ADD`
// ppc64x/power9:`ADD`,`MOVWZ\s+\(R[0-9]+\),\sR[0-9]+`
// ppc64x/power8:`ADD`,`MOVWZ\s+\(R[0-9]+\),\sR[0-9]+`
c3 := sw.w[1<<28]
// ppc64x:`MOVWZ\s+\(R[0-9]+\)\(R[0-9]+\),\sR[0-9]+`
d3 := sw.w[1<<29]
// ppc64x:`MOVD\s+[0-9]+\(R[0-9]+\)`,-`ADD`
a4 := sd.d[1<<10]
// ppc64le/power10:`MOVD\s+[0-9]+\(R[0-9]+\)`,-`ADD`
// ppc64x/power9:`ADD`,`MOVD\s+\(R[0-9]+\),\sR[0-9]+`
// ppc64x/power8:`ADD`,`MOVD\s+\(R[0-9]+\),\sR[0-9]+`
b4 := sd.d[1<<16]
// ppc64le/power10`:`MOVD\s+[0-9]+\(R[0-9]+\)`,-`ADD`
// ppc64x/power9:`ADD`,`MOVD\s+\(R[0-9]+\),\sR[0-9]+`
// ppc64x/power8:`ADD`,`MOVD\s+\(R[0-9]+\),\sR[0-9]+`
c4 := sd.d[1<<27]
// ppc64x:`MOVD\s+\(R[0-9]+\)\(R[0-9]+\),\sR[0-9]+`
d4 := sd.d[1<<28]
return a3 + b3 + c3 + d3, a4 + b4 + c4 + d4
}
func storeLargeOffset(sw *big1, sd *big2) {
// ppc64x:`MOVW\s+R[0-9]+,\s[0-9]+\(R[0-9]+\)`,-`ADD`
sw.w[1<<10] = uint32(10)
// ppc64le/power10:`MOVW\s+R[0-9]+,\s[0-9]+\(R[0-9]+\)`,-`ADD`
// ppc64x/power9:`MOVW\s+R[0-9]+\,\s\(R[0-9]+\)`,`ADD`
// ppc64x/power8:`MOVW\s+R[0-9]+\,\s\(R[0-9]+\)`,`ADD`
sw.w[1<<16] = uint32(20)
// ppc64le/power10:`MOVW\s+R[0-9]+,\s[0-9]+\(R[0-9]+\)`,-`ADD`
// ppc64x/power9:`MOVW\s+R[0-9]+,\s\(R[0-9]+\)`,`ADD`
// ppc64x/power8:`MOVW\s+R[0-9]+,\s\(R[0-9]+\)`,`ADD`
sw.w[1<<28] = uint32(30)
// ppc64x:`MOVW\s+R[0-9]+,\s\(R[0-9]+\)`
sw.w[1<<29] = uint32(40)
// ppc64x:`MOVD\s+R[0-9]+,\s[0-9]+\(R[0-9]+\)`,-`ADD`
sd.d[1<<10] = uint64(40)
// ppc64le/power10:`MOVD\s+R[0-9]+,\s[0-9]+\(R[0-9]+\)`,-`ADD`
// ppc64x/power9:`MOVD\s+R[0-9]+,\s\(R[0-9]+\)`,`ADD`
// ppc64x/power8:`MOVD\s+R[0-9]+,\s\(R[0-9]+\)`,`ADD`
sd.d[1<<16] = uint64(50)
// ppc64le/power10`:`MOVD\s+R[0-9]+,\s[0-9]+\(R[0-9]+\)`,-`ADD`
// ppc64x/power9:`MOVD\s+R[0-9]+,\s\(R[0-9]+\)`,`ADD`
// ppc64x/power8:`MOVD\s+R[0-9]+,\s\(R[0-9]+\)`,`ADD`
sd.d[1<<27] = uint64(60)
// ppc64x:`MOVD\s+R[0-9]+,\s\(R[0-9]+\)`
sd.d[1<<28] = uint64(70)
}

372
test/codegen/multiply.go Normal file
View File

@@ -0,0 +1,372 @@
// asmcheck
// Copyright 2024 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 codegen
// This file contains codegen tests related to strength
// reduction of integer multiply.
func m0(x int64) int64 {
// amd64: "XORL"
// arm64: "MOVD ZR"
// loong64: "MOVV R0"
return x * 0
}
func m2(x int64) int64 {
// amd64: "ADDQ"
// arm64: "ADD"
// loong64: "ADDVU"
return x * 2
}
func m3(x int64) int64 {
// amd64: "LEAQ .*[*]2"
// arm64: "ADD R[0-9]+<<1,"
// loong64: "ALSLV [$]1,"
return x * 3
}
func m4(x int64) int64 {
// amd64: "SHLQ [$]2,"
// arm64: "LSL [$]2,"
// loong64: "SLLV [$]2,"
return x * 4
}
func m5(x int64) int64 {
// amd64: "LEAQ .*[*]4"
// arm64: "ADD R[0-9]+<<2,"
// loong64: "ALSLV [$]2,"
return x * 5
}
func m6(x int64) int64 {
// amd64: "LEAQ .*[*]1", "LEAQ .*[*]2"
// arm64: "ADD R[0-9]+,", "ADD R[0-9]+<<1,"
// loong64: "ADDVU", "ADDVU", "ADDVU"
return x * 6
}
func m7(x int64) int64 {
// amd64: "LEAQ .*[*]2"
// arm64: "LSL [$]3,", "SUB R[0-9]+,"
// loong64: "ALSLV [$]1,", "ALSLV [$]1,"
return x * 7
}
func m8(x int64) int64 {
// amd64: "SHLQ [$]3,"
// arm64: "LSL [$]3,"
// loong64: "SLLV [$]3,"
return x * 8
}
func m9(x int64) int64 {
// amd64: "LEAQ .*[*]8"
// arm64: "ADD R[0-9]+<<3,"
// loong64: "ALSLV [$]3,"
return x * 9
}
func m10(x int64) int64 {
// amd64: "LEAQ .*[*]1", "LEAQ .*[*]4"
// arm64: "ADD R[0-9]+,", "ADD R[0-9]+<<2,"
// loong64: "ADDVU", "ALSLV [$]2,"
return x * 10
}
func m11(x int64) int64 {
// amd64: "LEAQ .*[*]4", "LEAQ .*[*]2"
// arm64: "MOVD [$]11,", "MUL"
// loong64: "ALSLV [$]2,", "ALSLV [$]1,"
return x * 11
}
func m12(x int64) int64 {
// amd64: "LEAQ .*[*]2", "SHLQ [$]2,"
// arm64: "LSL [$]2,", "ADD R[0-9]+<<1,"
// loong64: "SLLV", "ALSLV [$]1,"
return x * 12
}
func m13(x int64) int64 {
// amd64: "LEAQ .*[*]2", "LEAQ .*[*]4"
// arm64: "MOVD [$]13,", "MUL"
// loong64: "ALSLV [$]1,", "ALSLV [$]2,"
return x * 13
}
func m14(x int64) int64 {
// amd64: "IMUL3Q [$]14,"
// arm64: "LSL [$]4,", "SUB R[0-9]+<<1,"
// loong64: "ADDVU", "ALSLV [$]1", "ALSLV [$]2"
return x * 14
}
func m15(x int64) int64 {
// amd64: "LEAQ .*[*]2", "LEAQ .*[*]4"
// arm64: "LSL [$]4,", "SUB R[0-9]+,"
// loong64: "ALSLV [$]1,", "ALSLV [$]2,"
return x * 15
}
func m16(x int64) int64 {
// amd64: "SHLQ [$]4,"
// arm64: "LSL [$]4,"
// loong64: "SLLV [$]4,"
return x * 16
}
func m17(x int64) int64 {
// amd64: "LEAQ .*[*]1", "LEAQ .*[*]8"
// arm64: "ADD R[0-9]+<<4,"
// loong64: "ALSLV [$]"
return x * 17
}
func m18(x int64) int64 {
// amd64: "LEAQ .*[*]1", "LEAQ .*[*]8"
// arm64: "ADD R[0-9]+,", "ADD R[0-9]+<<3,"
// loong64: "ADDVU", "ALSLV [$]3,"
return x * 18
}
func m19(x int64) int64 {
// amd64: "LEAQ .*[*]8", "LEAQ .*[*]2"
// arm64: "MOVD [$]19,", "MUL"
// loong64: "ALSLV [$]3,", "ALSLV [$]1,"
return x * 19
}
func m20(x int64) int64 {
// amd64: "LEAQ .*[*]4", "SHLQ [$]2,"
// arm64: "LSL [$]2,", "ADD R[0-9]+<<2,"
// loong64: "SLLV [$]2,", "ALSLV [$]2,"
return x * 20
}
func m21(x int64) int64 {
// amd64: "LEAQ .*[*]4", "LEAQ .*[*]4"
// arm64: "MOVD [$]21,", "MUL"
// loong64: "ALSLV [$]2,", "ALSLV [$]2,"
return x * 21
}
func m22(x int64) int64 {
// amd64: "IMUL3Q [$]22,"
// arm64: "MOVD [$]22,", "MUL"
// loong64: "ADDVU", "ALSLV [$]2,", "ALSLV [$]2,"
return x * 22
}
func m23(x int64) int64 {
// amd64: "IMUL3Q [$]23,"
// arm64: "MOVD [$]23,", "MUL"
// loong64: "ALSLV [$]1,", "SUBVU", "ALSLV [$]3,"
return x * 23
}
func m24(x int64) int64 {
// amd64: "LEAQ .*[*]2", "SHLQ [$]3,"
// arm64: "LSL [$]3,", "ADD R[0-9]+<<1,"
// loong64: "SLLV [$]3", "ALSLV [$]1,"
return x * 24
}
func m25(x int64) int64 {
// amd64: "LEAQ .*[*]4", "LEAQ .*[*]4"
// arm64: "MOVD [$]25,", "MUL"
// loong64: "ALSLV [$]2,", "ALSLV [$]2,"
return x * 25
}
func m26(x int64) int64 {
// amd64: "IMUL3Q [$]26,"
// arm64: "MOVD [$]26,", "MUL"
// loong64: "ADDVU", "ALSLV [$]1,", "ALSLV [$]3,"
return x * 26
}
func m27(x int64) int64 {
// amd64: "LEAQ .*[*]2", "LEAQ .*[*]8"
// arm64: "MOVD [$]27,", "MUL"
// loong64: "ALSLV [$]1,", "ALSLV [$]3,"
return x * 27
}
func m28(x int64) int64 {
// amd64: "IMUL3Q [$]28,"
// arm64: "LSL [$]5, "SUB R[0-9]+<<2,"
// loong64: "ALSLV [$]1," "SLLV [$]2," "ALSLV [$]3,"
return x * 28
}
func m29(x int64) int64 {
// amd64: "IMUL3Q [$]29,"
// arm64: "MOVD [$]29,", "MUL"
// loong64: "ALSLV [$]1," "SLLV [$]5," "SUBVU"
return x * 29
}
func m30(x int64) int64 {
// amd64: "IMUL3Q [$]30,"
// arm64: "LSL [$]5,", "SUB R[0-9]+<<1,"
// loong64: "ADDVU" "SLLV [$]5," "SUBVU"
return x * 30
}
func m31(x int64) int64 {
// amd64: "SHLQ [$]5,", "SUBQ"
// arm64: "LSL [$]5,", "SUB R[0-9]+,"
// loong64: "SLLV [$]5," "SUBVU"
return x * 31
}
func m32(x int64) int64 {
// amd64: "SHLQ [$]5,"
// arm64: "LSL [$]5,"
// loong64: "SLLV [$]5,"
return x * 32
}
func m33(x int64) int64 {
// amd64: "SHLQ [$]2,", "LEAQ .*[*]8"
// arm64: "ADD R[0-9]+<<5,"
// loong64: "ADDVU", "ALSLV [$]4,"
return x * 33
}
func m34(x int64) int64 {
// amd64: "SHLQ [$]5,", "LEAQ .*[*]2"
// arm64: "ADD R[0-9]+,", "ADD R[0-9]+<<4,"
// loong64: "ADDVU", "ALSLV [$]4,"
return x * 34
}
func m35(x int64) int64 {
// amd64: "IMUL3Q [$]35,"
// arm64: "MOVD [$]35,", "MUL"
// loong64: "ALSLV [$]4,", "ALSLV [$]1,"
return x * 35
}
func m36(x int64) int64 {
// amd64: "LEAQ .*[*]8", "SHLQ [$]2,"
// arm64: "LSL [$]2,", "ADD R[0-9]+<<3,"
// loong64: "SLLV [$]2,", "ALSLV [$]3,"
return x * 36
}
func m37(x int64) int64 {
// amd64: "LEAQ .*[*]8", "LEAQ .*[*]4"
// arm64: "MOVD [$]37,", "MUL"
// loong64: "ALSLV [$]3,", "ALSLV [$]2,"
return x * 37
}
func m38(x int64) int64 {
// amd64: "IMUL3Q [$]38,"
// arm64: "MOVD [$]38,", "MUL"
// loong64: "ALSLV [$]3,", "ALSLV [$]2,"
return x * 38
}
func m39(x int64) int64 {
// amd64: "IMUL3Q [$]39,"
// arm64: "MOVD [$]39,", "MUL"
// loong64: "ALSLV [$]2,", "SUBVU", "ALSLV [$]3,"
return x * 39
}
func m40(x int64) int64 {
// amd64: "LEAQ .*[*]4", "SHLQ [$]3,"
// arm64: "LSL [$]3,", "ADD R[0-9]+<<2,"
// loong64: "SLLV [$]3,", "ALSLV [$]2,"
return x * 40
}
func mn1(x int64) int64 {
// amd64: "NEGQ "
// arm64: "NEG R[0-9]+,"
// loong64: "SUBVU R[0-9], R0,"
return x * -1
}
func mn2(x int64) int64 {
// amd64: "NEGQ", "ADDQ"
// arm64: "NEG R[0-9]+<<1,"
// loong64: "ADDVU" "SUBVU R[0-9], R0,"
return x * -2
}
func mn3(x int64) int64 {
// amd64: "NEGQ", "LEAQ .*[*]2"
// arm64: "SUB R[0-9]+<<2,"
// loong64: "SUBVU", "ALSLV [$]1,"
return x * -3
}
func mn4(x int64) int64 {
// amd64: "NEGQ", "SHLQ [$]2,"
// arm64: "NEG R[0-9]+<<2,"
// loong64: "SLLV [$]2," "SUBVU R[0-9], R0,"
return x * -4
}
func mn5(x int64) int64 {
// amd64: "NEGQ", "LEAQ .*[*]4"
// arm64: "NEG R[0-9]+,", "ADD R[0-9]+<<2,"
// loong64: "SUBVU", "ALSLV [$]2,"
return x * -5
}
func mn6(x int64) int64 {
// amd64: "IMUL3Q [$]-6,"
// arm64: "ADD R[0-9]+,", "SUB R[0-9]+<<2,"
// loong64: "ADDVU", "SUBVU", "ALSLV [$]3,"
return x * -6
}
func mn7(x int64) int64 {
// amd64: "NEGQ", "LEAQ .*[*]8"
// arm64: "SUB R[0-9]+<<3,"
// loong64: "SUBVU", "ALSLV [$]3,"
return x * -7
}
func mn8(x int64) int64 {
// amd64: "NEGQ", "SHLQ [$]3,"
// arm64: "NEG R[0-9]+<<3,"
// loong64: "SLLV [$]3" "SUBVU R[0-9], R0,"
return x * -8
}
func mn9(x int64) int64 {
// amd64: "NEGQ", "LEAQ .*[*]8"
// arm64: "NEG R[0-9]+,", "ADD R[0-9]+<<3,"
// loong64: "SUBVU", "ALSLV [$]3,"
return x * -9
}
func mn10(x int64) int64 {
// amd64: "IMUL3Q [$]-10,"
// arm64: "MOVD [$]-10,", "MUL"
// loong64: "ADDVU", "ALSLV [$]3", "SUBVU"
return x * -10
}
func mn11(x int64) int64 {
// amd64: "IMUL3Q [$]-11,"
// arm64: "MOVD [$]-11,", "MUL"
// loong64: "ALSLV [$]2,", "SUBVU", "ALSLV [$]4,"
return x * -11
}
func mn12(x int64) int64 {
// amd64: "IMUL3Q [$]-12,"
// arm64: "LSL [$]2,", "SUB R[0-9]+<<2,"
// loong64: "SUBVU", "SLLV [$]2,", "ALSLV [$]4,"
return x * -12
}
func mn13(x int64) int64 {
// amd64: "IMUL3Q [$]-13,"
// arm64: "MOVD [$]-13,", "MUL"
// loong64: "ALSLV [$]4,", "SLLV [$]2, ", "SUBVU"
return x * -13
}
func mn14(x int64) int64 {
// amd64: "IMUL3Q [$]-14,"
// arm64: "ADD R[0-9]+,", "SUB R[0-9]+<<3,"
// loong64: "ADDVU", "SUBVU", "ALSLV [$]4,"
return x * -14
}
func mn15(x int64) int64 {
// amd64: "SHLQ [$]4,", "SUBQ"
// arm64: "SUB R[0-9]+<<4,"
// loong64: "SUBVU", "ALSLV [$]4,"
return x * -15
}
func mn16(x int64) int64 {
// amd64: "NEGQ", "SHLQ [$]4,"
// arm64: "NEG R[0-9]+<<4,"
// loong64: "SLLV [$]4," "SUBVU R[0-9], R0,"
return x * -16
}
func mn17(x int64) int64 {
// amd64: "IMUL3Q [$]-17,"
// arm64: "NEG R[0-9]+,", "ADD R[0-9]+<<4,"
// loong64: "SUBVU", "ALSLV [$]4,"
return x * -17
}
func mn18(x int64) int64 {
// amd64: "IMUL3Q [$]-18,"
// arm64: "MOVD [$]-18,", "MUL"
// loong64: "ADDVU", "ALSLV [$]4,", "SUBVU"
return x * -18
}
func mn19(x int64) int64 {
// amd64: "IMUL3Q [$]-19,"
// arm64: "MOVD [$]-19,", "MUL"
// loong64: "ALSLV [$]1,", "ALSLV [$]4,", "SUBVU"
return x * -19
}
func mn20(x int64) int64 {
// amd64: "IMUL3Q [$]-20,"
// arm64: "MOVD [$]-20,", "MUL"
// loong64: "SLLV [$]2,", "ALSLV [$]4,", "SUBVU"
return x * -20
}

285
test/codegen/noextend.go Normal file
View File

@@ -0,0 +1,285 @@
// asmcheck
// Copyright 2018 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 codegen
import "math/bits"
var sval64 [8]int64
var sval32 [8]int32
var sval16 [8]int16
var sval8 [8]int8
var val64 [8]uint64
var val32 [8]uint32
var val16 [8]uint16
var val8 [8]uint8
// Avoid zero/sign extensions following a load
// which has extended the value correctly.
// Note: No tests are done for int8 since
// an extra extension is usually needed due to
// no signed byte load.
func set16(x8 int8, u8 *uint8, y8 int8, z8 uint8) {
// Truncate not needed, load does sign/zero extend
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
val16[0] = uint16(*u8)
// AND not needed due to size
// ppc64x:-"ANDCC"
sval16[1] = 255 & int16(x8+y8)
// ppc64x:-"ANDCC"
val16[1] = 255 & uint16(*u8+z8)
}
func shiftidx(u8 *uint8, x16 *int16, u16 *uint16) {
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
val16[0] = uint16(sval16[*u8>>2])
// ppc64x:-"MOVH R\\d+,\\sR\\d+"
sval16[1] = int16(val16[*x16>>1])
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
val16[1] = uint16(sval16[*u16>>2])
}
func setnox(x8 int8, u8 *uint8, y8 *int8, z8 *uint8, x16 *int16, u16 *uint16, x32 *int32, u32 *uint32) {
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
val16[0] = uint16(*u8)
// AND not needed due to size
// ppc64x:-"ANDCC"
sval16[1] = 255 & int16(x8+*y8)
// ppc64x:-"ANDCC"
val16[1] = 255 & uint16(*u8+*z8)
// ppc64x:-"MOVH R\\d+,\\sR\\d+"
sval32[1] = int32(*x16)
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
val32[0] = uint32(*u8)
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
val32[1] = uint32(*u16)
// ppc64x:-"MOVH R\\d+,\\sR\\d+"
sval64[1] = int64(*x16)
// ppc64x:-"MOVW R\\d+,\\sR\\d+"
sval64[2] = int64(*x32)
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
val64[0] = uint64(*u8)
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
val64[1] = uint64(*u16)
// ppc64x:-"MOVWZ R\\d+,\\sR\\d+"
val64[2] = uint64(*u32)
}
func cmp16(u8 *uint8, x32 *int32, u32 *uint32, x64 *int64, u64 *uint64) bool {
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
if uint16(*u8) == val16[0] {
return true
}
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
if uint16(*u32>>16) == val16[0] {
return true
}
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
if uint16(*u64>>48) == val16[0] {
return true
}
// Verify the truncates are using the correct sign.
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
if int16(*x32) == sval16[0] {
return true
}
// ppc64x:-"MOVH R\\d+,\\sR\\d+"
if uint16(*u32) == val16[0] {
return true
}
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
if int16(*x64) == sval16[0] {
return true
}
// ppc64x:-"MOVH R\\d+,\\sR\\d+"
if uint16(*u64) == val16[0] {
return true
}
return false
}
func cmp32(u8 *uint8, x16 *int16, u16 *uint16, x64 *int64, u64 *uint64) bool {
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
if uint32(*u8) == val32[0] {
return true
}
// ppc64x:-"MOVH R\\d+,\\sR\\d+"
if int32(*x16) == sval32[0] {
return true
}
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
if uint32(*u16) == val32[0] {
return true
}
// Verify the truncates are using the correct sign.
// ppc64x:-"MOVWZ R\\d+,\\sR\\d+"
if int32(*x64) == sval32[0] {
return true
}
// ppc64x:-"MOVW R\\d+,\\sR\\d+"
if uint32(*u64) == val32[0] {
return true
}
return false
}
func cmp64(u8 *uint8, x16 *int16, u16 *uint16, x32 *int32, u32 *uint32) bool {
// ppc64x:-"MOVBZ R\\d+,\\sR\\d+"
if uint64(*u8) == val64[0] {
return true
}
// ppc64x:-"MOVH R\\d+,\\sR\\d+"
if int64(*x16) == sval64[0] {
return true
}
// ppc64x:-"MOVHZ R\\d+,\\sR\\d+"
if uint64(*u16) == val64[0] {
return true
}
// ppc64x:-"MOVW R\\d+,\\sR\\d+"
if int64(*x32) == sval64[0] {
return true
}
// ppc64x:-"MOVWZ R\\d+,\\sR\\d+"
if uint64(*u32) == val64[0] {
return true
}
return false
}
// no unsign extension following 32 bits ops
func noUnsignEXT(t1, t2, t3, t4 uint32, k int64) uint64 {
var ret uint64
// arm64:"RORW" -"MOVWU"
ret += uint64(bits.RotateLeft32(t1, 7))
// arm64:"MULW" -"MOVWU"
ret *= uint64(t1 * t2)
// arm64:"MNEGW" -"MOVWU"
ret += uint64(-t1 * t3)
// arm64:"UDIVW" -"MOVWU"
ret += uint64(t1 / t4)
// arm64:-"MOVWU"
ret += uint64(t2 % t3)
// arm64:"MSUBW" -"MOVWU"
ret += uint64(t1 - t2*t3)
// arm64:"MADDW" -"MOVWU"
ret += uint64(t3*t4 + t2)
// arm64:"REVW" -"MOVWU"
ret += uint64(bits.ReverseBytes32(t1))
// arm64:"RBITW" -"MOVWU"
ret += uint64(bits.Reverse32(t1))
// arm64:"CLZW" -"MOVWU"
ret += uint64(bits.LeadingZeros32(t1))
// arm64:"REV16W" -"MOVWU"
ret += uint64(((t1 & 0xff00ff00) >> 8) | ((t1 & 0x00ff00ff) << 8))
// arm64:"EXTRW" -"MOVWU"
ret += uint64((t1 << 25) | (t2 >> 7))
return ret
}
// no sign extension when the upper bits of the result are zero
func noSignEXT(x int) int64 {
t1 := int32(x)
var ret int64
// arm64:-"MOVW"
ret += int64(t1 & 1)
// arm64:-"MOVW"
ret += int64(int32(x & 0x7fffffff))
// arm64:-"MOVH"
ret += int64(int16(x & 0x7fff))
// arm64:-"MOVB"
ret += int64(int8(x & 0x7f))
return ret
}
// corner cases that sign extension must not be omitted
func shouldSignEXT(x int) int64 {
t1 := int32(x)
var ret int64
// arm64:"MOVW"
ret += int64(t1 & (-1))
// arm64:"MOVW"
ret += int64(int32(x & 0x80000000))
// arm64:"MOVW"
ret += int64(int32(x & 0x1100000011111111))
// arm64:"MOVH"
ret += int64(int16(x & 0x1100000000001111))
// arm64:"MOVB"
ret += int64(int8(x & 0x1100000000000011))
return ret
}
func noIntermediateExtension(a, b, c uint32) uint32 {
// arm64:-"MOVWU"
return a*b*9 + c
}

22
test/codegen/race.go Normal file
View File

@@ -0,0 +1,22 @@
// asmcheck -race
// Copyright 2019 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 codegen
// Check that we elide racefuncenter/racefuncexit for
// functions with no calls (but which might panic
// in various ways). See issue 31219.
// amd64:-"CALL.*racefuncenter.*"
// arm64:-"CALL.*racefuncenter.*"
// ppc64le:-"CALL.*racefuncenter.*"
func RaceMightPanic(a []int, i, j, k, s int) {
var b [4]int
_ = b[i] // panicIndex
_ = a[i:j] // panicSlice
_ = a[i:j:k] // also panicSlice
_ = i << s // panicShift
_ = i / j // panicDivide
}

View File

@@ -0,0 +1,21 @@
// asmcheck
// Copyright 2025 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 codegen
import "reflect"
func intPtrTypeSize() uintptr {
// amd64:"MOVL [$]8," -"CALL"
// arm64:"MOVD [$]8," -"CALL"
return reflect.TypeFor[*int]().Size()
}
func intPtrTypeKind() reflect.Kind {
// amd64:"MOVL [$]22," -"CALL"
// arm64:"MOVD [$]22," -"CALL"
return reflect.TypeFor[*int]().Kind()
}

View File

@@ -0,0 +1,23 @@
// asmcheck
// Copyright 2021 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 codegen
//go:registerparams
func f1(a, b int) {
// amd64:"MOVQ BX, CX", "MOVQ AX, BX", "MOVL [$]1, AX", -"MOVQ .*DX"
g(1, a, b)
}
//go:registerparams
func f2(a, b int) {
// amd64:"MOVQ BX, AX", "MOVQ [AB]X, CX", -"MOVQ .*, BX"
g(b, b, b)
}
//go:noinline
//go:registerparams
func g(int, int, int) {}

43
test/codegen/retpoline.go Normal file
View File

@@ -0,0 +1,43 @@
// asmcheck -gcflags=-spectre=ret
//go:build amd64
package codegen
func CallFunc(f func()) {
// amd64:`CALL runtime.retpoline`
f()
}
func CallInterface(x interface{ M() }) {
// amd64:`CALL runtime.retpoline`
x.M()
}
// Check to make sure that jump tables are disabled
// when retpoline is on. See issue 57097.
func noJumpTables(x int) int {
switch x {
case 0:
return 0
case 1:
return 1
case 2:
return 2
case 3:
return 3
case 4:
return 4
case 5:
return 5
case 6:
return 6
case 7:
return 7
case 8:
return 8
case 9:
return 9
}
return 10
}

281
test/codegen/rotate.go Normal file
View File

@@ -0,0 +1,281 @@
// asmcheck
// Copyright 2018 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 codegen
import "math/bits"
// ------------------- //
// const rotates //
// ------------------- //
func rot64(x uint64) uint64 {
var a uint64
// amd64:"ROLQ [$]7"
// ppc64x:"ROTL [$]7"
// loong64: "ROTRV [$]57"
// riscv64: "RORI [$]57"
a += x<<7 | x>>57
// amd64:"ROLQ [$]8"
// arm64:"ROR [$]56"
// s390x:"RISBGZ [$]0, [$]63, [$]8, "
// ppc64x:"ROTL [$]8"
// loong64: "ROTRV [$]56"
// riscv64: "RORI [$]56"
a += x<<8 + x>>56
// amd64:"ROLQ [$]9"
// arm64:"ROR [$]55"
// s390x:"RISBGZ [$]0, [$]63, [$]9, "
// ppc64x:"ROTL [$]9"
// loong64: "ROTRV [$]55"
// riscv64: "RORI [$]55"
a += x<<9 ^ x>>55
// amd64:"ROLQ [$]10"
// arm64:"ROR [$]54"
// s390x:"RISBGZ [$]0, [$]63, [$]10, "
// ppc64x:"ROTL [$]10"
// arm64:"ROR [$]54"
// s390x:"RISBGZ [$]0, [$]63, [$]10, "
// loong64: "ROTRV [$]54"
// riscv64: "RORI [$]54"
a += bits.RotateLeft64(x, 10)
return a
}
func rot32(x uint32) uint32 {
var a uint32
// amd64:"ROLL [$]7"
// arm:"MOVW R\\d+@>25"
// ppc64x:"ROTLW [$]7"
// loong64: "ROTR [$]25"
// riscv64: "RORIW [$]25"
a += x<<7 | x>>25
// amd64:`ROLL [$]8`
// arm:"MOVW R\\d+@>24"
// arm64:"RORW [$]24"
// s390x:"RLL [$]8"
// ppc64x:"ROTLW [$]8"
// loong64: "ROTR [$]24"
// riscv64: "RORIW [$]24"
a += x<<8 + x>>24
// amd64:"ROLL [$]9"
// arm:"MOVW R\\d+@>23"
// arm64:"RORW [$]23"
// s390x:"RLL [$]9"
// ppc64x:"ROTLW [$]9"
// loong64: "ROTR [$]23"
// riscv64: "RORIW [$]23"
a += x<<9 ^ x>>23
// amd64:"ROLL [$]10"
// arm:"MOVW R\\d+@>22"
// arm64:"RORW [$]22"
// s390x:"RLL [$]10"
// ppc64x:"ROTLW [$]10"
// arm64:"RORW [$]22"
// s390x:"RLL [$]10"
// loong64: "ROTR [$]22"
// riscv64: "RORIW [$]22"
a += bits.RotateLeft32(x, 10)
return a
}
func rot16(x uint16) uint16 {
var a uint16
// amd64:"ROLW [$]7"
// riscv64: "OR" "SLLI" "SRLI" -"AND"
a += x<<7 | x>>9
// amd64:`ROLW [$]8`
// riscv64: "OR" "SLLI" "SRLI" -"AND"
a += x<<8 + x>>8
// amd64:"ROLW [$]9"
// riscv64: "OR" "SLLI" "SRLI" -"AND"
a += x<<9 ^ x>>7
return a
}
func rot8(x uint8) uint8 {
var a uint8
// amd64:"ROLB [$]5"
// riscv64: "OR" "SLLI" "SRLI" -"AND"
a += x<<5 | x>>3
// amd64:`ROLB [$]6`
// riscv64: "OR" "SLLI" "SRLI" -"AND"
a += x<<6 + x>>2
// amd64:"ROLB [$]7"
// riscv64: "OR" "SLLI" "SRLI" -"AND"
a += x<<7 ^ x>>1
return a
}
// ----------------------- //
// non-const rotates //
// ----------------------- //
func rot64nc(x uint64, z uint) uint64 {
var a uint64
z &= 63
// amd64:"ROLQ" -"AND"
// arm64:"ROR" "NEG" -"AND"
// ppc64x:"ROTL" -"NEG" -"AND"
// loong64: "ROTRV", -"AND"
// riscv64: "ROL" -"AND"
a += x<<z | x>>(64-z)
// amd64:"RORQ" -"AND"
// arm64:"ROR" -"NEG" -"AND"
// ppc64x:"ROTL" "NEG" -"AND"
// loong64: "ROTRV", -"AND"
// riscv64: "ROR" -"AND"
a += x>>z | x<<(64-z)
return a
}
func rot32nc(x uint32, z uint) uint32 {
var a uint32
z &= 31
// amd64:"ROLL" -"AND"
// arm64:"ROR" "NEG" -"AND"
// ppc64x:"ROTLW" -"NEG" -"AND"
// loong64: "ROTR", -"AND"
// riscv64: "ROLW" -"AND"
a += x<<z | x>>(32-z)
// amd64:"RORL" -"AND"
// arm64:"ROR" -"NEG" -"AND"
// ppc64x:"ROTLW" "NEG" -"AND"
// loong64: "ROTR", -"AND"
// riscv64: "RORW" -"AND"
a += x>>z | x<<(32-z)
return a
}
func rot16nc(x uint16, z uint) uint16 {
var a uint16
z &= 15
// amd64:"ROLW" -"ANDQ"
// riscv64: "OR" "SLL" "SRL" -"AND "
a += x<<z | x>>(16-z)
// amd64:"RORW" -"ANDQ"
// riscv64: "OR" "SLL" "SRL" -"AND "
a += x>>z | x<<(16-z)
return a
}
func rot8nc(x uint8, z uint) uint8 {
var a uint8
z &= 7
// amd64:"ROLB" -"ANDQ"
// riscv64: "OR" "SLL" "SRL" -"AND "
a += x<<z | x>>(8-z)
// amd64:"RORB" -"ANDQ"
// riscv64: "OR" "SLL" "SRL" -"AND "
a += x>>z | x<<(8-z)
return a
}
// Issue 18254: rotate after inlining
func f32(x uint32) uint32 {
// amd64:"ROLL [$]7"
return rot32nc(x, 7)
}
func doubleRotate(x uint64) uint64 {
x = (x << 5) | (x >> 59)
// amd64:"ROLQ [$]15"
// arm64:"ROR [$]49"
x = (x << 10) | (x >> 54)
return x
}
// --------------------------------------- //
// Combined Rotate + Masking operations //
// --------------------------------------- //
func checkMaskedRotate32(a []uint32, r int) {
i := 0
// ppc64x: "RLWNM [$]16, R[0-9]+, [$]8, [$]15, R[0-9]+"
a[i] = bits.RotateLeft32(a[i], 16) & 0xFF0000
i++
// ppc64x: "RLWNM [$]16, R[0-9]+, [$]8, [$]15, R[0-9]+"
a[i] = bits.RotateLeft32(a[i]&0xFF, 16)
i++
// ppc64x: "RLWNM [$]4, R[0-9]+, [$]20, [$]27, R[0-9]+"
a[i] = bits.RotateLeft32(a[i], 4) & 0xFF0
i++
// ppc64x: "RLWNM [$]16, R[0-9]+, [$]24, [$]31, R[0-9]+"
a[i] = bits.RotateLeft32(a[i]&0xFF0000, 16)
i++
// ppc64x: "RLWNM R[0-9]+, R[0-9]+, [$]8, [$]15, R[0-9]+"
a[i] = bits.RotateLeft32(a[i], r) & 0xFF0000
i++
// ppc64x: "RLWNM R[0-9]+, R[0-9]+, [$]16, [$]23, R[0-9]+"
a[i] = bits.RotateLeft32(a[i], r) & 0xFF00
i++
// ppc64x: "RLWNM R[0-9]+, R[0-9]+, [$]20, [$]11, R[0-9]+"
a[i] = bits.RotateLeft32(a[i], r) & 0xFFF00FFF
i++
// ppc64x: "RLWNM [$]4, R[0-9]+, [$]20, [$]11, R[0-9]+"
a[i] = bits.RotateLeft32(a[i], 4) & 0xFFF00FFF
i++
}
// combined arithmetic and rotate on arm64
func checkArithmeticWithRotate(a *[1000]uint64) {
// arm64: "AND R[0-9]+@>51, R[0-9]+, R[0-9]+"
a[2] = a[1] & bits.RotateLeft64(a[0], 13)
// arm64: "ORR R[0-9]+@>51, R[0-9]+, R[0-9]+"
a[5] = a[4] | bits.RotateLeft64(a[3], 13)
// arm64: "EOR R[0-9]+@>51, R[0-9]+, R[0-9]+"
a[8] = a[7] ^ bits.RotateLeft64(a[6], 13)
// arm64: "MVN R[0-9]+@>51, R[0-9]+"
a[10] = ^bits.RotateLeft64(a[9], 13)
// arm64: "BIC R[0-9]+@>51, R[0-9]+, R[0-9]+"
a[13] = a[12] &^ bits.RotateLeft64(a[11], 13)
// arm64: "EON R[0-9]+@>51, R[0-9]+, R[0-9]+"
a[16] = a[15] ^ ^bits.RotateLeft64(a[14], 13)
// arm64: "ORN R[0-9]+@>51, R[0-9]+, R[0-9]+"
a[19] = a[18] | ^bits.RotateLeft64(a[17], 13)
// arm64: "TST R[0-9]+@>51, R[0-9]+"
if a[18]&bits.RotateLeft64(a[19], 13) == 0 {
a[20] = 1
}
}

17
test/codegen/schedule.go Normal file
View File

@@ -0,0 +1,17 @@
// asmcheck
// Copyright 2025 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 codegen
func f(n int) int {
r := 0
// arm64:-"MOVD R"
// amd64:-"LEAQ" "INCQ"
for i := range n {
r += i
}
return r
}

20
test/codegen/select.go Normal file
View File

@@ -0,0 +1,20 @@
// asmcheck
// Copyright 2020 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 codegen
func f() {
ch1 := make(chan int)
ch2 := make(chan int)
for {
// amd64:-`MOVQ [$]0, command-line-arguments..autotmp_3`
select {
case <-ch1:
case <-ch2:
default:
}
}
}

697
test/codegen/shift.go Normal file
View File

@@ -0,0 +1,697 @@
// asmcheck
// Copyright 2018 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 codegen
// ------------------ //
// constant shifts //
// ------------------ //
func lshConst64x64(v int64) int64 {
// loong64:"SLLV"
// ppc64x:"SLD"
// riscv64:"SLLI" -"AND" -"SLTIU"
return v << uint64(33)
}
func rshConst64Ux64(v uint64) uint64 {
// loong64:"SRLV"
// ppc64x:"SRD"
// riscv64:"SRLI " -"AND" -"SLTIU"
return v >> uint64(33)
}
func rshConst64Ux64Overflow32(v uint32) uint64 {
// loong64:"MOVV R0," -"SRL "
// riscv64:"MOV [$]0," -"SRL"
return uint64(v) >> 32
}
func rshConst64Ux64Overflow16(v uint16) uint64 {
// loong64:"MOVV R0," -"SRLV"
// riscv64:"MOV [$]0," -"SRL"
return uint64(v) >> 16
}
func rshConst64Ux64Overflow8(v uint8) uint64 {
// loong64:"MOVV R0," -"SRLV"
// riscv64:"MOV [$]0," -"SRL"
return uint64(v) >> 8
}
func rshConst64x64(v int64) int64 {
// loong64:"SRAV"
// ppc64x:"SRAD"
// riscv64:"SRAI " -"OR" -"SLTIU"
return v >> uint64(33)
}
func rshConst64x64Overflow32(v int32) int64 {
// loong64:"SRA [$]31"
// riscv64:"SRAIW" -"SLLI" -"SRAI "
return int64(v) >> 32
}
func rshConst64x64Overflow16(v int16) int64 {
// loong64:"SLLV [$]48" "SRAV [$]63"
// riscv64:"SLLI" "SRAI" -"SRAIW"
return int64(v) >> 16
}
func rshConst64x64Overflow8(v int8) int64 {
// loong64:"SLLV [$]56" "SRAV [$]63"
// riscv64:"SLLI" "SRAI" -"SRAIW"
return int64(v) >> 8
}
func lshConst32x1(v int32) int32 {
// amd64:"ADDL", -"SHLL"
return v << 1
}
func lshConst64x1(v int64) int64 {
// amd64:"ADDQ", -"SHLQ"
return v << 1
}
func lshConst32x64(v int32) int32 {
// loong64:"SLL "
// ppc64x:"SLW"
// riscv64:"SLLI" -"AND" -"SLTIU", -"MOVW"
return v << uint64(29)
}
func rshConst32Ux64(v uint32) uint32 {
// loong64:"SRL "
// ppc64x:"SRW"
// riscv64:"SRLIW" -"AND" -"SLTIU", -"MOVW"
return v >> uint64(29)
}
func rshConst32x64(v int32) int32 {
// loong64:"SRA "
// ppc64x:"SRAW"
// riscv64:"SRAIW" -"OR" -"SLTIU", -"MOVW"
return v >> uint64(29)
}
func lshConst64x32(v int64) int64 {
// loong64:"SLLV"
// ppc64x:"SLD"
// riscv64:"SLLI" -"AND" -"SLTIU"
return v << uint32(33)
}
func rshConst64Ux32(v uint64) uint64 {
// loong64:"SRLV"
// ppc64x:"SRD"
// riscv64:"SRLI " -"AND" -"SLTIU"
return v >> uint32(33)
}
func rshConst64x32(v int64) int64 {
// loong64:"SRAV"
// ppc64x:"SRAD"
// riscv64:"SRAI " -"OR" -"SLTIU"
return v >> uint32(33)
}
func lshConst32x1Add(x int32) int32 {
// amd64:"SHLL [$]2"
// loong64:"SLL [$]2"
// riscv64:"SLLI [$]2"
return (x + x) << 1
}
func lshConst64x1Add(x int64) int64 {
// amd64:"SHLQ [$]2"
// loong64:"SLLV [$]2"
// riscv64:"SLLI [$]2"
return (x + x) << 1
}
func lshConst32x2Add(x int32) int32 {
// amd64:"SHLL [$]3"
// loong64:"SLL [$]3"
// riscv64:"SLLI [$]3"
return (x + x) << 2
}
func lshConst64x2Add(x int64) int64 {
// amd64:"SHLQ [$]3"
// loong64:"SLLV [$]3"
// riscv64:"SLLI [$]3"
return (x + x) << 2
}
func lshConst32x31Add(x int32) int32 {
// loong64:-"SLL " "MOVV R0"
// riscv64:-"SLLI" "MOV [$]0"
return (x + x) << 31
}
func lshConst64x63Add(x int64) int64 {
// loong64:-"SLLV" "MOVV R0"
// riscv64:-"SLLI" "MOV [$]0"
return (x + x) << 63
}
// ------------------ //
// masked shifts //
// ------------------ //
func lshMask64x64(v int64, s uint64) int64 {
// arm64:"LSL" -"AND"
// loong64:"SLLV" -"AND"
// ppc64x:"RLDICL" -"ORN" -"ISEL"
// riscv64:"SLL" -"AND " -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v << (s & 63)
}
func rshMask64Ux64(v uint64, s uint64) uint64 {
// arm64:"LSR" -"AND" -"CSEL"
// loong64:"SRLV" -"AND"
// ppc64x:"RLDICL" -"ORN" -"ISEL"
// riscv64:"SRL " -"AND " -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> (s & 63)
}
func rshMask64x64(v int64, s uint64) int64 {
// arm64:"ASR" -"AND" -"CSEL"
// loong64:"SRAV" -"AND"
// ppc64x:"RLDICL" -"ORN" -"ISEL"
// riscv64:"SRA " -"OR" -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> (s & 63)
}
func lshMask32x64(v int32, s uint64) int32 {
// arm64:"LSL" -"AND"
// loong64:"SLL " "AND" "SGTU" "MASKEQZ"
// ppc64x:"ISEL" -"ORN"
// riscv64:"SLL" -"AND " -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v << (s & 63)
}
func lsh5Mask32x64(v int32, s uint64) int32 {
// loong64:"SLL " -"AND"
return v << (s & 31)
}
func rshMask32Ux64(v uint32, s uint64) uint32 {
// arm64:"LSR" -"AND"
// loong64:"SRL " "AND" "SGTU" "MASKEQZ"
// ppc64x:"ISEL" -"ORN"
// riscv64:"SRLW" "SLTIU" "NEG" "AND " -"SRL "
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> (s & 63)
}
func rsh5Mask32Ux64(v uint32, s uint64) uint32 {
// loong64:"SRL " -"AND"
// riscv64:"SRLW" -"AND " -"SLTIU" -"SRL "
return v >> (s & 31)
}
func rshMask32x64(v int32, s uint64) int32 {
// arm64:"ASR" -"AND"
// loong64:"SRA " "AND" "SGTU" "SUBVU" "OR"
// ppc64x:"ISEL" -"ORN"
// riscv64:"SRAW" "OR" "SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> (s & 63)
}
func rsh5Mask32x64(v int32, s uint64) int32 {
// loong64:"SRA " -"AND"
// riscv64:"SRAW" -"OR" -"SLTIU"
return v >> (s & 31)
}
func lshMask64x32(v int64, s uint32) int64 {
// arm64:"LSL" -"AND"
// loong64:"SLLV" -"AND"
// ppc64x:"RLDICL" -"ORN"
// riscv64:"SLL" -"AND " -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v << (s & 63)
}
func rshMask64Ux32(v uint64, s uint32) uint64 {
// arm64:"LSR" -"AND" -"CSEL"
// loong64:"SRLV" -"AND"
// ppc64x:"RLDICL" -"ORN"
// riscv64:"SRL " -"AND " -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> (s & 63)
}
func rshMask64x32(v int64, s uint32) int64 {
// arm64:"ASR" -"AND" -"CSEL"
// loong64:"SRAV" -"AND"
// ppc64x:"RLDICL" -"ORN" -"ISEL"
// riscv64:"SRA " -"OR" -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> (s & 63)
}
func lshMask64x32Ext(v int64, s int32) int64 {
// ppc64x:"RLDICL" -"ORN" -"ISEL"
// riscv64:"SLL" -"AND " -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v << uint(s&63)
}
func rshMask64Ux32Ext(v uint64, s int32) uint64 {
// ppc64x:"RLDICL" -"ORN" -"ISEL"
// riscv64:"SRL " -"AND " -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> uint(s&63)
}
func rshMask64x32Ext(v int64, s int32) int64 {
// ppc64x:"RLDICL" -"ORN" -"ISEL"
// riscv64:"SRA " -"OR" -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
return v >> uint(s&63)
}
// --------------- //
// signed shifts //
// --------------- //
// We do want to generate a test + panicshift for these cases.
func lshSigned(v8 int8, v16 int16, v32 int32, v64 int64, x int) {
// amd64:"TESTB"
_ = x << v8
// amd64:"TESTW"
_ = x << v16
// amd64:"TESTL"
_ = x << v32
// amd64:"TESTQ"
_ = x << v64
}
// We want to avoid generating a test + panicshift for these cases.
func lshSignedMasked(v8 int8, v16 int16, v32 int32, v64 int64, x int) {
// amd64:-"TESTB"
_ = x << (v8 & 7)
// amd64:-"TESTW"
_ = x << (v16 & 15)
// amd64:-"TESTL"
_ = x << (v32 & 31)
// amd64:-"TESTQ"
_ = x << (v64 & 63)
}
// ------------------ //
// bounded shifts //
// ------------------ //
func lshGuarded64(v int64, s uint) int64 {
if s < 64 {
// riscv64:"SLL" -"AND" -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
// wasm:-"Select" -".*LtU"
// arm64:"LSL" -"CSEL"
return v << s
}
panic("shift too large")
}
func rshGuarded64U(v uint64, s uint) uint64 {
if s < 64 {
// riscv64:"SRL " -"AND" -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
// wasm:-"Select" -".*LtU"
// arm64:"LSR" -"CSEL"
return v >> s
}
panic("shift too large")
}
func rshGuarded64(v int64, s uint) int64 {
if s < 64 {
// riscv64:"SRA " -"OR" -"SLTIU"
// s390x:-"RISBGZ" -"AND" -"LOCGR"
// wasm:-"Select" -".*LtU"
// arm64:"ASR" -"CSEL"
return v >> s
}
panic("shift too large")
}
func provedUnsignedShiftLeft(val64 uint64, val32 uint32, val16 uint16, val8 uint8, shift int) (r1 uint64, r2 uint32, r3 uint16, r4 uint8) {
if shift >= 0 && shift < 64 {
// arm64:"LSL" -"CSEL"
r1 = val64 << shift
}
if shift >= 0 && shift < 32 {
// arm64:"LSL" -"CSEL"
r2 = val32 << shift
}
if shift >= 0 && shift < 16 {
// arm64:"LSL" -"CSEL"
r3 = val16 << shift
}
if shift >= 0 && shift < 8 {
// arm64:"LSL" -"CSEL"
r4 = val8 << shift
}
return r1, r2, r3, r4
}
func provedSignedShiftLeft(val64 int64, val32 int32, val16 int16, val8 int8, shift int) (r1 int64, r2 int32, r3 int16, r4 int8) {
if shift >= 0 && shift < 64 {
// arm64:"LSL" -"CSEL"
r1 = val64 << shift
}
if shift >= 0 && shift < 32 {
// arm64:"LSL" -"CSEL"
r2 = val32 << shift
}
if shift >= 0 && shift < 16 {
// arm64:"LSL" -"CSEL"
r3 = val16 << shift
}
if shift >= 0 && shift < 8 {
// arm64:"LSL" -"CSEL"
r4 = val8 << shift
}
return r1, r2, r3, r4
}
func provedUnsignedShiftRight(val64 uint64, val32 uint32, val16 uint16, val8 uint8, shift int) (r1 uint64, r2 uint32, r3 uint16, r4 uint8) {
if shift >= 0 && shift < 64 {
// arm64:"LSR" -"CSEL"
r1 = val64 >> shift
}
if shift >= 0 && shift < 32 {
// arm64:"LSR" -"CSEL"
r2 = val32 >> shift
}
if shift >= 0 && shift < 16 {
// arm64:"LSR" -"CSEL"
r3 = val16 >> shift
}
if shift >= 0 && shift < 8 {
// arm64:"LSR" -"CSEL"
r4 = val8 >> shift
}
return r1, r2, r3, r4
}
func provedSignedShiftRight(val64 int64, val32 int32, val16 int16, val8 int8, shift int) (r1 int64, r2 int32, r3 int16, r4 int8) {
if shift >= 0 && shift < 64 {
// arm64:"ASR" -"CSEL"
r1 = val64 >> shift
}
if shift >= 0 && shift < 32 {
// arm64:"ASR" -"CSEL"
r2 = val32 >> shift
}
if shift >= 0 && shift < 16 {
// arm64:"ASR" -"CSEL"
r3 = val16 >> shift
}
if shift >= 0 && shift < 8 {
// arm64:"ASR" -"CSEL"
r4 = val8 >> shift
}
return r1, r2, r3, r4
}
func checkUnneededTrunc(tab *[100000]uint32, d uint64, v uint32, h uint16, b byte) (uint32, uint64) {
// ppc64x:-".*RLWINM" -".*RLDICR" ".*CLRLSLDI"
f := tab[byte(v)^b]
// ppc64x:-".*RLWINM" -".*RLDICR" ".*CLRLSLDI"
f += tab[byte(v)&b]
// ppc64x:-".*RLWINM" -".*RLDICR" ".*CLRLSLDI"
f += tab[byte(v)|b]
// ppc64x:-".*RLWINM" -".*RLDICR" ".*CLRLSLDI"
f += tab[uint16(v)&h]
// ppc64x:-".*RLWINM" -".*RLDICR" ".*CLRLSLDI"
f += tab[uint16(v)^h]
// ppc64x:-".*RLWINM" -".*RLDICR" ".*CLRLSLDI"
f += tab[uint16(v)|h]
// ppc64x:-".*AND" -"RLDICR" ".*CLRLSLDI"
f += tab[v&0xff]
// ppc64x:-".*AND" ".*CLRLSLWI"
f += 2 * uint32(uint16(d))
// ppc64x:-".*AND" -"RLDICR" ".*CLRLSLDI"
g := 2 * uint64(uint32(d))
return f, g
}
func checkCombinedShifts(v8 uint8, v16 uint16, v32 uint32, x32 int32, v64 uint64) (uint8, uint16, uint32, uint64, int64) {
// ppc64x:-"AND" "CLRLSLWI"
f := (v8 & 0xF) << 2
// ppc64x:"CLRLSLWI"
f += byte(v16) << 3
// ppc64x:-"AND" "CLRLSLWI"
g := (v16 & 0xFF) << 3
// ppc64x:-"AND" "CLRLSLWI"
h := (v32 & 0xFFFFF) << 2
// ppc64x:"CLRLSLDI"
i := (v64 & 0xFFFFFFFF) << 5
// ppc64x:-"CLRLSLDI"
i += (v64 & 0xFFFFFFF) << 38
// ppc64x/power9:-"CLRLSLDI"
i += (v64 & 0xFFFF00) << 10
// ppc64x/power9:-"SLD" "EXTSWSLI"
j := int64(x32+32) * 8
return f, g, h, i, j
}
func checkWidenAfterShift(v int64, u uint64) (int64, uint64) {
// ppc64x:-".*MOVW"
f := int32(v >> 32)
// ppc64x:".*MOVW"
f += int32(v >> 31)
// ppc64x:-".*MOVH"
g := int16(v >> 48)
// ppc64x:".*MOVH"
g += int16(v >> 30)
// ppc64x:-".*MOVH"
g += int16(f >> 16)
// ppc64x:-".*MOVB"
h := int8(v >> 56)
// ppc64x:".*MOVB"
h += int8(v >> 28)
// ppc64x:-".*MOVB"
h += int8(f >> 24)
// ppc64x:".*MOVB"
h += int8(f >> 16)
return int64(h), uint64(g)
}
func checkShiftAndMask32(v []uint32) {
i := 0
// ppc64x: "RLWNM [$]24, R[0-9]+, [$]12, [$]19, R[0-9]+"
v[i] = (v[i] & 0xFF00000) >> 8
i++
// ppc64x: "RLWNM [$]26, R[0-9]+, [$]22, [$]29, R[0-9]+"
v[i] = (v[i] & 0xFF00) >> 6
i++
// ppc64x: "MOVW R0"
v[i] = (v[i] & 0xFF) >> 8
i++
// ppc64x: "MOVW R0"
v[i] = (v[i] & 0xF000000) >> 28
i++
// ppc64x: "RLWNM [$]26, R[0-9]+, [$]24, [$]31, R[0-9]+"
v[i] = (v[i] >> 6) & 0xFF
i++
// ppc64x: "RLWNM [$]26, R[0-9]+, [$]12, [$]19, R[0-9]+"
v[i] = (v[i] >> 6) & 0xFF000
i++
// ppc64x: "MOVW R0"
v[i] = (v[i] >> 20) & 0xFF000
i++
// ppc64x: "MOVW R0"
v[i] = (v[i] >> 24) & 0xFF00
i++
}
func checkMergedShifts32(a [256]uint32, b [256]uint64, u uint32, v uint32) {
// ppc64x: -"CLRLSLDI", "RLWNM [$]10, R[0-9]+, [$]22, [$]29, R[0-9]+"
a[0] = a[uint8(v>>24)]
// ppc64x: -"CLRLSLDI", "RLWNM [$]11, R[0-9]+, [$]21, [$]28, R[0-9]+"
b[0] = b[uint8(v>>24)]
// ppc64x: -"CLRLSLDI", "RLWNM [$]15, R[0-9]+, [$]21, [$]28, R[0-9]+"
b[1] = b[(v>>20)&0xFF]
// ppc64x: -"SLD", "RLWNM [$]10, R[0-9]+, [$]22, [$]28, R[0-9]+"
b[2] = b[v>>25]
}
func checkMergedShifts64(a [256]uint32, b [256]uint64, c [256]byte, v uint64) {
// ppc64x: -"CLRLSLDI", "RLWNM [$]10, R[0-9]+, [$]22, [$]29, R[0-9]+"
a[0] = a[uint8(v>>24)]
// ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
a[1] = a[uint8(v>>25)]
// ppc64x: -"CLRLSLDI", "RLWNM [$]9, R[0-9]+, [$]23, [$]29, R[0-9]+"
a[2] = a[v>>25&0x7F]
// ppc64x: -"CLRLSLDI", "RLWNM [$]3, R[0-9]+, [$]29, [$]29, R[0-9]+"
a[3] = a[(v>>31)&0x01]
// ppc64x: -"CLRLSLDI", "RLWNM [$]12, R[0-9]+, [$]21, [$]28, R[0-9]+"
b[0] = b[uint8(v>>23)]
// ppc64x: -"CLRLSLDI", "RLWNM [$]15, R[0-9]+, [$]21, [$]28, R[0-9]+"
b[1] = b[(v>>20)&0xFF]
// ppc64x: "RLWNM", -"SLD"
b[2] = b[((uint64((uint32(v) >> 21)) & 0x3f) << 4)]
// ppc64x: -"RLWNM"
b[3] = (b[3] << 24) & 0xFFFFFF000000
// ppc64x: "RLWNM [$]24, R[0-9]+, [$]0, [$]7,"
b[4] = (b[4] << 24) & 0xFF000000
// ppc64x: "RLWNM [$]24, R[0-9]+, [$]0, [$]7,"
b[5] = (b[5] << 24) & 0xFF00000F
// ppc64x: -"RLWNM"
b[6] = (b[6] << 0) & 0xFF00000F
// ppc64x: "RLWNM [$]4, R[0-9]+, [$]28, [$]31,"
b[7] = (b[7] >> 28) & 0xF
// ppc64x: "RLWNM [$]11, R[0-9]+, [$]10, [$]15"
c[0] = c[((v>>5)&0x3F)<<16]
// ppc64x: "ANDCC [$]8064,"
c[1] = c[((v>>7)&0x3F)<<7]
}
func checkShiftMask(a uint32, b uint64, z []uint32, y []uint64) {
_ = y[128]
_ = z[128]
// ppc64x: -"MOVBZ", -"SRW", "RLWNM"
z[0] = uint32(uint8(a >> 5))
// ppc64x: -"MOVBZ", -"SRW", "RLWNM"
z[1] = uint32(uint8((a >> 4) & 0x7e))
// ppc64x: "RLWNM [$]25, R[0-9]+, [$]27, [$]29, R[0-9]+"
z[2] = uint32(uint8(a>>7)) & 0x1c
// ppc64x: -"MOVWZ"
y[0] = uint64((a >> 6) & 0x1c)
// ppc64x: -"MOVWZ"
y[1] = uint64(uint32(b)<<6) + 1
// ppc64x: -"MOVHZ", -"MOVWZ"
y[2] = uint64((uint16(a) >> 9) & 0x1F)
// ppc64x: -"MOVHZ", -"MOVWZ", -"ANDCC"
y[3] = uint64(((uint16(a) & 0xFF0) >> 9) & 0x1F)
}
// 128 bit shifts
func check128bitShifts(x, y uint64, bits uint) (uint64, uint64) {
s := bits & 63
ŝ := (64 - bits) & 63
// check that the shift operation has two commas (three operands)
// amd64:"SHRQ.*,.*,"
shr := x>>s | y<<ŝ
// amd64:"SHLQ.*,.*,"
shl := x<<s | y>>ŝ
return shr, shl
}
func checkShiftToMask(u []uint64, s []int64) {
// amd64:-"SHR" -"SHL" "ANDQ"
u[0] = u[0] >> 5 << 5
// amd64:-"SAR" -"SHL" "ANDQ"
s[0] = s[0] >> 5 << 5
// amd64:-"SHR" -"SHL" "ANDQ"
u[1] = u[1] << 5 >> 5
}
//
// Left shift with addition.
//
func checkLeftShiftWithAddition(a int64, b int64) int64 {
// riscv64/rva20u64: "SLLI" "ADD"
// riscv64/rva22u64,riscv64/rva23u64: "SH1ADD"
a = a + b<<1
// riscv64/rva20u64: "SLLI" "ADD"
// riscv64/rva22u64,riscv64/rva23u64: "SH2ADD"
a = a + b<<2
// riscv64/rva20u64: "SLLI" "ADD"
// riscv64/rva22u64,riscv64/rva23u64: "SH3ADD"
a = a + b<<3
return a
}
//
// Convert and shift.
//
func rsh64Uto32U(v uint64) uint32 {
x := uint32(v)
// riscv64:"MOVWU"
if x > 8 {
// riscv64:"SRLIW" -"MOVWU" -"SLLI"
x >>= 2
}
return x
}
func rsh64Uto16U(v uint64) uint16 {
x := uint16(v)
// riscv64:"MOVHU"
if x > 8 {
// riscv64:"SLLI" "SRLI"
x >>= 2
}
return x
}
func rsh64Uto8U(v uint64) uint8 {
x := uint8(v)
// riscv64:"MOVBU"
if x > 8 {
// riscv64:"SLLI" "SRLI"
x >>= 2
}
return x
}
func rsh64to32(v int64) int32 {
x := int32(v)
// riscv64:"MOVW"
if x > 8 {
// riscv64:"SRLIW" -"MOVW" -"SLLI"
x >>= 2
}
return x
}
func rsh64to16(v int64) int16 {
x := int16(v)
// riscv64:"MOVH"
if x > 8 {
// riscv64:"SLLI" "SRLI"
x >>= 2
}
return x
}
func rsh64to8(v int64) int8 {
x := int8(v)
// riscv64:"MOVB"
if x > 8 {
// riscv64:"SLLI" "SRLI"
x >>= 2
}
return x
}
// We don't need to worry about shifting
// more than the type size.
// (There is still a negative shift test, but
// no shift-too-big test.)
func signedModShift(i int) int64 {
// arm64:-"CMP" -"CSEL"
return 1 << (i % 64)
}

View File

@@ -0,0 +1,17 @@
// asmcheck
// Copyright 2020 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 codegen
func efaceExtract(e interface{}) int {
// This should be compiled with only
// a single conditional jump.
// amd64:-"JMP"
if x, ok := e.(int); ok {
return x
}
return 0
}

106
test/codegen/simd.go Normal file
View File

@@ -0,0 +1,106 @@
// asmcheck
// Copyright 2025 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.
// These tests check code generation of simd peephole optimizations.
//go:build goexperiment.simd && amd64
package codegen
import (
"math"
"simd/archsimd"
)
func vptest1() bool {
v1 := archsimd.LoadUint64x2Slice([]uint64{0, 1})
v2 := archsimd.LoadUint64x2Slice([]uint64{0, 0})
// amd64:`VPTEST\s(.*)(.*)$`
// amd64:`SETCS\s(.*)$`
return v1.AndNot(v2).IsZero()
}
func vptest2() bool {
v1 := archsimd.LoadUint64x2Slice([]uint64{0, 1})
v2 := archsimd.LoadUint64x2Slice([]uint64{0, 0})
// amd64:`VPTEST\s(.*)(.*)$`
// amd64:`SETEQ\s(.*)$`
return v1.And(v2).IsZero()
}
type Args2 struct {
V0 archsimd.Uint8x32
V1 archsimd.Uint8x32
x string
}
//go:noinline
func simdStructNoSpill(a Args2) archsimd.Uint8x32 {
// amd64:-`VMOVDQU\s.*$`
return a.V0.Xor(a.V1)
}
func simdStructWrapperNoSpill(a Args2) archsimd.Uint8x32 {
// amd64:-`VMOVDQU\s.*$`
a.x = "test"
return simdStructNoSpill(a)
}
//go:noinline
func simdArrayNoSpill(a [1]Args2) archsimd.Uint8x32 {
// amd64:-`VMOVDQU\s.*$`
return a[0].V0.Xor(a[0].V1)
}
func simdArrayWrapperNoSpill(a [1]Args2) archsimd.Uint8x32 {
// amd64:-`VMOVDQU\s.*$`
a[0].x = "test"
return simdArrayNoSpill(a)
}
func simdFeatureGuardedMaskOpt() archsimd.Int16x16 {
var x, y archsimd.Int16x16
if archsimd.X86.AVX512() {
mask := archsimd.Mask16x16FromBits(5)
return x.Add(y).Masked(mask) // amd64:`VPADDW.Z\s.*$`
}
mask := archsimd.Mask16x16FromBits(5)
return x.Add(y).Masked(mask) // amd64:`VPAND\s.*$`
}
func simdMaskedMerge() archsimd.Int16x16 {
var x, y archsimd.Int16x16
if archsimd.X86.AVX512() {
mask := archsimd.Mask16x16FromBits(5)
return x.Add(y).Merge(x, mask) // amd64:-`VPBLENDVB\s.*$`
}
mask := archsimd.Mask16x16FromBits(5)
return x.Add(y).Merge(x, mask) // amd64:`VPBLENDVB\s.*$`
}
var nan = math.NaN()
var floats64s = []float64{0, 1, 2, nan, 4, nan, 6, 7, 8, 9, 10, 11, nan, 13, 14, 15}
var sinkInt64s = make([]int64, 100)
func simdIsNaN() {
x := archsimd.LoadFloat64x4Slice(floats64s)
y := archsimd.LoadFloat64x4Slice(floats64s[4:])
a := x.IsNaN()
b := y.IsNaN()
// amd64:"VCMPPD [$]3," -"VPOR"
c := a.Or(b)
c.ToInt64x4().StoreSlice(sinkInt64s)
}
func simdIsNaN512() {
x := archsimd.LoadFloat64x8Slice(floats64s)
y := archsimd.LoadFloat64x8Slice(floats64s[8:])
a := x.IsNaN()
b := y.IsNaN()
// amd64:"VCMPPD [$]3," -"VPOR"
c := a.Or(b)
c.ToInt64x8().StoreSlice(sinkInt64s)
}

470
test/codegen/slices.go Normal file
View File

@@ -0,0 +1,470 @@
// asmcheck
// Copyright 2018 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 codegen
import "unsafe"
// This file contains code generation tests related to the handling of
// slice types.
// ------------------ //
// Clear //
// ------------------ //
// Issue #5373 optimize memset idiom
// Some of the clears get inlined, see #56997
func SliceClear(s []int) []int {
// amd64:`.*memclrNoHeapPointers`
// ppc64x:`.*memclrNoHeapPointers`
for i := range s {
s[i] = 0
}
return s
}
func SliceClearPointers(s []*int) []*int {
// amd64:`.*memclrHasPointers`
// ppc64x:`.*memclrHasPointers`
for i := range s {
s[i] = nil
}
return s
}
// ------------------ //
// Extension //
// ------------------ //
// Issue #21266 - avoid makeslice in append(x, make([]T, y)...)
func SliceExtensionConst(s []int) []int {
// amd64:-`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS X15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, 1<<2)...)
}
func SliceExtensionConstInt64(s []int) []int {
// amd64:-`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS X15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, int64(1<<2))...)
}
func SliceExtensionConstUint64(s []int) []int {
// amd64:-`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS X15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, uint64(1<<2))...)
}
func SliceExtensionConstUint(s []int) []int {
// amd64:-`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS X15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, uint(1<<2))...)
}
// On ppc64x and loong64 continue to use memclrNoHeapPointers
// for sizes >= 512.
func SliceExtensionConst512(s []int) []int {
// amd64:-`.*runtime\.memclrNoHeapPointers`
// loong64:`.*runtime\.memclrNoHeapPointers`
// ppc64x:`.*runtime\.memclrNoHeapPointers`
return append(s, make([]int, 1<<9)...)
}
func SliceExtensionPointer(s []*int, l int) []*int {
// amd64:`.*runtime\.memclrHasPointers`
// amd64:-`.*runtime\.makeslice`
// ppc64x:`.*runtime\.memclrHasPointers`
// ppc64x:-`.*runtime\.makeslice`
return append(s, make([]*int, l)...)
}
func SliceExtensionVar(s []byte, l int) []byte {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// ppc64x:`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
return append(s, make([]byte, l)...)
}
func SliceExtensionVarInt64(s []byte, l int64) []byte {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:`.*runtime\.panicmakeslicelen`
return append(s, make([]byte, l)...)
}
func SliceExtensionVarUint64(s []byte, l uint64) []byte {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:`.*runtime\.panicmakeslicelen`
return append(s, make([]byte, l)...)
}
func SliceExtensionVarUint(s []byte, l uint) []byte {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:`.*runtime\.panicmakeslicelen`
return append(s, make([]byte, l)...)
}
func SliceExtensionInt64(s []int, l64 int64) []int {
// 386:`.*runtime\.makeslice`
// 386:-`.*runtime\.memclr`
return append(s, make([]int, l64)...)
}
// ------------------ //
// Make+Copy //
// ------------------ //
// Issue #26252 - avoid memclr for make+copy
func SliceMakeCopyLen(s []int) []int {
// amd64:`.*runtime\.mallocgc`
// amd64:`.*runtime\.memmove`
// amd64:-`.*runtime\.makeslice`
// ppc64x:`.*runtime\.mallocgc`
// ppc64x:`.*runtime\.memmove`
// ppc64x:-`.*runtime\.makeslice`
a := make([]int, len(s))
copy(a, s)
return a
}
func SliceMakeCopyLenPtr(s []*int) []*int {
// amd64:`.*runtime\.makeslicecopy`
// amd64:-`.*runtime\.makeslice\(`
// amd64:-`.*runtime\.typedslicecopy
// ppc64x:`.*runtime\.makeslicecopy`
// ppc64x:-`.*runtime\.makeslice\(`
// ppc64x:-`.*runtime\.typedslicecopy
a := make([]*int, len(s))
copy(a, s)
return a
}
func SliceMakeCopyConst(s []int) []int {
// amd64:`.*runtime\.makeslicecopy`
// amd64:-`.*runtime\.makeslice\(`
// amd64:-`.*runtime\.memmove`
a := make([]int, 4)
copy(a, s)
return a
}
func SliceMakeCopyConstPtr(s []*int) []*int {
// amd64:`.*runtime\.makeslicecopy`
// amd64:-`.*runtime\.makeslice\(`
// amd64:-`.*runtime\.typedslicecopy
a := make([]*int, 4)
copy(a, s)
return a
}
func SliceMakeCopyNoOptNoDeref(s []*int) []*int {
a := new([]*int)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
*a = make([]*int, 4)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.typedslicecopy`
copy(*a, s)
return *a
}
func SliceMakeCopyNoOptNoVar(s []*int) []*int {
a := make([][]*int, 1)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a[0] = make([]*int, 4)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.typedslicecopy`
copy(a[0], s)
return a[0]
}
func SliceMakeCopyNoOptBlank(s []*int) []*int {
var a []*int
// amd64:-`.*runtime\.makeslicecopy`
_ = make([]*int, 4)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.typedslicecopy`
copy(a, s)
return a
}
func SliceMakeCopyNoOptNoMake(s []*int) []*int {
// amd64:-`.*runtime\.makeslicecopy`
// amd64:-`.*runtime\.objectnew`
a := *new([]*int)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.typedslicecopy`
copy(a, s)
return a
}
func SliceMakeCopyNoOptNoHeapAlloc(s []*int) int {
// amd64:-`.*runtime\.makeslicecopy`
a := make([]*int, 4)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.typedslicecopy`
copy(a, s)
return cap(a)
}
func SliceMakeCopyNoOptNoCap(s []*int) []*int {
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a := make([]*int, 0, 4)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.typedslicecopy`
copy(a, s)
return a
}
func SliceMakeCopyNoOptNoCopy(s []*int) []*int {
copy := func(x, y []*int) {}
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a := make([]*int, 4)
// amd64:-`.*runtime\.makeslicecopy`
copy(a, s)
return a
}
func SliceMakeCopyNoOptWrongOrder(s []*int) []*int {
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a := make([]*int, 4)
// amd64:`.*runtime\.typedslicecopy`
// amd64:-`.*runtime\.makeslicecopy`
copy(s, a)
return a
}
func SliceMakeCopyNoOptWrongAssign(s []*int) []*int {
var a []*int
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
s = make([]*int, 4)
// amd64:`.*runtime\.typedslicecopy`
// amd64:-`.*runtime\.makeslicecopy`
copy(a, s)
return s
}
func SliceMakeCopyNoOptCopyLength(s []*int) (int, []*int) {
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a := make([]*int, 4)
// amd64:`.*runtime\.typedslicecopy`
// amd64:-`.*runtime\.makeslicecopy`
n := copy(a, s)
return n, a
}
func SliceMakeCopyNoOptSelfCopy(s []*int) []*int {
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a := make([]*int, 4)
// amd64:`.*runtime\.typedslicecopy`
// amd64:-`.*runtime\.makeslicecopy`
copy(a, a)
return a
}
func SliceMakeCopyNoOptTargetReference(s []*int) []*int {
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a := make([]*int, 4)
// amd64:`.*runtime\.typedslicecopy`
// amd64:-`.*runtime\.makeslicecopy`
copy(a, s[:len(a)])
return a
}
func SliceMakeCopyNoOptCap(s []int) []int {
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.makeslice\(`
a := make([]int, len(s), 9)
// amd64:-`.*runtime\.makeslicecopy`
// amd64:`.*runtime\.memmove`
copy(a, s)
return a
}
func SliceMakeCopyNoMemmoveDifferentLen(s []int) []int {
// amd64:`.*runtime\.makeslicecopy`
// amd64:-`.*runtime\.memmove`
a := make([]int, len(s)-1)
// amd64:-`.*runtime\.memmove`
copy(a, s)
return a
}
func SliceMakeEmptyPointerToZerobase() []int {
// amd64:`LEAQ.+runtime\.zerobase`
// amd64:-`.*runtime\.makeslice`
return make([]int, 0)
}
// ---------------------- //
// Nil check of &s[0] //
// ---------------------- //
// See issue 30366
func SliceNilCheck(s []int) {
p := &s[0]
// amd64:-`TESTB`
_ = *p
}
// ---------------------- //
// Init slice literal //
// ---------------------- //
// See issue 21561
func InitSmallSliceLiteral() []int {
// amd64:`MOVQ [$]42`
return []int{42}
}
func InitNotSmallSliceLiteral() []int {
// amd64:`LEAQ .*stmp_`
return []int{
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
}
}
// --------------------------------------- //
// Test PPC64 SUBFCconst folding rules //
// triggered by slice operations. //
// --------------------------------------- //
func SliceWithConstCompare(a []int, b int) []int {
var c []int = []int{1, 2, 3, 4, 5}
if b+len(a) < len(c) {
// ppc64x:-"NEG"
return c[b:]
}
return a
}
func SliceWithSubtractBound(a []int, b int) []int {
// ppc64x:"SUBC" -"NEG"
return a[(3 - b):]
}
// --------------------------------------- //
// ARM64 folding for slice masks //
// --------------------------------------- //
func SliceAndIndex(a []int, b int) int {
// arm64:"AND R[0-9]+->63" "ADD R[0-9]+<<3"
return a[b:][b]
}
// --------------------------------------- //
// Code generation for unsafe.Slice //
// --------------------------------------- //
func Slice1(p *byte, i int) []byte {
// amd64:-"MULQ"
return unsafe.Slice(p, i)
}
func Slice0(p *struct{}, i int) []struct{} {
// amd64:-"MULQ"
return unsafe.Slice(p, i)
}
// --------------------------------------- //
// Code generation for slice bounds //
// checking comparison //
// --------------------------------------- //
func SlicePut(a []byte, c uint8) []byte {
// arm64:`CBZ R1`
a[0] = c
// arm64:`CMP \$1, R1`
a = a[1:]
a[0] = c
// arm64:`CMP \$2, R1`
a = a[1:]
a[0] = c
a = a[1:]
return a
}
func Issue61730() {
var x int
// amd64:-"MOVQ .*stmp_"
_ = [...][]*int{
{&x},
nil,
nil,
nil,
nil,
}
}

View File

@@ -0,0 +1,22 @@
// asmcheck
package codegen
// Copyright 2020 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.
func booliface() interface{} {
// amd64:`LEAQ runtime.staticuint64s\+8\(SB\)`
return true
}
func smallint8iface() interface{} {
// amd64:`LEAQ runtime.staticuint64s\+2024\(SB\)`
return int8(-3)
}
func smalluint8iface() interface{} {
// amd64:`LEAQ runtime.staticuint64s\+24\(SB\)`
return uint8(3)
}

39
test/codegen/spectre.go Normal file
View File

@@ -0,0 +1,39 @@
// asmcheck -gcflags=-spectre=index
//go:build amd64
// Copyright 2020 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 codegen
func IndexArray(x *[10]int, i int) int {
// amd64:`CMOVQCC`
return x[i]
}
func IndexString(x string, i int) byte {
// amd64:`CMOVQ(LS|CC)`
return x[i]
}
func IndexSlice(x []float64, i int) float64 {
// amd64:`CMOVQ(LS|CC)`
return x[i]
}
func SliceArray(x *[10]int, i, j int) []int {
// amd64:`CMOVQHI`
return x[i:j]
}
func SliceString(x string, i, j int) string {
// amd64:`CMOVQHI`
return x[i:j]
}
func SliceSlice(x []float64, i, j int) []float64 {
// amd64:`CMOVQHI`
return x[i:j]
}

31
test/codegen/spills.go Normal file
View File

@@ -0,0 +1,31 @@
// asmcheck
// Copyright 2024 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 codegen
func i64(a, b int64) int64 { // arm64:`STP\s`,`LDP\s`
g()
return a + b
}
func i32(a, b int32) int32 { // arm64:`STPW`,`LDPW`
g()
return a + b
}
func f64(a, b float64) float64 { // arm64:`FSTPD`,`FLDPD`
g()
return a + b
}
func f32(a, b float32) float32 { // arm64:`FSTPS`,`FLDPS`
g()
return a + b
}
//go:noinline
func g() {
}

176
test/codegen/stack.go Normal file
View File

@@ -0,0 +1,176 @@
// asmcheck
// Copyright 2018 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 codegen
import (
"runtime"
"unsafe"
)
// This file contains code generation tests related to the use of the
// stack.
// Check that stack stores are optimized away.
// 386:"TEXT .*, [$]0-"
// amd64:"TEXT .*, [$]0-"
// arm:"TEXT .*, [$]-4-"
// arm64:"TEXT .*, [$]0-"
// mips:"TEXT .*, [$]-4-"
// ppc64x:"TEXT .*, [$]0-"
// s390x:"TEXT .*, [$]0-"
func StackStore() int {
var x int
return *(&x)
}
type T struct {
A, B, C, D int // keep exported fields
x, y, z int // reset unexported fields
}
// Check that large structs are cleared directly (issue #24416).
// 386:"TEXT .*, [$]0-"
// amd64:"TEXT .*, [$]0-"
// arm:"TEXT .*, [$]0-" (spills return address)
// arm64:"TEXT .*, [$]0-"
// mips:"TEXT .*, [$]-4-"
// ppc64x:"TEXT .*, [$]0-"
// s390x:"TEXT .*, [$]0-"
func ZeroLargeStruct(x *T) {
t := T{}
*x = t
}
// Check that structs are partially initialised directly (issue #24386).
// Notes:
// - 386 fails due to spilling a register
// amd64:"TEXT .*, [$]0-"
// arm:"TEXT .*, [$]0-" (spills return address)
// arm64:"TEXT .*, [$]0-"
// ppc64x:"TEXT .*, [$]0-"
// s390x:"TEXT .*, [$]0-"
// Note: that 386 currently has to spill a register.
func KeepWanted(t *T) {
*t = T{A: t.A, B: t.B, C: t.C, D: t.D}
}
// Check that small array operations avoid using the stack (issue #15925).
// Notes:
// - 386 fails due to spilling a register
// - arm & mips fail due to softfloat calls
// amd64:"TEXT .*, [$]0-"
// arm64:"TEXT .*, [$]0-"
// ppc64x:"TEXT .*, [$]0-"
// s390x:"TEXT .*, [$]0-"
func ArrayAdd64(a, b [4]float64) [4]float64 {
return [4]float64{a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]}
}
// Check that small array initialization avoids using the stack.
// 386:"TEXT .*, [$]0-"
// amd64:"TEXT .*, [$]0-"
// arm:"TEXT .*, [$]0-" (spills return address)
// arm64:"TEXT .*, [$]0-"
// mips:"TEXT .*, [$]-4-"
// ppc64x:"TEXT .*, [$]0-"
// s390x:"TEXT .*, [$]0-"
func ArrayInit(i, j int) [4]int {
return [4]int{i, 0, j, 0}
}
// Check that assembly output has matching offset and base register
// (issue #21064).
func check_asmout(b [2]int) int {
runtime.GC() // use some frame
// amd64:`.*b\+24\(SP\)`
// arm:`.*b\+4\(FP\)`
return b[1]
}
// Check that simple functions get promoted to nosplit, even when
// they might panic in various ways. See issue 31219.
// amd64:"TEXT .*NOSPLIT.*"
func MightPanic(a []int, i, j, k, s int) {
_ = a[i] // panicIndex
_ = a[i:j] // panicSlice
_ = a[i:j:k] // also panicSlice
_ = i << s // panicShift
_ = i / j // panicDivide
}
// Put a defer in a loop, so second defer is not open-coded
func Defer() {
for i := 0; i < 2; i++ {
defer func() {}()
}
// amd64:`CALL runtime\.deferprocStack`
defer func() {}()
}
// Check that stack slots are shared among values of the same
// type, but not pointer-identical types. See issue 65783.
func spillSlotReuse() {
// The return values of getp1 and getp2 need to be
// spilled around the calls to nopInt. Make sure that
// spill slot gets reused.
//arm64:`.*autotmp_2-8\(SP\)`
getp1()[nopInt()] = 0
//arm64:`.*autotmp_2-8\(SP\)`
getp2()[nopInt()] = 0
}
// Check that no stack frame space is needed for simple slice initialization with underlying structure.
type mySlice struct {
array unsafe.Pointer
len int
cap int
}
// amd64:"TEXT .*, [$]0-"
func sliceInit(base uintptr) []uintptr {
const ptrSize = 8
size := uintptr(4096)
bitmapSize := size / ptrSize / 8
elements := int(bitmapSize / ptrSize)
var sl mySlice
sl = mySlice{
unsafe.Pointer(base + size - bitmapSize),
elements,
elements,
}
// amd64:-"POPQ" -"SP"
return *(*[]uintptr)(unsafe.Pointer(&sl))
}
//go:noinline
func nopInt() int {
return 0
}
//go:noinline
func getp1() *[4]int {
return nil
}
//go:noinline
func getp2() *[4]int {
return nil
}
// Store to an argument without read can be removed.
func storeArg(a [2]int) {
// amd64:-`MOVQ \$123,.*\.a\+\d+\(SP\)`
a[1] = 123
}

113
test/codegen/strings.go Normal file
View File

@@ -0,0 +1,113 @@
// asmcheck
// Copyright 2018 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 codegen
import "strings"
// This file contains code generation tests related to the handling of
// string types.
func CountRunes(s string) int { // Issue #24923
// amd64:`.*countrunes`
return len([]rune(s))
}
func CountBytes(s []byte) int {
// amd64:-`.*runtime.slicebytetostring`
return len(string(s))
}
func ToByteSlice() []byte { // Issue #24698
// amd64:`LEAQ type:\[3\]uint8`
// amd64:`CALL runtime\.(newobject|mallocgcTinySize3)`
// amd64:-`.*runtime.stringtoslicebyte`
return []byte("foo")
}
func ConvertToByteSlice(a, b, c string) []byte {
// amd64:`.*runtime.concatbyte3`
return []byte(a + b + c)
}
// Loading from read-only symbols should get transformed into constants.
func ConstantLoad() {
// 12592 = 0x3130
// 50 = 0x32
// amd64:`MOVW \$12592, \(`,`MOVB \$50, 2\(`
// 386:`MOVW \$12592, \(`,`MOVB \$50, 2\(`
// arm:`MOVW \$48`,`MOVW \$49`,`MOVW \$50`
// arm64:`MOVD \$12592`,`MOVD \$50`
// loong64:`MOVV \$12592`,`MOVV \$50`
// wasm:`I64Const \$12592`,`I64Store16 \$0`,`I64Const \$50`,`I64Store8 \$2`
// mips64:`MOVV \$48`,`MOVV \$49`,`MOVV \$50`
bsink = []byte("012")
// 858927408 = 0x33323130
// 13620 = 0x3534
// amd64:`MOVL \$858927408`,`MOVW \$13620, 4\(`
// 386:`MOVL \$858927408`,`MOVW \$13620, 4\(`
// arm64:`MOVD \$858927408`,`MOVD \$13620`
// loong64:`MOVV \$858927408`,`MOVV \$13620`
// wasm:`I64Const \$858927408`,`I64Store32 \$0`,`I64Const \$13620`,`I64Store16 \$4`
bsink = []byte("012345")
// 3978425819141910832 = 0x3736353433323130
// 7306073769690871863 = 0x6564636261393837
// amd64:`MOVQ \$3978425819141910832`,`MOVQ \$7306073769690871863`
// 386:`MOVL \$858927408, \(`,`DUFFCOPY`
// arm64:`MOVD \$3978425819141910832`,`MOVD \$7306073769690871863`,`MOVD \$15`
// loong64:`MOVV \$3978425819141910832`,`MOVV \$7306073769690871863`,`MOVV \$15`
// wasm:`I64Const \$3978425819141910832`,`I64Store \$0`,`I64Const \$7306073769690871863`,`I64Store \$7`
bsink = []byte("0123456789abcde")
// 56 = 0x38
// amd64:`MOVQ \$3978425819141910832`,`MOVB \$56`
// loong64:`MOVV \$3978425819141910832`,`MOVV \$56`
bsink = []byte("012345678")
// 14648 = 0x3938
// amd64:`MOVQ \$3978425819141910832`,`MOVW \$14648`
// loong64:`MOVV \$3978425819141910832`,`MOVV \$14648`
bsink = []byte("0123456789")
// 1650538808 = 0x62613938
// amd64:`MOVQ \$3978425819141910832`,`MOVL \$1650538808`
// loong64:`MOVV \$3978425819141910832`,`MOVV \$1650538808`
bsink = []byte("0123456789ab")
}
// self-equality is always true. See issue 60777.
func EqualSelf(s string) bool {
// amd64:`MOVL \$1, AX`,-`.*memequal.*`
return s == s
}
func NotEqualSelf(s string) bool {
// amd64:`XORL AX, AX`,-`.*memequal.*`
return s != s
}
var bsink []byte
func HasPrefix3(s string) bool {
// amd64:-`.*memequal.*`
return strings.HasPrefix(s, "str")
}
func HasPrefix5(s string) bool {
// amd64:-`.*memequal.*`
return strings.HasPrefix(s, "strin")
}
func HasPrefix6(s string) bool {
// amd64:-`.*memequal.*`
return strings.HasPrefix(s, "string")
}
func HasPrefix7(s string) bool {
// amd64:-`.*memequal.*`
return strings.HasPrefix(s, "strings")
}

48
test/codegen/structs.go Normal file
View File

@@ -0,0 +1,48 @@
// asmcheck
//go:build !goexperiment.cgocheck2
// Copyright 2018 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 codegen
// This file contains code generation tests related to the handling of
// struct types.
// ------------- //
// Zeroing //
// ------------- //
type Z1 struct {
a, b, c int
}
func Zero1(t *Z1) { // Issue #18370
// amd64:`MOVUPS X[0-9]+, \(.*\)`,`MOVQ \$0, 16\(.*\)`
*t = Z1{}
}
type Z2 struct {
a, b, c *int
}
func Zero2(t *Z2) {
// amd64:`MOVUPS X[0-9]+, \(.*\)`,`MOVQ \$0, 16\(.*\)`
// amd64:`.*runtime[.]gcWriteBarrier.*\(SB\)`
*t = Z2{}
}
// ------------------ //
// Initializing //
// ------------------ //
type I1 struct {
a, b, c, d int
}
func Init1(p *I1) { // Issue #18872
// amd64:`MOVQ [$]1`,`MOVQ [$]2`,`MOVQ [$]3`,`MOVQ [$]4`
*p = I1{1, 2, 3, 4}
}

201
test/codegen/switch.go Normal file
View File

@@ -0,0 +1,201 @@
// asmcheck
// Copyright 2019 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.
// These tests check code generation of switch statements.
package codegen
// see issue 33934
func f(x string) int {
// amd64:-`cmpstring`
switch x {
case "":
return -1
case "1", "2", "3":
return -2
default:
return -3
}
}
// use jump tables for 8+ int cases
func square(x int) int {
// amd64:`JMP\s\(.*\)\(.*\)$`
// arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$`
// loong64: `ALSLV`,`MOVV`,`JMP`
switch x {
case 1:
return 1
case 2:
return 4
case 3:
return 9
case 4:
return 16
case 5:
return 25
case 6:
return 36
case 7:
return 49
case 8:
return 64
default:
return x * x
}
}
// use jump tables for 8+ string lengths
func length(x string) int {
// amd64:`JMP\s\(.*\)\(.*\)$`
// arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$`
// loong64:`ALSLV`,`MOVV`,`JMP`
switch x {
case "a":
return 1
case "bb":
return 2
case "ccc":
return 3
case "dddd":
return 4
case "eeeee":
return 5
case "ffffff":
return 6
case "ggggggg":
return 7
case "hhhhhhhh":
return 8
default:
return len(x)
}
}
// Use single-byte ordered comparisons for binary searching strings.
// See issue 53333.
func mimetype(ext string) string {
// amd64: `CMPB\s1\(.*\), \$104$`,-`cmpstring`
// arm64: `MOVB\s1\(R.*\), R.*$`, `CMPW\s\$104, R.*$`, -`cmpstring`
switch ext {
// amd64: `CMPL\s\(.*\), \$1836345390$`
// arm64: `MOVD\s\$1836345390`, `CMPW\sR.*, R.*$`
case ".htm":
return "A"
// amd64: `CMPL\s\(.*\), \$1953457454$`
// arm64: `MOVD\s\$1953457454`, `CMPW\sR.*, R.*$`
case ".eot":
return "B"
// amd64: `CMPL\s\(.*\), \$1735815982$`
// arm64: `MOVD\s\$1735815982`, `CMPW\sR.*, R.*$`
case ".svg":
return "C"
// amd64: `CMPL\s\(.*\), \$1718907950$`
// arm64: `MOVD\s\$1718907950`, `CMPW\sR.*, R.*$`
case ".ttf":
return "D"
default:
return ""
}
}
// use jump tables for type switches to concrete types.
func typeSwitch(x any) int {
// amd64:`JMP\s\(.*\)\(.*\)$`
// arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$`
switch x.(type) {
case int:
return 0
case int8:
return 1
case int16:
return 2
case int32:
return 3
case int64:
return 4
}
return 7
}
type I interface {
foo()
}
type J interface {
bar()
}
type IJ interface {
I
J
}
type K interface {
baz()
}
// use a runtime call for type switches to interface types.
func interfaceSwitch(x any) int {
// amd64:`CALL runtime.interfaceSwitch`,`MOVL 16\(AX\)`,`MOVQ 8\(.*\)(.*\*8)`
// arm64:`CALL runtime.interfaceSwitch`,`LDAR`,`MOVWU 16\(R0\)`,`MOVD \(R.*\)\(R.*\)`
switch x.(type) {
case I:
return 1
case J:
return 2
default:
return 3
}
}
func interfaceSwitch2(x K) int {
// amd64:`CALL runtime.interfaceSwitch`,`MOVL 16\(AX\)`,`MOVQ 8\(.*\)(.*\*8)`
// arm64:`CALL runtime.interfaceSwitch`,`LDAR`,`MOVWU 16\(R0\)`,`MOVD \(R.*\)\(R.*\)`
switch x.(type) {
case I:
return 1
case J:
return 2
default:
return 3
}
}
func interfaceCast(x any) int {
// amd64:`CALL runtime.typeAssert`,`MOVL 16\(AX\)`,`MOVQ 8\(.*\)(.*\*1)`
// arm64:`CALL runtime.typeAssert`,`LDAR`,`MOVWU 16\(R0\)`,`MOVD \(R.*\)\(R.*\)`
if _, ok := x.(I); ok {
return 3
}
return 5
}
func interfaceCast2(x K) int {
// amd64:`CALL runtime.typeAssert`,`MOVL 16\(AX\)`,`MOVQ 8\(.*\)(.*\*1)`
// arm64:`CALL runtime.typeAssert`,`LDAR`,`MOVWU 16\(R0\)`,`MOVD \(R.*\)\(R.*\)`
if _, ok := x.(I); ok {
return 3
}
return 5
}
func interfaceConv(x IJ) I {
// amd64:`CALL runtime.typeAssert`,`MOVL 16\(AX\)`,`MOVQ 8\(.*\)(.*\*1)`
// arm64:`CALL runtime.typeAssert`,`LDAR`,`MOVWU 16\(R0\)`,`MOVD \(R.*\)\(R.*\)`
return x
}
// Make sure we can constant fold after inlining. See issue 71699.
func stringSwitchInlineable(s string) {
switch s {
case "foo", "bar", "baz", "goo":
default:
println("no")
}
}
func stringSwitch() {
// amd64:-"CMP" -"CALL"
// arm64:-"CMP" -"CALL"
stringSwitchInlineable("foo")
}

View File

@@ -0,0 +1,67 @@
// asmcheck
// Copyright 2024 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 codegen
type Ix interface {
X()
}
type Iy interface {
Y()
}
type Iz interface {
Z()
}
func swXYZ(a Ix) {
switch t := a.(type) {
case Iy: // amd64:-".*typeAssert"
t.Y()
case Iz: // amd64:-".*typeAssert"
t.Z()
}
}
type Ig[T any] interface {
G() T
}
func swGYZ[T any](a Ig[T]) {
switch t := a.(type) {
case Iy: // amd64:-".*typeAssert"
t.Y()
case Iz: // amd64:-".*typeAssert"
t.Z()
case interface{ G() T }: // amd64:-".*typeAssert" -".*assertE2I\\(" ".*assertE2I2"
t.G()
}
}
func swE2G[T any](a any) {
switch t := a.(type) {
case Iy:
t.Y()
case Ig[T]: // amd64:-".*assertE2I\\(" ".*assertE2I2"
t.G()
}
}
func swI2G[T any](a Ix) {
switch t := a.(type) {
case Iy:
t.Y()
case Ig[T]: // amd64:-".*assertE2I\\(" ".*assertE2I2"
t.G()
}
}
func swCaller() {
swGYZ[int]((Ig[int])(nil))
swE2G[int]((Ig[int])(nil))
swI2G[int]((Ix)(nil))
}

24
test/codegen/unique.go Normal file
View File

@@ -0,0 +1,24 @@
// asmcheck
// Copyright 2015 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 codegen
import "unique"
func BytesToHandle(b []byte) unique.Handle[string] {
// amd64:-`.*runtime\.slicebytetostring\(`
return unique.Make(string(b))
}
type Pair struct {
S1 string
S2 string
}
func BytesPairToHandle(b1, b2 []byte) unique.Handle[Pair] {
// TODO: should not copy b1 and b2.
return unique.Make(Pair{string(b1), string(b2)})
}

16
test/codegen/unsafe.go Normal file
View File

@@ -0,0 +1,16 @@
// asmcheck
// Copyright 2024 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 codegen
import "unsafe"
func f(p unsafe.Pointer, x, y uintptr) int64 {
p = unsafe.Pointer(uintptr(p) + x + y)
// amd64:`MOVQ\s\(.*\)\(.*\*1\), `
// arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\), `
return *(*int64)(p)
}

View File

@@ -0,0 +1,103 @@
// asmcheck
// Copyright 2023 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 codegen
func combine2string(p *[2]string, a, b string) {
// amd64:`.*runtime[.]gcWriteBarrier4\(SB\)`
// arm64:`.*runtime[.]gcWriteBarrier4\(SB\)`
p[0] = a
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[1] = b
}
func combine4string(p *[4]string, a, b, c, d string) {
// amd64:`.*runtime[.]gcWriteBarrier8\(SB\)`
// arm64:`.*runtime[.]gcWriteBarrier8\(SB\)`
p[0] = a
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[1] = b
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[2] = c
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[3] = d
}
func combine2slice(p *[2][]byte, a, b []byte) {
// amd64:`.*runtime[.]gcWriteBarrier4\(SB\)`
// arm64:`.*runtime[.]gcWriteBarrier4\(SB\)`
p[0] = a
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[1] = b
}
func combine4slice(p *[4][]byte, a, b, c, d []byte) {
// amd64:`.*runtime[.]gcWriteBarrier8\(SB\)`
// arm64:`.*runtime[.]gcWriteBarrier8\(SB\)`
p[0] = a
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[1] = b
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[2] = c
// amd64:-`.*runtime[.]gcWriteBarrier`
// arm64:-`.*runtime[.]gcWriteBarrier`
p[3] = d
}
func trickyWriteNil(p *int, q **int) {
if p == nil {
// We change "= p" to "= 0" in the prove pass, which
// means we have one less pointer that needs to go
// into the write barrier buffer.
// amd64:`.*runtime[.]gcWriteBarrier1`
*q = p
}
}
type S struct {
a, b string
c *int
}
var g1, g2 *int
func issue71228(dst *S, ptr *int) {
// Make sure that the non-write-barrier write.
// "sp.c = ptr" happens before the large write
// barrier "*dst = *sp". We approximate testing
// that by ensuring that two global variable write
// barriers aren't combined.
_ = *dst
var s S
sp := &s
//amd64:`.*runtime[.]gcWriteBarrier1`
g1 = nil
sp.c = ptr // outside of any write barrier
//amd64:`.*runtime[.]gcWriteBarrier1`
g2 = nil
//amd64:`.*runtime[.]wbMove`
*dst = *sp
}
func writeDouble(p *[2]*int, x, y *int) {
// arm64: `LDP\s`, `STP\s\(R[0-9]+, R[0-9]+\), \(`,
p[0] = x
// arm64: `STP\s\(R[0-9]+, R[0-9]+\), 16\(`,
p[1] = y
}
func writeDoubleNil(p *[2]*int) {
// arm64: `LDP\s`, `STP\s\(R[0-9]+, R[0-9]+\),`, `STP\s\(ZR, ZR\),`
p[0] = nil
p[1] = nil
}

44
test/codegen/zerosize.go Normal file
View File

@@ -0,0 +1,44 @@
// asmcheck
// Copyright 2018 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.
// Make sure a pointer variable and a zero-sized variable
// aren't allocated to the same stack slot.
// See issue 24993.
package codegen
func zeroSize() {
c := make(chan struct{})
// amd64:`MOVQ \$0, command-line-arguments\.s\+56\(SP\)`
var s *int
// force s to be a stack object, also use some (fixed) stack space
g(&s, 1, 2, 3, 4, 5)
// amd64:`LEAQ command-line-arguments\..*\+55\(SP\)`
c <- noliteral(struct{}{})
}
// Like zeroSize, but without hiding the zero-sized struct.
func zeroSize2() {
c := make(chan struct{})
// amd64:`MOVQ \$0, command-line-arguments\.s\+48\(SP\)`
var s *int
// force s to be a stack object, also use some (fixed) stack space
g(&s, 1, 2, 3, 4, 5)
// amd64:`LEAQ command-line-arguments\..*stmp_\d+\(SB\)`
c <- struct{}{}
}
//go:noinline
func g(**int, int, int, int, int, int) {}
// noliteral prevents the compiler from recognizing a literal value.
//
//go:noinline
func noliteral[T any](t T) T {
return t
}