Update to go1.24.0

This commit is contained in:
Vorapol Rinsatitnon
2025-02-14 12:42:07 +07:00
parent 25e497e367
commit bf266cebe6
3169 changed files with 236789 additions and 60275 deletions

View File

@@ -46,8 +46,8 @@ var _ A0 = T0{}
var _ T0 = A0{}
// But aliases and original types cannot be used with new types based on them.
var _ N0 = T0{} // ERROR "cannot use T0{} \(value of type T0\) as N0 value in variable declaration"
var _ N0 = A0{} // ERROR "cannot use A0{} \(value of type A0\) as N0 value in variable declaration"
var _ N0 = T0{} // ERROR "cannot use T0{} \(value of struct type T0\) as N0 value in variable declaration"
var _ N0 = A0{} // ERROR "cannot use A0{} \(value of struct type A0\) as N0 value in variable declaration"
var _ A5 = Value{}
@@ -82,10 +82,10 @@ func _() {
var _ A0 = T0{}
var _ T0 = A0{}
var _ N0 = T0{} // ERROR "cannot use T0{} \(value of type T0\) as N0 value in variable declaration"
var _ N0 = A0{} // ERROR "cannot use A0{} \(value of type A0\) as N0 value in variable declaration"
var _ N0 = T0{} // ERROR "cannot use T0{} \(value of struct type T0\) as N0 value in variable declaration"
var _ N0 = A0{} // ERROR "cannot use A0{} \(value of struct type A0\) as N0 value in variable declaration"
var _ A5 = Value{} // ERROR "cannot use Value{} \(value of type reflect\.Value\) as A5 value in variable declaration"
var _ A5 = Value{} // ERROR "cannot use Value{} \(value of struct type reflect\.Value\) as A5 value in variable declaration"
}
// Invalid type alias declarations.

View File

@@ -9,7 +9,10 @@
package main
import "sync"
import (
"sync"
"time"
)
type T struct {
int
@@ -38,7 +41,7 @@ func main() {
_ = x
}
{
x := sync.Mutex{0, 0} // ERROR "assignment.*Mutex"
x := time.Time{0, 0, nil} // ERROR "assignment.*Time"
_ = x
}
{

View File

@@ -159,16 +159,14 @@ func decode1(data []byte) (x uint64) {
}
func decode2(data []byte) (x uint64) {
// TODO(rasky): this should behave like decode1 and compile to no
// boundchecks. We're currently not able to remove all of them.
for len(data) >= 32 {
x += binary.BigEndian.Uint64(data)
data = data[8:]
x += binary.BigEndian.Uint64(data) // ERROR "Found IsInBounds$"
x += binary.BigEndian.Uint64(data)
data = data[8:]
x += binary.BigEndian.Uint64(data) // ERROR "Found IsInBounds$"
x += binary.BigEndian.Uint64(data)
data = data[8:]
x += binary.BigEndian.Uint64(data) // ERROR "Found IsInBounds$"
x += binary.BigEndian.Uint64(data)
data = data[8:]
}
return x

View File

@@ -20,6 +20,7 @@ func main() {
if x := func() int { // ERROR "can inline main.func2" "func literal does not escape"
return 1
}; x() != 1 { // ERROR "inlining call to main.func2"
_ = x // prevent simple deadcode elimination after inlining
ppanic("x() != 1")
}
}
@@ -33,6 +34,7 @@ func main() {
if y := func(x int) int { // ERROR "can inline main.func4" "func literal does not escape"
return x + 2
}; y(40) != 42 { // ERROR "inlining call to main.func4"
_ = y // prevent simple deadcode elimination after inlining
ppanic("y(40) != 42")
}
}
@@ -50,7 +52,7 @@ func main() {
}
{
func() { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func7"
y := func(x int) int { // ERROR "can inline main.func7.1" "func literal does not escape"
return x + 2
}
@@ -60,7 +62,7 @@ func main() {
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}() // ERROR "func literal does not escape" "inlining call to main.func7"
}
{
@@ -76,7 +78,7 @@ func main() {
}
{
func() { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func10"
y := func(x int) int { // ERROR "can inline main.func10.1" "func literal does not escape"
return x + 2
}
@@ -86,7 +88,7 @@ func main() {
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}() // ERROR "func literal does not escape" "inlining call to main.func10"
}
{
@@ -94,7 +96,7 @@ func main() {
return x + 2
}
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func12"
return func(x int) int { // ERROR "can inline main.func12"
return func(x int) int { // ERROR "can inline main.func12" "func literal escapes to heap"
return x + 1
}, 42
}() // ERROR "func literal does not escape" "inlining call to main.func12"
@@ -104,19 +106,19 @@ func main() {
}
{
func() { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func13"
y := func(x int) int { // ERROR "func literal does not escape" "can inline main.func13.1"
return x + 2
}
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2"
return func(x int) int { // ERROR "can inline main.func13.2"
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2" "can inline main.main.func13.func35"
return func(x int) int { // ERROR "can inline main.func13.2" "func literal escapes to heap"
return x + 1
}, 42
}() // ERROR "func literal does not escape" "inlining call to main.func13.2"
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}() // ERROR "func literal does not escape" "inlining call to main.func13" "inlining call to main.main.func13.func35"
}
{
@@ -132,7 +134,7 @@ func main() {
}
{
func() { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func16"
y := func(x int) int { // ERROR "can inline main.func16.1" "func literal does not escape"
return x + 2
}
@@ -142,7 +144,7 @@ func main() {
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}() // ERROR "func literal does not escape" "inlining call to main.func16" "map\[int\]func\(int\) int{...} does not escape" "func literal escapes to heap"
}
{
@@ -158,7 +160,7 @@ func main() {
}
{
func() { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func19"
y := func(x int) int { // ERROR "can inline main.func19.1" "func literal does not escape"
return x + 2
}
@@ -168,7 +170,7 @@ func main() {
if y(40) != 41 {
ppanic("y(40) != 41")
}
}()
}() // ERROR "func literal does not escape" "inlining call to main.func19"
}
{
@@ -181,6 +183,7 @@ func main() {
if y := func() int { // ERROR "can inline main.func21" "func literal does not escape"
return x
}; y() != 42 { // ERROR "inlining call to main.func21"
_ = y // prevent simple deadcode elimination after inlining
ppanic("y() != 42")
}
}
@@ -188,17 +191,18 @@ func main() {
{
x := 42
if z := func(y int) int { // ERROR "can inline main.func22"
return func() int { // ERROR "can inline main.func22.1" "can inline main.main.func22.func30"
return func() int { // ERROR "can inline main.func22.1" "can inline main.main.func22.func40"
return x + y
}() // ERROR "inlining call to main.func22.1"
}(1); z != 43 { // ERROR "inlining call to main.func22" "inlining call to main.main.func22.func30"
}(1); z != 43 { // ERROR "inlining call to main.func22" "inlining call to main.main.func22.func40"
ppanic("z != 43")
}
if z := func(y int) int { // ERROR "func literal does not escape" "can inline main.func23"
return func() int { // ERROR "can inline main.func23.1" "can inline main.main.func23.func31"
return func() int { // ERROR "can inline main.func23.1" "can inline main.main.func23.func41"
return x + y
}() // ERROR "inlining call to main.func23.1"
}; z(1) != 43 { // ERROR "inlining call to main.func23" "inlining call to main.main.func23.func31"
}; z(1) != 43 { // ERROR "inlining call to main.func23" "inlining call to main.main.func23.func41"
_ = z // prevent simple deadcode elimination after inlining
ppanic("z(1) != 43")
}
}
@@ -206,10 +210,10 @@ func main() {
{
a := 1
func() { // ERROR "can inline main.func24"
func() { // ERROR "can inline main.func24" "can inline main.main.func24.func32"
func() { // ERROR "can inline main.func24" "can inline main.main.func24.func42"
a = 2
}() // ERROR "inlining call to main.func24"
}() // ERROR "inlining call to main.func24" "inlining call to main.main.func24.func32"
}() // ERROR "inlining call to main.func24" "inlining call to main.main.func24.func42"
if a != 2 {
ppanic("a != 2")
}
@@ -217,14 +221,14 @@ func main() {
{
b := 2
func(b int) { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func25.1"
func(b int) { // ERROR "can inline main.func25"
func() { // ERROR "can inline main.func25.1" "can inline main.main.func25.func43"
b = 3
}() // ERROR "inlining call to main.func25.1"
if b != 3 {
ppanic("b != 3")
}
}(b)
}(b) // ERROR "inlining call to main.func25" "inlining call to main.main.func25.func43"
if b != 2 {
ppanic("b != 2")
}
@@ -254,13 +258,13 @@ func main() {
// revisit those. E.g., func34 and func36 are constructed by the inliner.
if r := func(x int) int { // ERROR "can inline main.func27"
b := 3
return func(y int) int { // ERROR "can inline main.func27.1" "can inline main.main.func27.func34"
return func(y int) int { // ERROR "can inline main.func27.1" "can inline main.main.func27.func45"
c := 5
return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.main.func27.func34.1" "can inline main.func27.main.func27.1.2" "can inline main.main.func27.main.main.func27.func34.func36"
return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.main.func27.func45.1" "can inline main.func27.main.func27.1.2" "can inline main.main.func27.main.main.func27.func45.func48"
return a*x + b*y + c*z
}(10) // ERROR "inlining call to main.func27.1.1"
}(100) // ERROR "inlining call to main.func27.1" "inlining call to main.func27.main.func27.1.2"
}(1000); r != 2350 { // ERROR "inlining call to main.func27" "inlining call to main.main.func27.func34" "inlining call to main.main.func27.main.main.func27.func34.func36"
}(1000); r != 2350 { // ERROR "inlining call to main.func27" "inlining call to main.main.func27.func45" "inlining call to main.main.func27.main.main.func27.func45.func48"
ppanic("r != 2350")
}
}
@@ -269,16 +273,16 @@ func main() {
a := 2
if r := func(x int) int { // ERROR "can inline main.func28"
b := 3
return func(y int) int { // ERROR "can inline main.func28.1" "can inline main.main.func28.func35"
return func(y int) int { // ERROR "can inline main.func28.1" "can inline main.main.func28.func46"
c := 5
func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.main.func28.1.2" "can inline main.main.func28.func35.1" "can inline main.main.func28.main.main.func28.func35.func37"
func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.main.func28.1.2" "can inline main.main.func28.func46.1" "can inline main.main.func28.main.main.func28.func46.func49"
a = a * x
b = b * y
c = c * z
}(10) // ERROR "inlining call to main.func28.1.1"
return a + c
}(100) + b // ERROR "inlining call to main.func28.1" "inlining call to main.func28.main.func28.1.2"
}(1000); r != 2350 { // ERROR "inlining call to main.func28" "inlining call to main.main.func28.func35" "inlining call to main.main.func28.main.main.func28.func35.func37"
}(1000); r != 2350 { // ERROR "inlining call to main.func28" "inlining call to main.main.func28.func46" "inlining call to main.main.func28.main.main.func28.func46.func49"
ppanic("r != 2350")
}
if a != 2000 {
@@ -287,6 +291,25 @@ func main() {
}
}
//go:noinline
func notmain() {
{
// This duplicates the first block in main, but without the "_ = x" for closure x.
// This allows dead code elimination of x before escape analysis,
// thus "func literal does not escape" should not appear.
if x := func() int { // ERROR "can inline notmain.func1"
return 1
}(); x != 1 { // ERROR "inlining call to notmain.func1"
ppanic("x != 1")
}
if x := func() int { // ERROR "can inline notmain.func2"
return 1
}; x() != 1 { // ERROR "inlining call to notmain.func2"
ppanic("x() != 1")
}
}
}
//go:noinline
func ppanic(s string) { // ERROR "leaking param: s"
panic(s) // ERROR "s escapes to heap"

View File

@@ -629,3 +629,39 @@ func constantFold3(i, j int) int {
r := (5 * i) * (6 * j)
return r
}
// ----------------- //
// Integer Min/Max //
// ----------------- //
func Int64Min(a, b int64) int64 {
// amd64: "CMPQ","CMOVQLT"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLT\t"
// riscv64/rva22u64:"MIN\t"
return min(a, b)
}
func Int64Max(a, b int64) int64 {
// amd64: "CMPQ","CMOVQGT"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLT\t"
// riscv64/rva22u64:"MAX\t"
return max(a, b)
}
func Uint64Min(a, b uint64) uint64 {
// amd64: "CMPQ","CMOVQCS"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLTU"
// riscv64/rva22u64:"MINU"
return min(a, b)
}
func Uint64Max(a, b uint64) uint64 {
// amd64: "CMPQ","CMOVQHI"
// arm64: "CMP","CSEL"
// riscv64/rva20u64:"BLTU"
// riscv64/rva22u64:"MAXU"
return max(a, b)
}

View File

@@ -22,6 +22,74 @@ func (c *Counter) Increment() {
// arm64/v8.1:"LDADDALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK",-"CMPXCHG"
atomic.AddInt32(&c.count, 1)
}
func atomicLogical64(x *atomic.Uint64) uint64 {
var r uint64
// arm64/v8.0:"LDCLRALD"
// arm64/v8.1:"LDCLRALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+AND instead of CMPXCHG when we don't use the result.
// amd64:"LOCK",-"CMPXCHGQ"
x.And(11)
// arm64/v8.0:"LDCLRALD"
// arm64/v8.1:"LDCLRALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK","CMPXCHGQ"
r += x.And(22)
// arm64/v8.0:"LDORALD"
// arm64/v8.1:"LDORALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+OR instead of CMPXCHG when we don't use the result.
// amd64:"LOCK",-"CMPXCHGQ"
x.Or(33)
// arm64/v8.0:"LDORALD"
// arm64/v8.1:"LDORALD"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK","CMPXCHGQ"
r += x.Or(44)
return r
}
func atomicLogical32(x *atomic.Uint32) uint32 {
var r uint32
// arm64/v8.0:"LDCLRALW"
// arm64/v8.1:"LDCLRALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+AND instead of CMPXCHG when we don't use the result.
// amd64:"LOCK",-"CMPXCHGL"
x.And(11)
// arm64/v8.0:"LDCLRALW"
// arm64/v8.1:"LDCLRALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK","CMPXCHGL"
r += x.And(22)
// arm64/v8.0:"LDORALW"
// arm64/v8.1:"LDORALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// On amd64, make sure we use LOCK+OR instead of CMPXCHG when we don't use the result.
// amd64:"LOCK",-"CMPXCHGL"
x.Or(33)
// arm64/v8.0:"LDORALW"
// arm64/v8.1:"LDORALW"
// arm64/v8.0:".*arm64HasATOMICS"
// arm64/v8.1:-".*arm64HasATOMICS"
// amd64:"LOCK","CMPXCHGL"
r += x.Or(44)
return r
}

View File

@@ -358,11 +358,15 @@ func rev16w(c uint32) (uint32, uint32, uint32) {
func shift(x uint32, y uint16, z uint8) uint64 {
// arm64:-`MOVWU`,-`LSR\t[$]32`
// loong64:-`MOVWU`,-`SRLV\t[$]32`
a := uint64(x) >> 32
// arm64:-`MOVHU
// loong64:-`MOVHU`,-`SRLV\t[$]16`
b := uint64(y) >> 16
// arm64:-`MOVBU`
// loong64:-`MOVBU`,-`SRLV\t[$]8`
c := uint64(z) >> 8
// arm64:`MOVD\tZR`,-`ADD\tR[0-9]+>>16`,-`ADD\tR[0-9]+>>8`,
// loong64:`MOVV\t[$]0`,-`ADDVU`
return a + b + c
}

View File

@@ -366,6 +366,7 @@ func issue48467(x, y uint64) uint64 {
func foldConst(x, y uint64) uint64 {
// arm64: "ADDS\t[$]7",-"MOVD\t[$]7"
// ppc64x: "ADDC\t[$]7,"
d, b := bits.Add64(x, 7, 0)
return b & d
}

View File

@@ -217,53 +217,53 @@ func TestSetInvGeFp64(x float64, y float64) bool {
}
func TestLogicalCompareZero(x *[64]uint64) {
// ppc64x:"ANDCC",^"AND"
b := x[0]&3
if b!=0 {
b := x[0] & 3
if b != 0 {
x[0] = b
}
// ppc64x:"ANDCC",^"AND"
b = x[1]&x[2]
if b!=0 {
b = x[1] & x[2]
if b != 0 {
x[1] = b
}
// ppc64x:"ANDNCC",^"ANDN"
b = x[1]&^x[2]
if b!=0 {
b = x[1] &^ x[2]
if b != 0 {
x[1] = b
}
// ppc64x:"ORCC",^"OR"
b = x[3]|x[4]
if b!=0 {
b = x[3] | x[4]
if b != 0 {
x[3] = b
}
// ppc64x:"SUBCC",^"SUB"
b = x[5]-x[6]
if b!=0 {
b = x[5] - x[6]
if b != 0 {
x[5] = b
}
// ppc64x:"NORCC",^"NOR"
b = ^(x[5]|x[6])
if b!=0 {
b = ^(x[5] | x[6])
if b != 0 {
x[5] = b
}
// ppc64x:"XORCC",^"XOR"
b = x[7]^x[8]
if b!=0 {
b = x[7] ^ x[8]
if b != 0 {
x[7] = b
}
// ppc64x:"ADDCC",^"ADD"
b = x[9]+x[10]
if b!=0 {
b = x[9] + x[10]
if b != 0 {
x[9] = b
}
// ppc64x:"NEGCC",^"NEG"
b = -x[11]
if b!=0 {
if b != 0 {
x[11] = b
}
// ppc64x:"CNTLZDCC",^"CNTLZD"
b = uint64(bits.LeadingZeros64(x[12]))
if b!=0 {
if b != 0 {
x[12] = b
}
@@ -273,4 +273,17 @@ func TestLogicalCompareZero(x *[64]uint64) {
x[12] = uint64(c)
}
// ppc64x:"MULHDUCC",^"MULHDU"
hi, _ := bits.Mul64(x[13], x[14])
if hi != 0 {
x[14] = hi
}
}
func constantWrite(b bool, p *bool) {
if b {
// amd64:`MOVB\t[$]1, \(`
*p = b
}
}

View File

@@ -233,9 +233,9 @@ func CmpToZero(a, b, d int32, e, f int64, deOptC0, deOptC1 bool) int32 {
// arm:`AND`,-`TST`
// 386:`ANDL`
c6 := a&d >= 0
// arm64:`TST\sR[0-9]+<<3,\sR[0-9]+`
// For arm64, could be TST+BGE or AND+TBZ
c7 := e&(f<<3) < 0
// arm64:`CMN\sR[0-9]+<<3,\sR[0-9]+`
// For arm64, could be CMN+BPL or ADD+TBZ
c8 := e+(f<<3) < 0
// arm64:`TST\sR[0-9],\sR[0-9]+`
c9 := e&(-19) < 0
@@ -268,7 +268,7 @@ func CmpToZero(a, b, d int32, e, f int64, deOptC0, deOptC1 bool) int32 {
}
}
func CmpLogicalToZero(a, b, c uint32, d, e uint64) uint64 {
func CmpLogicalToZero(a, b, c uint32, d, e, f, g uint64) uint64 {
// ppc64x:"ANDCC",-"CMPW"
// wasm:"I64Eqz",-"I32Eqz",-"I64ExtendI32U",-"I32WrapI64"
@@ -289,7 +289,7 @@ func CmpLogicalToZero(a, b, c uint32, d, e uint64) uint64 {
}
// ppc64x:"ORCC",-"CMP"
// wasm:"I64Eqz",-"I32Eqz",-"I64ExtendI32U",-"I32WrapI64"
if d|e == 0 {
if f|g == 0 {
return 1
}

View File

@@ -95,6 +95,7 @@ func moveArchLowering1(b []byte, x *[1]byte) {
_ = b[1]
// amd64:-".*memmove"
// arm64:-".*memmove"
// loong64:-".*memmove"
// ppc64x:-".*memmove"
copy(b, x[:])
}
@@ -103,6 +104,7 @@ func moveArchLowering2(b []byte, x *[2]byte) {
_ = b[2]
// amd64:-".*memmove"
// arm64:-".*memmove"
// loong64:-".*memmove"
// ppc64x:-".*memmove"
copy(b, x[:])
}
@@ -111,6 +113,7 @@ func moveArchLowering4(b []byte, x *[4]byte) {
_ = b[4]
// amd64:-".*memmove"
// arm64:-".*memmove"
// loong64:-".*memmove"
// ppc64x:-".*memmove"
copy(b, x[:])
}

View File

@@ -54,11 +54,13 @@ func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
func indexLoad(b0 []float32, b1 float32, idx int) float32 {
// arm64:`FMOVS\s\(R[0-9]+\)\(R[0-9]+<<2\),\sF[0-9]+`
// loong64:`MOVF\s\(R[0-9]+\)\(R[0-9]+\),\sF[0-9]+`
return b0[idx] * b1
}
func indexStore(b0 []float64, b1 float64, idx int) {
// arm64:`FMOVD\sF[0-9]+,\s\(R[0-9]+\)\(R[0-9]+<<3\)`
// loong64:`MOVD\sF[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`
b0[idx] = b1
}
@@ -70,6 +72,7 @@ func FusedAdd32(x, y, z float32) float32 {
// s390x:"FMADDS\t"
// ppc64x:"FMADDS\t"
// arm64:"FMADDS"
// loong64:"FMADDF\t"
// riscv64:"FMADDS\t"
return x*y + z
}
@@ -78,11 +81,13 @@ func FusedSub32_a(x, y, z float32) float32 {
// s390x:"FMSUBS\t"
// ppc64x:"FMSUBS\t"
// riscv64:"FMSUBS\t"
// loong64:"FMSUBF\t"
return x*y - z
}
func FusedSub32_b(x, y, z float32) float32 {
// arm64:"FMSUBS"
// loong64:"FNMSUBF\t"
// riscv64:"FNMSUBS\t"
return z - x*y
}
@@ -91,6 +96,7 @@ func FusedAdd64(x, y, z float64) float64 {
// s390x:"FMADD\t"
// ppc64x:"FMADD\t"
// arm64:"FMADDD"
// loong64:"FMADDD\t"
// riscv64:"FMADDD\t"
return x*y + z
}
@@ -99,11 +105,13 @@ func FusedSub64_a(x, y, z float64) float64 {
// s390x:"FMSUB\t"
// ppc64x:"FMSUB\t"
// riscv64:"FMSUBD\t"
// loong64:"FMSUBD\t"
return x*y - z
}
func FusedSub64_b(x, y, z float64) float64 {
// arm64:"FMSUBD"
// loong64:"FNMSUBD\t"
// riscv64:"FNMSUBD\t"
return z - x*y
}
@@ -164,6 +172,7 @@ func ArrayCopy(a [16]byte) (b [16]byte) {
func Float64Min(a, b float64) float64 {
// amd64:"MINSD"
// arm64:"FMIND"
// loong64:"FMIND"
// riscv64:"FMIN"
// ppc64/power9:"XSMINJDP"
// ppc64/power10:"XSMINJDP"
@@ -173,6 +182,7 @@ func Float64Min(a, b float64) float64 {
func Float64Max(a, b float64) float64 {
// amd64:"MINSD"
// arm64:"FMAXD"
// loong64:"FMAXD"
// riscv64:"FMAX"
// ppc64/power9:"XSMAXJDP"
// ppc64/power10:"XSMAXJDP"
@@ -182,6 +192,7 @@ func Float64Max(a, b float64) float64 {
func Float32Min(a, b float32) float32 {
// amd64:"MINSS"
// arm64:"FMINS"
// loong64:"FMINF"
// riscv64:"FMINS"
// ppc64/power9:"XSMINJDP"
// ppc64/power10:"XSMINJDP"
@@ -191,6 +202,7 @@ func Float32Min(a, b float32) float32 {
func Float32Max(a, b float32) float32 {
// amd64:"MINSS"
// arm64:"FMAXS"
// loong64:"FMAXF"
// riscv64:"FMAXS"
// ppc64/power9:"XSMAXJDP"
// ppc64/power10:"XSMAXJDP"
@@ -227,3 +239,12 @@ func Float64DenormalFloat32Constant() float64 {
// ppc64x:"FMOVD\t[$]f64\\.3800000000000000\\(SB\\)"
return 0x1p-127
}
func Float64ConstantStore(p *float64) {
// amd64: "MOVQ\t[$]4617801906721357038"
*p = 5.432
}
func Float32ConstantStore(p *float32) {
// amd64: "MOVL\t[$]1085133554"
*p = 5.432
}

View File

@@ -0,0 +1,17 @@
// asmcheck
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package codegen
func f(x, y int, p *int) {
// amd64:`MOVQ\sAX, BX`
h(8, x)
*p = y
}
//go:noinline
func h(a, b int) {
}

View File

@@ -0,0 +1,52 @@
// asmcheck
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package codegen
type T1 struct {
x string
}
func f1() *T1 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T1{}
}
type T2 struct {
x, y string
}
func f2() *T2 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T2{}
}
type T3 struct {
x complex128
}
func f3() *T3 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T3{}
}
type T4 struct {
x []byte
}
func f4() *T4 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T4{}
}
type T5 struct {
x any
}
func f5() *T5 {
// amd64:-`MOVQ\s[$]0`,-`MOVUPS\sX15`
return &T5{}
}

View File

@@ -0,0 +1,13 @@
// asmcheck
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package codegen
func calc(a uint64) uint64 {
v := a >> 20 & 0x7f
// amd64: `SHRQ\s\$17, AX$`, `ANDL\s\$1016, AX$`
return v << 3
}

View File

@@ -74,7 +74,7 @@ func LookupStringConversionKeyedArrayLit(m map[[2]string]int, bytes []byte) int
func MapClearReflexive(m map[int]int) {
// amd64:`.*runtime\.mapclear`
// amd64:-`.*runtime\.mapiterinit`
// amd64:-`.*runtime\.(mapiterinit|mapIterStart)`
for k := range m {
delete(m, k)
}
@@ -83,7 +83,7 @@ func MapClearReflexive(m map[int]int) {
func MapClearIndirect(m map[int]int) {
s := struct{ m map[int]int }{m: m}
// amd64:`.*runtime\.mapclear`
// amd64:-`.*runtime\.mapiterinit`
// amd64:-`.*runtime\.(mapiterinit|mapIterStart)`
for k := range s.m {
delete(s.m, k)
}
@@ -91,14 +91,14 @@ func MapClearIndirect(m map[int]int) {
func MapClearPointer(m map[*byte]int) {
// amd64:`.*runtime\.mapclear`
// amd64:-`.*runtime\.mapiterinit`
// amd64:-`.*runtime\.(mapiterinit|mapIterStart)`
for k := range m {
delete(m, k)
}
}
func MapClearNotReflexive(m map[float64]int) {
// amd64:`.*runtime\.mapiterinit`
// amd64:`.*runtime\.(mapiterinit|mapIterStart)`
// amd64:-`.*runtime\.mapclear`
for k := range m {
delete(m, k)
@@ -106,7 +106,7 @@ func MapClearNotReflexive(m map[float64]int) {
}
func MapClearInterface(m map[interface{}]int) {
// amd64:`.*runtime\.mapiterinit`
// amd64:`.*runtime\.(mapiterinit|mapIterStart)`
// amd64:-`.*runtime\.mapclear`
for k := range m {
delete(m, k)
@@ -115,7 +115,7 @@ func MapClearInterface(m map[interface{}]int) {
func MapClearSideEffect(m map[int]int) int {
k := 0
// amd64:`.*runtime\.mapiterinit`
// amd64:`.*runtime\.(mapiterinit|mapIterStart)`
// amd64:-`.*runtime\.mapclear`
for k = range m {
delete(m, k)

View File

@@ -132,6 +132,7 @@ func fma(x, y, z float64) float64 {
// amd64:"VFMADD231SD"
// arm/6:"FMULAD"
// arm64:"FMADDD"
// loong64:"FMADDD"
// s390x:"FMADD"
// ppc64x:"FMADD"
// riscv64:"FMADDD"
@@ -156,6 +157,7 @@ func fnma(x, y, z float64) float64 {
func fromFloat64(f64 float64) uint64 {
// amd64:"MOVQ\tX.*, [^X].*"
// arm64:"FMOVD\tF.*, R.*"
// loong64:"MOVV\tF.*, R.*"
// ppc64x:"MFVSRD"
// mips64/hardfloat:"MOVV\tF.*, R.*"
return math.Float64bits(f64+1) + 1
@@ -164,6 +166,7 @@ func fromFloat64(f64 float64) uint64 {
func fromFloat32(f32 float32) uint32 {
// amd64:"MOVL\tX.*, [^X].*"
// arm64:"FMOVS\tF.*, R.*"
// loong64:"MOVW\tF.*, R.*"
// mips64/hardfloat:"MOVW\tF.*, R.*"
return math.Float32bits(f32+1) + 1
}
@@ -171,6 +174,7 @@ func fromFloat32(f32 float32) uint32 {
func toFloat64(u64 uint64) float64 {
// amd64:"MOVQ\t[^X].*, X.*"
// arm64:"FMOVD\tR.*, F.*"
// loong64:"MOVV\tR.*, F.*"
// ppc64x:"MTVSRD"
// mips64/hardfloat:"MOVV\tR.*, F.*"
return math.Float64frombits(u64+1) + 1
@@ -179,6 +183,7 @@ func toFloat64(u64 uint64) float64 {
func toFloat32(u32 uint32) float32 {
// amd64:"MOVL\t[^X].*, X.*"
// arm64:"FMOVS\tR.*, F.*"
// loong64:"MOVW\tR.*, F.*"
// mips64/hardfloat:"MOVW\tR.*, F.*"
return math.Float32frombits(u32+1) + 1
}

View File

@@ -17,6 +17,7 @@ func LeadingZeros(n uint) int {
// amd64/v3:"LZCNTQ", -"BSRQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV",-"SUB"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
@@ -28,6 +29,7 @@ func LeadingZeros64(n uint64) int {
// amd64/v3:"LZCNTQ", -"BSRQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV",-"SUB"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
@@ -39,6 +41,7 @@ func LeadingZeros32(n uint32) int {
// amd64/v3: "LZCNTL",- "BSRL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZW"
// loong64:"CLZW",-"SUB"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZW"
@@ -50,6 +53,7 @@ func LeadingZeros16(n uint16) int {
// amd64/v3: "LZCNTL",- "BSRL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
@@ -61,6 +65,7 @@ func LeadingZeros8(n uint8) int {
// amd64/v3: "LZCNTL",- "BSRL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"CNTLZD"
@@ -76,6 +81,7 @@ func Len(n uint) int {
// amd64/v3: "LZCNTQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
@@ -87,6 +93,7 @@ func Len64(n uint64) int {
// amd64/v3: "LZCNTQ"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
@@ -94,15 +101,22 @@ func Len64(n uint64) int {
}
func SubFromLen64(n uint64) int {
// loong64:"CLZV",-"ADD"
// ppc64x:"CNTLZD",-"SUBC"
return 64 - bits.Len64(n)
}
func CompareWithLen64(n uint64) bool {
// loong64:"CLZV",-"ADD",-"[$]64",-"[$]9"
return bits.Len64(n) < 9
}
func Len32(n uint32) int {
// amd64/v1,amd64/v2:"BSRQ","LEAQ",-"CMOVQEQ"
// amd64/v3: "LZCNTL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZW"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x: "CNTLZW"
@@ -114,6 +128,7 @@ func Len16(n uint16) int {
// amd64/v3: "LZCNTL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
@@ -125,6 +140,7 @@ func Len8(n uint8) int {
// amd64/v3: "LZCNTL"
// s390x:"FLOGR"
// arm:"CLZ" arm64:"CLZ"
// loong64:"CLZV"
// mips:"CLZ"
// wasm:"I64Clz"
// ppc64x:"SUBC","CNTLZD"
@@ -140,6 +156,7 @@ func OnesCount(n uint) int {
// amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT"
// amd64:"POPCNTQ"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTV"
// s390x:"POPCNT"
// ppc64x:"POPCNTD"
// wasm:"I64Popcnt"
@@ -150,6 +167,7 @@ func OnesCount64(n uint64) int {
// amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT"
// amd64:"POPCNTQ"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTV"
// s390x:"POPCNT"
// ppc64x:"POPCNTD"
// wasm:"I64Popcnt"
@@ -160,6 +178,7 @@ func OnesCount32(n uint32) int {
// amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT"
// amd64:"POPCNTL"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTW"
// s390x:"POPCNT"
// ppc64x:"POPCNTW"
// wasm:"I64Popcnt"
@@ -170,6 +189,7 @@ func OnesCount16(n uint16) int {
// amd64/v2:-".*x86HasPOPCNT" amd64/v3:-".*x86HasPOPCNT"
// amd64:"POPCNTL"
// arm64:"VCNT","VUADDLV"
// loong64:"VPCNTH"
// s390x:"POPCNT"
// ppc64x:"POPCNTW"
// wasm:"I64Popcnt"
@@ -183,6 +203,35 @@ func OnesCount8(n uint8) int {
return bits.OnesCount8(n)
}
// ------------------ //
// bits.Reverse //
// ------------------ //
func Reverse(n uint) uint {
// loong64:"BITREVV"
return bits.Reverse(n)
}
func Reverse64(n uint64) uint64 {
// loong64:"BITREVV"
return bits.Reverse64(n)
}
func Reverse32(n uint32) uint32 {
// loong64:"BITREVW"
return bits.Reverse32(n)
}
func Reverse16(n uint16) uint16 {
// loong64:"BITREV4B","REVB2H"
return bits.Reverse16(n)
}
func Reverse8(n uint8) uint8 {
// loong64:"BITREV4B"
return bits.Reverse8(n)
}
// ----------------------- //
// bits.ReverseBytes //
// ----------------------- //
@@ -192,6 +241,7 @@ func ReverseBytes(n uint) uint {
// 386:"BSWAPL"
// s390x:"MOVDBR"
// arm64:"REV"
// loong64:"REVBV"
return bits.ReverseBytes(n)
}
@@ -201,6 +251,7 @@ func ReverseBytes64(n uint64) uint64 {
// s390x:"MOVDBR"
// arm64:"REV"
// ppc64x/power10: "BRD"
// loong64:"REVBV"
return bits.ReverseBytes64(n)
}
@@ -209,6 +260,7 @@ func ReverseBytes32(n uint32) uint32 {
// 386:"BSWAPL"
// s390x:"MOVWBR"
// arm64:"REVW"
// loong64:"REVB2W"
// ppc64x/power10: "BRW"
return bits.ReverseBytes32(n)
}
@@ -219,6 +271,7 @@ func ReverseBytes16(n uint16) uint16 {
// arm/5:"SLL","SRL","ORR"
// arm/6:"REV16"
// arm/7:"REV16"
// loong64:"REVB2H"
// ppc64x/power10: "BRH"
return bits.ReverseBytes16(n)
}
@@ -230,7 +283,9 @@ func ReverseBytes16(n uint16) uint16 {
func RotateLeft64(n uint64) uint64 {
// amd64:"ROLQ"
// arm64:"ROR"
// loong64:"ROTRV"
// ppc64x:"ROTL"
// riscv64:"RORI"
// s390x:"RISBGZ\t[$]0, [$]63, [$]37, "
// wasm:"I64Rotl"
return bits.RotateLeft64(n, 37)
@@ -240,7 +295,9 @@ func RotateLeft32(n uint32) uint32 {
// amd64:"ROLL" 386:"ROLL"
// arm:`MOVW\tR[0-9]+@>23`
// arm64:"RORW"
// loong64:"ROTR\t"
// ppc64x:"ROTLW"
// riscv64:"RORIW"
// s390x:"RLL"
// wasm:"I32Rotl"
return bits.RotateLeft32(n, 9)
@@ -249,19 +306,23 @@ func RotateLeft32(n uint32) uint32 {
func RotateLeft16(n uint16, s int) uint16 {
// amd64:"ROLW" 386:"ROLW"
// arm64:"RORW",-"CSEL"
// loong64:"ROTR\t","SLLV"
return bits.RotateLeft16(n, s)
}
func RotateLeft8(n uint8, s int) uint8 {
// amd64:"ROLB" 386:"ROLB"
// arm64:"LSL","LSR",-"CSEL"
// loong64:"OR","SLLV","SRLV"
return bits.RotateLeft8(n, s)
}
func RotateLeftVariable(n uint, m int) uint {
// amd64:"ROLQ"
// arm64:"ROR"
// loong64:"ROTRV"
// ppc64x:"ROTL"
// riscv64:"ROL"
// s390x:"RLLG"
// wasm:"I64Rotl"
return bits.RotateLeft(n, m)
@@ -270,7 +331,9 @@ func RotateLeftVariable(n uint, m int) uint {
func RotateLeftVariable64(n uint64, m int) uint64 {
// amd64:"ROLQ"
// arm64:"ROR"
// loong64:"ROTRV"
// ppc64x:"ROTL"
// riscv64:"ROL"
// s390x:"RLLG"
// wasm:"I64Rotl"
return bits.RotateLeft64(n, m)
@@ -280,7 +343,9 @@ func RotateLeftVariable32(n uint32, m int) uint32 {
// arm:`MOVW\tR[0-9]+@>R[0-9]+`
// amd64:"ROLL"
// arm64:"RORW"
// loong64:"ROTR\t"
// ppc64x:"ROTLW"
// riscv64:"ROLW"
// s390x:"RLL"
// wasm:"I32Rotl"
return bits.RotateLeft32(n, m)
@@ -296,6 +361,7 @@ func TrailingZeros(n uint) int {
// 386:"BSFL"
// arm:"CLZ"
// arm64:"RBIT","CLZ"
// loong64:"CTZV"
// s390x:"FLOGR"
// ppc64x/power8:"ANDN","POPCNTD"
// ppc64x/power9: "CNTTZD"
@@ -308,6 +374,7 @@ func TrailingZeros64(n uint64) int {
// amd64/v3:"TZCNTQ"
// 386:"BSFL"
// arm64:"RBIT","CLZ"
// loong64:"CTZV"
// s390x:"FLOGR"
// ppc64x/power8:"ANDN","POPCNTD"
// ppc64x/power9: "CNTTZD"
@@ -327,6 +394,7 @@ func TrailingZeros32(n uint32) int {
// 386:"BSFL"
// arm:"CLZ"
// arm64:"RBITW","CLZW"
// loong64:"CTZW"
// s390x:"FLOGR","MOVWZ"
// ppc64x/power8:"ANDN","POPCNTW"
// ppc64x/power9: "CNTTZW"
@@ -339,6 +407,7 @@ func TrailingZeros16(n uint16) int {
// 386:"BSFL\t"
// 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/power9:"CNTTZD","ORIS\\t\\$1"
@@ -351,6 +420,7 @@ func TrailingZeros8(n uint8) int {
// 386:"BSFL"
// arm:"ORR\t\\$256","CLZ",-"MOVBU\tR"
// arm64:"ORR\t\\$256","RBITW","CLZW",-"MOVBU\tR",-"RBIT\t",-"CLZ\t"
// loong64:"CTZV"
// s390x:"FLOGR","OR\t\\$256"
// wasm:"I64Ctz"
return bits.TrailingZeros8(n)

View File

@@ -19,6 +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]+\),`
// ppc64le:`MOVD\s`,-`MOV[BHW]Z`
// ppc64:`MOVDBR\s`,-`MOV[BHW]Z`
return binary.LittleEndian.Uint64(b)
@@ -28,6 +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]+\),`
// ppc64le:`MOVD\s`,-`MOV[BHW]Z\s`
// ppc64:`MOVDBR\s`,-`MOV[BHW]Z\s`
return binary.LittleEndian.Uint64(b[idx:])
@@ -38,6 +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]+\),`
// ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s`
// ppc64:`MOVWBR\s`,-`MOV[BH]Z\s`
return binary.LittleEndian.Uint32(b)
@@ -48,6 +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]+\),`
// ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s`
// ppc64:`MOVWBR\s`,-`MOV[BH]Z\s'
return binary.LittleEndian.Uint32(b[idx:])
@@ -57,6 +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]+\),`
// s390x:`MOVHBR\s\(.*\),`
// ppc64:`MOVHBR\s`,-`MOVBZ`
return binary.LittleEndian.Uint16(b)
@@ -67,6 +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]+\),`
// s390x:`MOVHBR\s\(.*\)\(.*\*1\),`
return binary.LittleEndian.Uint16(b[idx:])
}
@@ -938,3 +944,29 @@ func issue66413(p *struct {
p.c = true
p.d = 12
}
func issue70300(v uint64) (b [8]byte) {
// amd64:"MOVQ",-"MOVB"
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
}
func issue70300Reverse(v uint64) (b [8]byte) {
// amd64:"MOVQ",-"MOVB"
b[7] = byte(v >> 56)
b[6] = byte(v >> 48)
b[5] = byte(v >> 40)
b[4] = byte(v >> 32)
b[3] = byte(v >> 24)
b[2] = byte(v >> 16)
b[1] = byte(v >> 8)
b[0] = byte(v)
return b
}

View File

@@ -462,12 +462,6 @@ func checkMergedShifts64(a [256]uint32, b [256]uint64, c [256]byte, v uint64) {
a[2] = a[v>>25&0x7F]
// ppc64x: -"CLRLSLDI", "RLWNM\t[$]3, R[0-9]+, [$]29, [$]29, R[0-9]+"
a[3] = a[(v>>31)&0x01]
// ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
a[4] = a[(v>>30)&0x07]
// ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
a[5] = a[(v>>32)&0x01]
// ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
a[6] = a[(v>>34)&0x03]
// ppc64x: -"CLRLSLDI", "RLWNM\t[$]12, R[0-9]+, [$]21, [$]28, R[0-9]+"
b[0] = b[uint8(v>>23)]
// ppc64x: -"CLRLSLDI", "RLWNM\t[$]15, R[0-9]+, [$]21, [$]28, R[0-9]+"
@@ -476,7 +470,7 @@ func checkMergedShifts64(a [256]uint32, b [256]uint64, c [256]byte, v uint64) {
b[2] = b[((uint64((uint32(v) >> 21)) & 0x3f) << 4)]
// ppc64x: "RLWNM\t[$]11, R[0-9]+, [$]10, [$]15"
c[0] = c[((v>>5)&0x3F)<<16]
// ppc64x: "RLWNM\t[$]0, R[0-9]+, [$]19, [$]24"
// ppc64x: "ANDCC\t[$]8064,"
c[1] = c[((v>>7)&0x3F)<<7]
}
@@ -520,3 +514,20 @@ func checkShiftToMask(u []uint64, s []int64) {
// amd64:-"SHR",-"SHL","ANDQ"
u[1] = u[1] << 5 >> 5
}
//
// Left shift with addition.
//
func checkLeftShiftWithAddition(a int64, b int64) int64 {
// riscv64/rva20u64: "SLLI","ADD"
// riscv64/rva22u64: "SH1ADD"
a = a + b<<1
// riscv64/rva20u64: "SLLI","ADD"
// riscv64/rva22u64: "SH2ADD"
a = a + b<<2
// riscv64/rva20u64: "SLLI","ADD"
// riscv64/rva22u64: "SH3ADD"
a = a + b<<3
return a
}

View File

@@ -47,6 +47,7 @@ func SliceExtensionConst(s []int) []int {
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS\tX15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
@@ -58,6 +59,7 @@ func SliceExtensionConstInt64(s []int) []int {
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS\tX15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
@@ -69,6 +71,7 @@ func SliceExtensionConstUint64(s []int) []int {
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS\tX15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
@@ -80,16 +83,18 @@ func SliceExtensionConstUint(s []int) []int {
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
// amd64:"MOVUPS\tX15"
// loong64:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.memclrNoHeapPointers`
// ppc64x:-`.*runtime\.makeslice`
// ppc64x:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, uint(1<<2))...)
}
// On ppc64x continue to use memclrNoHeapPointers
// On ppc64x and loong64 continue to use memclrNoHeapPointers
// for sizes >= 512.
func SliceExtensionConst512(s []int) []int {
// amd64:-`.*runtime\.memclrNoHeapPointers`
// loong64:`.*runtime\.memclrNoHeapPointers`
// ppc64x:`.*runtime\.memclrNoHeapPointers`
return append(s, make([]int, 1<<9)...)
}

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

@@ -0,0 +1,31 @@
// asmcheck
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package codegen
func i64(a, b int64) int64 { // arm64:`STP\s`,`LDP\s`
g()
return a + b
}
func i32(a, b int32) int32 { // arm64:`STPW`,`LDPW`
g()
return a + b
}
func f64(a, b float64) float64 { // arm64:`FSTPD`,`FLDPD`
g()
return a + b
}
func f32(a, b float32) float32 { // arm64:`FSTPS`,`FLDPS`
g()
return a + b
}
//go:noinline
func g() {
}

View File

@@ -26,6 +26,11 @@ func ToByteSlice() []byte { // Issue #24698
return []byte("foo")
}
func ConvertToByteSlice(a, b, c string) []byte {
// amd64:`.*runtime.concatbyte3`
return []byte(a + b + c)
}
// Loading from read-only symbols should get transformed into constants.
func ConstantLoad() {
// 12592 = 0x3130

View File

@@ -0,0 +1,67 @@
// asmcheck
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package codegen
type Ix interface {
X()
}
type Iy interface {
Y()
}
type Iz interface {
Z()
}
func swXYZ(a Ix) {
switch t := a.(type) {
case Iy: // amd64:-".*typeAssert"
t.Y()
case Iz: // amd64:-".*typeAssert"
t.Z()
}
}
type Ig[T any] interface {
G() T
}
func swGYZ[T any](a Ig[T]) {
switch t := a.(type) {
case Iy: // amd64:-".*typeAssert"
t.Y()
case Iz: // amd64:-".*typeAssert"
t.Z()
case interface{ G() T }: // amd64:-".*typeAssert",-".*assertE2I\\(",".*assertE2I2"
t.G()
}
}
func swE2G[T any](a any) {
switch t := a.(type) {
case Iy:
t.Y()
case Ig[T]: // amd64:-".*assertE2I\\(",".*assertE2I2"
t.G()
}
}
func swI2G[T any](a Ix) {
switch t := a.(type) {
case Iy:
t.Y()
case Ig[T]: // amd64:-".*assertE2I\\(",".*assertE2I2"
t.G()
}
}
func swCaller() {
swGYZ[int]((Ig[int])(nil))
swE2G[int]((Ig[int])(nil))
swI2G[int]((Ix)(nil))
}

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

@@ -0,0 +1,16 @@
// asmcheck
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package codegen
import "unsafe"
func f(p unsafe.Pointer, x, y uintptr) int64 {
p = unsafe.Pointer(uintptr(p) + x + y)
// amd64:`MOVQ\s\(.*\)\(.*\*1\), `
// arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\), `
return *(*int64)(p)
}

View File

@@ -54,6 +54,16 @@ func combine4slice(p *[4][]byte, a, b, c, d []byte) {
p[3] = d
}
func trickyWriteNil(p *int, q **int) {
if p == nil {
// We change "= p" to "= 0" in the prove pass, which
// means we have one less pointer that needs to go
// into the write barrier buffer.
// amd64:`.*runtime[.]gcWriteBarrier1`
*q = p
}
}
type S struct {
a, b string
c *int

View File

@@ -84,3 +84,57 @@ label:
fmt.Println("defer")
}()
}
// Test for function with too many exits, which will disable open-coded defer
// even though the number of defer statements is not greater than 8.
func f7() {
defer println(1) // ERROR "open-coded defer"
defer println(1) // ERROR "open-coded defer"
defer println(1) // ERROR "open-coded defer"
defer println(1) // ERROR "open-coded defer"
switch glob {
case 1:
return
case 2:
return
case 3:
return
}
}
func f8() {
defer println(1) // ERROR "stack-allocated defer"
defer println(1) // ERROR "stack-allocated defer"
defer println(1) // ERROR "stack-allocated defer"
defer println(1) // ERROR "stack-allocated defer"
switch glob {
case 1:
return
case 2:
return
case 3:
return
case 4:
return
}
}
func f9() {
defer println(1) // ERROR "open-coded defer"
defer println(1) // ERROR "open-coded defer"
defer println(1) // ERROR "open-coded defer"
defer println(1) // ERROR "open-coded defer"
switch glob {
case 1:
return
case 2:
return
case 3:
return
case 4:
panic("")
}
}

View File

@@ -25,7 +25,7 @@ func f1() {
// Escape analysis used to miss inlined code in closures.
func() { // ERROR "can inline f1.func1"
p = alloc(3) // ERROR "inlining call to alloc"
p = alloc(3) // ERROR "inlining call to alloc" "moved to heap: x"
}() // ERROR "inlining call to f1.func1" "inlining call to alloc" "moved to heap: x"
f = func() { // ERROR "func literal escapes to heap" "can inline f1.func2"

View File

@@ -134,7 +134,7 @@ func ClosureCallArgs14() {
func ClosureCallArgs15() {
x := 0 // ERROR "moved to heap: x"
p := &x
sink = func(p **int) *int { // ERROR "leaking param content: p" "func literal does not escape"
sink = func(p **int) *int { // ERROR "leaking param: p to result ~r0 level=1" "func literal does not escape"
return *p
}(&p)
}

View File

@@ -423,7 +423,7 @@ func mapdelete(m map[string]string, k string) { // ERROR "m does not escape" "le
}
// Unfortunate: v doesn't need to leak.
func setiterkey1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "it does not escape"
func setiterkey1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "leaking param content: it$"
v.SetIterKey(it)
}
@@ -434,7 +434,7 @@ func setiterkey2(v reflect.Value, m map[string]string) { // ERROR "leaking param
}
// Unfortunate: v doesn't need to leak.
func setitervalue1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "it does not escape"
func setitervalue1(v reflect.Value, it *reflect.MapIter) { // ERROR "leaking param: v$" "leaking param content: it$"
v.SetIterValue(it)
}

View File

@@ -18,7 +18,7 @@ type I4 interface { // GC_ERROR "invalid recursive type: I4 refers to itself"
I4 // GCCGO_ERROR "interface"
}
type I5 interface { // GC_ERROR "invalid recursive type I5\n\tLINE:.* I5 refers to\n\tLINE+4:.* I6 refers to\n\tLINE:.* I5$"
type I5 interface { // GC_ERROR "invalid recursive type I5\n\tLINE:.* I5 refers to I6\n\tLINE+4:.* I6 refers to I5$"
I6
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
package main
import "runtime"
func foo(runtime.UintType, i int) { // ERROR "cannot declare name runtime.UintType|mixed named and unnamed|undefined identifier"
func foo(runtime.UintType, i int) { // ERROR "cannot declare name runtime.UintType|missing parameter name|undefined identifier"
println(i, runtime.UintType) // GCCGO_ERROR "undefined identifier"
}

View File

@@ -10,7 +10,7 @@
package a
var? // ERROR "invalid character U\+003F '\?'|invalid character 0x3f in input file"
var x int // ERROR "unexpected var|expected identifier|expected type"
var x int // ERROR "unexpected keyword var|expected identifier|expected type"
func main() {
}

View File

@@ -0,0 +1,79 @@
// errorcheck -0 -m -l
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package foo
import "sync/atomic"
func AddInt32(x *int32) { // ERROR "x does not escape$"
atomic.AddInt32(x, 42)
}
func AddUint32(x *uint32) { // ERROR "x does not escape$"
atomic.AddUint32(x, 42)
}
func AddUintptr(x *uintptr) { // ERROR "x does not escape$"
atomic.AddUintptr(x, 42)
}
func AndInt32(x *int32) { // ERROR "x does not escape$"
atomic.AndInt32(x, 42)
}
func AndUint32(x *uint32) { // ERROR "x does not escape$"
atomic.AndUint32(x, 42)
}
func AndUintptr(x *uintptr) { // ERROR "x does not escape$"
atomic.AndUintptr(x, 42)
}
func CompareAndSwapInt32(x *int32) { // ERROR "x does not escape$"
atomic.CompareAndSwapInt32(x, 42, 42)
}
func CompareAndSwapUint32(x *uint32) { // ERROR "x does not escape$"
atomic.CompareAndSwapUint32(x, 42, 42)
}
func CompareAndSwapUintptr(x *uintptr) { // ERROR "x does not escape$"
atomic.CompareAndSwapUintptr(x, 42, 42)
}
func LoadInt32(x *int32) { // ERROR "x does not escape$"
atomic.LoadInt32(x)
}
func LoadUint32(x *uint32) { // ERROR "x does not escape$"
atomic.LoadUint32(x)
}
func LoadUintptr(x *uintptr) { // ERROR "x does not escape$"
atomic.LoadUintptr(x)
}
func OrInt32(x *int32) { // ERROR "x does not escape$"
atomic.OrInt32(x, 42)
}
func OrUint32(x *uint32) { // ERROR "x does not escape$"
atomic.OrUint32(x, 42)
}
func OrUintptr(x *uintptr) { // ERROR "x does not escape$"
atomic.OrUintptr(x, 42)
}
func StoreInt32(x *int32) { // ERROR "x does not escape$"
atomic.StoreInt32(x, 42)
}
func StoreUint32(x *uint32) { // ERROR "x does not escape$"
atomic.StoreUint32(x, 42)
}
func StoreUintptr(x *uintptr) { // ERROR "x does not escape$"
atomic.StoreUintptr(x, 42)
}
func SwapInt32(x *int32) { // ERROR "x does not escape$"
atomic.SwapInt32(x, 42)
}
func SwapUint32(x *uint32) { // ERROR "x does not escape$"
atomic.SwapUint32(x, 42)
}
func SwapUintptr(x *uintptr) { // ERROR "x does not escape$"
atomic.SwapUintptr(x, 42)
}

View File

@@ -0,0 +1,60 @@
//go:build !(386 || arm || mips || mipsle)
// errorcheck -0 -m -l
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package foo
import "sync/atomic"
func AddInt64(x *int64) { // ERROR "x does not escape$"
atomic.AddInt64(x, 42)
}
func AddUint64(x *uint64) { // ERROR "x does not escape$"
atomic.AddUint64(x, 42)
}
func AndInt64(x *int64) { // ERROR "x does not escape$"
atomic.AndInt64(x, 42)
}
func AndUint64(x *uint64) { // ERROR "x does not escape$"
atomic.AndUint64(x, 42)
}
func CompareAndSwapInt64(x *int64) { // ERROR "x does not escape$"
atomic.CompareAndSwapInt64(x, 42, 42)
}
func CompareAndSwapUint64(x *uint64) { // ERROR "x does not escape$"
atomic.CompareAndSwapUint64(x, 42, 42)
}
func LoadInt64(x *int64) { // ERROR "x does not escape$"
atomic.LoadInt64(x)
}
func LoadUint64(x *uint64) { // ERROR "x does not escape$"
atomic.LoadUint64(x)
}
func OrInt64(x *int64) { // ERROR "x does not escape$"
atomic.OrInt64(x, 42)
}
func OrUint64(x *uint64) { // ERROR "x does not escape$"
atomic.OrUint64(x, 42)
}
func StoreInt64(x *int64) { // ERROR "x does not escape$"
atomic.StoreInt64(x, 42)
}
func StoreUint64(x *uint64) { // ERROR "x does not escape$"
atomic.StoreUint64(x, 42)
}
func SwapInt64(x *int64) { // ERROR "x does not escape$"
atomic.SwapInt64(x, 42)
}
func SwapUint64(x *uint64) { // ERROR "x does not escape$"
atomic.SwapUint64(x, 42)
}

View File

@@ -0,0 +1,13 @@
// errorcheck
// 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 p
var _ chan [0x2FFFF]byte // ERROR "channel element type too large"
var _ = make(chan [0x2FFFF]byte) // ERROR "channel element type too large"
var c1 chan [0x2FFFF]byte // ERROR "channel element type too large"
var c2 = make(chan [0x2FFFF]byte) // ERROR "channel element type too large"

View File

@@ -0,0 +1,19 @@
// errorcheck
// 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 p
type I interface {
F()
}
type T struct {
}
const _ = I((*T)(nil)) // ERROR "is not constant"
func (*T) F() {
}

View File

@@ -27,3 +27,12 @@ func z() {
z := t{&i}.f // ERROR "t{...}.f escapes to heap"
z()
}
// Should match cmd/compile/internal/ir/cfg.go:MaxStackVarSize.
const maxStack = 128 * 1024
func w(i int) byte {
var x [maxStack]byte
var y [maxStack + 1]byte // ERROR "moved to heap: y"
return x[i] + y[i]
}

View File

@@ -6,7 +6,7 @@
package p
type T1 struct { // ERROR "invalid recursive type T1\n\tLINE: T1 refers to\n\tLINE+4: T2 refers to\n\tLINE: T1$|invalid recursive type"
type T1 struct { // ERROR "invalid recursive type T1\n.*T1 refers to T2\n.*T2 refers to T1|invalid recursive type"
f2 T2
}
@@ -15,21 +15,21 @@ type T2 struct { // GCCGO_ERROR "invalid recursive type"
}
type a b // GCCGO_ERROR "invalid recursive type"
type b c // ERROR "invalid recursive type b\n\tLINE: b refers to\n\tLINE+1: c refers to\n\tLINE: b$|invalid recursive type"
type b c // ERROR "invalid recursive type b\n.*b refers to c\n.*c refers to b|invalid recursive type|invalid recursive type"
type c b // GCCGO_ERROR "invalid recursive type"
type d e
type e f
type f f // ERROR "invalid recursive type f\n\tLINE: f refers to\n\tLINE: f$|invalid recursive type"
type f f // ERROR "invalid recursive type: f refers to itself|invalid recursive type|invalid recursive type"
type g struct { // ERROR "invalid recursive type g\n\tLINE: g refers to\n\tLINE: g$|invalid recursive type"
type g struct { // ERROR "invalid recursive type: g refers to itself|invalid recursive type"
h struct {
g
}
}
type w x
type x y // ERROR "invalid recursive type x\n\tLINE: x refers to\n\tLINE+1: y refers to\n\tLINE+2: z refers to\n\tLINE: x$|invalid recursive type"
type x y // ERROR "invalid recursive type x\n.*x refers to y\n.*y refers to z\n.*z refers to x|invalid recursive type"
type y struct{ z } // GCCGO_ERROR "invalid recursive type"
type z [10]x

View File

@@ -51,6 +51,6 @@ func g() {
_ = i.(T6) // ERROR "impossible type assertion: i.\(T6\)\n\tT6 does not implement I \(missing method M\)\n\t\thave m\(int\) string\n\t\twant M\(int\)"
var t *T4
t = i // ERROR "cannot use i \(variable of type I\) as \*T4 value in assignment: need type assertion"
t = i // ERROR "cannot use i \(variable of interface type I\) as \*T4 value in assignment: need type assertion"
_ = t
}

View File

@@ -6,4 +6,4 @@ package b
import "./a"
type T a.T[T] // ERROR "invalid recursive type T\n.*T refers to\n.*a\.T refers to\n.*T"
type T a.T[T] // ERROR "invalid recursive type T\n.*T refers to a\.T\n.*a\.T refers to T"

View File

@@ -6,27 +6,6 @@
package p
import (
"crypto/ecdh"
"crypto/rand"
)
func F(peerShare []byte) ([]byte, error) { // ERROR "leaking param: peerShare"
p256 := ecdh.P256() // ERROR "inlining call to ecdh.P256"
ourKey, err := p256.GenerateKey(rand.Reader) // ERROR "devirtualizing p256.GenerateKey" "inlining call to ecdh.*GenerateKey"
if err != nil {
return nil, err
}
peerPublic, err := p256.NewPublicKey(peerShare) // ERROR "devirtualizing p256.NewPublicKey" "inlining call to ecdh.*NewPublicKey"
if err != nil {
return nil, err
}
return ourKey.ECDH(peerPublic)
}
// Test that inlining doesn't break if devirtualization exposes a new
// inlinable callee.

View File

@@ -11,6 +11,7 @@ func run() { // ERROR "cannot inline run: recursive"
g() // ERROR "inlining call to g"
}
f() // ERROR "inlining call to run.func1" "inlining call to g"
_ = f
run()
}

View File

@@ -0,0 +1,27 @@
// errorcheck -0 -d=ssa/check_bce/debug=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.
package x
func Found(x []string) string {
switch len(x) {
default:
return x[0]
case 0, 1:
return ""
}
}
func NotFound(x []string) string {
switch len(x) {
default:
return x[0]
case 0:
return ""
case 1:
return ""
}
}

View File

@@ -0,0 +1,12 @@
// errorcheck
// 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 main
func f[S any, T any](T) {}
func g() {
f(0) // ERROR "in call to f, cannot infer S \(declared at issue68292.go:9:8\)"
}

View File

@@ -0,0 +1,16 @@
// 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.
//go:build goexperiment.aliastypeparams
package a
type A[T any] = struct{ F T }
type B = struct{ F int }
func F() B {
type a[T any] = struct{ F T }
return a[int]{}
}

View File

@@ -0,0 +1,45 @@
// 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.
//go:build goexperiment.aliastypeparams
package main
import (
"fmt"
"issue68526.dir/a"
)
func main() {
unexported()
exported()
}
func unexported() {
var want struct{ F int }
if any(want) != any(a.B{}) || any(want) != any(a.F()) {
panic("zero value of alias and concrete type not identical")
}
}
func exported() {
var (
astr a.A[string]
aint a.A[int]
)
if any(astr) != any(struct{ F string }{}) || any(aint) != any(struct{ F int }{}) {
panic("zero value of alias and concrete type not identical")
}
if any(astr) == any(aint) {
panic("zero value of struct{ F string } and struct{ F int } are not distinct")
}
if got := fmt.Sprintf("%T", astr); got != "struct { F string }" {
panic(got)
}
}

View File

@@ -0,0 +1,7 @@
// runindir -goexperiment aliastypeparams -gomodversion "1.23"
// 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 ignored

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.
// The gofrontend had a bug handling panic of an untyped constant expression.
package issue68734
func F1() {
panic(1 + 2)
}
func F2() {
panic("a" + "b")
}

View File

@@ -0,0 +1,19 @@
// run
// 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 main
func main() {
cnt := 0
for i := 1; i <= 11; i++ {
if i-6 > 4 {
cnt++
}
}
if cnt != 1 {
panic("bad")
}
}

View File

@@ -0,0 +1,41 @@
// run
// 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 main
func main() {
mustPanic(func() {
f1(1)
})
f2(1, 0) // must not panic
mustPanic(func() {
f2(1, 2)
})
}
var v []func()
//go:noinline
func f1(i int) {
v = make([]func(), -2|i)
}
//go:noinline
func f2(i, j int) {
if j > 0 {
v = make([]func(), -2|i)
}
}
func mustPanic(f func()) {
defer func() {
r := recover()
if r == nil {
panic("didn't panic")
}
}()
f()
}

View File

@@ -1,5 +1,7 @@
// run
//go:build !goexperiment.swissmap
// 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.

View File

@@ -7,167 +7,55 @@
package main
import (
"bufio"
"fmt"
"io"
"iter"
"math/rand"
"os"
"strings"
"unicode"
)
// WordReader is the struct that implements io.Reader
type WordReader struct {
scanner *bufio.Scanner
}
// NewWordReader creates a new WordReader from an io.Reader
func NewWordReader(r io.Reader) *WordReader {
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanWords)
return &WordReader{
scanner: scanner,
}
}
// Read reads data from the input stream and returns a single lowercase word at a time
func (wr *WordReader) Read(p []byte) (n int, err error) {
if !wr.scanner.Scan() {
if err := wr.scanner.Err(); err != nil {
return 0, err
}
return 0, io.EOF
}
word := wr.scanner.Text()
cleanedWord := removeNonAlphabetic(word)
if len(cleanedWord) == 0 {
return wr.Read(p)
}
n = copy(p, []byte(cleanedWord))
return n, nil
}
// All returns an iterator allowing the caller to iterate over the WordReader using for/range.
func (wr *WordReader) All() iter.Seq[string] {
word := make([]byte, 1024)
return func(yield func(string) bool) {
var err error
var n int
for n, err = wr.Read(word); err == nil; n, err = wr.Read(word) {
if !yield(string(word[:n])) {
func All() iter.Seq[int] {
return func(yield func(int) bool) {
for i := 0; i < 10; i++ {
growStack(512)
if !yield(i) {
return
}
}
if err != io.EOF {
fmt.Fprintf(os.Stderr, "error reading word: %v\n", err)
}
}
}
// removeNonAlphabetic removes non-alphabetic characters from a word using strings.Map
func removeNonAlphabetic(word string) string {
return strings.Map(func(r rune) rune {
if unicode.IsLetter(r) {
return unicode.ToLower(r)
}
return -1
}, word)
type S struct {
round int
}
// ProbabilisticSkipper determines if an item should be retained with probability 1/(1<<n)
type ProbabilisticSkipper struct {
n int
counter uint64
bitmask uint64
func NewS(round int) *S {
s := &S{round: round}
return s
}
// NewProbabilisticSkipper initializes the ProbabilisticSkipper
func NewProbabilisticSkipper(n int) *ProbabilisticSkipper {
pr := &ProbabilisticSkipper{n: n}
pr.refreshCounter()
return pr
}
// check panics if pr.n is not the expected value
func (pr *ProbabilisticSkipper) check(n int) {
if pr.n != n {
panic(fmt.Sprintf("check: pr.n != n %d != %d", pr.n, n))
func (s *S) check(round int) {
if s.round != round {
panic("bad round")
}
}
// refreshCounter refreshes the counter with a new random value
func (pr *ProbabilisticSkipper) refreshCounter() {
if pr.n == 0 {
pr.bitmask = ^uint64(0) // All bits set to 1
} else {
pr.bitmask = rand.Uint64()
for i := 0; i < pr.n-1; i++ {
pr.bitmask &= rand.Uint64()
}
}
pr.counter = 64
}
// ShouldSkip returns true with probability 1/(1<<n)
func (pr *ProbabilisticSkipper) ShouldSkip() bool {
remove := pr.bitmask&1 == 0
pr.bitmask >>= 1
pr.counter--
if pr.counter == 0 {
pr.refreshCounter()
}
return remove
}
// EstimateUniqueWordsIter estimates the number of unique words using a probabilistic counting method
func EstimateUniqueWordsIter(reader io.Reader, memorySize int) int {
wordReader := NewWordReader(reader)
words := make(map[string]struct{}, memorySize)
func f() {
rounds := 0
roundRemover := NewProbabilisticSkipper(1)
wordSkipper := NewProbabilisticSkipper(rounds)
wordSkipper.check(rounds)
s := NewS(rounds)
s.check(rounds)
for word := range wordReader.All() {
wordSkipper.check(rounds)
if wordSkipper.ShouldSkip() {
delete(words, word)
} else {
words[word] = struct{}{}
if len(words) >= memorySize {
rounds++
wordSkipper = NewProbabilisticSkipper(rounds)
for w := range words {
if roundRemover.ShouldSkip() {
delete(words, w)
}
}
}
}
wordSkipper.check(rounds)
for range All() {
s.check(rounds)
rounds++
s = NewS(rounds)
s.check(rounds)
}
}
if len(words) == 0 {
return 0
func growStack(i int) {
if i == 0 {
return
}
invProbability := 1 << rounds
estimatedUniqueWords := len(words) * invProbability
return estimatedUniqueWords
growStack(i - 1)
}
func main() {
input := "Hello, world! This is a test. Hello, world, hello!"
expectedUniqueWords := 6 // "hello", "world", "this", "is", "a", "test" (but "hello" and "world" are repeated)
memorySize := 6
reader := strings.NewReader(input)
estimatedUniqueWords := EstimateUniqueWordsIter(reader, memorySize)
if estimatedUniqueWords != expectedUniqueWords {
// ...
}
f()
}

View File

@@ -0,0 +1,18 @@
// compile -d=libfuzzer
// 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 main
type T struct {
A
}
type A struct {
}
//go:noinline
func (a *A) Foo(s [2]string) {
}

View File

@@ -0,0 +1,23 @@
// run
// 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 main
import (
"reflect"
)
func main() {
pi := new(interface{})
v := reflect.ValueOf(pi).Elem()
if v.Kind() != reflect.Interface {
panic(0)
}
if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() {
return
}
panic(1)
}

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 main
func f() {
_:
_:
}
func main() {
f()
}

View File

@@ -0,0 +1,38 @@
// run -goexperiment noswissmap
// 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 main
func nan() float64 {
var x, y float64
return x / y
}
func main() {
m := map[float64]int{}
// Make a small map with nan keys
for i := 0; i < 8; i++ {
m[nan()] = i
}
// Start iterating on it.
start := true
for _, v := range m {
if start {
// Add some more elements.
for i := 0; i < 10; i++ {
m[float64(i)] = i
}
// Now clear the map.
clear(m)
start = false
} else {
// We should never reach here.
panic(v)
}
}
}

View File

@@ -0,0 +1,20 @@
// run
// 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 main
const maxUint64 = (1 << 64) - 1
//go:noinline
func f(n uint64) uint64 {
return maxUint64 - maxUint64%n
}
func main() {
for i := uint64(1); i < 20; i++ {
println(i, maxUint64-f(i))
}
}

View File

@@ -0,0 +1,19 @@
1 0
2 1
3 0
4 3
5 0
6 3
7 1
8 7
9 6
10 5
11 4
12 3
13 2
14 1
15 0
16 15
17 0
18 15
19 16

View File

@@ -4,12 +4,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Test type-checking errors for go:notinheap.
// Test type-checking errors for not-in-heap types.
//go:build cgo
package p
//go:notinheap
type nih struct{}
import "runtime/cgo"
type nih struct{ _ cgo.Incomplete }
type embed4 map[nih]int // ERROR "incomplete \(or unallocatable\) map key not allowed"
@@ -27,25 +30,8 @@ type okay4 interface {
f(x nih) nih
}
// Type conversions don't let you sneak past notinheap.
type t1 struct{ x int }
//go:notinheap
type t2 t1
//go:notinheap
type t3 byte
//go:notinheap
type t4 rune
var sink interface{}
func i() {
sink = new(t1) // no error
sink = (*t2)(new(t1)) // ERROR "cannot convert(.|\n)*t2 is incomplete \(or unallocatable\)"
sink = (*t2)(new(struct{ x int })) // ERROR "cannot convert(.|\n)*t2 is incomplete \(or unallocatable\)"
sink = []t3("foo") // ERROR "cannot convert(.|\n)*t3 is incomplete \(or unallocatable\)"
sink = []t4("bar") // ERROR "cannot convert(.|\n)*t4 is incomplete \(or unallocatable\)"
func f() {
type embed7 map[nih]int // ERROR "incomplete \(or unallocatable\) map key not allowed"
type embed8 map[int]nih // ERROR "incomplete \(or unallocatable\) map value not allowed"
type emebd9 chan nih // ERROR "chan of incomplete \(or unallocatable\) type not allowed"
}

View File

@@ -13,8 +13,8 @@ type t1 int
type t2 int
type t3 int
func f1(*t2, x t3) // ERROR "named"
func f2(t1, *t2, x t3) // ERROR "named"
func f3() (x int, *string) // ERROR "named"
func f1(*t2, x t3) // ERROR "missing parameter name"
func f2(t1, *t2, x t3) // ERROR "missing parameter name"
func f3() (x int, *string) // ERROR "missing parameter name"
func f4() (t1 t1) // legal - scope of parameter named t1 starts in body of f4.

View File

@@ -148,11 +148,11 @@ func fEqInterEqInter(a interface{}, f float64) bool {
}
func fEqInterNeqInter(a interface{}, f float64) bool {
return a == nil && f > Cf2 || a != nil && f < -Cf2
return a == nil && f > Cf2 || a != nil && f < -Cf2 // ERROR "Redirect IsNonNil based on IsNonNil"
}
func fNeqInterEqInter(a interface{}, f float64) bool {
return a != nil && f > Cf2 || a == nil && f < -Cf2
return a != nil && f > Cf2 || a == nil && f < -Cf2 // ERROR "Redirect IsNonNil based on IsNonNil"
}
func fNeqInterNeqInter(a interface{}, f float64) bool {
@@ -164,11 +164,11 @@ func fEqSliceEqSlice(a []int, f float64) bool {
}
func fEqSliceNeqSlice(a []int, f float64) bool {
return a == nil && f > Cf2 || a != nil && f < -Cf2
return a == nil && f > Cf2 || a != nil && f < -Cf2 // ERROR "Redirect IsNonNil based on IsNonNil"
}
func fNeqSliceEqSlice(a []int, f float64) bool {
return a != nil && f > Cf2 || a == nil && f < -Cf2
return a != nil && f > Cf2 || a == nil && f < -Cf2 // ERROR "Redirect IsNonNil based on IsNonNil"
}
func fNeqSliceNeqSlice(a []int, f float64) bool {

View File

@@ -11,7 +11,7 @@ package main
var (
x int = a
a int = b // ERROR "a refers to\n.*b refers to\n.*c refers to\n.*a|initialization loop"
a int = b // ERROR "a refers to b\n.*b refers to c\n.*c refers to a|initialization loop"
b int = c
c int = a
)

View File

@@ -73,6 +73,7 @@ func l(x, y int) (int, int, error) { // ERROR "can inline l"
f := e
f(nil) // ERROR "inlining call to l.func1"
}
_ = e // prevent simple deadcode elimination after inlining
return y, x, nil
}
@@ -109,6 +110,7 @@ func p() int { // ERROR "can inline p"
func q(x int) int { // ERROR "can inline q"
foo := func() int { return x * 2 } // ERROR "can inline q.func1" "func literal does not escape"
_ = foo // prevent simple deadcode elimination after inlining
return foo() // ERROR "inlining call to q.func1"
}
@@ -121,6 +123,8 @@ func r(z int) int {
return 2*y + x*z
}(x) // ERROR "inlining call to r.func2.1"
}
_, _ = foo, bar // prevent simple deadcode elimination after inlining
return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.r.func2.func3"
}
@@ -128,7 +132,8 @@ func s0(x int) int { // ERROR "can inline s0"
foo := func() { // ERROR "can inline s0.func1" "func literal does not escape"
x = x + 1
}
foo() // ERROR "inlining call to s0.func1"
foo() // ERROR "inlining call to s0.func1"
_ = foo // prevent simple deadcode elimination after inlining
return x
}
@@ -137,6 +142,7 @@ func s1(x int) int { // ERROR "can inline s1"
return x
}
x = x + 1
_ = foo // prevent simple deadcode elimination after inlining
return foo() // ERROR "inlining call to s1.func1"
}

View File

@@ -0,0 +1,37 @@
// errorcheck -0 -m=2
// 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 no inlining of function calls in testing.B.Loop.
// See issue #61515.
package foo
import "testing"
func caninline(x int) int { // ERROR "can inline caninline"
return x
}
func cannotinline(b *testing.B) { // ERROR "b does not escape" "cannot inline cannotinline.*"
for i := 0; i < b.N; i++ {
caninline(1) // ERROR "inlining call to caninline"
}
for b.Loop() { // ERROR "skip inlining within testing.B.loop" "inlining call to testing\.\(\*B\)\.Loop"
caninline(1)
}
for i := 0; i < b.N; i++ {
caninline(1) // ERROR "inlining call to caninline"
}
for b.Loop() { // ERROR "skip inlining within testing.B.loop" "inlining call to testing\.\(\*B\)\.Loop"
caninline(1)
}
for i := 0; i < b.N; i++ {
caninline(1) // ERROR "inlining call to caninline"
}
for b.Loop() { // ERROR "skip inlining within testing.B.loop" "inlining call to testing\.\(\*B\)\.Loop"
caninline(1)
}
}

View File

@@ -0,0 +1,7 @@
// 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.
The internal/runtime/sys directory contains tests that specifically need to be
compiled as-if in the internal/runtime/sys package. For error-check tests,
these require the additional flags -+ and -p=internal/runtime/sys.

View File

@@ -1,19 +1,19 @@
// errorcheck -0 -+ -p=runtime -m
// errorcheck -0 -+ -p=internal/runtime/sys -m
// Copyright 2019 The Go Authors. All rights reserved.
// 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 runtime
package sys
// A function that calls runtime.getcallerpc or runtime.getcallersp()
// A function that calls sys.GetCallerPC or sys.GetCallerSP
// cannot be inlined, no matter how small it is.
func getcallerpc() uintptr
func getcallersp() uintptr
func GetCallerPC() uintptr
func GetCallerSP() uintptr
func pc() uintptr {
return getcallerpc() + 1
return GetCallerPC() + 1
}
func cpc() uintptr { // ERROR "can inline cpc"
@@ -21,7 +21,7 @@ func cpc() uintptr { // ERROR "can inline cpc"
}
func sp() uintptr {
return getcallersp() + 3
return GetCallerSP() + 3
}
func csp() uintptr { // ERROR "can inline csp"

View File

@@ -6,7 +6,7 @@ package main
import (
"fmt"
T "runtime/internal/sys"
T "internal/runtime/sys"
)
var A = []uint64{0x0102030405060708, 0x1122334455667788}

View File

@@ -458,14 +458,14 @@ func f28(b bool) {
func f29(b bool) {
if b {
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hiter$"
for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$"
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
}
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$"
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$"
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
}
@@ -659,15 +659,9 @@ func newT40() *T40 {
return &ret
}
func bad40() {
t := newT40()
_ = t
printnl()
}
func good40() {
ret := T40{} // ERROR "stack object ret T40$"
ret.m = make(map[int]int) // ERROR "live at call to rand32: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
ret.m = make(map[int]int) // ERROR "live at call to rand(32)?: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hmap|internal/runtime/maps.Map)$"
t := &ret
printnl() // ERROR "live at call to printnl: ret$"
// Note: ret is live at the printnl because the compiler moves &ret
@@ -675,6 +669,12 @@ func good40() {
useT40(t)
}
func bad40() {
t := newT40()
_ = t
printnl()
}
func ddd1(x, y *int) { // ERROR "live at entry to ddd1: x y$"
ddd2(x, y) // ERROR "stack object .autotmp_[0-9]+ \[2\]\*int$"
printnl()

View File

@@ -27,14 +27,14 @@ func newT40() *T40 {
}
func bad40() {
t := newT40() // ERROR "stack object ret T40$" "stack object .autotmp_[0-9]+ runtime.hmap$"
t := newT40() // ERROR "stack object ret T40$" "stack object .autotmp_[0-9]+ (runtime.hmap|internal/runtime/maps.Map)$"
printnl() // ERROR "live at call to printnl: ret$"
useT40(t)
}
func good40() {
ret := T40{} // ERROR "stack object ret T40$"
ret.m = make(map[int]int, 42) // ERROR "stack object .autotmp_[0-9]+ runtime.hmap$"
ret.m = make(map[int]int, 42) // ERROR "stack object .autotmp_[0-9]+ (runtime.hmap|internal/runtime/maps.Map)$"
t := &ret
printnl() // ERROR "live at call to printnl: ret$"
useT40(t)

View File

@@ -278,6 +278,7 @@ func f17b(p *byte) { // ERROR "live at entry to f17b: p$"
// key temporary
if b {
m2s[str()] = p // ERROR "live at call to mapassign_faststr: p$" "live at call to str: p$"
}
m2s[str()] = p // ERROR "live at call to mapassign_faststr: p$" "live at call to str: p$"
m2s[str()] = p // ERROR "live at call to mapassign_faststr: p$" "live at call to str: p$"
@@ -455,14 +456,14 @@ func f28(b bool) {
func f29(b bool) {
if b {
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hiter$"
for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$"
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
}
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$"
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$"
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
}
@@ -656,15 +657,9 @@ func newT40() *T40 {
return &ret
}
func bad40() {
t := newT40()
_ = t
printnl()
}
func good40() {
ret := T40{} // ERROR "stack object ret T40$"
ret.m = make(map[int]int) // ERROR "live at call to rand32: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
ret.m = make(map[int]int) // ERROR "live at call to rand(32)?: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hmap|internal/runtime/maps.Map)$"
t := &ret
printnl() // ERROR "live at call to printnl: ret$"
// Note: ret is live at the printnl because the compiler moves &ret
@@ -672,6 +667,12 @@ func good40() {
useT40(t)
}
func bad40() {
t := newT40()
_ = t
printnl()
}
func ddd1(x, y *int) { // ERROR "live at entry to ddd1: x y$"
ddd2(x, y) // ERROR "stack object .autotmp_[0-9]+ \[2\]\*int$"
printnl()

View File

@@ -27,7 +27,7 @@ func f0c(a []int) int {
x := 0
for i := range a { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
b := a[:i+1] // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
x += b[0]
x += b[0] // ERROR "(\([0-9]+\) )?Proved IsInBounds$"
}
return x
}
@@ -168,7 +168,7 @@ func g2() int {
func g3a() {
a := "this string has length 25"
for i := 0; i < len(a); i += 5 { // ERROR "Induction variable: limits \[0,20\], increment 5$"
useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
useString(a[:i+3]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
useString(a[:i+5]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
useString(a[:i+6])
@@ -294,8 +294,10 @@ func k3neg2(a [100]int) [100]int {
}
func k4(a [100]int) [100]int {
min := (-1) << 63
for i := min; i < min+50; i++ { // ERROR "Induction variable: limits \[-9223372036854775808,-9223372036854775758\), increment 1$"
// Note: can't use (-1)<<63 here, because i-min doesn't get rewritten to i+(-min),
// and it isn't worth adding that special case to prove.
min := (-1)<<63 + 1
for i := min; i < min+50; i++ { // ERROR "Induction variable: limits \[-9223372036854775807,-9223372036854775757\), increment 1$"
a[i-min] = i // ERROR "(\([0-9]+\) )?Proved IsInBounds$"
}
return a
@@ -314,7 +316,7 @@ func d1(a [100]int) [100]int {
for i := 0; i < 100; i++ { // ERROR "Induction variable: limits \[0,100\), increment 1$"
for j := 0; j < i; j++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
a[j] = 0 // ERROR "Proved IsInBounds$"
a[j+1] = 0 // FIXME: this boundcheck should be eliminated
a[j+1] = 0 // ERROR "Proved IsInBounds$"
a[j+2] = 0
}
}
@@ -325,7 +327,7 @@ func d2(a [100]int) [100]int {
for i := 0; i < 100; i++ { // ERROR "Induction variable: limits \[0,100\), increment 1$"
for j := 0; i > j; j++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
a[j] = 0 // ERROR "Proved IsInBounds$"
a[j+1] = 0 // FIXME: this boundcheck should be eliminated
a[j+1] = 0 // ERROR "Proved IsInBounds$"
a[j+2] = 0
}
}
@@ -419,12 +421,12 @@ func nobce2(a string) {
for i := int64(0); i < int64(len(a))-31337; i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
}
for i := int64(0); i < int64(len(a))+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
for i := int64(0); i < int64(len(a))+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Disproved Less64"
useString(a[i:])
}
j := int64(len(a)) - 123
for i := int64(0); i < j+123+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
useString(a[i:]) // ERROR "(\([0-9]+\) )?Proved IsSliceInBounds$"
for i := int64(0); i < j+123+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Disproved Less64"
useString(a[i:])
}
for i := int64(0); i < j+122+int64(-1<<63); i++ { // ERROR "Induction variable: limits \[0,\?\), increment 1$"
// len(a)-123+122+MinInt overflows when len(a) == 0, so a bound check is needed here

View File

@@ -73,6 +73,7 @@ func l(x, y int) (int, int, error) { // ERROR "can inline l"
f := e
f(nil) // ERROR "inlining call to l.func1"
}
_ = e // prevent simple deadcode elimination
return y, x, nil
}
@@ -109,6 +110,7 @@ func p() int { // ERROR "can inline p"
func q(x int) int { // ERROR "can inline q"
foo := func() int { return x * 2 } // ERROR "can inline q.func1" "func literal does not escape"
_ = foo // prevent simple deadcode elimination
return foo() // ERROR "inlining call to q.func1"
}
@@ -121,6 +123,8 @@ func r(z int) int { // ERROR "can inline r"
return 2*y + x*z
}(x) // ERROR "inlining call to r.func2.1"
}
_ = foo // prevent simple deadcode elimination
_ = bar // prevent simple deadcode elimination
return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.r.func2.func3"
}
@@ -128,7 +132,8 @@ func s0(x int) int { // ERROR "can inline s0"
foo := func() { // ERROR "can inline s0.func1" "func literal does not escape"
x = x + 1
}
foo() // ERROR "inlining call to s0.func1"
foo() // ERROR "inlining call to s0.func1"
_ = foo // prevent simple deadcode elimination
return x
}
@@ -137,6 +142,7 @@ func s1(x int) int { // ERROR "can inline s1"
return x
}
x = x + 1
_ = foo // prevent simple deadcode elimination
return foo() // ERROR "inlining call to s1.func1"
}

View File

@@ -1,6 +1,6 @@
// run
//go:build !nacl && !js && !aix && !wasip1 && !gcflags_noopt && gc
//go:build !nacl && !js && !aix && !openbsd && !wasip1 && !gcflags_noopt && gc
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style

View File

@@ -8,7 +8,10 @@
package main
import "math"
import (
"math"
"math/bits"
)
func f0(a []int) int {
a[0] = 1
@@ -400,8 +403,8 @@ func f13f(a, b int64) int64 {
if b != math.MaxInt64 {
return 42
}
if a > b {
if a == 0 { // ERROR "Disproved Eq64$"
if a > b { // ERROR "Disproved Less64$"
if a == 0 {
return 1
}
}
@@ -684,20 +687,6 @@ func constsuffix(s string) bool {
return suffix(s, "abc") // ERROR "Proved IsSliceInBounds$"
}
// oforuntil tests the pattern created by OFORUNTIL blocks. These are
// handled by addLocalInductiveFacts rather than findIndVar.
func oforuntil(b []int) {
i := 0
if len(b) > i {
top:
println(b[i]) // ERROR "Induction variable: limits \[0,\?\), increment 1$" "Proved IsInBounds$"
i++
if i < len(b) {
goto top
}
}
}
func atexit(foobar []func()) {
for i := len(foobar) - 1; i >= 0; i-- { // ERROR "Induction variable: limits \[0,\?\], increment 1"
f := foobar[i]
@@ -877,11 +866,11 @@ func unrollDecMin(a []int, b int) int {
return 42
}
var i, x int
for i = len(a); i >= b; i -= 2 {
for i = len(a); i >= b; i -= 2 { // ERROR "Proved Leq64"
x += a[i-1]
x += a[i-2]
}
if i == 1 { // ERROR "Disproved Eq64$"
if i == 1 {
x += a[i-1]
}
return x
@@ -893,11 +882,11 @@ func unrollIncMin(a []int, b int) int {
return 42
}
var i, x int
for i = len(a); i >= b; i += 2 {
for i = len(a); i >= b; i += 2 { // ERROR "Proved Leq64"
x += a[i-1]
x += a[i-2]
}
if i == 1 { // ERROR "Disproved Eq64$"
if i == 1 {
x += a[i-1]
}
return x
@@ -1107,7 +1096,7 @@ func modu2(x, y uint) int {
func issue57077(s []int) (left, right []int) {
middle := len(s) / 2
left = s[:middle] // ERROR "Proved IsSliceInBounds$"
left = s[:middle] // ERROR "Proved IsSliceInBounds$"
right = s[middle:] // ERROR "Proved IsSliceInBounds$"
return
}
@@ -1124,6 +1113,605 @@ func issue45928(x int) {
useInt(combinedFrac)
}
func constantBounds1(i, j uint) int {
var a [10]int
if j < 11 && i < j {
return a[i] // ERROR "Proved IsInBounds$"
}
return 0
}
func constantBounds2(i, j uint) int {
var a [10]int
if i < j && j < 11 {
return a[i] // ERROR "Proved IsInBounds"
}
return 0
}
func constantBounds3(i, j, k, l uint) int {
var a [8]int
if i < j && j < k && k < l && l < 11 {
return a[i] // ERROR "Proved IsInBounds"
}
return 0
}
func equalityPropagation(a [1]int, i, j uint) int {
if i == j && i == 5 {
return a[j-5] // ERROR "Proved IsInBounds"
}
return 0
}
func inequalityPropagation(a [1]int, i, j uint) int {
if i != j && j >= 5 && j <= 6 && i == 5 {
return a[j-6] // ERROR "Proved IsInBounds"
}
return 0
}
func issue66826a(a [21]byte) {
for i := 0; i <= 10; i++ { // ERROR "Induction variable: limits \[0,10\], increment 1$"
_ = a[2*i] // ERROR "Proved IsInBounds"
}
}
func issue66826b(a [31]byte, i int) {
if i < 0 || i > 10 {
return
}
_ = a[3*i] // ERROR "Proved IsInBounds"
}
func f20(a, b bool) int {
if a == b {
if a {
if b { // ERROR "Proved Arg"
return 1
}
}
}
return 0
}
func f21(a, b *int) int {
if a == b {
if a != nil {
if b != nil { // ERROR "Proved IsNonNil"
return 1
}
}
}
return 0
}
func f22(b bool, x, y int) int {
b2 := x < y
if b == b2 {
if b {
if x >= y { // ERROR "Disproved Leq64$"
return 1
}
}
}
return 0
}
func ctz64(x uint64, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint64
sz := bits.Len64(max)
log2half := uint64(max) >> (sz / 2)
if x >= log2half || x == 0 {
return 42
}
y := bits.TrailingZeros64(x) // ERROR "Proved Ctz64 non-zero$""
z := sz / 2
if ensureBothBranchesCouldHappen {
if y < z { // ERROR "Proved Less64$"
return -42
}
} else {
if y >= z { // ERROR "Disproved Leq64$"
return 1337
}
}
return y
}
func ctz32(x uint32, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint32
sz := bits.Len32(max)
log2half := uint32(max) >> (sz / 2)
if x >= log2half || x == 0 {
return 42
}
y := bits.TrailingZeros32(x) // ERROR "Proved Ctz32 non-zero$""
z := sz / 2
if ensureBothBranchesCouldHappen {
if y < z { // ERROR "Proved Less64$"
return -42
}
} else {
if y >= z { // ERROR "Disproved Leq64$"
return 1337
}
}
return y
}
func ctz16(x uint16, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint16
sz := bits.Len16(max)
log2half := uint16(max) >> (sz / 2)
if x >= log2half || x == 0 {
return 42
}
y := bits.TrailingZeros16(x) // ERROR "Proved Ctz16 non-zero$""
z := sz / 2
if ensureBothBranchesCouldHappen {
if y < z { // ERROR "Proved Less64$"
return -42
}
} else {
if y >= z { // ERROR "Disproved Leq64$"
return 1337
}
}
return y
}
func ctz8(x uint8, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint8
sz := bits.Len8(max)
log2half := uint8(max) >> (sz / 2)
if x >= log2half || x == 0 {
return 42
}
y := bits.TrailingZeros8(x) // ERROR "Proved Ctz8 non-zero$""
z := sz / 2
if ensureBothBranchesCouldHappen {
if y < z { // ERROR "Proved Less64$"
return -42
}
} else {
if y >= z { // ERROR "Disproved Leq64$"
return 1337
}
}
return y
}
func bitLen64(x uint64, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint64
sz := bits.Len64(max)
if x >= max>>3 {
return 42
}
if x <= max>>6 {
return 42
}
y := bits.Len64(x)
if ensureBothBranchesCouldHappen {
if sz-6 <= y && y <= sz-3 { // ERROR "Proved Leq64$"
return -42
}
} else {
if y < sz-6 || sz-3 < y { // ERROR "Disproved Less64$"
return 1337
}
}
return y
}
func bitLen32(x uint32, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint32
sz := bits.Len32(max)
if x >= max>>3 {
return 42
}
if x <= max>>6 {
return 42
}
y := bits.Len32(x)
if ensureBothBranchesCouldHappen {
if sz-6 <= y && y <= sz-3 { // ERROR "Proved Leq64$"
return -42
}
} else {
if y < sz-6 || sz-3 < y { // ERROR "Disproved Less64$"
return 1337
}
}
return y
}
func bitLen16(x uint16, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint16
sz := bits.Len16(max)
if x >= max>>3 {
return 42
}
if x <= max>>6 {
return 42
}
y := bits.Len16(x)
if ensureBothBranchesCouldHappen {
if sz-6 <= y && y <= sz-3 { // ERROR "Proved Leq64$"
return -42
}
} else {
if y < sz-6 || sz-3 < y { // ERROR "Disproved Less64$"
return 1337
}
}
return y
}
func bitLen8(x uint8, ensureBothBranchesCouldHappen bool) int {
const max = math.MaxUint8
sz := bits.Len8(max)
if x >= max>>3 {
return 42
}
if x <= max>>6 {
return 42
}
y := bits.Len8(x)
if ensureBothBranchesCouldHappen {
if sz-6 <= y && y <= sz-3 { // ERROR "Proved Leq64$"
return -42
}
} else {
if y < sz-6 || sz-3 < y { // ERROR "Disproved Less64$"
return 1337
}
}
return y
}
func xor64(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0xff
b &= 0xfff
z := a ^ b
if ensureBothBranchesCouldHappen {
if z > 0xfff { // ERROR "Disproved Less64U$"
return 42
}
} else {
if z <= 0xfff { // ERROR "Proved Leq64U$"
return 1337
}
}
return int(z)
}
func or64(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0xff
b &= 0xfff
z := a | b
if ensureBothBranchesCouldHappen {
if z > 0xfff { // ERROR "Disproved Less64U$"
return 42
}
} else {
if z <= 0xfff { // ERROR "Proved Leq64U$"
return 1337
}
}
return int(z)
}
func mod64uWithSmallerDividendMax(a, b uint64, ensureBothBranchesCouldHappen bool) int {
a &= 0xff
b &= 0xfff
z := bits.Len64(a % b) // see go.dev/issue/68857 for bits.Len64
if ensureBothBranchesCouldHappen {
if z > bits.Len64(0xff) { // ERROR "Disproved Less64$"
return 42
}
} else {
if z <= bits.Len64(0xff) { // ERROR "Proved Leq64$"
return 1337
}
}
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)
z := bits.Len64(a % b) // see go.dev/issue/68857 for bits.Len64
if ensureBothBranchesCouldHappen {
if z > bits.Len64(0x10-1) { // ERROR "Disproved Less64$"
return 42
}
} else {
if z <= bits.Len64(0x10-1) { // ERROR "Proved Leq64$"
return 1337
}
}
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)
z := bits.Len64(a % b) // see go.dev/issue/68857 for bits.Len64
if ensureBothBranchesCouldHappen {
if z > bits.Len64(0x10-1) { // ERROR "Disproved Less64$"
return 42
}
} else {
if z <= bits.Len64(0x10-1) { // ERROR "Proved Leq64$"
return 1337
}
}
return z
}
func mod64sPositiveWithSmallerDividendMax(a, b int64, ensureBothBranchesCouldHappen bool) int64 {
if a < 0 || b < 0 {
return 42
}
a &= 0xff
b &= 0xfff
z := a % b // ERROR "Proved Mod64 does not need fix-up$"
if ensureBothBranchesCouldHappen {
if z > 0xff { // ERROR "Disproved Less64$"
return 42
}
} else {
if z <= 0xff { // ERROR "Proved Leq64$"
return 1337
}
}
return z
}
func mod64sPositiveWithSmallerDivisorMax(a, b int64, ensureBothBranchesCouldHappen bool) int64 {
if a < 0 || b < 0 {
return 42
}
a &= 0xfff
b &= 0xff
z := a % b // ERROR "Proved Mod64 does not need fix-up$"
if ensureBothBranchesCouldHappen {
if z > 0xff-1 { // ERROR "Disproved Less64$"
return 42
}
} else {
if z <= 0xff-1 { // ERROR "Proved Leq64$"
return 1337
}
}
return z
}
func mod64sPositiveWithIdenticalMax(a, b int64, ensureBothBranchesCouldHappen bool) int64 {
if a < 0 || b < 0 {
return 42
}
a &= 0xfff
b &= 0xfff
z := a % b // ERROR "Proved Mod64 does not need fix-up$"
if ensureBothBranchesCouldHappen {
if z > 0xfff-1 { // ERROR "Disproved Less64$"
return 42
}
} else {
if z <= 0xfff-1 { // ERROR "Proved Leq64$"
return 1337
}
}
return z
}
func div64u(a, b uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
a &= 0xffff
a |= 0xfff
b &= 0xff
b |= 0xf
z := a / b // ERROR "Proved Neq64$"
if ensureAllBranchesCouldHappen() && z > 0xffff/0xf { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && z <= 0xffff/0xf { // ERROR "Proved Leq64U$"
return 1337
}
if ensureAllBranchesCouldHappen() && z < 0xfff/0xff { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && z >= 0xfff/0xff { // ERROR "Proved Leq64U$"
return 42
}
return z
}
func div64s(a, b int64, ensureAllBranchesCouldHappen func() bool) int64 {
if a < 0 || b < 0 {
return 42
}
a &= 0xffff
a |= 0xfff
b &= 0xff
b |= 0xf
z := a / b // ERROR "(Proved Div64 does not need fix-up|Proved Neq64)$"
if ensureAllBranchesCouldHappen() && z > 0xffff/0xf { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && z <= 0xffff/0xf { // ERROR "Proved Leq64$"
return 1337
}
if ensureAllBranchesCouldHappen() && z < 0xfff/0xff { // ERROR "Disproved Less64$"
return 42
}
if ensureAllBranchesCouldHappen() && z >= 0xfff/0xff { // ERROR "Proved Leq64$"
return 42
}
return z
}
func trunc64to16(a uint64, ensureAllBranchesCouldHappen func() bool) uint16 {
a &= 0xfff
a |= 0xff
z := uint16(a)
if ensureAllBranchesCouldHappen() && z > 0xfff { // ERROR "Disproved Less16U$"
return 42
}
if ensureAllBranchesCouldHappen() && z <= 0xfff { // ERROR "Proved Leq16U$"
return 1337
}
if ensureAllBranchesCouldHappen() && z < 0xff { // ERROR "Disproved Less16U$"
return 42
}
if ensureAllBranchesCouldHappen() && z >= 0xff { // ERROR "Proved Leq16U$"
return 1337
}
return z
}
func com64(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
a &= 0xffff
a |= 0xff
z := ^a
if ensureAllBranchesCouldHappen() && z > ^uint64(0xff) { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && z <= ^uint64(0xff) { // ERROR "Proved Leq64U$"
return 1337
}
if ensureAllBranchesCouldHappen() && z < ^uint64(0xffff) { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && z >= ^uint64(0xffff) { // ERROR "Proved Leq64U$"
return 1337
}
return z
}
func neg64(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
var lo, hi uint64 = 0xff, 0xfff
a &= hi
a |= lo
z := -a
if ensureAllBranchesCouldHappen() && z > -lo { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && z <= -lo { // ERROR "Proved Leq64U$"
return 1337
}
if ensureAllBranchesCouldHappen() && z < -hi { // ERROR "Disproved Less64U$"
return 42
}
if ensureAllBranchesCouldHappen() && z >= -hi { // ERROR "Proved Leq64U$"
return 1337
}
return z
}
func neg64mightOverflowDuringNeg(a uint64, ensureAllBranchesCouldHappen func() bool) uint64 {
var lo, hi uint64 = 0, 0xfff
a &= hi
a |= lo
z := -a
if ensureAllBranchesCouldHappen() && z > -lo {
return 42
}
if ensureAllBranchesCouldHappen() && z <= -lo {
return 1337
}
if ensureAllBranchesCouldHappen() && z < -hi {
return 42
}
if ensureAllBranchesCouldHappen() && z >= -hi {
return 1337
}
return z
}
func phiMin(a, b []byte) {
_ = a[:min(len(a), len(b))] // ERROR "Proved IsSliceInBounds"
_ = b[:min(len(a), len(b))] // ERROR "Proved IsSliceInBounds"
_ = a[:max(len(a), len(b))]
_ = b[:max(len(a), len(b))]
x := len(a)
if x > len(b) {
x = len(b)
useInt(0)
}
_ = a[:x] // ERROR "Proved IsSliceInBounds"
y := len(a)
if y > len(b) {
y = len(b)
useInt(0)
} else {
useInt(1)
}
_ = b[:y] // ERROR "Proved IsSliceInBounds"
}
func issue16833(a, b []byte) {
n := copy(a, b)
_ = a[n:] // ERROR "Proved IsSliceInBounds"
_ = b[n:] // ERROR "Proved IsSliceInBounds"
_ = a[:n] // ERROR "Proved IsSliceInBounds"
_ = b[:n] // ERROR "Proved IsSliceInBounds"
}
func clampedIdx1(x []int, i int) int {
if len(x) == 0 {
return 0
}
return x[min(max(0, i), len(x)-1)] // ERROR "Proved IsInBounds"
}
func clampedIdx2(x []int, i int) int {
if len(x) == 0 {
return 0
}
return x[max(min(i, len(x)-1), 0)] // TODO: can't get rid of this bounds check yet
}
//go:noinline
func useInt(a int) {
}

View File

@@ -14,7 +14,7 @@ func f0i(x int) int {
}
if (x + 20) == 20 {
return x + 5 // ERROR "Proved.+is constant 0$"
return x + 5 // ERROR "Proved.+is constant 0$" "Proved.+is constant 5$"
}
return x / 2
@@ -26,7 +26,7 @@ func f0u(x uint) uint {
}
if (x + 20) == 20 {
return x + 5 // ERROR "Proved.+is constant 0$"
return x + 5 // ERROR "Proved.+is constant 0$" "Proved.+is constant 5$"
}
return x / 2

View File

@@ -1,7 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
The runtime directory contains tests that specifically need
to be compiled as-if in the runtime package. For error-check
tests, these require the additional flags -+ and -p=runtime.

View File

@@ -25,12 +25,12 @@ func f() {
switch {
case 0: f(); case 0:
case 0: f() case 0: // ERROR "unexpected case at end of statement"
case 0: f() case 0: // ERROR "unexpected keyword case at end of statement"
}
switch {
case 0: f(); default:
case 0: f() default: // ERROR "unexpected default at end of statement"
case 0: f() default: // ERROR "unexpected keyword default at end of statement"
}
switch {

View File

@@ -8,7 +8,7 @@ package main
func main() {
if x { } // GCCGO_ERROR "undefined"
else { } // ERROR "unexpected semicolon or newline before .?else.?|unexpected else"
else { } // ERROR "unexpected semicolon or newline before .?else.?|unexpected keyword else"
}

29
test/tailcall.go Normal file
View File

@@ -0,0 +1,29 @@
// errorcheck -0 -d=tailcall=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.
package p
// Test that when generating wrappers for methods, we generate a tail call to the pointer version of
// the method, if that method is not inlineable. We use go:noinline here to force the non-inlineability
// condition.
//go:noinline
func (f *Foo) Get2Vals() [2]int { return [2]int{f.Val, f.Val + 1} }
func (f *Foo) Get3Vals() [3]int { return [3]int{f.Val, f.Val + 1, f.Val + 2} }
type Foo struct{ Val int }
type Bar struct { // ERROR "tail call emitted for the method \(\*Foo\).Get2Vals wrapper"
int64
*Foo // needs a method wrapper
string
}
var i any
func init() {
i = Bar{1, nil, "first"}
}

19
test/wasmexport.go Normal file
View File

@@ -0,0 +1,19 @@
// errorcheck
// 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.
// Verify that misplaced directives are diagnosed.
//go:build wasm
package p
//go:wasmexport F
func F() {} // OK
type S int32
//go:wasmexport M
func (S) M() {} // ERROR "cannot use //go:wasmexport on a method"

92
test/wasmexport2.go Normal file
View File

@@ -0,0 +1,92 @@
// errorcheck
// 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.
// Verify that wasmexport supports allowed types and rejects
// unallowed types.
//go:build wasm
package p
import (
"structs"
"unsafe"
)
//go:wasmexport good1
func good1(int32, uint32, int64, uint64, float32, float64, unsafe.Pointer) {} // allowed types
type MyInt32 int32
//go:wasmexport good2
func good2(MyInt32) {} // named type is ok
//go:wasmexport good3
func good3() int32 { return 0 } // one result is ok
//go:wasmexport good4
func good4() unsafe.Pointer { return nil } // one result is ok
//go:wasmexport good5
func good5(string, uintptr) bool { return false } // bool, string, and uintptr are allowed
//go:wasmexport bad1
func bad1(any) {} // ERROR "go:wasmexport: unsupported parameter type"
//go:wasmexport bad2
func bad2(func()) {} // ERROR "go:wasmexport: unsupported parameter type"
//go:wasmexport bad3
func bad3(uint8) {} // ERROR "go:wasmexport: unsupported parameter type"
//go:wasmexport bad4
func bad4(int) {} // ERROR "go:wasmexport: unsupported parameter type"
// Struct and array types are also not allowed.
type S struct { x, y int32 }
type H struct { _ structs.HostLayout; x, y int32 }
type A = structs.HostLayout
type AH struct { _ A; x, y int32 }
//go:wasmexport bad5
func bad5(S) {} // ERROR "go:wasmexport: unsupported parameter type"
//go:wasmexport bad6
func bad6(H) {} // ERROR "go:wasmexport: unsupported parameter type"
//go:wasmexport bad7
func bad7([4]int32) {} // ERROR "go:wasmexport: unsupported parameter type"
// Pointer types are not allowed, with resitrictions on
// the element type.
//go:wasmexport good6
func good6(*int32, *uint8, *bool) {}
//go:wasmexport bad8
func bad8(*S) {} // ERROR "go:wasmexport: unsupported parameter type" // without HostLayout, not allowed
//go:wasmexport bad9
func bad9() *S { return nil } // ERROR "go:wasmexport: unsupported result type"
//go:wasmexport good7
func good7(*H, *AH) {} // pointer to struct with HostLayout is allowed
//go:wasmexport good8
func good8(*struct{}) {} // pointer to empty struct is allowed
//go:wasmexport good9
func good9(*[4]int32, *[2]H) {} // pointer to array is allowed, if the element type is okay
//go:wasmexport toomanyresults
func toomanyresults() (int32, int32) { return 0, 0 } // ERROR "go:wasmexport: too many return values"
//go:wasmexport bad10
func bad10() string { return "" } // ERROR "go:wasmexport: unsupported result type" // string cannot be a result

View File

@@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
TEXT ·currentMemory(SB), NOSPLIT, $0
Get SP
CurrentMemory
I32Store ret+0(FP)
RET

View File

@@ -0,0 +1,30 @@
// 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 main
import (
"fmt"
"io"
)
// Expect 8 MB of memory usage for a small wasm program.
// This reflects the current allocator. We test an exact
// value here, but if the allocator changes, we can update
// or relax this.
const want = 8 << 20
var w = io.Discard
func main() {
fmt.Fprintln(w, "hello world")
const pageSize = 64 * 1024
sz := uintptr(currentMemory()) * pageSize
if sz != want {
fmt.Printf("FAIL: unexpected memory size %d, want %d\n", sz, want)
}
}
func currentMemory() int32 // implemented in assembly

11
test/wasmmemsize.go Normal file
View File

@@ -0,0 +1,11 @@
// runindir
// 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.
// This test checks the memory size of a small wasm program.
//go:build wasm
package ignored

24
test/weak.go Normal file
View File

@@ -0,0 +1,24 @@
// errorcheck
// 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 weak pointers.
package p
import (
"runtime"
"weak"
)
// Adapted from example in https://github.com/golang/go/issues/67552#issuecomment-2639661220
func conversion() {
p := "hello"
a := weak.Make(&p)
b := (weak.Pointer[*byte])(a) // ERROR "cannot convert a \(variable of struct type weak\.Pointer\[string\]\) to type weak.Pointer\[\*byte\]"
c := b.Value()
println(**c)
runtime.KeepAlive(p)
}