1
0
mirror of https://github.com/golang/go synced 2024-11-26 03:57:57 -07:00

Merge branch 'golang:master' into update-clean-help-message

This commit is contained in:
Mohamed Yousif 2024-06-24 19:13:11 +03:00 committed by GitHub
commit 3048b2e4bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 287 additions and 305 deletions

View File

@ -4,302 +4,7 @@
package arm64 package arm64
import ( import "testing"
"bytes"
"fmt"
"internal/testenv"
"os"
"path/filepath"
"regexp"
"testing"
)
func TestSplitImm24uScaled(t *testing.T) {
tests := []struct {
v int32
shift int
wantErr bool
wantHi int32
wantLo int32
}{
{
v: 0,
shift: 0,
wantHi: 0,
wantLo: 0,
},
{
v: 0x1001,
shift: 0,
wantHi: 0x1000,
wantLo: 0x1,
},
{
v: 0xffffff,
shift: 0,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0xffffff,
shift: 1,
wantErr: true,
},
{
v: 0xfe,
shift: 1,
wantHi: 0x0,
wantLo: 0x7f,
},
{
v: 0x10fe,
shift: 1,
wantHi: 0x0,
wantLo: 0x87f,
},
{
v: 0x2002,
shift: 1,
wantHi: 0x2000,
wantLo: 0x1,
},
{
v: 0xfffffe,
shift: 1,
wantHi: 0xffe000,
wantLo: 0xfff,
},
{
v: 0x1000ffe,
shift: 1,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0x1001000,
shift: 1,
wantErr: true,
},
{
v: 0xfffffe,
shift: 2,
wantErr: true,
},
{
v: 0x4004,
shift: 2,
wantHi: 0x4000,
wantLo: 0x1,
},
{
v: 0xfffffc,
shift: 2,
wantHi: 0xffc000,
wantLo: 0xfff,
},
{
v: 0x1002ffc,
shift: 2,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0x1003000,
shift: 2,
wantErr: true,
},
{
v: 0xfffffe,
shift: 3,
wantErr: true,
},
{
v: 0x8008,
shift: 3,
wantHi: 0x8000,
wantLo: 0x1,
},
{
v: 0xfffff8,
shift: 3,
wantHi: 0xff8000,
wantLo: 0xfff,
},
{
v: 0x1006ff8,
shift: 3,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0x1007000,
shift: 3,
wantErr: true,
},
}
for _, test := range tests {
hi, lo, err := splitImm24uScaled(test.v, test.shift)
switch {
case err == nil && test.wantErr:
t.Errorf("splitImm24uScaled(%v, %v) succeeded, want error", test.v, test.shift)
case err != nil && !test.wantErr:
t.Errorf("splitImm24uScaled(%v, %v) failed: %v", test.v, test.shift, err)
case !test.wantErr:
if got, want := hi, test.wantHi; got != want {
t.Errorf("splitImm24uScaled(%x, %x) - got hi %x, want %x", test.v, test.shift, got, want)
}
if got, want := lo, test.wantLo; got != want {
t.Errorf("splitImm24uScaled(%x, %x) - got lo %x, want %x", test.v, test.shift, got, want)
}
}
}
for shift := 0; shift <= 3; shift++ {
for v := int32(0); v < 0xfff000+0xfff<<shift; v = v + 1<<shift {
hi, lo, err := splitImm24uScaled(v, shift)
if err != nil {
t.Fatalf("splitImm24uScaled(%x, %x) failed: %v", v, shift, err)
}
if hi+lo<<shift != v {
t.Fatalf("splitImm24uScaled(%x, %x) = (%x, %x) is incorrect", v, shift, hi, lo)
}
}
}
}
// TestLarge generates a very large file to verify that large
// program builds successfully, in particular, too-far
// conditional branches are fixed, and also verify that the
// instruction's pc can be correctly aligned even when branches
// need to be fixed.
func TestLarge(t *testing.T) {
if testing.Short() {
t.Skip("Skip in short mode")
}
testenv.MustHaveGoBuild(t)
dir, err := os.MkdirTemp("", "testlarge")
if err != nil {
t.Fatalf("could not create directory: %v", err)
}
defer os.RemoveAll(dir)
// generate a very large function
buf := bytes.NewBuffer(make([]byte, 0, 7000000))
gen(buf)
tmpfile := filepath.Join(dir, "x.s")
err = os.WriteFile(tmpfile, buf.Bytes(), 0644)
if err != nil {
t.Fatalf("can't write output: %v\n", err)
}
pattern := `0x0080\s00128\s\(.*\)\tMOVD\t\$3,\sR3`
// assemble generated file
cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOOS=linux")
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("Assemble failed: %v, output: %s", err, out)
}
matched, err := regexp.MatchString(pattern, string(out))
if err != nil {
t.Fatal(err)
}
if !matched {
t.Errorf("The alignment is not correct: %t, output:%s\n", matched, out)
}
// build generated file
cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOOS=linux")
out, err = cmd.CombinedOutput()
if err != nil {
t.Errorf("Build failed: %v, output: %s", err, out)
}
}
// gen generates a very large program, with a very far conditional branch.
func gen(buf *bytes.Buffer) {
fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
fmt.Fprintln(buf, "TBZ $5, R0, label")
fmt.Fprintln(buf, "CBZ R0, label")
fmt.Fprintln(buf, "BEQ label")
fmt.Fprintln(buf, "PCALIGN $128")
fmt.Fprintln(buf, "MOVD $3, R3")
for i := 0; i < 1<<19; i++ {
fmt.Fprintln(buf, "MOVD R0, R1")
}
fmt.Fprintln(buf, "label:")
fmt.Fprintln(buf, "RET")
}
// Issue 20348.
func TestNoRet(t *testing.T) {
dir, err := os.MkdirTemp("", "testnoret")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
tmpfile := filepath.Join(dir, "x.s")
if err := os.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
t.Fatal(err)
}
cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOOS=linux")
if out, err := cmd.CombinedOutput(); err != nil {
t.Errorf("%v\n%s", err, out)
}
}
// TestPCALIGN verifies the correctness of the PCALIGN by checking if the
// code can be aligned to the alignment value.
func TestPCALIGN(t *testing.T) {
testenv.MustHaveGoBuild(t)
dir, err := os.MkdirTemp("", "testpcalign")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
tmpfile := filepath.Join(dir, "test.s")
tmpout := filepath.Join(dir, "test.o")
code1 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $8\nMOVD $1, R1\nRET\n")
code2 := []byte("TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $16\nMOVD $2, R2\nRET\n")
// If the output contains this pattern, the pc-offsite of "MOVD $1, R1" is 8 bytes aligned.
out1 := `0x0008\s00008\s\(.*\)\tMOVD\t\$1,\sR1`
// If the output contains this pattern, the pc-offsite of "MOVD $2, R2" is 16 bytes aligned.
out2 := `0x0010\s00016\s\(.*\)\tMOVD\t\$2,\sR2`
var testCases = []struct {
name string
code []byte
out string
}{
{"8-byte alignment", code1, out1},
{"16-byte alignment", code2, out2},
}
for _, test := range testCases {
if err := os.WriteFile(tmpfile, test.code, 0644); err != nil {
t.Fatal(err)
}
cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile)
cmd.Env = append(os.Environ(), "GOOS=linux")
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("The %s build failed: %v, output: %s", test.name, err, out)
continue
}
matched, err := regexp.MatchString(test.out, string(out))
if err != nil {
t.Fatal(err)
}
if !matched {
t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
}
}
}
func testvmovs() (r1, r2 uint64) func testvmovs() (r1, r2 uint64)
func testvmovd() (r1, r2 uint64) func testvmovd() (r1, r2 uint64)

View File

@ -0,0 +1,258 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package arm64
import (
"bytes"
"fmt"
"internal/testenv"
"os"
"path/filepath"
"regexp"
"testing"
)
func runAssembler(t *testing.T, srcdata string) []byte {
dir := t.TempDir()
defer os.RemoveAll(dir)
srcfile := filepath.Join(dir, "testdata.s")
outfile := filepath.Join(dir, "testdata.o")
os.WriteFile(srcfile, []byte(srcdata), 0644)
cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", outfile, srcfile)
cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=arm64")
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("The build failed: %v, output:\n%s", err, out)
}
return out
}
func TestSplitImm24uScaled(t *testing.T) {
tests := []struct {
v int32
shift int
wantErr bool
wantHi int32
wantLo int32
}{
{
v: 0,
shift: 0,
wantHi: 0,
wantLo: 0,
},
{
v: 0x1001,
shift: 0,
wantHi: 0x1000,
wantLo: 0x1,
},
{
v: 0xffffff,
shift: 0,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0xffffff,
shift: 1,
wantErr: true,
},
{
v: 0xfe,
shift: 1,
wantHi: 0x0,
wantLo: 0x7f,
},
{
v: 0x10fe,
shift: 1,
wantHi: 0x0,
wantLo: 0x87f,
},
{
v: 0x2002,
shift: 1,
wantHi: 0x2000,
wantLo: 0x1,
},
{
v: 0xfffffe,
shift: 1,
wantHi: 0xffe000,
wantLo: 0xfff,
},
{
v: 0x1000ffe,
shift: 1,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0x1001000,
shift: 1,
wantErr: true,
},
{
v: 0xfffffe,
shift: 2,
wantErr: true,
},
{
v: 0x4004,
shift: 2,
wantHi: 0x4000,
wantLo: 0x1,
},
{
v: 0xfffffc,
shift: 2,
wantHi: 0xffc000,
wantLo: 0xfff,
},
{
v: 0x1002ffc,
shift: 2,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0x1003000,
shift: 2,
wantErr: true,
},
{
v: 0xfffffe,
shift: 3,
wantErr: true,
},
{
v: 0x8008,
shift: 3,
wantHi: 0x8000,
wantLo: 0x1,
},
{
v: 0xfffff8,
shift: 3,
wantHi: 0xff8000,
wantLo: 0xfff,
},
{
v: 0x1006ff8,
shift: 3,
wantHi: 0xfff000,
wantLo: 0xfff,
},
{
v: 0x1007000,
shift: 3,
wantErr: true,
},
}
for _, test := range tests {
hi, lo, err := splitImm24uScaled(test.v, test.shift)
switch {
case err == nil && test.wantErr:
t.Errorf("splitImm24uScaled(%v, %v) succeeded, want error", test.v, test.shift)
case err != nil && !test.wantErr:
t.Errorf("splitImm24uScaled(%v, %v) failed: %v", test.v, test.shift, err)
case !test.wantErr:
if got, want := hi, test.wantHi; got != want {
t.Errorf("splitImm24uScaled(%x, %x) - got hi %x, want %x", test.v, test.shift, got, want)
}
if got, want := lo, test.wantLo; got != want {
t.Errorf("splitImm24uScaled(%x, %x) - got lo %x, want %x", test.v, test.shift, got, want)
}
}
}
for shift := 0; shift <= 3; shift++ {
for v := int32(0); v < 0xfff000+0xfff<<shift; v = v + 1<<shift {
hi, lo, err := splitImm24uScaled(v, shift)
if err != nil {
t.Fatalf("splitImm24uScaled(%x, %x) failed: %v", v, shift, err)
}
if hi+lo<<shift != v {
t.Fatalf("splitImm24uScaled(%x, %x) = (%x, %x) is incorrect", v, shift, hi, lo)
}
}
}
}
// TestLarge generates a very large file to verify that large
// program builds successfully, in particular, too-far
// conditional branches are fixed, and also verify that the
// instruction's pc can be correctly aligned even when branches
// need to be fixed.
func TestLarge(t *testing.T) {
if testing.Short() {
t.Skip("Skip in short mode")
}
testenv.MustHaveGoBuild(t)
// generate a very large function
buf := bytes.NewBuffer(make([]byte, 0, 7000000))
fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
fmt.Fprintln(buf, "TBZ $5, R0, label")
fmt.Fprintln(buf, "CBZ R0, label")
fmt.Fprintln(buf, "BEQ label")
fmt.Fprintln(buf, "PCALIGN $128")
fmt.Fprintln(buf, "MOVD $3, R3")
for i := 0; i < 1<<19; i++ {
fmt.Fprintln(buf, "MOVD R0, R1")
}
fmt.Fprintln(buf, "label:")
fmt.Fprintln(buf, "RET")
// assemble generated file
out := runAssembler(t, buf.String())
pattern := `0x0080\s00128\s\(.*\)\tMOVD\t\$3,\sR3`
matched, err := regexp.MatchString(pattern, string(out))
if err != nil {
t.Fatal(err)
}
if !matched {
t.Errorf("The alignment is not correct: %t\n", matched)
}
}
// Issue 20348.
func TestNoRet(t *testing.T) {
runAssembler(t, "TEXT ·stub(SB),$0-0\nNOP\n")
}
// TestPCALIGN verifies the correctness of the PCALIGN by checking if the
// code can be aligned to the alignment value.
func TestPCALIGN(t *testing.T) {
testenv.MustHaveGoBuild(t)
code1 := "TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $8\nMOVD $1, R1\nRET\n"
code2 := "TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $16\nMOVD $2, R2\nRET\n"
// If the output contains this pattern, the pc-offset of "MOVD $1, R1" is 8 bytes aligned.
out1 := `0x0008\s00008\s\(.*\)\tMOVD\t\$1,\sR1`
// If the output contains this pattern, the pc-offset of "MOVD $2, R2" is 16 bytes aligned.
out2 := `0x0010\s00016\s\(.*\)\tMOVD\t\$2,\sR2`
var testCases = []struct {
name string
code string
out string
}{
{"8-byte alignment", code1, out1},
{"16-byte alignment", code2, out2},
}
for _, test := range testCases {
out := runAssembler(t, test.code)
matched, err := regexp.MatchString(test.out, string(out))
if err != nil {
t.Fatal(err)
}
if !matched {
t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
}
}
}

View File

@ -16,6 +16,10 @@ type Uint128 = uint128
type AddrDetail = addrDetail type AddrDetail = addrDetail
func MakeAddrDetail(isV6 bool, zoneV6 string) AddrDetail {
return AddrDetail{isV6: isV6, zoneV6: zoneV6}
}
func Mk128(hi, lo uint64) Uint128 { func Mk128(hi, lo uint64) Uint128 {
return uint128{hi, lo} return uint128{hi, lo}
} }

View File

@ -59,8 +59,8 @@ type Addr struct {
// addrDetail represents the details of an Addr, like address family and IPv6 zone. // addrDetail represents the details of an Addr, like address family and IPv6 zone.
type addrDetail struct { type addrDetail struct {
IsV6 bool // IPv4 is false, IPv6 is true. isV6 bool // IPv4 is false, IPv6 is true.
ZoneV6 string // != "" only if IsV6 is true. zoneV6 string // != "" only if IsV6 is true.
} }
// z0, z4, and z6noz are sentinel Addr.z values. // z0, z4, and z6noz are sentinel Addr.z values.
@ -68,7 +68,7 @@ type addrDetail struct {
var ( var (
z0 unique.Handle[addrDetail] z0 unique.Handle[addrDetail]
z4 = unique.Make(addrDetail{}) z4 = unique.Make(addrDetail{})
z6noz = unique.Make(addrDetail{IsV6: true}) z6noz = unique.Make(addrDetail{isV6: true})
) )
// IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast // IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast
@ -410,7 +410,7 @@ func (ip Addr) Zone() string {
if ip.z == z0 { if ip.z == z0 {
return "" return ""
} }
return ip.z.Value().ZoneV6 return ip.z.Value().zoneV6
} }
// Compare returns an integer comparing two IPs. // Compare returns an integer comparing two IPs.
@ -495,7 +495,7 @@ func (ip Addr) WithZone(zone string) Addr {
ip.z = z6noz ip.z = z6noz
return ip return ip
} }
ip.z = unique.Make(addrDetail{IsV6: true, ZoneV6: zone}) ip.z = unique.Make(addrDetail{isV6: true, zoneV6: zone})
return ip return ip
} }

View File

@ -112,18 +112,18 @@ func TestParseAddr(t *testing.T) {
// IPv6 with a zone specifier. // IPv6 with a zone specifier.
{ {
in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0", in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0",
ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth0"})), ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), unique.Make(MakeAddrDetail(true, "eth0"))),
}, },
// IPv6 with dotted decimal and zone specifier. // IPv6 with dotted decimal and zone specifier.
{ {
in: "1:2::ffff:192.168.140.255%eth1", in: "1:2::ffff:192.168.140.255%eth1",
ip: MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth1"})), ip: MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
str: "1:2::ffff:c0a8:8cff%eth1", str: "1:2::ffff:c0a8:8cff%eth1",
}, },
// 4-in-6 with zone // 4-in-6 with zone
{ {
in: "::ffff:192.168.140.255%eth1", in: "::ffff:192.168.140.255%eth1",
ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth1"})), ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))),
str: "::ffff:192.168.140.255%eth1", str: "::ffff:192.168.140.255%eth1",
}, },
// IPv6 with capital letters. // IPv6 with capital letters.
@ -893,6 +893,15 @@ func TestAddrLessCompare(t *testing.T) {
{mustIP("::1%a"), mustIP("::1%b"), true}, {mustIP("::1%a"), mustIP("::1%b"), true},
{mustIP("::1%a"), mustIP("::1%a"), false}, {mustIP("::1%a"), mustIP("::1%a"), false},
{mustIP("::1%b"), mustIP("::1%a"), false}, {mustIP("::1%b"), mustIP("::1%a"), false},
// For Issue 68113, verify that an IPv4 address and a
// v4-mapped-IPv6 address differing only in their zone
// pointer are unequal via all three of
// ==/Compare/reflect.DeepEqual. In Go 1.22 and
// earlier, these were accidentally equal via
// DeepEqual due to their zone pointers (z) differing
// but pointing to identical structures.
{mustIP("::ffff:11.1.1.12"), mustIP("11.1.1.12"), false},
} }
for _, tt := range tests { for _, tt := range tests {
got := tt.a.Less(tt.b) got := tt.a.Less(tt.b)
@ -920,6 +929,12 @@ func TestAddrLessCompare(t *testing.T) {
t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a) t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a)
} }
} }
// Also check reflect.DeepEqual. See issue 68113.
deepEq := reflect.DeepEqual(tt.a, tt.b)
if (cmp == 0) != deepEq {
t.Errorf("%q and %q differ in == (%v) vs reflect.DeepEqual (%v)", tt.a, tt.b, cmp == 0, deepEq)
}
} }
// And just sort. // And just sort.
@ -1723,7 +1738,7 @@ var parseBenchInputs = []struct {
} }
func BenchmarkParseAddr(b *testing.B) { func BenchmarkParseAddr(b *testing.B) {
sinkInternValue = unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth1"}) // Pin to not benchmark the intern package sinkInternValue = unique.Make(MakeAddrDetail(true, "eth1")) // Pin to not benchmark the intern package
for _, test := range parseBenchInputs { for _, test := range parseBenchInputs {
b.Run(test.name, func(b *testing.B) { b.Run(test.name, func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()