Update to go1.25.0

This commit is contained in:
Vorapol Rinsatitnon
2025-08-13 21:50:03 +07:00
parent 4fdddd398d
commit c7759f4edb
2303 changed files with 161699 additions and 70952 deletions

View File

@@ -85,56 +85,92 @@ func SubMem(arr []int, b, c, d int) int {
func SubFromConst(a int) int {
// ppc64x: `SUBC\tR[0-9]+,\s[$]40,\sR`
// riscv64: "ADDI\t\\$-40","NEG"
b := 40 - a
return b
}
func SubFromConstNeg(a int) int {
// arm64: "ADD\t\\$40"
// loong64: "ADDV[U]\t\\$40"
// mips: "ADD[U]\t\\$40"
// mips64: "ADDV[U]\t\\$40"
// ppc64x: `ADD\t[$]40,\sR[0-9]+,\sR`
// riscv64: "ADDI\t\\$40",-"NEG"
c := 40 - (-a)
return c
}
func SubSubFromConst(a int) int {
// arm64: "ADD\t\\$20"
// loong64: "ADDV[U]\t\\$20"
// mips: "ADD[U]\t\\$20"
// mips64: "ADDV[U]\t\\$20"
// ppc64x: `ADD\t[$]20,\sR[0-9]+,\sR`
// riscv64: "ADDI\t\\$20",-"NEG"
c := 40 - (20 - a)
return c
}
func AddSubFromConst(a int) int {
// ppc64x: `SUBC\tR[0-9]+,\s[$]60,\sR`
// riscv64: "ADDI\t\\$-60","NEG"
c := 40 + (20 - a)
return c
}
func NegSubFromConst(a int) int {
// arm64: "SUB\t\\$20"
// loong64: "ADDV[U]\t\\$-20"
// mips: "ADD[U]\t\\$-20"
// mips64: "ADDV[U]\t\\$-20"
// ppc64x: `ADD\t[$]-20,\sR[0-9]+,\sR`
// riscv64: "ADDI\t\\$-20"
c := -(20 - a)
return c
}
func NegAddFromConstNeg(a int) int {
// arm64: "SUB\t\\$40","NEG"
// loong64: "ADDV[U]\t\\$-40","SUBV"
// mips: "ADD[U]\t\\$-40","SUB"
// mips64: "ADDV[U]\t\\$-40","SUBV"
// ppc64x: `SUBC\tR[0-9]+,\s[$]40,\sR`
// riscv64: "ADDI\t\\$-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"
r := (a + b) - (a + c)
// amd64:-"ADDQ"
r1 := (a + b) - (c + a)
@@ -143,6 +179,10 @@ func SubAddSimplify2(a, b, c int) (int, int, int, int, int, int) {
// amd64:-"ADDQ"
r3 := (b + a) - (c + a)
// amd64:-"SUBQ"
// arm64:-"SUB"
// mips:"ADD",-"SUB"
// mips64:"ADDV",-"SUBV"
// loong64:"ADDV",-"SUBV"
r4 := (a - c) + (c + b)
// amd64:-"SUBQ"
r5 := (a - c) + (b + c)
@@ -151,18 +191,34 @@ func SubAddSimplify2(a, b, c int) (int, int, int, int, int, int) {
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 //
// -------------------- //
@@ -185,6 +241,15 @@ func Pow2Muls(n1, n2 int) (int, int) {
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\t[$]5`,`LEAQ\t\(.*\)\(.*\*2\),`,-`IMULQ`
// 386:`SHLL\t[$]5`,`LEAL\t\(.*\)\(.*\*2\),`,-`IMULL`
@@ -624,7 +689,7 @@ func constantFold2(i0, j0, i1, j1 int) (int, int) {
}
func constantFold3(i, j int) int {
// arm64: "MOVD\t[$]30","MUL",-"ADD",-"LSL"
// arm64: "LSL\t[$]5,","SUB\tR[0-9]+<<1,",-"ADD"
// ppc64x:"MULLD\t[$]30","MULLD"
r := (5 * i) * (6 * j)
return r
@@ -638,7 +703,7 @@ func Int64Min(a, b int64) int64 {
// amd64: "CMPQ","CMOVQLT"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLT\t"
// riscv64/rva22u64:"MIN\t"
// riscv64/rva22u64,riscv64/rva23u64:"MIN\t"
return min(a, b)
}
@@ -646,7 +711,7 @@ func Int64Max(a, b int64) int64 {
// amd64: "CMPQ","CMOVQGT"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLT\t"
// riscv64/rva22u64:"MAX\t"
// riscv64/rva22u64,riscv64/rva23u64:"MAX\t"
return max(a, b)
}
@@ -654,7 +719,7 @@ func Uint64Min(a, b uint64) uint64 {
// amd64: "CMPQ","CMOVQCS"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLTU"
// riscv64/rva22u64:"MINU"
// riscv64/rva22u64,riscv64/rva23u64:"MINU"
return min(a, b)
}
@@ -662,6 +727,6 @@ func Uint64Max(a, b uint64) uint64 {
// amd64: "CMPQ","CMOVQHI"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLTU"
// riscv64/rva22u64:"MAXU"
// riscv64/rva22u64,riscv64/rva23u64:"MAXU"
return max(a, b)
}

View File

@@ -120,6 +120,16 @@ func bitoff64(a, b uint64) (n uint64) {
return n
}
func clearLastBit(x int64, y int32) (int64, int32) {
// amd64:"ANDQ\t[$]-2"
a := (x >> 1) << 1
// amd64:"ANDL\t[$]-2"
b := (y >> 1) << 1
return a, b
}
func bitcompl64(a, b uint64) (n uint64) {
// amd64:"BTCQ"
n += b ^ (1 << (a & 63))
@@ -322,9 +332,24 @@ func op_eon(x, y, z uint32, a []uint32, n, m uint64) uint64 {
func op_orn(x, y uint32) uint32 {
// arm64:`ORN\t`,-`ORR`
// loong64:"ORN"\t,-"OR\t"
return x | ^y
}
func op_nor(x int64, a []int64) {
// loong64: "MOVV\t[$]0","NOR\tR"
a[0] = ^(0x1234 | x)
// loong64:"NOR",-"XOR"
a[1] = (-1) ^ x
// loong64: "MOVV\t[$]-55",-"OR",-"NOR"
a[2] = ^(0x12 | 0x34)
}
func op_andn(x, y uint32) uint32 {
// loong64:"ANDN\t",-"AND\t"
return x &^ y
}
// check bitsets
func bitSetPowerOf2Test(x int) bool {
// amd64:"BTL\t[$]3"

View File

@@ -47,6 +47,7 @@ func convertNeqBool32(x uint32) bool {
func convertEqBool32(x uint32) bool {
// ppc64x:"RLDICL",-"CMPW","XOR",-"ISEL"
// amd64:"ANDL","XORL",-"BTL",-"SETCC"
return x&1 == 0
}
@@ -57,9 +58,34 @@ func convertNeqBool64(x uint64) bool {
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\tCR0EQ",-"ISEL"
// ppc64x/power9:"CMP","ISEL",-"SETBC\tCR0EQ"

View File

@@ -241,4 +241,14 @@ func ui64x0(x chan uint64) {
for <-x < 1 {
dummy()
}
// riscv64:"BNEZ"
for 0 < <-x {
dummy()
}
// riscv64:"BEQZ"
for 0 >= <-x {
dummy()
}
}

View File

@@ -730,6 +730,54 @@ func cmpToCmnLessThan(a, b, c, d int) int {
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\tPL`,-`CMP`

View File

@@ -74,6 +74,7 @@ func FusedAdd32(x, y, z float32) float32 {
// arm64:"FMADDS"
// loong64:"FMADDF\t"
// riscv64:"FMADDS\t"
// amd64/v3:"VFMADD231SS\t"
return x*y + z
}
@@ -98,6 +99,7 @@ func FusedAdd64(x, y, z float64) float64 {
// arm64:"FMADDD"
// loong64:"FMADDD\t"
// riscv64:"FMADDD\t"
// amd64/v3:"VFMADD231SD\t"
return x*y + z
}
@@ -147,20 +149,14 @@ func CmpWithAdd(a float64, b float64) bool {
// Non-floats //
// ---------------- //
// We should make sure that the compiler doesn't generate floating point
// instructions for non-float operations on Plan 9, because floating point
// operations are not allowed in the note handler.
func ArrayZero() [16]byte {
// amd64:"MOVUPS"
// plan9/amd64/:-"MOVUPS"
var a [16]byte
return a
}
func ArrayCopy(a [16]byte) (b [16]byte) {
// amd64:"MOVUPS"
// plan9/amd64/:-"MOVUPS"
b = a
return
}

View File

@@ -25,3 +25,39 @@ func ConvToM(x any) I {
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
return x.(I)
}
func e1(x any, y *int) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET\tEQ`
return x == y
}
func e2(x any, y *int) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET\tEQ`
return y == x
}
type E *int
func e3(x any, y E) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET\tEQ`
return x == y
}
type T int
func (t *T) M() {}
func i1(x I, y *T) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET\tEQ`
return x == y
}
func i2(x I, y *T) bool {
// amd64:-`.*faceeq`,`SETEQ`
// arm64:-`.*faceeq`,`CSET\tEQ`
return y == x
}

View File

@@ -12,6 +12,7 @@ package codegen
type T struct {
a *[10]int
b [10]int
s []int
}
func (t *T) f() {
@@ -38,4 +39,15 @@ func (t *T) f() {
for i := range *t.a {
(*t.a)[i] = 0
}
// amd64:-".*runtime.memclrNoHeapPointers"
// amd64:"DUFFZERO"
for i := range t.b {
t.b[i] = 0
}
// amd64:".*runtime.memclrNoHeapPointers"
for i := range t.s {
t.s[i] = 0
}
}

View File

@@ -8,7 +8,7 @@
// is constant. We check this by making sure that the constant length
// is folded into a load offset.
package p
package codegen
func f(x []int) int {
s := make([]int, 3)

View File

@@ -4,7 +4,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
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++ {

View File

@@ -4,7 +4,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
package codegen
var x = func() int {
n := 0

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,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\ttype:.*Impl`
a.(A).A()
}

View File

@@ -66,6 +66,28 @@ func LookupStringConversionKeyedArrayLit(m map[[2]string]int, bytes []byte) int
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 //
// ------------------- //

View File

@@ -240,10 +240,11 @@ func nanGenerate64() float64 {
// amd64:"DIVSD"
z0 := zero / zero
// amd64:"MULSD"
// amd64/v1,amd64/v2:"MULSD"
z1 := zero * inf
// amd64:"SQRTSD"
z2 := math.Sqrt(negone)
// amd64/v3:"VFMADD231SD"
return z0 + z1 + z2
}
@@ -254,7 +255,8 @@ func nanGenerate32() float32 {
// amd64:"DIVSS"
z0 := zero / zero
// amd64:"MULSS"
// amd64/v1,amd64/v2:"MULSS"
z1 := zero * inf
// amd64/v3:"VFMADD231SS"
return z0 + z1
}

View File

@@ -6,7 +6,10 @@
package codegen
import "math/bits"
import (
"math/bits"
"unsafe"
)
// ----------------------- //
// bits.LeadingZeros //
@@ -15,60 +18,70 @@ import "math/bits"
func LeadingZeros(n uint) int {
// amd64/v1,amd64/v2:"BSRQ"
// amd64/v3:"LZCNTQ", -"BSRQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZV",-"SUB"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t",-"SUB"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.LeadingZeros(n)
}
func LeadingZeros64(n uint64) int {
// amd64/v1,amd64/v2:"BSRQ"
// amd64/v3:"LZCNTQ", -"BSRQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm:"CLZ"
// arm64:"CLZ"
// loong64:"CLZV",-"SUB"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t",-"ADDI"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.LeadingZeros64(n)
}
func LeadingZeros32(n uint32) int {
// amd64/v1,amd64/v2:"BSRQ","LEAQ",-"CMOVQEQ"
// amd64/v3: "LZCNTL",- "BSRL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZW"
// arm:"CLZ"
// arm64:"CLZW"
// loong64:"CLZW",-"SUB"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZW"
// riscv64/rva22u64,riscv64/rva23u64:"CLZW",-"ADDI"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.LeadingZeros32(n)
}
func LeadingZeros16(n uint16) int {
// amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ"
// amd64/v3: "LZCNTL",- "BSRL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t","ADDI\t\\$-48",-"NEG"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.LeadingZeros16(n)
}
func LeadingZeros8(n uint8) int {
// amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ"
// amd64/v3: "LZCNTL",- "BSRL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t","ADDI\t\\$-56",-"NEG"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.LeadingZeros8(n)
}
@@ -79,30 +92,35 @@ func LeadingZeros8(n uint8) int {
func Len(n uint) int {
// amd64/v1,amd64/v2:"BSRQ"
// amd64/v3: "LZCNTQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t","ADDI\t\\$-64"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.Len(n)
}
func Len64(n uint64) int {
// amd64/v1,amd64/v2:"BSRQ"
// amd64/v3: "LZCNTQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t","ADDI\t\\$-64"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.Len64(n)
}
func SubFromLen64(n uint64) int {
// loong64:"CLZV",-"ADD"
// ppc64x:"CNTLZD",-"SUBC"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t",-"ADDI",-"NEG"
return 64 - bits.Len64(n)
}
@@ -114,36 +132,42 @@ func CompareWithLen64(n uint64) bool {
func Len32(n uint32) int {
// amd64/v1,amd64/v2:"BSRQ","LEAQ",-"CMOVQEQ"
// amd64/v3: "LZCNTL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZW"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x: "CNTLZW"
// riscv64/rva22u64,riscv64/rva23u64:"CLZW","ADDI\t\\$-32"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.Len32(n)
}
func Len16(n uint16) int {
// amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ"
// amd64/v3: "LZCNTL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t","ADDI\t\\$-64"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.Len16(n)
}
func Len8(n uint8) int {
// amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ"
// amd64/v3: "LZCNTL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// arm64:"CLZ"
// arm:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
// riscv64/rva22u64,riscv64/rva23u64:"CLZ\t","ADDI\t\\$-64"
// s390x:"FLOGR"
// wasm:"I64Clz"
return bits.Len8(n)
}
@@ -157,8 +181,9 @@ func OnesCount(n uint) int {
// amd64:"POPCNTQ"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTV"
// s390x:"POPCNT"
// ppc64x:"POPCNTD"
// riscv64:"CPOP\t"
// s390x:"POPCNT"
// wasm:"I64Popcnt"
return bits.OnesCount(n)
}
@@ -168,8 +193,9 @@ func OnesCount64(n uint64) int {
// amd64:"POPCNTQ"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTV"
// s390x:"POPCNT"
// ppc64x:"POPCNTD"
// riscv64:"CPOP\t"
// s390x:"POPCNT"
// wasm:"I64Popcnt"
return bits.OnesCount64(n)
}
@@ -179,8 +205,9 @@ func OnesCount32(n uint32) int {
// amd64:"POPCNTL"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTW"
// s390x:"POPCNT"
// ppc64x:"POPCNTW"
// riscv64:"CPOPW"
// s390x:"POPCNT"
// wasm:"I64Popcnt"
return bits.OnesCount32(n)
}
@@ -190,15 +217,17 @@ func OnesCount16(n uint16) int {
// amd64:"POPCNTL"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTH"
// s390x:"POPCNT"
// ppc64x:"POPCNTW"
// riscv64:"CPOP\t"
// s390x:"POPCNT"
// wasm:"I64Popcnt"
return bits.OnesCount16(n)
}
func OnesCount8(n uint8) int {
// s390x:"POPCNT"
// ppc64x:"POPCNTB"
// riscv64/rva22u64,riscv64/rva23u64:"CPOP\t"
// s390x:"POPCNT"
// wasm:"I64Popcnt"
return bits.OnesCount8(n)
}
@@ -237,42 +266,46 @@ func Reverse8(n uint8) uint8 {
// ----------------------- //
func ReverseBytes(n uint) uint {
// amd64:"BSWAPQ"
// 386:"BSWAPL"
// s390x:"MOVDBR"
// amd64:"BSWAPQ"
// arm64:"REV"
// loong64:"REVBV"
// riscv64/rva22u64,riscv64/rva23u64:"REV8"
// s390x:"MOVDBR"
return bits.ReverseBytes(n)
}
func ReverseBytes64(n uint64) uint64 {
// amd64:"BSWAPQ"
// 386:"BSWAPL"
// s390x:"MOVDBR"
// amd64:"BSWAPQ"
// arm64:"REV"
// ppc64x/power10: "BRD"
// loong64:"REVBV"
// ppc64x/power10: "BRD"
// riscv64/rva22u64,riscv64/rva23u64:"REV8"
// s390x:"MOVDBR"
return bits.ReverseBytes64(n)
}
func ReverseBytes32(n uint32) uint32 {
// amd64:"BSWAPL"
// 386:"BSWAPL"
// s390x:"MOVWBR"
// amd64:"BSWAPL"
// arm64:"REVW"
// loong64:"REVB2W"
// ppc64x/power10: "BRW"
// riscv64/rva22u64,riscv64/rva23u64:"REV8","SRLI\t\\$32"
// s390x:"MOVWBR"
return bits.ReverseBytes32(n)
}
func ReverseBytes16(n uint16) uint16 {
// amd64:"ROLW"
// arm64:"REV16W",-"UBFX",-"ORR"
// arm/5:"SLL","SRL","ORR"
// arm/6:"REV16"
// arm/7:"REV16"
// arm64:"REV16W",-"UBFX",-"ORR"
// loong64:"REVB2H"
// ppc64x/power10: "BRH"
// riscv64/rva22u64,riscv64/rva23u64:"REV8","SRLI\t\\$48"
return bits.ReverseBytes16(n)
}
@@ -356,28 +389,30 @@ func RotateLeftVariable32(n uint32, m int) uint32 {
// ------------------------ //
func TrailingZeros(n uint) int {
// 386:"BSFL"
// amd64/v1,amd64/v2:"BSFQ","MOVL\t\\$64","CMOVQEQ"
// amd64/v3:"TZCNTQ"
// 386:"BSFL"
// arm:"CLZ"
// arm64:"RBIT","CLZ"
// loong64:"CTZV"
// s390x:"FLOGR"
// ppc64x/power8:"ANDN","POPCNTD"
// ppc64x/power9: "CNTTZD"
// riscv64/rva22u64,riscv64/rva23u64: "CTZ\t"
// s390x:"FLOGR"
// wasm:"I64Ctz"
return bits.TrailingZeros(n)
}
func TrailingZeros64(n uint64) int {
// 386:"BSFL","JNE"
// amd64/v1,amd64/v2:"BSFQ","MOVL\t\\$64","CMOVQEQ"
// amd64/v3:"TZCNTQ"
// 386:"BSFL"
// arm64:"RBIT","CLZ"
// loong64:"CTZV"
// s390x:"FLOGR"
// ppc64x/power8:"ANDN","POPCNTD"
// ppc64x/power9: "CNTTZD"
// riscv64/rva22u64,riscv64/rva23u64: "CTZ\t"
// s390x:"FLOGR"
// wasm:"I64Ctz"
return bits.TrailingZeros64(n)
}
@@ -389,38 +424,43 @@ func TrailingZeros64Subtract(n uint64) int {
}
func TrailingZeros32(n uint32) int {
// 386:"BSFL"
// amd64/v1,amd64/v2:"BTSQ\\t\\$32","BSFQ"
// amd64/v3:"TZCNTL"
// 386:"BSFL"
// arm:"CLZ"
// arm64:"RBITW","CLZW"
// loong64:"CTZW"
// s390x:"FLOGR","MOVWZ"
// ppc64x/power8:"ANDN","POPCNTW"
// ppc64x/power9: "CNTTZW"
// riscv64/rva22u64,riscv64/rva23u64: "CTZW"
// s390x:"FLOGR","MOVWZ"
// wasm:"I64Ctz"
return bits.TrailingZeros32(n)
}
func TrailingZeros16(n uint16) int {
// amd64:"BSFL","ORL\\t\\$65536"
// 386:"BSFL\t"
// amd64:"BSFL","ORL\\t\\$65536"
// arm:"ORR\t\\$65536","CLZ",-"MOVHU\tR"
// arm64:"ORR\t\\$65536","RBITW","CLZW",-"MOVHU\tR",-"RBIT\t",-"CLZ\t"
// loong64:"CTZV"
// s390x:"FLOGR","OR\t\\$65536"
// ppc64x/power8:"POPCNTD","ORIS\\t\\$1"
// ppc64x/power8:"POPCNTW","ADD\t\\$-1"
// ppc64x/power9:"CNTTZD","ORIS\\t\\$1"
// riscv64/rva22u64,riscv64/rva23u64: "ORI\t\\$65536","CTZW"
// s390x:"FLOGR","OR\t\\$65536"
// wasm:"I64Ctz"
return bits.TrailingZeros16(n)
}
func TrailingZeros8(n uint8) int {
// amd64:"BSFL","ORL\\t\\$256"
// 386:"BSFL"
// amd64:"BSFL","ORL\\t\\$256"
// arm:"ORR\t\\$256","CLZ",-"MOVBU\tR"
// arm64:"ORR\t\\$256","RBITW","CLZW",-"MOVBU\tR",-"RBIT\t",-"CLZ\t"
// loong64:"CTZV"
// ppc64x/power8:"POPCNTB","ADD\t\\$-1"
// ppc64x/power9:"CNTTZD","OR\t\\$256"
// riscv64/rva22u64,riscv64/rva23u64: "ORI\t\\$256","CTZW"
// s390x:"FLOGR","OR\t\\$256"
// wasm:"I64Ctz"
return bits.TrailingZeros8(n)
@@ -444,6 +484,7 @@ func IterateBits64(n uint64) int {
for n != 0 {
// amd64/v1,amd64/v2:"BSFQ",-"CMOVEQ"
// amd64/v3:"TZCNTQ"
// riscv64/rva22u64,riscv64/rva23u64: "CTZ\t"
i += bits.TrailingZeros64(n)
n &= n - 1
}
@@ -455,6 +496,7 @@ func IterateBits32(n uint32) int {
for n != 0 {
// amd64/v1,amd64/v2:"BSFL",-"BTSQ"
// amd64/v3:"TZCNTL"
// riscv64/rva22u64,riscv64/rva23u64: "CTZ\t"
i += bits.TrailingZeros32(n)
n &= n - 1
}
@@ -467,6 +509,7 @@ func IterateBits16(n uint16) int {
// amd64/v1,amd64/v2:"BSFL",-"BTSL"
// amd64/v3:"TZCNTL"
// arm64:"RBITW","CLZW",-"ORR"
// riscv64/rva22u64,riscv64/rva23u64: "CTZ\t",-"ORR"
i += bits.TrailingZeros16(n)
n &= n - 1
}
@@ -479,6 +522,7 @@ func IterateBits8(n uint8) int {
// amd64/v1,amd64/v2:"BSFL",-"BTSL"
// amd64/v3:"TZCNTL"
// arm64:"RBITW","CLZW",-"ORR"
// riscv64/rva22u64,riscv64/rva23u64: "CTZ\t",-"ORR"
i += bits.TrailingZeros8(n)
n &= n - 1
}
@@ -925,6 +969,17 @@ func Mul64LoOnly(x, y uint64) uint64 {
return lo
}
func Mul64Const() (uint64, uint64) {
// 7133701809754865664 == 99<<56
// arm64:"MOVD\t[$]7133701809754865664, R1", "MOVD\t[$]88, R0"
return bits.Mul64(99+88<<8, 1<<56)
}
func MulUintOverflow(p *uint64) []uint64 {
// arm64:"CMP\t[$]72"
return unsafe.Slice(p, 9)
}
// --------------- //
// bits.Div* //
// --------------- //

View File

@@ -19,7 +19,7 @@ func load_le64(b []byte) uint64 {
// amd64:`MOVQ\s\(.*\),`,-`MOV[BWL]\t[^$]`,-`OR`
// s390x:`MOVDBR\s\(.*\),`
// arm64:`MOVD\s\(R[0-9]+\),`,-`MOV[BHW]`
// loong64:`MOVBU\s\(R[0-9]+\),`
// loong64:`MOVV\s\(R[0-9]+\),`
// ppc64le:`MOVD\s`,-`MOV[BHW]Z`
// ppc64:`MOVDBR\s`,-`MOV[BHW]Z`
return binary.LittleEndian.Uint64(b)
@@ -29,7 +29,7 @@ func load_le64_idx(b []byte, idx int) uint64 {
// amd64:`MOVQ\s\(.*\)\(.*\*1\),`,-`MOV[BWL]\t[^$]`,-`OR`
// s390x:`MOVDBR\s\(.*\)\(.*\*1\),`
// arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BHW]`
// loong64:`MOVBU\s\(R[0-9]+\)\(R[0-9]+\),`
// loong64:`MOVV\s\(R[0-9]+\)\(R[0-9]+\),`
// ppc64le:`MOVD\s`,-`MOV[BHW]Z\s`
// ppc64:`MOVDBR\s`,-`MOV[BHW]Z\s`
return binary.LittleEndian.Uint64(b[idx:])
@@ -40,7 +40,7 @@ func load_le32(b []byte) uint32 {
// 386:`MOVL\s\(.*\),`,-`MOV[BW]`,-`OR`
// s390x:`MOVWBR\s\(.*\),`
// arm64:`MOVWU\s\(R[0-9]+\),`,-`MOV[BH]`
// loong64:`MOVBU\s\(R[0-9]+\),`
// loong64:`MOVWU\s\(R[0-9]+\),`
// ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s`
// ppc64:`MOVWBR\s`,-`MOV[BH]Z\s`
return binary.LittleEndian.Uint32(b)
@@ -51,7 +51,7 @@ func load_le32_idx(b []byte, idx int) uint32 {
// 386:`MOVL\s\(.*\)\(.*\*1\),`,-`MOV[BW]`,-`OR`
// s390x:`MOVWBR\s\(.*\)\(.*\*1\),`
// arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BH]`
// loong64:`MOVBU\s\(R[0-9]+\)\(R[0-9]+\),`
// loong64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`
// ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s`
// ppc64:`MOVWBR\s`,-`MOV[BH]Z\s'
return binary.LittleEndian.Uint32(b[idx:])
@@ -61,7 +61,7 @@ func load_le16(b []byte) uint16 {
// amd64:`MOVWLZX\s\(.*\),`,-`MOVB`,-`OR`
// ppc64le:`MOVHZ\s`,-`MOVBZ`
// arm64:`MOVHU\s\(R[0-9]+\),`,-`MOVB`
// loong64:`MOVBU\s\(R[0-9]+\),`
// loong64:`MOVHU\s\(R[0-9]+\),`
// s390x:`MOVHBR\s\(.*\),`
// ppc64:`MOVHBR\s`,-`MOVBZ`
return binary.LittleEndian.Uint16(b)
@@ -72,7 +72,7 @@ func load_le16_idx(b []byte, idx int) uint16 {
// ppc64le:`MOVHZ\s`,-`MOVBZ`
// ppc64:`MOVHBR\s`,-`MOVBZ`
// arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB`
// loong64:`MOVBU\s\(R[0-9]+\)\(R[0-9]+\),`
// loong64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`
// s390x:`MOVHBR\s\(.*\)\(.*\*1\),`
return binary.LittleEndian.Uint16(b[idx:])
}
@@ -396,6 +396,15 @@ func load_op_no_merge(p, q *int) {
}
}
func load_op_in_loop(a []int) int {
r := 0
for _, x := range a {
// amd64:`ADDQ\t\([A-Z]+\)\([A-Z]+\*8\), [A-Z]+`
r += x
}
return r
}
// Make sure offsets are folded into loads and stores.
func offsets_fold(_, a [20]byte) (b [20]byte) {
// arm64:`MOVD\tcommand-line-arguments\.a\+[0-9]+\(FP\), R[0-9]+`,`MOVD\tR[0-9]+, command-line-arguments\.b\+[0-9]+\(FP\)`
@@ -899,9 +908,11 @@ func store32le(p *struct{ a, b uint32 }, x uint64) {
p.b = uint32(x >> 32)
}
func store32be(p *struct{ a, b uint32 }, x uint64) {
// arm64:"STPW"
// ppc64:"MOVD",-"MOVW",-"SRD"
// s390x:"MOVD",-"MOVW",-"SRD"
p.a = uint32(x >> 32)
// arm64:-"STPW"
// ppc64:-"MOVW",-"SRD"
// s390x:-"MOVW",-"SRD"
p.b = uint32(x)
@@ -970,3 +981,109 @@ func issue70300Reverse(v uint64) (b [8]byte) {
b[0] = byte(v)
return b
}
// --------------------------------- //
// Arm64 double-register loads //
// --------------------------------- //
func dwloadI64(p *struct{ a, b int64 }) int64 {
// arm64:"LDP\t"
return p.a + p.b
}
func dwloadI32(p *struct{ a, b int32 }) int32 {
// arm64:"LDPSW\t"
return p.a + p.b
}
func dwloadU32(p *struct{ a, b uint32 }) uint32 {
// arm64:"LDPW\t"
return p.a + p.b
}
func dwloadF64(p *struct{ a, b float64 }) float64 {
// arm64:"FLDPD\t"
return p.a + p.b
}
func dwloadF32(p *struct{ a, b float32 }) float32 {
// arm64:"FLDPS\t"
return p.a + p.b
}
func dwloadBig(p *struct{ a, b, c, d, e, f int64 }) int64 {
// arm64:"LDP\t\\(", "LDP\t16", "LDP\t32"
return p.c + p.f + p.a + p.e + p.d + p.b
}
func dwloadArg(a [2]int64) int64 {
// arm64:"LDP\t"
return a[0] + a[1]
}
func dwloadResult1(p *string) string {
// arm64:"LDP\t\\(R0\\), \\(R0, R1\\)"
return *p
}
func dwloadResult2(p *[2]int64) (int64, int64) {
// arm64:"LDP\t\\(R0\\), \\(R1, R0\\)"
return p[1], p[0]
}
// ---------------------------------- //
// Arm64 double-register stores //
// ---------------------------------- //
func dwstoreI64(p *struct{ a, b int64 }, x, y int64) {
// arm64:"STP\t"
p.a = x
p.b = y
}
func dwstoreI32(p *struct{ a, b int32 }, x, y int32) {
// arm64:"STPW\t"
p.a = x
p.b = y
}
func dwstoreF64(p *struct{ a, b float64 }, x, y float64) {
// arm64:"FSTPD\t"
p.a = x
p.b = y
}
func dwstoreF32(p *struct{ a, b float32 }, x, y float32) {
// arm64:"FSTPS\t"
p.a = x
p.b = y
}
func dwstoreBig(p *struct{ a, b, c, d, e, f int64 }, a, b, c, d, e, f int64) {
// This is not perfect. We merge b+a, then d+e, then c and f have no pair.
p.c = c
p.f = f
// arm64:`STP\s\(R[0-9]+, R[0-9]+\), \(R[0-9]+\)`
p.a = a
// arm64:`STP\s\(R[0-9]+, R[0-9]+\), 24\(R[0-9]+\)`
p.e = e
p.d = d
p.b = b
}
func dwstoreRet() [2]int {
// arm64:"STP\t"
return [2]int{5, 6}
}
func dwstoreLocal(i int) int64 {
var a [2]int64
a[0] = 5
// arm64:"STP\t"
a[1] = 6
return a[i]
}
func dwstoreOrder(p *struct {
a, b int64
c, d, e, f bool
}, a, b int64) {
// arm64:"STP\t"
p.a = a
p.c = true
p.e = true
p.b = b
}

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

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

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\t R"
// amd64:-"LEAQ","INCQ"
for i := range n {
r += i
}
return r
}

View File

@@ -11,95 +11,141 @@ package codegen
// ------------------ //
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\t",-"AND",-"SLTIU"
return v >> uint64(33)
}
func rshConst64Ux64Overflow32(v uint32) uint64 {
// loong64:"MOVV\t\\$0,",-"SRL\t"
// riscv64:"MOV\t\\$0,",-"SRL"
return uint64(v) >> 32
}
func rshConst64Ux64Overflow16(v uint16) uint64 {
// loong64:"MOVV\t\\$0,",-"SRLV"
// riscv64:"MOV\t\\$0,",-"SRL"
return uint64(v) >> 16
}
func rshConst64Ux64Overflow8(v uint8) uint64 {
// loong64:"MOVV\t\\$0,",-"SRLV"
// riscv64:"MOV\t\\$0,",-"SRL"
return uint64(v) >> 8
}
func rshConst64x64(v int64) int64 {
// loong64:"SRAV"
// ppc64x:"SRAD"
// riscv64:"SRAI\t",-"OR",-"SLTIU"
return v >> uint64(33)
}
func rshConst64x64Overflow32(v int32) int64 {
// loong64:"SRA\t\\$31"
// riscv64:"SRAIW",-"SLLI",-"SRAI\t"
return int64(v) >> 32
}
func rshConst64x64Overflow16(v int16) int64 {
// loong64:"SLLV\t\\$48","SRAV\t\\$63"
// riscv64:"SLLI","SRAI",-"SRAIW"
return int64(v) >> 16
}
func rshConst64x64Overflow8(v int8) int64 {
// loong64:"SLLV\t\\$56","SRAV\t\\$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\t"
// ppc64x:"SLW"
// riscv64:"SLLI",-"AND",-"SLTIU", -"MOVW"
return v << uint64(29)
}
func rshConst32Ux64(v uint32) uint32 {
// loong64:"SRL\t"
// ppc64x:"SRW"
// riscv64:"SRLIW",-"AND",-"SLTIU", -"MOVW"
return v >> uint64(29)
}
func rshConst32x64(v int32) int32 {
// loong64:"SRA\t"
// 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\t",-"AND",-"SLTIU"
return v >> uint32(33)
}
func rshConst64x32(v int64) int64 {
// loong64:"SRAV"
// ppc64x:"SRAD"
// riscv64:"SRAI\t",-"OR",-"SLTIU"
return v >> uint32(33)
}
func lshConst32x1Add(x int32) int32 {
// amd64:"SHLL\t[$]2"
return (x + x) << 1
}
func lshConst64x1Add(x int64) int64 {
// amd64:"SHLQ\t[$]2"
return (x + x) << 1
}
func lshConst32x2Add(x int32) int32 {
// amd64:"SHLL\t[$]3"
return (x + x) << 2
}
func lshConst64x2Add(x int64) int64 {
// amd64:"SHLQ\t[$]3"
return (x + x) << 2
}
// ------------------ //
// masked shifts //
// ------------------ //
func lshMask64x64(v int64, s uint64) int64 {
// arm64:"LSL",-"AND"
// loong64:"SLLV",-"AND"
// ppc64x:"RLDICL",-"ORN",-"ISEL"
// riscv64:"SLL",-"AND\t",-"SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -108,6 +154,7 @@ func lshMask64x64(v int64, s uint64) int64 {
func rshMask64Ux64(v uint64, s uint64) uint64 {
// arm64:"LSR",-"AND",-"CSEL"
// loong64:"SRLV",-"AND"
// ppc64x:"RLDICL",-"ORN",-"ISEL"
// riscv64:"SRL\t",-"AND\t",-"SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -116,6 +163,7 @@ func rshMask64Ux64(v uint64, s uint64) uint64 {
func rshMask64x64(v int64, s uint64) int64 {
// arm64:"ASR",-"AND",-"CSEL"
// loong64:"SRAV",-"AND"
// ppc64x:"RLDICL",-"ORN",-"ISEL"
// riscv64:"SRA\t",-"OR",-"SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -124,14 +172,21 @@ func rshMask64x64(v int64, s uint64) int64 {
func lshMask32x64(v int32, s uint64) int32 {
// arm64:"LSL",-"AND"
// loong64:"SLL\t","AND","SGTU","MASKEQZ"
// ppc64x:"ISEL",-"ORN"
// riscv64:"SLL",-"AND\t",-"SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
return v << (s & 63)
}
func lsh5Mask32x64(v int32, s uint64) int32 {
// loong64:"SLL\t",-"AND"
return v << (s & 31)
}
func rshMask32Ux64(v uint32, s uint64) uint32 {
// arm64:"LSR",-"AND"
// loong64:"SRL\t","AND","SGTU","MASKEQZ"
// ppc64x:"ISEL",-"ORN"
// riscv64:"SRLW","SLTIU","NEG","AND\t",-"SRL\t"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -139,12 +194,14 @@ func rshMask32Ux64(v uint32, s uint64) uint32 {
}
func rsh5Mask32Ux64(v uint32, s uint64) uint32 {
// loong64:"SRL\t",-"AND"
// riscv64:"SRLW",-"AND\t",-"SLTIU",-"SRL\t"
return v >> (s & 31)
}
func rshMask32x64(v int32, s uint64) int32 {
// arm64:"ASR",-"AND"
// loong64:"SRA\t","AND","SGTU","SUBVU","OR"
// ppc64x:"ISEL",-"ORN"
// riscv64:"SRAW","OR","SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -152,12 +209,14 @@ func rshMask32x64(v int32, s uint64) int32 {
}
func rsh5Mask32x64(v int32, s uint64) int32 {
// loong64:"SRA\t",-"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\t",-"SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -166,6 +225,7 @@ func lshMask64x32(v int64, s uint32) int64 {
func rshMask64Ux32(v uint64, s uint32) uint64 {
// arm64:"LSR",-"AND",-"CSEL"
// loong64:"SRLV",-"AND"
// ppc64x:"RLDICL",-"ORN"
// riscv64:"SRL\t",-"AND\t",-"SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -174,6 +234,7 @@ func rshMask64Ux32(v uint64, s uint32) uint64 {
func rshMask64x32(v int64, s uint32) int64 {
// arm64:"ASR",-"AND",-"CSEL"
// loong64:"SRAV",-"AND"
// ppc64x:"RLDICL",-"ORN",-"ISEL"
// riscv64:"SRA\t",-"OR",-"SLTIU"
// s390x:-"RISBGZ",-"AND",-"LOCGR"
@@ -531,13 +592,86 @@ func checkShiftToMask(u []uint64, s []int64) {
func checkLeftShiftWithAddition(a int64, b int64) int64 {
// riscv64/rva20u64: "SLLI","ADD"
// riscv64/rva22u64: "SH1ADD"
// riscv64/rva22u64,riscv64/rva23u64: "SH1ADD"
a = a + b<<1
// riscv64/rva20u64: "SLLI","ADD"
// riscv64/rva22u64: "SH2ADD"
// riscv64/rva22u64,riscv64/rva23u64: "SH2ADD"
a = a + b<<2
// riscv64/rva20u64: "SLLI","ADD"
// riscv64/rva22u64: "SH3ADD"
// 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:"SRAIW",-"MOVW",-"SLLI"
x >>= 2
}
return x
}
func rsh64to16(v int64) int16 {
x := int16(v)
// riscv64:"MOVH"
if x > 8 {
// riscv64:"SLLI","SRAI"
x >>= 2
}
return x
}
func rsh64to8(v int64) int8 {
x := int8(v)
// riscv64:"MOVB"
if x > 8 {
// riscv64:"SLLI","SRAI"
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

@@ -6,7 +6,10 @@
package codegen
import "runtime"
import (
"runtime"
"unsafe"
)
// This file contains code generation tests related to the use of the
// stack.
@@ -128,6 +131,29 @@ func spillSlotReuse() {
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\t.*, [$]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

View File

@@ -183,3 +183,17 @@ func interfaceConv(x IJ) I {
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(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")
}

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)})
}

View File

@@ -88,3 +88,16 @@ func issue71228(dst *S, ptr *int) {
//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
}

View File

@@ -18,8 +18,27 @@ func zeroSize() {
g(&s, 1, 2, 3, 4, 5)
// amd64:`LEAQ\tcommand-line-arguments\..*\+55\(SP\)`
c <- noliteral(struct{}{})
}
// Like zeroSize, but without hiding the zero-sized struct.
func zeroSize2() {
c := make(chan struct{})
// amd64:`MOVQ\t\$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\tcommand-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
}

View File

@@ -18,9 +18,9 @@ func fp() *[3]int
var mp map[int]*[3]int
var (
_ = [3]int{1, 2, 3}[:] // ERROR "slice of unaddressable value"
_ = m[0][:] // ERROR "slice of unaddressable value"
_ = f()[:] // ERROR "slice of unaddressable value"
_ = [3]int{1, 2, 3}[:] // ERROR "cannot slice unaddressable value"
_ = m[0][:] // ERROR "cannot slice unaddressable value"
_ = f()[:] // ERROR "cannot slice unaddressable value"
_ = 301[:] // ERROR "cannot slice|attempt to slice object that is not"
_ = 3.1[:] // ERROR "cannot slice|attempt to slice object that is not"

View File

@@ -20,8 +20,8 @@ func main() {
_ = copy(si, "hi") // ERROR "have different element types(.*int.*string| int and byte)"
_ = copy(si, sf) // ERROR "have different element types.*int.*float64"
_ = copy(1, 2) // ERROR "must be slices; have int, int|expects slice arguments"
_ = copy(1, si) // ERROR "first argument to copy should be|expects slice arguments"
_ = copy(si, 2) // ERROR "second argument to copy should be|expects slice arguments"
_ = copy(1, 2) // ERROR "must be slices; have int, int|argument must be a slice; have 1"
_ = copy(1, si) // ERROR "first argument to copy should be|argument must be a slice; have 1"
_ = copy(si, 2) // ERROR "second argument to copy should be|argument must be a slice; have 2"
}

View File

@@ -494,13 +494,13 @@ func foo70(mv1 *MV, m M) { // ERROR "leaking param: m$" "leaking param: mv1$"
func foo71(x *int) []*int { // ERROR "leaking param: x$"
var y []*int
y = append(y, x)
y = append(y, x) // ERROR "append escapes to heap"
return y
}
func foo71a(x int) []*int { // ERROR "moved to heap: x$"
var y []*int
y = append(y, &x)
y = append(y, &x) // ERROR "append escapes to heap"
return y
}
@@ -860,12 +860,12 @@ func foo104(x []*int) { // ERROR "leaking param content: x"
// does not leak x but does leak content
func foo105(x []*int) { // ERROR "leaking param content: x"
_ = append(y, x...)
_ = append(y, x...) // ERROR "append does not escape"
}
// does leak x
func foo106(x *int) { // ERROR "leaking param: x$"
_ = append(y, x)
_ = append(y, x) // ERROR "append does not escape"
}
func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$"

View File

@@ -494,13 +494,13 @@ func foo70(mv1 *MV, m M) { // ERROR "leaking param: m$" "leaking param: mv1$"
func foo71(x *int) []*int { // ERROR "leaking param: x$"
var y []*int
y = append(y, x)
y = append(y, x) // ERROR "append escapes to heap"
return y
}
func foo71a(x int) []*int { // ERROR "moved to heap: x$"
var y []*int
y = append(y, &x)
y = append(y, &x) // ERROR "append escapes to heap"
return y
}
@@ -860,12 +860,12 @@ func foo104(x []*int) { // ERROR "leaking param content: x"
// does not leak x but does leak content
func foo105(x []*int) { // ERROR "leaking param content: x"
_ = append(y, x...)
_ = append(y, x...) // ERROR "append does not escape"
}
// does leak x
func foo106(x *int) { // ERROR "leaking param: x$"
_ = append(y, x)
_ = append(y, x) // ERROR "append does not escape"
}
func foo107(x *int) map[*int]*int { // ERROR "leaking param: x$"

View File

@@ -252,7 +252,7 @@ func f29000(_ int, x interface{}) { // ERROR "leaking param: x"
func g29000() {
x := 1
f29000(2, x) // ERROR "x escapes to heap"
f29000(2, x) // ERROR "1 escapes to heap"
}
// Issue 28369: taking an address of a parameter and converting it into a uintptr causes an

59
test/escape6.go Normal file
View File

@@ -0,0 +1,59 @@
// errorcheck -0 -m -l
// 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.
// Tests for escaping variable-sized allocations.
// In particular, we need to make sure things assigned into
// variable-sized allocations escape even when the variable-sized
// allocations themselves don't escape.
package foo
type T string
func f1(n int, v T) { // ERROR "leaking param: v"
s := make([]T, n) // ERROR "make\(\[\]T, n\) does not escape"
s[0] = v
g(s)
}
func f2(n int, v T) { // ERROR "leaking param: v"
s := make([]T, n) // ERROR "make\(\[\]T, n\) does not escape"
p := &s[0]
*p = v
g(s)
}
func f3(n int, v T) { // ERROR "leaking param: v"
s := make([]T, n) // ERROR "make\(\[\]T, n\) does not escape"
t := (*[4]T)(s)
t[0] = v
g(s)
}
// TODO: imprecise: this does not need to leak v.
func f4(v T) { // ERROR "leaking param: v"
s := make([]T, 4) // ERROR "make\(\[\]T, 4\) does not escape"
s[0] = v
g(s)
}
// TODO: imprecise: this does not need to leak v.
func f5(v T) { // ERROR "leaking param: v"
var b [4]T
s := b[:]
s[0] = v
g(s)
}
func f6(v T) { // ERROR "v does not escape"
var b [4]T
s := b[:]
b[0] = v
g(s)
}
func g(s []T) { // ERROR "s does not escape"
}

View File

@@ -123,7 +123,7 @@ func doesMakeSlice(x *string, y *string) { // ERROR "leaking param: x" "leaking
func nonconstArray() {
n := 32
s1 := make([]int, n) // ERROR "make\(\[\]int, n\) escapes to heap"
s2 := make([]int, 0, n) // ERROR "make\(\[\]int, 0, n\) escapes to heap"
s1 := make([]int, n) // ERROR "make\(\[\]int, 32\) does not escape"
s2 := make([]int, 0, n) // ERROR "make\(\[\]int, 0, 32\) does not escape"
_, _ = s1, s2
}

View File

@@ -48,7 +48,7 @@ func prototype(xyz []string) {} // ERROR "xyz does not escape"
func bar() {
var got [][]string
f := prototype
f = func(ss []string) { got = append(got, ss) } // ERROR "leaking param: ss" "func literal does not escape"
f = func(ss []string) { got = append(got, ss) } // ERROR "leaking param: ss" "func literal does not escape" "append escapes to heap"
s := "string"
f([]string{s}) // ERROR "\[\]string{...} escapes to heap"
}

View File

@@ -228,8 +228,8 @@ func dotTypeEscape2() { // #13805, #15796
j := 0
var v int
var ok bool
var x interface{} = i // ERROR "i does not escape"
var y interface{} = j // ERROR "j does not escape"
var x interface{} = i // ERROR "0 does not escape"
var y interface{} = j // ERROR "0 does not escape"
*(&v) = x.(int)
*(&v), *(&ok) = y.(int)
@@ -238,8 +238,8 @@ func dotTypeEscape2() { // #13805, #15796
i := 0
j := 0
var ok bool
var x interface{} = i // ERROR "i does not escape"
var y interface{} = j // ERROR "j does not escape"
var x interface{} = i // ERROR "0 does not escape"
var y interface{} = j // ERROR "0 does not escape"
sink = x.(int) // ERROR "x.\(int\) escapes to heap"
sink, *(&ok) = y.(int) // ERROR "autotmp_.* escapes to heap"

297
test/escape_iface_data.go Normal file
View File

@@ -0,0 +1,297 @@
// errorcheck -0 -d=escapedebug=1
// 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.
// Test the data word used for interface conversions
// that might otherwise allocate.
package dataword
var sink interface{}
func string1() {
sink = "abc" // ERROR "using global for interface value"
}
func string2() {
v := "abc"
sink = v // ERROR "using global for interface value"
}
func string3() {
sink = "" // ERROR "using global for interface value"
}
func string4() {
v := ""
sink = v // ERROR "using global for interface value"
}
func string5() {
var a any = "abc" // ERROR "using global for interface value"
_ = a
}
func string6() {
var a any
v := "abc"
a = v // ERROR "using global for interface value"
_ = a
}
// string7 can be inlined.
func string7(v string) {
sink = v
}
func string8() {
v0 := "abc"
v := v0
string7(v) // ERROR "using global for interface value"
}
func string9() {
v0 := "abc"
v := v0
f := func() {
string7(v)
}
f() // ERROR "using global for interface value"
}
func string10() {
v0 := "abc"
v := v0
f := func() {
f2 := func() {
string7(v)
}
f2()
}
f() // ERROR "using global for interface value"
}
func string11() {
v0 := "abc"
v := v0
defer func() {
string7(v) // ERROR "using global for interface value"
}()
}
func integer1() {
sink = 42 // ERROR "using global for interface value"
}
func integer2() {
v := 42
sink = v // ERROR "using global for interface value"
}
func integer3() {
sink = 0 // ERROR "using global for interface value"
}
func integer4a() {
v := 0
sink = v // ERROR "using global for interface value"
}
func integer4b() {
v := uint8(0)
sink = v // ERROR "using global for single-byte interface value"
}
func integer5() {
var a any = 42 // ERROR "using global for interface value"
_ = a
}
func integer6() {
var a any
v := 42
a = v // ERROR "using global for interface value"
_ = a
}
func integer7(v int) {
sink = v
}
type M interface{ M() }
type MyInt int
func (m MyInt) M() {}
func escapes(m M) {
sink = m
}
func named1a() {
sink = MyInt(42) // ERROR "using global for interface value"
}
func named1b() {
escapes(MyInt(42)) // ERROR "using global for interface value"
}
func named2a() {
v := MyInt(0)
sink = v // ERROR "using global for interface value"
}
func named2b() {
v := MyInt(42)
escapes(v) // ERROR "using global for interface value"
}
func named2c() {
v := 42
sink = MyInt(v) // ERROR "using global for interface value"
}
func named2d() {
v := 42
escapes(MyInt(v)) // ERROR "using global for interface value"
}
func named3a() {
sink = MyInt(42) // ERROR "using global for interface value"
}
func named3b() {
escapes(MyInt(0)) // ERROR "using global for interface value"
}
func named4a() {
v := MyInt(0)
sink = v // ERROR "using global for interface value"
}
func named4b() {
v := MyInt(0)
escapes(v) // ERROR "using global for interface value"
}
func named4c() {
v := 0
sink = MyInt(v) // ERROR "using global for interface value"
}
func named4d() {
v := 0
escapes(MyInt(v)) // ERROR "using global for interface value"
}
func named5() {
var a any = MyInt(42) // ERROR "using global for interface value"
_ = a
}
func named6() {
var a any
v := MyInt(42)
a = v // ERROR "using global for interface value"
_ = a
}
func named7a(v MyInt) {
sink = v
}
func named7b(v MyInt) {
escapes(v)
}
type S struct{ a, b int64 }
func struct1() {
sink = S{1, 1} // ERROR "using global for interface value"
}
func struct2() {
v := S{1, 1}
sink = v // ERROR "using global for interface value"
}
func struct3() {
sink = S{} // ERROR "using global for zero value interface value"
}
func struct4() {
v := S{}
sink = v // ERROR "using global for zero value interface value"
}
func struct5() {
var a any = S{1, 1} // ERROR "using global for interface value"
_ = a
}
func struct6() {
var a any
v := S{1, 1}
a = v // ERROR "using global for interface value"
_ = a
}
func struct7(v S) {
sink = v
}
func emptyStruct1() {
sink = struct{}{} // ERROR "using global for zero-sized interface value"
}
func emptyStruct2() {
v := struct{}{}
sink = v // ERROR "using global for zero-sized interface value"
}
func emptyStruct3(v struct{}) { // ERROR "using global for zero-sized interface value"
sink = v
}
// Some light emulation of conditional debug printing (such as in #53465).
func Printf(format string, args ...any) {
for _, arg := range args {
sink = arg
}
}
var enabled = true
func debugf(format string, args ...interface{}) {
if enabled {
Printf(format, args...)
}
}
//go:noinline
func debugf2(format string, args ...interface{}) {
if enabled {
Printf(format, args...)
}
}
func f1() {
v := 1000
debugf("hello %d", v) // ERROR "using global for interface value"
}
func f2() {
v := 1000
debugf2("hello %d", v) // ERROR "using global for interface value"
}
//go:noinline
func f3(i int) {
debugf("hello %d", i)
}
func f4() {
f3(1000)
}

View File

@@ -0,0 +1,108 @@
// errorcheck -0 -m
// 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 escape
const globalConstSize = 128
var globalVarSize = 128
//go:noinline
func testSlices() {
{
size := 128
_ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
}
{
s := 128
size := s
_ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
}
{
size := 128
_ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
}
{
s := 128
size := s
_ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
}
{
s1 := 128
s2 := 256
_ = make([]byte, s2, s1) // ERROR "make\(\[\]byte, s2, 128\) does not escape"
}
allocLen(256) // ERROR "make\(\[\]byte, 256\) does not escape" "inlining call"
allocCap(256) // ERROR "make\(\[\]byte, 0, 256\) does not escape" "inlining call"
_ = newT(256) // ERROR "make\(\[\]byte, 256\) does not escape" "inlining call"
{
size := globalConstSize
_ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
}
allocLen(globalConstSize) // ERROR "make\(\[\]byte, 128\) does not escape" "inlining call"
allocCap(globalConstSize) // ERROR "make\(\[\]byte, 0, 128\) does not escape" "inlining call"
_ = newT(globalConstSize) // ERROR "make\(\[\]byte, 128\) does not escape" "inlining call"
{
c := 128
s := 256
_ = make([]byte, s, c) // ERROR "make\(\[\]byte, s, 128\) does not escape"
}
{
s := 256
_ = make([]byte, s, globalConstSize) // ERROR "make\(\[\]byte, s, 128\) does not escape"
}
{
_ = make([]byte, globalVarSize) // ERROR "make\(\[\]byte, globalVarSize\) does not escape"
_ = make([]byte, globalVarSize, globalConstSize) // ERROR "make\(\[\]byte, globalVarSize, 128\) does not escape"
}
}
func allocLen(l int) []byte { // ERROR "can inline"
return make([]byte, l) // ERROR "escapes to heap"
}
func allocCap(l int) []byte { // ERROR "can inline"
return make([]byte, 0, l) // ERROR "escapes to heap"
}
type t struct {
s []byte
}
func newT(l int) t { // ERROR "can inline"
return t{make([]byte, l)} // ERROR "make.*escapes to heap"
}
//go:noinline
func testMaps() {
size := 128
_ = make(map[string]int, size) // ERROR "does not escape"
_ = allocMapLen(128) // ERROR "does not escape" "inlining call"
_ = newM(128) // ERROR "does not escape" "inlining call"
}
func allocMapLen(l int) map[string]int { // ERROR "can inline"
return make(map[string]int, l) // ERROR "escapes to heap"
}
type m struct {
m map[string]int
}
func newM(l int) m { // ERROR "can inline"
return m{make(map[string]int, l)} // ERROR "make.*escapes to heap"
}

View File

@@ -45,7 +45,7 @@ func map3() []*int {
m[&i] = &j
var r []*int
for k := range m {
r = append(r, k)
r = append(r, k) // ERROR "append escapes to heap"
}
return r
}
@@ -61,7 +61,7 @@ func map4() []*int {
// We want to test exactly "for k, v := range m" rather than "for _, v := range m".
// The following if is merely to use (but not leak) k.
if k != nil {
r = append(r, v)
r = append(r, v) // ERROR "append escapes to heap"
}
}
return r

View File

@@ -18,29 +18,29 @@ var sink interface{}
func slice0() {
var s []*int
// BAD: i should not escape
i := 0 // ERROR "moved to heap: i"
s = append(s, &i)
i := 0 // ERROR "moved to heap: i"
s = append(s, &i) // ERROR "append does not escape"
_ = s
}
func slice1() *int {
var s []*int
i := 0 // ERROR "moved to heap: i"
s = append(s, &i)
i := 0 // ERROR "moved to heap: i"
s = append(s, &i) // ERROR "append does not escape"
return s[0]
}
func slice2() []*int {
var s []*int
i := 0 // ERROR "moved to heap: i"
s = append(s, &i)
i := 0 // ERROR "moved to heap: i"
s = append(s, &i) // ERROR "append escapes to heap"
return s
}
func slice3() *int {
var s []*int
i := 0 // ERROR "moved to heap: i"
s = append(s, &i)
i := 0 // ERROR "moved to heap: i"
s = append(s, &i) // ERROR "append does not escape"
for _, p := range s {
return p
}
@@ -124,7 +124,7 @@ NextVar:
continue NextVar
}
}
out = append(out, inkv)
out = append(out, inkv) // ERROR "append escapes to heap"
}
return out
}
@@ -167,7 +167,7 @@ var resolveIPAddrTests = []resolveIPAddrTest{
}
func setupTestData() {
resolveIPAddrTests = append(resolveIPAddrTests,
resolveIPAddrTests = append(resolveIPAddrTests, // ERROR "append escapes to heap"
[]resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest{...} does not escape"
{"ip",
"localhost",

62
test/escape_unique.go Normal file
View File

@@ -0,0 +1,62 @@
// errorcheck -0 -m -l
// 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 escape analysis for unique.
package escape
import "unique"
type T string
func f1(s string) unique.Handle[string] { // ERROR "s does not escape$"
return unique.Make(s)
}
func f1a(s []byte) unique.Handle[string] { // ERROR "s does not escape$"
return unique.Make(string(s)) // ERROR "string\(s\) does not escape$"
}
func gen[S ~string](s S) unique.Handle[S] {
return unique.Make(s)
}
func f2(s T) unique.Handle[T] { // ERROR "s does not escape$"
return unique.Make(s)
}
func f3(s T) unique.Handle[T] { // ERROR "s does not escape$"
return gen(s)
}
type pair struct {
s1 string
s2 string
}
func f4(s1 string, s2 string) unique.Handle[pair] { // ERROR "s1 does not escape$" "s2 does not escape$"
return unique.Make(pair{s1, s2})
}
type viaInterface struct {
s any
}
func f5(s string) unique.Handle[viaInterface] { // ERROR "leaking param: s$"
return unique.Make(viaInterface{s}) // ERROR "s escapes to heap$"
}
var sink any
func f6(s string) unique.Handle[string] { // ERROR "leaking param: s$"
sink = s // ERROR "s escapes to heap$"
return unique.Make(s)
}
func f6a(s []byte) unique.Handle[string] { // ERROR "leaking param: s$"
sink = s // ERROR "s escapes to heap$"
return unique.Make(string(s)) // ERROR "string\(s\) does not escape$"
}

View File

@@ -9,7 +9,7 @@ package main
func putint(digits *string) {
var i byte;
i = (*digits)[7]; // compiles
i = digits[7]; // ERROR "illegal|is not|invalid"
i = digits[7]; // ERROR "illegal|is not|cannot index"
_ = i;
}

View File

@@ -17,14 +17,14 @@ func FooN(vals ...*int) (s int) { // ERROR "vals does not escape"
// Append forces heap allocation and copies entries in vals to heap, therefore they escape to heap.
func FooNx(x *int, vals ...*int) (s int) { // ERROR "leaking param: x" "leaking param content: vals"
vals = append(vals, x)
vals = append(vals, x) // ERROR "append does not escape"
return FooN(vals...)
}
var sink []*int
func FooNy(x *int, vals ...*int) (s int) { // ERROR "leaking param: x" "leaking param: vals"
vals = append(vals, x)
vals = append(vals, x) // ERROR "append escapes to heap"
sink = vals
return FooN(vals...)
}
@@ -84,7 +84,7 @@ func TFooI() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
FooI(a, b, c) // ERROR "a escapes to heap" "b escapes to heap" "... argument does not escape"
FooI(a, b, c) // ERROR "a escapes to heap" ".cat. escapes to heap" "... argument does not escape"
}
func FooJ(args ...interface{}) *int32 { // ERROR "leaking param: args to result ~r0 level=1"
@@ -108,14 +108,14 @@ func TFooJ1() {
a := int32(1)
b := "cat"
c := &a
FooJ(a, b, c) // ERROR "a does not escape" "b does not escape" "... argument does not escape"
FooJ(a, b, c) // ERROR "a does not escape" ".cat. does not escape" "... argument does not escape"
}
func TFooJ2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
isink = FooJ(a, b, c) // ERROR "a escapes to heap" "b escapes to heap" "... argument does not escape"
isink = FooJ(a, b, c) // ERROR "a escapes to heap" ".cat. escapes to heap" "... argument does not escape"
}
type fakeSlice struct {
@@ -144,7 +144,7 @@ func TFooK2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" "b escapes to heap" "&\[4\]interface {}{...} does not escape"
fs := fakeSlice{3, &[4]interface{}{a, b, c, nil}} // ERROR "a escapes to heap" ".cat. escapes to heap" "&\[4\]interface {}{...} does not escape"
isink = FooK(fs)
}
@@ -169,6 +169,6 @@ func TFooL2() {
a := int32(1) // ERROR "moved to heap: a"
b := "cat"
c := &a
s := []interface{}{a, b, c} // ERROR "a escapes to heap" "b escapes to heap" "\[\]interface {}{...} does not escape"
s := []interface{}{a, b, c} // ERROR "a escapes to heap" ".cat. escapes to heap" "\[\]interface {}{...} does not escape"
isink = FooL(s)
}

View File

@@ -51,7 +51,7 @@ func test1(iter int) {
// var fn func() // this makes it work, because fn stays off heap
j := 0 // ERROR "moved to heap: j$"
fn = func() { // ERROR "func literal escapes to heap$"
m[i] = append(m[i], 0)
m[i] = append(m[i], 0) // ERROR "append escapes to heap"
if j < 25 {
j++
fn()
@@ -75,7 +75,7 @@ func test2(iter int) {
var fn func() // this makes it work, because fn stays off heap
j := 0
fn = func() { // ERROR "func literal does not escape$"
m[i] = append(m[i], 0)
m[i] = append(m[i], 0) // ERROR "append escapes to heap"
if j < 25 {
j++
fn()

View File

@@ -6,4 +6,4 @@
package main
const A = complex(0()) // ERROR "cannot call non-function"
const A = complex(0()) // ERROR "cannot call .* not a function"

View File

@@ -13,7 +13,7 @@ func F() {
slice := []int{1, 2, 3}
_ = slice
len := int(2)
println(len(slice)) // ERROR "cannot call non-function len .type int., declared at LINE-1|expected function|cannot call non-function len"
println(len(slice)) // ERROR "cannot call non-function len .type int., declared at LINE-1|expected function|cannot call len"
const iota = 1
println(iota(slice)) // ERROR "cannot call non-function iota .type int., declared at LINE-1|expected function|cannot call non-function iota"
println(iota(slice)) // ERROR "cannot call non-function iota .type int., declared at LINE-1|expected function|cannot call iota"
}

View File

@@ -11,9 +11,9 @@ package p
var a = []int{1,2,3}
func _(len int) {
_ = len(a) // ERROR "cannot call non-function|expected function"
_ = len(a) // ERROR "cannot call|expected function"
}
var cap = false
var _ = cap(a) // ERROR "cannot call non-function|expected function"
var _ = cap(a) // ERROR "cannot call|expected function"

View File

@@ -15,5 +15,5 @@ func debugf(format string, args ...interface{}) { // ERROR "can inline debugf" "
func bar() { // ERROR "can inline bar"
value := 10
debugf("value is %d", value) // ERROR "inlining call to debugf" "value does not escape" "\.\.\. argument does not escape"
debugf("value is %d", value) // ERROR "inlining call to debugf" "10 does not escape" "\.\.\. argument does not escape"
}

View File

@@ -4,6 +4,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !nacl && !js
//go:build !nacl && !js && !wasip1
package ignored

View File

@@ -6,4 +6,4 @@
package p
func init() // ERROR "missing function body|cannot declare init"
func init() // ERROR "func init must have a body|cannot declare init"

View File

@@ -12,6 +12,6 @@ func f() { // ERROR ""
_ = make([]byte, 100, 1<<17) // ERROR "too large for stack" ""
_ = make([]byte, n, 1<<17) // ERROR "too large for stack" ""
_ = make([]byte, n) // ERROR "non-constant size" ""
_ = make([]byte, 100, m) // ERROR "non-constant size" ""
_ = make([]byte, n) // ERROR "does not escape"
_ = make([]byte, 100, m) // ERROR "does not escape"
}

View File

@@ -0,0 +1,17 @@
// compile
// 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 x
func F[T int32]() {
_ = G[*[0]T]()[:]
}
func G[T any]() (v T) {
return
}
var _ = F[int32]

View File

@@ -0,0 +1,19 @@
// build
//go:build cgo
// 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 main
// #cgo CFLAGS: -Werror -Wunused-parameter
import "C"
func main() {
}
//export Fn
func Fn() {
}

View File

@@ -0,0 +1,29 @@
// build
//go:build cgo
// 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 main
/*
#cgo CFLAGS: -Werror -Wimplicit-function-declaration
#include <stdio.h>
static void CFn(_GoString_ gostr) {
printf("%.*s\n", (int)(_GoStringLen(gostr)), _GoStringPtr(gostr));
}
*/
import "C"
func main() {
C.CFn("hello, world")
}
// The bug only occurs if there is an exported function.
//export Fn
func Fn() {
}

View File

@@ -0,0 +1,20 @@
// run
// 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 main
//go:noinline
func f(p *[2]int32) (int64, int64) {
return int64(p[0]), int64(p[1])
}
func main() {
p := [2]int32{-1, -1}
x, y := f(&p)
if x != -1 || y != -1 {
println(x, y)
}
}

View File

@@ -0,0 +1,70 @@
// run
// 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 main
//go:noinline
func nilPtrFunc() *[4]int {
return nil
}
var nilPtrVar *[4]int
func testLen1() {
_ = len(*nilPtrFunc())
}
func testLen2() {
_ = len(nilPtrFunc())
}
func testLen3() {
_ = len(*nilPtrVar)
}
func testLen4() {
_ = len(nilPtrVar)
}
func testRange1() {
for range *nilPtrFunc() {
}
}
func testRange2() {
for range nilPtrFunc() {
}
}
func testRange3() {
for range *nilPtrVar {
}
}
func testRange4() {
for range nilPtrVar {
}
}
func main() {
shouldPanic(testLen1)
shouldNotPanic(testLen2)
shouldNotPanic(testLen3)
shouldNotPanic(testLen4)
shouldPanic(testRange1)
shouldNotPanic(testRange2)
shouldNotPanic(testRange3)
shouldNotPanic(testRange4)
}
func shouldPanic(f func()) {
defer func() {
if e := recover(); e == nil {
panic("should have panicked")
}
}()
f()
}
func shouldNotPanic(f func()) {
f()
}

View File

@@ -0,0 +1,24 @@
// run
// 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 main
//go:noinline
func f(p *int, b bool) int {
valid := *p >= 0
if !b || !valid {
return 5
}
return 6
}
func main() {
defer func() {
if e := recover(); e == nil {
println("should have panicked")
}
}()
f(nil, false)
}

View File

@@ -9,7 +9,7 @@
package main
func main() {
_ = copy(nil, []int{}) // ERROR "use of untyped nil|left argument must be a slice|expects slice arguments"
_ = copy([]int{}, nil) // ERROR "use of untyped nil|second argument must be slice or string|expects slice arguments"
_ = copy(nil, []int{}) // ERROR "use of untyped nil|left argument must be a slice|argument must be a slice; have untyped nil"
_ = copy([]int{}, nil) // ERROR "use of untyped nil|second argument must be slice or string|argument must be a slice; have untyped nil"
_ = 1 + true // ERROR "mismatched types untyped int and untyped bool|incompatible types|cannot convert"
}

View File

@@ -0,0 +1,15 @@
// build
// 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 p
func F(a, b map[float32]int) int {
var st *struct {
n int
f float32
}
return a[0] + b[st.f]
}

View File

@@ -0,0 +1,36 @@
// build
// 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 main
var g bool
func main() {
l_4 := uint32(0x6E54EE87)
v4 := int8(-Int64FromInt64(1))
g = int32(v4) >= safe_mod_func_int32_t_s_s(BoolInt32(l_4 >= 1), 7)
}
func safe_mod_func_int32_t_s_s(si1 int32, si2 int32) (r int32) {
var v1 int32
if si2 == 0 {
v1 = si1
} else {
v1 = si1 % si2
}
return v1
}
func Int64FromInt64(n int64) int64 {
return n
}
func BoolInt32(b bool) int32 {
if b {
return 1
}
return 0
}

View File

@@ -0,0 +1,18 @@
// compile
// 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 main
type B[T any] struct {
a A[T]
}
type A[T any] = func(B[T]) bool
func main() {
var s A[int]
println(s)
}

View File

@@ -0,0 +1,88 @@
// compile
// 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 main
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
// a Validator instance
type Validator []Validable
type Numeric interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
}
func (v Validator) Valid() bool {
for _, field := range v {
if !field.Validate() {
return false
}
}
return true
}
type Validable interface {
Validate() bool
}
type FieldDef[T any] struct {
value T
rules []Rule[T]
}
func (f FieldDef[T]) Validate() bool {
for _, rule := range f.rules {
if !rule(f) {
return false
}
}
return true
}
type Rule[T any] = func(FieldDef[T]) bool
func Field[T any](value T, rules ...Rule[T]) *FieldDef[T] {
return &FieldDef[T]{value: value, rules: rules}
}
type StringRule = Rule[string]
type NumericRule[T Numeric] = Rule[T]
type UnsignedRule[T Unsigned] = Rule[T]
func MinS(n int) StringRule {
return func(fd FieldDef[string]) bool {
return len(fd.value) < n
}
}
func MinD[T Numeric](n T) NumericRule[T] {
return func(fd FieldDef[T]) bool {
return fd.value < n
}
}
func MinU[T Unsigned](n T) UnsignedRule[T] {
return func(fd FieldDef[T]) bool {
return fd.value < n
}
}
func main() {
v := Validator{
Field("test", MinS(5)),
}
if !v.Valid() {
println("invalid")
return
}
println("valid")
}

View File

@@ -0,0 +1,17 @@
// run
// 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 main
//go:noinline
func f(p *[4]int) {
for i := range (*p) { // Note the parentheses! gofmt wants to remove them - don't let it!
println(i)
}
}
func main() {
f(nil)
}

View File

@@ -0,0 +1,4 @@
0
1
2
3

View File

@@ -0,0 +1,20 @@
// run -race
//go:build race && cgo
// 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 main
/*
int v[8192];
*/
import "C"
var x [8192]C.int
func main() {
copy(C.v[:], x[:])
}

View File

@@ -0,0 +1,25 @@
// build
// 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 main
type T int
const K T = 5
type P struct {
a [K]*byte
}
//go:noinline
func f(p *P) {
for i := range K {
p.a[i] = nil
}
}
func main() {
f(nil)
}

View File

@@ -0,0 +1,37 @@
// build
// 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.
// Issue 73716: cmd/compile: unnamed functions missing FuncInfo
package main
import "fmt"
type EP func()
type F func(EP) EP
func main() {
eps := []EP{ep1, ep2}
var h EP
for _, ep := range eps {
h = F(func(e EP) EP {
return func() {
ep()
e()
}
})(h)
}
h()
}
func ep1() {
fmt.Printf("ep1\n")
}
func ep2() {
fmt.Printf("ep2\n")
}

View File

@@ -0,0 +1,58 @@
// compile
// 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 p
type Backend interface {
Hash(ignores func(bucketName, keyName []byte) bool) (uint32, error)
}
type backend struct {
}
func first() (key []byte, value []byte) {
return
}
func (b *backend) View(fn func() error) error {
return nil
}
func (b *backend) Hash(ignores func(bucketName, keyName []byte) bool) (uint32, error) {
err := b.View(func() error {
for next, _ := first(); next != nil; next, _ = first() {
_ = next
}
return nil
})
return 0, err
}
func defragdb() error {
for next, _ := first(); next != nil; next, _ = first() {
_ = f(next)
ForEach(func(k, v []byte) error {
_ = next
return nil
})
}
return nil
}
func ForEach(fn func(k, v []byte) error) error {
for k, v := first(); k != nil; k, v = first() {
if err := fn(k, v); err != nil {
return err
}
}
return nil
}
//go:noinline
func f(any) string {
return ""
}

View File

@@ -0,0 +1,34 @@
// run
// 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 main
type SourceRange struct {
x, y int
}
func (r *SourceRange) String() string {
return "hello"
}
type SourceNode interface {
SourceRange()
}
type testNode SourceRange
func (tn testNode) SourceRange() {
}
func main() {
n := testNode(SourceRange{}) // zero value
Errorf(n)
}
//go:noinline
func Errorf(n SourceNode) {
n.SourceRange()
}

View File

@@ -0,0 +1,34 @@
// run
// 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 main
type SourceRange struct {
x, y int
}
func (r *SourceRange) String() string {
return "hello"
}
type SourceNode interface {
SourceRange()
}
type testNode SourceRange
func (tn testNode) SourceRange() {
}
func main() {
n := testNode(SourceRange{1, 1}) // not zero value
Errorf(n)
}
//go:noinline
func Errorf(n SourceNode) {
n.SourceRange()
}

View File

@@ -0,0 +1,30 @@
// run
// 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 main
import (
"errors"
"fmt"
"os"
)
func crashOnErr(err error) bool {
if err != nil {
panic(err)
}
return false
}
func main() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
fmt.Println(crashOnErr(errors.New("test error")))
}

View File

@@ -0,0 +1,32 @@
// run
// 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 main
import (
"errors"
"fmt"
"os"
)
func crashOnErr(err error) int {
if err != nil {
panic(err)
}
return 10
}
func main() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
s := make([]int, crashOnErr(errors.New("test error")))
println("unreachable: len(s) =", len(s))
}

View File

@@ -0,0 +1,54 @@
// run
// 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 main
import (
"errors"
"fmt"
"os"
)
type S struct{ a, b int }
func crashOnErr1(err error) S {
if err != nil {
panic(err)
}
return S{} // zero value struct
}
func f1() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
fmt.Println(crashOnErr1(errors.New("test error")))
}
func crashOnErr2(err error) S {
if err != nil {
panic(err)
}
return S{1, 2} // not zero value struct
}
func f2() {
defer func() {
if recover() == nil {
fmt.Println("failed to have expected panic")
os.Exit(1)
}
}()
fmt.Println(crashOnErr2(errors.New("test error")))
}
func main() {
f1()
f2()
}

View File

@@ -21,15 +21,15 @@ func endian(b []byte) uint64 { // ERROR "can inline endian" "b does not escape"
}
func appendLittleEndian(b []byte) []byte { // ERROR "can inline appendLittleEndian" "leaking param: b to result ~r0 level=0"
b = binary.LittleEndian.AppendUint64(b, 64) // ERROR "inlining call to binary.littleEndian.AppendUint64"
b = binary.LittleEndian.AppendUint32(b, 32) // ERROR "inlining call to binary.littleEndian.AppendUint32"
b = binary.LittleEndian.AppendUint16(b, 16) // ERROR "inlining call to binary.littleEndian.AppendUint16"
b = binary.LittleEndian.AppendUint64(b, 64) // ERROR "inlining call to binary.littleEndian.AppendUint64" "append escapes to heap"
b = binary.LittleEndian.AppendUint32(b, 32) // ERROR "inlining call to binary.littleEndian.AppendUint32" "append escapes to heap"
b = binary.LittleEndian.AppendUint16(b, 16) // ERROR "inlining call to binary.littleEndian.AppendUint16" "append escapes to heap"
return b
}
func appendBigEndian(b []byte) []byte { // ERROR "can inline appendBigEndian" "leaking param: b to result ~r0 level=0"
b = binary.BigEndian.AppendUint64(b, 64) // ERROR "inlining call to binary.bigEndian.AppendUint64"
b = binary.BigEndian.AppendUint32(b, 32) // ERROR "inlining call to binary.bigEndian.AppendUint32"
b = binary.BigEndian.AppendUint16(b, 16) // ERROR "inlining call to binary.bigEndian.AppendUint16"
b = binary.BigEndian.AppendUint64(b, 64) // ERROR "inlining call to binary.bigEndian.AppendUint64" "append escapes to heap"
b = binary.BigEndian.AppendUint32(b, 32) // ERROR "inlining call to binary.bigEndian.AppendUint32" "append escapes to heap"
b = binary.BigEndian.AppendUint16(b, 16) // ERROR "inlining call to binary.bigEndian.AppendUint16" "append escapes to heap"
return b
}

View File

@@ -37,7 +37,7 @@ var once *sync.Once
func small7() { // ERROR "can inline small7"
// the Do fast path should be inlined
once.Do(small5) // ERROR "inlining call to sync\.\(\*Once\)\.Do" "inlining call to atomic\.\(\*Uint32\)\.Load"
once.Do(small5) // ERROR "inlining call to sync\.\(\*Once\)\.Do" "inlining call to atomic\.\(\*Bool\)\.Load"
}
var rwmutex *sync.RWMutex

View File

@@ -337,23 +337,34 @@ func f20() {
ch <- byteptr()
}
func f21() {
func f21(x, y string) { // ERROR "live at entry to f21: x y"
// key temporary for mapaccess using array literal key.
var z *byte
if b {
z = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
z = m2[[2]string{x, y}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
z = m2[[2]string{"x", "y"}]
z = m2[[2]string{"x", "y"}]
printbytepointer(z)
}
func f23() {
func f21b() {
// key temporary for mapaccess using array literal key.
var z *byte
if b {
z = m2[[2]string{"x", "y"}]
}
z = m2[[2]string{"x", "y"}]
z = m2[[2]string{"x", "y"}]
printbytepointer(z)
}
func f23(x, y string) { // ERROR "live at entry to f23: x y"
// key temporary for two-result map access using array literal key.
var z *byte
var ok bool
if b {
z, ok = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
z, ok = m2[[2]string{x, y}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
z, ok = m2[[2]string{"x", "y"}]
z, ok = m2[[2]string{"x", "y"}]
@@ -361,11 +372,34 @@ func f23() {
print(ok)
}
func f24() {
func f23b() {
// key temporary for two-result map access using array literal key.
var z *byte
var ok bool
if b {
z, ok = m2[[2]string{"x", "y"}]
}
z, ok = m2[[2]string{"x", "y"}]
z, ok = m2[[2]string{"x", "y"}]
printbytepointer(z)
print(ok)
}
func f24(x, y string) { // ERROR "live at entry to f24: x y"
// key temporary for map access using array literal key.
// value temporary too.
if b {
m2[[2]string{"x", "y"}] = nil // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
m2[[2]string{x, y}] = nil // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
m2[[2]string{"x", "y"}] = nil
m2[[2]string{"x", "y"}] = nil
}
func f24b() {
// key temporary for map access using array literal key.
// value temporary too.
if b {
m2[[2]string{"x", "y"}] = nil
}
m2[[2]string{"x", "y"}] = nil
m2[[2]string{"x", "y"}] = nil

View File

@@ -335,23 +335,34 @@ func f20() {
ch <- byteptr()
}
func f21() {
func f21(x, y string) { // ERROR "live at entry to f21: x y"
// key temporary for mapaccess using array literal key.
var z *byte
if b {
z = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
z = m2[[2]string{x, y}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
z = m2[[2]string{"x", "y"}]
z = m2[[2]string{"x", "y"}]
printbytepointer(z)
}
func f23() {
func f21b() {
// key temporary for mapaccess using array literal key.
var z *byte
if b {
z = m2[[2]string{"x", "y"}]
}
z = m2[[2]string{"x", "y"}]
z = m2[[2]string{"x", "y"}]
printbytepointer(z)
}
func f23(x, y string) { // ERROR "live at entry to f23: x y"
// key temporary for two-result map access using array literal key.
var z *byte
var ok bool
if b {
z, ok = m2[[2]string{"x", "y"}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
z, ok = m2[[2]string{x, y}] // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
z, ok = m2[[2]string{"x", "y"}]
z, ok = m2[[2]string{"x", "y"}]
@@ -359,11 +370,34 @@ func f23() {
print(ok)
}
func f24() {
func f23b() {
// key temporary for two-result map access using array literal key.
var z *byte
var ok bool
if b {
z, ok = m2[[2]string{"x", "y"}]
}
z, ok = m2[[2]string{"x", "y"}]
z, ok = m2[[2]string{"x", "y"}]
printbytepointer(z)
print(ok)
}
func f24(x, y string) { // ERROR "live at entry to f24: x y"
// key temporary for map access using array lit3ral key.
// value temporary too.
if b {
m2[[2]string{x, y}] = nil // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
m2[[2]string{"x", "y"}] = nil
m2[[2]string{"x", "y"}] = nil
}
func f24b() {
// key temporary for map access using array literal key.
// value temporary too.
if b {
m2[[2]string{"x", "y"}] = nil // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
m2[[2]string{"x", "y"}] = nil
}
m2[[2]string{"x", "y"}] = nil
m2[[2]string{"x", "y"}] = nil

View File

@@ -7,8 +7,8 @@
// Test that the implementation catches nil ptr indirection
// in a large address space.
// Address space starts at 1<<32 on AIX and on darwin/arm64 and on windows/arm64, so dummy is too far.
//go:build !aix && (!darwin || !arm64) && (!windows || !arm64)
// Address space starts at 1<<32 on AIX and on darwin/arm64 and on windows/[amd64/arm64], so dummy is too far.
//go:build !aix && (!darwin || !arm64) && (!windows || (!amd64 && !arm64))
package main

View File

@@ -129,5 +129,27 @@ func f9(a, b int) bool {
return c
}
//go:noinline
func f10and(a bool, b bool) bool {
var x bool
if a {
x = b
} else {
x = a
}
return x // ERROR "converted OpPhi to AndB$"
}
//go:noinline
func f11or(a bool, b bool) bool {
var x bool
if a {
x = a
} else {
x = b
}
return x // ERROR "converted OpPhi to OrB$"
}
func main() {
}

View File

@@ -1391,8 +1391,8 @@ func bitLen8(x uint8, ensureBothBranchesCouldHappen bool) int {
}
func xor64(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0xff
b &= 0xfff
a = min(a, 0xff)
b = min(b, 0xfff)
z := a ^ b
@@ -1409,8 +1409,8 @@ func xor64(a, b uint64, ensureBothBranchesCouldHappen bool) int {
}
func or64(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0xff
b &= 0xfff
a = min(a, 0xff)
b = min(b, 0xfff)
z := a | b
@@ -1427,8 +1427,8 @@ func or64(a, b uint64, ensureBothBranchesCouldHappen bool) int {
}
func mod64uWithSmallerDividendMax(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0xff
b &= 0xfff
a = min(a, 0xff)
b = min(b, 0xfff)
z := bits.Len64(a % b) // see go.dev/issue/68857 for bits.Len64
@@ -1444,8 +1444,8 @@ func mod64uWithSmallerDividendMax(a, b uint64, ensureBothBranchesCouldHappen boo
return z
}
func mod64uWithSmallerDivisorMax(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0xfff
b &= 0x10 // we need bits.Len64(b.umax) != bits.Len64(b.umax-1)
a = min(a, 0xfff)
b = min(b, 0x10) // we need bits.Len64(b.umax) != bits.Len64(b.umax-1)
z := bits.Len64(a % b) // see go.dev/issue/68857 for bits.Len64
@@ -1461,8 +1461,8 @@ func mod64uWithSmallerDivisorMax(a, b uint64, ensureBothBranchesCouldHappen bool
return z
}
func mod64uWithIdenticalMax(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0x10
b &= 0x10 // we need bits.Len64(b.umax) != bits.Len64(b.umax-1)
a = min(a, 0x10)
b = min(b, 0x10) // we need bits.Len64(b.umax) != bits.Len64(b.umax-1)
z := bits.Len64(a % b) // see go.dev/issue/68857 for bits.Len64
@@ -1481,8 +1481,8 @@ func mod64sPositiveWithSmallerDividendMax(a, b int64, ensureBothBranchesCouldHap
if a < 0 || b < 0 {
return 42
}
a &= 0xff
b &= 0xfff
a = min(a, 0xff)
b = min(b, 0xfff)
z := a % b // ERROR "Proved Mod64 does not need fix-up$"
@@ -1501,8 +1501,8 @@ func mod64sPositiveWithSmallerDivisorMax(a, b int64, ensureBothBranchesCouldHapp
if a < 0 || b < 0 {
return 42
}
a &= 0xfff
b &= 0xff
a = min(a, 0xfff)
b = min(b, 0xff)
z := a % b // ERROR "Proved Mod64 does not need fix-up$"
@@ -1521,8 +1521,8 @@ func mod64sPositiveWithIdenticalMax(a, b int64, ensureBothBranchesCouldHappen bo
if a < 0 || b < 0 {
return 42
}
a &= 0xfff
b &= 0xfff
a = min(a, 0xfff)
b = min(b, 0xfff)
z := a % b // ERROR "Proved Mod64 does not need fix-up$"
@@ -1539,10 +1539,10 @@ func mod64sPositiveWithIdenticalMax(a, b int64, ensureBothBranchesCouldHappen bo
}
func div64u(a, b uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
a &= 0xffff
a |= 0xfff
b &= 0xff
b |= 0xf
a = min(a, 0xffff)
a = max(a, 0xfff)
b = min(b, 0xff)
b = max(b, 0xf)
z := a / b // ERROR "Proved Neq64$"
@@ -1564,10 +1564,10 @@ func div64s(a, b int64, ensureAllBranchesCouldHappen func() bool) int64 {
if a < 0 || b < 0 {
return 42
}
a &= 0xffff
a |= 0xfff
b &= 0xff
b |= 0xf
a = min(a, 0xffff)
a = max(a, 0xfff)
b = min(b, 0xff)
b = max(b, 0xf)
z := a / b // ERROR "(Proved Div64 does not need fix-up|Proved Neq64)$"
@@ -1587,8 +1587,8 @@ func div64s(a, b int64, ensureAllBranchesCouldHappen func() bool) int64 {
}
func trunc64to16(a uint64, ensureAllBranchesCouldHappen func() bool) uint16 {
a &= 0xfff
a |= 0xff
a = min(a, 0xfff)
a = max(a, 0xff)
z := uint16(a)
if ensureAllBranchesCouldHappen() && z > 0xfff { // ERROR "Disproved Less16U$"
@@ -1607,8 +1607,8 @@ func trunc64to16(a uint64, ensureAllBranchesCouldHappen func() bool) uint16 {
}
func com64(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
a &= 0xffff
a |= 0xff
a = min(a, 0xffff)
a = max(a, 0xff)
z := ^a
@@ -1629,8 +1629,8 @@ func com64(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
func neg64(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
var lo, hi uint64 = 0xff, 0xfff
a &= hi
a |= lo
a = min(a, hi)
a = max(a, lo)
z := -a
@@ -1650,8 +1650,8 @@ func neg64(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
}
func neg64mightOverflowDuringNeg(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
var lo, hi uint64 = 0, 0xfff
a &= hi
a |= lo
a = min(a, hi)
a = max(a, lo)
z := -a
@@ -1691,6 +1691,317 @@ func phiMin(a, b []byte) {
_ = b[:y] // ERROR "Proved IsSliceInBounds"
}
func minPhiLeq[T uint | int](x, y T) (z T) {
if x <= y {
z = x
} else {
z = y
}
return z
}
func maxPhiLeq[T uint | int](x, y T) (z T) {
if y <= x {
z = x
} else {
z = y
}
return z
}
func mathBasedOnPhiLosangeMinUFirstLeq(x uint, ensureAllBranchesCouldHappen func() bool) uint {
const maxc = 0xf2a
const minc = 0xf0a
x = minPhiLeq(x, maxc)
x = maxPhiLeq(x, minc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64U$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64U$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64U$"
return 42424242
}
return x
}
func mathBasedOnPhiLosangeMinUSecondLeq(x uint, ensureAllBranchesCouldHappen func() bool) uint {
const maxc = 0xf2a
const minc = 0xf0a
x = maxPhiLeq(x, minc)
x = minPhiLeq(x, maxc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64U$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64U$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64U$"
return 42424242
}
return x
}
func mathBasedOnPhiLosangeMinFirstLeq(x int, ensureAllBranchesCouldHappen func() bool) int {
const maxc = 0xf2a
const minc = 0xf0a
x = minPhiLeq(x, maxc)
x = maxPhiLeq(x, minc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64$"
return 42424242
}
return x
}
func mathBasedOnPhiLosangeMinSecondLeq(x int, ensureAllBranchesCouldHappen func() bool) int {
const maxc = 0xf2a
const minc = 0xf0a
x = maxPhiLeq(x, minc)
x = minPhiLeq(x, maxc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64$"
return 42424242
}
return x
}
func minPhiLess[T uint | int](x, y T) (z T) {
if x < y {
z = x
} else {
z = y
}
return z
}
func maxPhiLess[T uint | int](x, y T) (z T) {
if y < x {
z = x
} else {
z = y
}
return z
}
func mathBasedOnPhiLosangeMinUFirstLess(x uint, ensureAllBranchesCouldHappen func() bool) uint {
const maxc = 0xf2a
const minc = 0xf0a
x = minPhiLess(x, maxc)
x = maxPhiLess(x, minc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64U$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64U$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64U$"
return 42424242
}
return x
}
func mathBasedOnPhiLosangeMinUSecondLess(x uint, ensureAllBranchesCouldHappen func() bool) uint {
const maxc = 0xf2a
const minc = 0xf0a
x = maxPhiLess(x, minc)
x = minPhiLess(x, maxc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64U$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64U$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64U$"
return 42424242
}
return x
}
func mathBasedOnPhiLosangeMinFirstLess(x int, ensureAllBranchesCouldHappen func() bool) int {
const maxc = 0xf2a
const minc = 0xf0a
x = minPhiLess(x, maxc)
x = maxPhiLess(x, minc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64$"
return 42424242
}
return x
}
func mathBasedOnPhiLosangeMinSecondLess(x int, ensureAllBranchesCouldHappen func() bool) int {
const maxc = 0xf2a
const minc = 0xf0a
x = maxPhiLess(x, minc)
x = minPhiLess(x, maxc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64$"
return 42424242
}
return x
}
func mathBasedOnPhiBuiltinMinUFirst(x uint, ensureAllBranchesCouldHappen func() bool) uint {
const maxc = 0xf2a
const minc = 0xf0a
x = min(x, maxc)
x = max(x, minc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64U$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64U$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64U$"
return 42424242
}
return x
}
func mathBasedOnPhiBuiltinMinUSecond(x uint, ensureAllBranchesCouldHappen func() bool) uint {
const maxc = 0xf2a
const minc = 0xf0a
x = max(x, minc)
x = min(x, maxc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64U$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64U$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64U$"
return 42424242
}
return x
}
func mathBasedOnPhiBuiltinMinFirst(x int, ensureAllBranchesCouldHappen func() bool) int {
const maxc = 0xf2a
const minc = 0xf0a
x = min(x, maxc)
x = max(x, minc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64$"
return 42424242
}
return x
}
func mathBasedOnPhiBuiltinMinSecond(x int, ensureAllBranchesCouldHappen func() bool) int {
const maxc = 0xf2a
const minc = 0xf0a
x = max(x, minc)
x = min(x, maxc)
const k = 1
x += k
if ensureAllBranchesCouldHappen() && x > maxc+k { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && x <= maxc+k { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && x < minc+k { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && x >= minc+k { // ERROR "Proved Leq64$"
return 42424242
}
return x
}
func issue16833(a, b []byte) {
n := copy(a, b)
_ = a[n:] // ERROR "Proved IsSliceInBounds"
@@ -1712,6 +2023,24 @@ func clampedIdx2(x []int, i int) int {
return x[max(min(i, len(x)-1), 0)] // TODO: can't get rid of this bounds check yet
}
func cvtBoolToUint8Disprove(b bool) byte {
var c byte
if b {
c = 1
}
if c == 2 { // ERROR "Disproved Eq8"
c = 3
}
return c
}
func cvtBoolToUint8BCE(b bool, a [2]int64) int64 {
c := byte(0)
if b {
c = 1
}
return a[c] // ERROR "Proved IsInBounds$"
}
//go:noinline
func useInt(a int) {
}

60
test/prove_popcount.go Normal file
View File

@@ -0,0 +1,60 @@
// errorcheck -0 -d=ssa/prove/debug=1
//go:build amd64.v3 || arm64
// 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.
// FIXME(@Jorropo): this file exists because I havn't yet bothered to
// make prove work on the pure go function call fallback.
// My idea was to wait until CL 637936 is merged, then we can always emit
// the PopCount SSA operation and translate them to pure function calls
// in late-opt.
package main
import "math/bits"
func onesCountsBounds(x uint64, ensureAllBranchesCouldHappen func() bool) int {
z := bits.OnesCount64(x)
if ensureAllBranchesCouldHappen() && z > 64 { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && z <= 64 { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && z < 0 { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && z >= 0 { // ERROR "Proved Leq64$"
return 42424242
}
return z
}
func onesCountsTight(x uint64, ensureAllBranchesCouldHappen func() bool) int {
const maxv = 0xff0f
const minv = 0xff00
x = max(x, minv)
x = min(x, maxv)
z := bits.OnesCount64(x)
if ensureAllBranchesCouldHappen() && z > bits.OnesCount64(maxv) { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && z <= bits.OnesCount64(maxv) { // ERROR "Proved Leq64$"
return 4242
}
if ensureAllBranchesCouldHappen() && z < bits.OnesCount64(minv) { // ERROR "Disproved Less64$"
return 424242
}
if ensureAllBranchesCouldHappen() && z >= bits.OnesCount64(minv) { // ERROR "Proved Leq64$"
return 42424242
}
return z
}
func main() {
}

View File

@@ -9,14 +9,20 @@
package main
var (
e any
ts uint16
ga, gb, gc, gd int
)
func moveValuesWithMemoryArg(len int) {
for n := 0; n < len; n++ {
// Load of e.data is lowed as a MOVDload op, which has a memory
// argument. It's moved near where it's used.
_ = e != ts // ERROR "MOVDload is moved$" "MOVDaddr is moved$"
// Loads of b and d can be delayed until inside the outer "if".
a := ga
b := gb // ERROR "MOVDload is moved$"
c := gc
d := gd // ERROR "MOVDload is moved$"
if a == c {
if b == d {
return
}
}
}
}