diff --git a/AUTHORS b/AUTHORS index 08caca5f68..96704bd564 100644 --- a/AUTHORS +++ b/AUTHORS @@ -829,6 +829,7 @@ Liberty Fund Inc Linaro Limited Lion Yang Lloyd Dewolf +Loongson Technology Corporation Limited Lorenzo Masini Lorenzo Stoakes Luan Santos diff --git a/api/except.txt b/api/except.txt index b9972c121c..2acd444adf 100644 --- a/api/except.txt +++ b/api/except.txt @@ -505,3 +505,5 @@ pkg unicode, const Version = "6.3.0" pkg unicode, const Version = "7.0.0" pkg unicode, const Version = "8.0.0" pkg unicode, const Version = "9.0.0" +pkg html/template, method (*Template) Funcs(FuncMap) *Template +pkg html/template, type FuncMap map[string]interface{} diff --git a/api/go1.1.txt b/api/go1.1.txt index bb00b3b02c..06291faa4b 100644 --- a/api/go1.1.txt +++ b/api/go1.1.txt @@ -371,7 +371,7 @@ pkg debug/elf, const ELFCLASSNONE = 0 pkg debug/elf, const ELFDATA2LSB = 1 pkg debug/elf, const ELFDATA2MSB = 2 pkg debug/elf, const ELFDATANONE = 0 -pkg debug/elf, const ELFMAG = "\u007fELF" +pkg debug/elf, const ELFMAG = "\x7fELF" pkg debug/elf, const ELFOSABI_86OPEN = 5 pkg debug/elf, const ELFOSABI_AIX = 7 pkg debug/elf, const ELFOSABI_ARM = 97 diff --git a/api/next/46057.txt b/api/next/46057.txt new file mode 100644 index 0000000000..d971aa7ffd --- /dev/null +++ b/api/next/46057.txt @@ -0,0 +1 @@ +pkg crypto/x509, method (*CertPool) Equal(*CertPool) bool #46057 diff --git a/api/next/46121.txt b/api/next/46121.txt new file mode 100644 index 0000000000..a50d6456c8 --- /dev/null +++ b/api/next/46121.txt @@ -0,0 +1,2 @@ +pkg html/template, method (*Template) Funcs(template.FuncMap) *Template #46121 +pkg html/template, type FuncMap = template.FuncMap #46121 diff --git a/api/next/46229.txt b/api/next/46229.txt new file mode 100644 index 0000000000..ebaaefda55 --- /dev/null +++ b/api/next/46229.txt @@ -0,0 +1,105 @@ +pkg debug/elf, const EM_LOONGARCH = 258 #46229 +pkg debug/elf, const EM_LOONGARCH Machine #46229 +pkg debug/elf, const R_LARCH_32 = 1 #46229 +pkg debug/elf, const R_LARCH_32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_64 = 2 #46229 +pkg debug/elf, const R_LARCH_64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD16 = 48 #46229 +pkg debug/elf, const R_LARCH_ADD16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD24 = 49 #46229 +pkg debug/elf, const R_LARCH_ADD24 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD32 = 50 #46229 +pkg debug/elf, const R_LARCH_ADD32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD64 = 51 #46229 +pkg debug/elf, const R_LARCH_ADD64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_ADD8 = 47 #46229 +pkg debug/elf, const R_LARCH_ADD8 R_LARCH #46229 +pkg debug/elf, const R_LARCH_COPY = 4 #46229 +pkg debug/elf, const R_LARCH_COPY R_LARCH #46229 +pkg debug/elf, const R_LARCH_IRELATIVE = 12 #46229 +pkg debug/elf, const R_LARCH_IRELATIVE R_LARCH #46229 +pkg debug/elf, const R_LARCH_JUMP_SLOT = 5 #46229 +pkg debug/elf, const R_LARCH_JUMP_SLOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_MARK_LA = 20 #46229 +pkg debug/elf, const R_LARCH_MARK_LA R_LARCH #46229 +pkg debug/elf, const R_LARCH_MARK_PCREL = 21 #46229 +pkg debug/elf, const R_LARCH_MARK_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_NONE = 0 #46229 +pkg debug/elf, const R_LARCH_NONE R_LARCH #46229 +pkg debug/elf, const R_LARCH_RELATIVE = 3 #46229 +pkg debug/elf, const R_LARCH_RELATIVE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_ADD = 35 #46229 +pkg debug/elf, const R_LARCH_SOP_ADD R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_AND = 36 #46229 +pkg debug/elf, const R_LARCH_SOP_AND R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_ASSERT = 30 #46229 +pkg debug/elf, const R_LARCH_SOP_ASSERT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_IF_ELSE = 37 #46229 +pkg debug/elf, const R_LARCH_SOP_IF_ELSE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_NOT = 31 #46229 +pkg debug/elf, const R_LARCH_SOP_NOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 = 45 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_10_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 = 44 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_0_5_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 = 40 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_12 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 = 41 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 = 42 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_16_S2 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 = 38 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_10_5 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 = 43 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_S_5_20 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U = 46 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 = 39 #46229 +pkg debug/elf, const R_LARCH_SOP_POP_32_U_10_12 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE = 23 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_ABSOLUTE R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_DUP = 24 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_DUP R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL = 25 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_GPREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL = 22 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL = 29 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_PLT_PCREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD = 28 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GD R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT = 27 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_GOT R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL = 26 #46229 +pkg debug/elf, const R_LARCH_SOP_PUSH_TLS_TPREL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SL = 33 #46229 +pkg debug/elf, const R_LARCH_SOP_SL R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SR = 34 #46229 +pkg debug/elf, const R_LARCH_SOP_SR R_LARCH #46229 +pkg debug/elf, const R_LARCH_SOP_SUB = 32 #46229 +pkg debug/elf, const R_LARCH_SOP_SUB R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB16 = 53 #46229 +pkg debug/elf, const R_LARCH_SUB16 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB24 = 54 #46229 +pkg debug/elf, const R_LARCH_SUB24 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB32 = 55 #46229 +pkg debug/elf, const R_LARCH_SUB32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB64 = 56 #46229 +pkg debug/elf, const R_LARCH_SUB64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_SUB8 = 52 #46229 +pkg debug/elf, const R_LARCH_SUB8 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD32 = 6 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD64 = 7 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPMOD64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL32 = 8 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL64 = 9 #46229 +pkg debug/elf, const R_LARCH_TLS_DTPREL64 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL32 = 10 #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL32 R_LARCH #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL64 = 11 #46229 +pkg debug/elf, const R_LARCH_TLS_TPREL64 R_LARCH #46229 +pkg debug/elf, method (R_LARCH) GoString() string #46229 +pkg debug/elf, method (R_LARCH) String() string #46229 +pkg debug/elf, type R_LARCH int #46229 diff --git a/api/next/51868.txt b/api/next/51868.txt new file mode 100644 index 0000000000..cbf0324d5f --- /dev/null +++ b/api/next/51868.txt @@ -0,0 +1,36 @@ +pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY = 2 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ANY ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_ASSOCIATIVE ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH = 4 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_EXACT_MATCH ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST = 6 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_LARGEST ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES = 1 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_NODUPLICATES ideal-int #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE = 3 #51686 +pkg debug/pe, const IMAGE_COMDAT_SELECT_SAME_SIZE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_CNT_CODE = 32 #51686 +pkg debug/pe, const IMAGE_SCN_CNT_CODE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA = 64 #51686 +pkg debug/pe, const IMAGE_SCN_CNT_INITIALIZED_DATA ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA = 128 #51686 +pkg debug/pe, const IMAGE_SCN_CNT_UNINITIALIZED_DATA ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_LNK_COMDAT = 4096 #51686 +pkg debug/pe, const IMAGE_SCN_LNK_COMDAT ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE = 33554432 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_DISCARDABLE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE = 536870912 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_EXECUTE ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_READ = 1073741824 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_READ ideal-int #51686 +pkg debug/pe, const IMAGE_SCN_MEM_WRITE = 2147483648 #51686 +pkg debug/pe, const IMAGE_SCN_MEM_WRITE ideal-int #51686 +pkg debug/pe, method (*File) COFFSymbolReadSectionDefAux(int) (*COFFSymbolAuxFormat5, error) #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Checksum uint32 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumLineNumbers uint16 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, NumRelocs uint16 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, SecNum uint16 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Selection uint8 #51686 +pkg debug/pe, type COFFSymbolAuxFormat5 struct, Size uint32 #51686 diff --git a/api/next/regexpdepth.txt b/api/next/regexpdepth.txt new file mode 100644 index 0000000000..9810218560 --- /dev/null +++ b/api/next/regexpdepth.txt @@ -0,0 +1,3 @@ +pkg regexp/syntax, const ErrInvalidDepth = "invalid nesting depth" #0 +pkg regexp/syntax, const ErrInvalidDepth ErrorCode #0 + diff --git a/doc/go1.19.html b/doc/go1.19.html index c71d5e760b..857d8ed8ce 100644 --- a/doc/go1.19.html +++ b/doc/go1.19.html @@ -34,6 +34,23 @@ Do not send CLs removing the interior tags from such phrases.

TODO: complete this section, or delete if not needed

+ +

New unix build constraint

+ +

+ The build constraint unix is now recognized + in //go:build lines. The constraint is satisfied + if the target operating system, also known as GOOS, is + a Unix or Unix-like system. For the 1.19 release it is satisfied + if GOOS is one of + aix, android, darwin, + dragonfly, freebsd, hurd, + illumos, ios, linux, + netbsd, openbsd, or solaris. + In future releases the unix constraint may match + additional newly supported operating systems. +

+

Runtime

TODO: complete this section, or delete if not needed @@ -72,5 +89,27 @@ Do not send CLs removing the interior tags from such phrases. Please report any such problems on the issue tracker.

+ +

+ When a net package function or method returns an "I/O timeout" + error, the error will now satisfy errors.Is(err, + context.Canceled). When a net package function returns + an "operation was canceled" error, the error will now satisfy + errors.Is(err, context.DeadlineExceeded). + These changes are intended to make it easier for code to test + for cases in which a context cancelation or timeout causes a net + package function or method to return an error, while preserving + backward compatibility for error messages. +

- + + +
strconv
+
+

+ strconv.Quote + and related functions now quote the rune 007F as \x7f, + not \u007f. +

+
+
diff --git a/doc/go_spec.html b/doc/go_spec.html index ad12fcfaa9..b496e9e48f 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -529,6 +529,7 @@ escaped_char = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | ` 'aa' // illegal: too many characters '\xa' // illegal: too few hexadecimal digits '\0' // illegal: too few octal digits +'\400' // illegal: octal value over 255 '\uDFFF' // illegal: surrogate half '\U00110000' // illegal: invalid Unicode code point @@ -1454,7 +1455,8 @@ type Float interface {

-In a union, a term cannot be a type parameter, and the type sets of all +The type T in a term of the form T or ~T cannot +be a type parameter, and the type sets of all non-interface terms must be pairwise disjoint (the pairwise intersection of the type sets must be empty). Given a type parameter P:

@@ -1462,7 +1464,7 @@ Given a type parameter P:
 interface {
 	P                // illegal: P is a type parameter
-	int | P          // illegal: P is a type parameter
+	int | ~P         // illegal: P is a type parameter
 	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
 	float32 | Float  // overlapping type sets but Float is an interface
 }
@@ -1878,7 +1880,7 @@ A4, func(int, float64) *[]string, and A5
 B0 and C0
 D0[int, string] and E0
 []int and []int
-struct{ a, b *T5 } and struct{ a, b *T5 }
+struct{ a, b *B5 } and struct{ a, b *B5 }
 func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5
 
@@ -4195,7 +4197,7 @@ type parameter list type arguments after substitution

For a generic function, type arguments may be provided explicitly, or they may be partially or completely inferred. -A generic function that is is not called requires a +A generic function that is not called requires a type argument list for instantiation; if the list is partial, all remaining type arguments must be inferrable. A generic function that is called may provide a (possibly partial) type diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go index 7628ffc595..d03769ea87 100644 --- a/misc/cgo/testgodefs/testgodefs_test.go +++ b/misc/cgo/testgodefs/testgodefs_test.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "testing" ) @@ -58,9 +59,32 @@ func TestGoDefs(t *testing.T) { t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) } - if err := os.WriteFile(filepath.Join(dir, fp+"_defs.go"), out, 0644); err != nil { + fn := fp + "_defs.go" + if err := os.WriteFile(filepath.Join(dir, fn), out, 0644); err != nil { t.Fatal(err) } + + // Verify that command line arguments are not rewritten in the generated comment, + // see go.dev/issue/52063 + hasGeneratedByComment := false + for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { + cgoExe := "cgo" + if runtime.GOOS == "windows" { + cgoExe = "cgo.exe" + } + if !strings.HasPrefix(line, "// "+cgoExe+" -godefs") { + continue + } + if want := "// " + cgoExe + " " + strings.Join(cmd.Args[3:], " "); line != want { + t.Errorf("%s: got generated comment %q, want %q", fn, line, want) + } + hasGeneratedByComment = true + break + } + + if !hasGeneratedByComment { + t.Errorf("%s: comment with generating cgo -godefs command not found", fn) + } } main, err := os.ReadFile(filepath.Join("testdata", "main.go")) diff --git a/src/archive/tar/stat_unix.go b/src/archive/tar/stat_unix.go index 717a0b3abc..0f3428bc24 100644 --- a/src/archive/tar/stat_unix.go +++ b/src/archive/tar/stat_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris +//go:build unix package tar diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go index d1a9bdd334..9bc23642c0 100644 --- a/src/archive/zip/reader_test.go +++ b/src/archive/zip/reader_test.go @@ -865,7 +865,6 @@ func returnRecursiveZip() (r io.ReaderAt, size int64) { // // It's here in hex for the same reason as rZipBytes above: to avoid // problems with on-disk virus scanners or other zip processors. -// func biggestZipBytes() []byte { s := ` 0000000 50 4b 03 04 14 00 08 00 08 00 00 00 00 00 00 00 diff --git a/src/bufio/scan.go b/src/bufio/scan.go index 4846d4f733..e247cbcf32 100644 --- a/src/bufio/scan.go +++ b/src/bufio/scan.go @@ -26,7 +26,6 @@ import ( // advanced arbitrarily far past the last token. Programs that need more // control over error handling or large tokens, or must run sequential scans // on a reader, should use bufio.Reader instead. -// type Scanner struct { r io.Reader // The reader provided by the client. split SplitFunc // The function to split the tokens. diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go index 4d374cb828..403e70eee7 100644 --- a/src/cmd/asm/internal/arch/arch.go +++ b/src/cmd/asm/internal/arch/arch.go @@ -278,46 +278,7 @@ func archArm64() *Arch { } register["LR"] = arm64.REGLINK - register["DAIFSet"] = arm64.REG_DAIFSet - register["DAIFClr"] = arm64.REG_DAIFClr - register["PLDL1KEEP"] = arm64.REG_PLDL1KEEP - register["PLDL1STRM"] = arm64.REG_PLDL1STRM - register["PLDL2KEEP"] = arm64.REG_PLDL2KEEP - register["PLDL2STRM"] = arm64.REG_PLDL2STRM - register["PLDL3KEEP"] = arm64.REG_PLDL3KEEP - register["PLDL3STRM"] = arm64.REG_PLDL3STRM - register["PLIL1KEEP"] = arm64.REG_PLIL1KEEP - register["PLIL1STRM"] = arm64.REG_PLIL1STRM - register["PLIL2KEEP"] = arm64.REG_PLIL2KEEP - register["PLIL2STRM"] = arm64.REG_PLIL2STRM - register["PLIL3KEEP"] = arm64.REG_PLIL3KEEP - register["PLIL3STRM"] = arm64.REG_PLIL3STRM - register["PSTL1KEEP"] = arm64.REG_PSTL1KEEP - register["PSTL1STRM"] = arm64.REG_PSTL1STRM - register["PSTL2KEEP"] = arm64.REG_PSTL2KEEP - register["PSTL2STRM"] = arm64.REG_PSTL2STRM - register["PSTL3KEEP"] = arm64.REG_PSTL3KEEP - register["PSTL3STRM"] = arm64.REG_PSTL3STRM - // Conditional operators, like EQ, NE, etc. - register["EQ"] = arm64.COND_EQ - register["NE"] = arm64.COND_NE - register["HS"] = arm64.COND_HS - register["CS"] = arm64.COND_HS - register["LO"] = arm64.COND_LO - register["CC"] = arm64.COND_LO - register["MI"] = arm64.COND_MI - register["PL"] = arm64.COND_PL - register["VS"] = arm64.COND_VS - register["VC"] = arm64.COND_VC - register["HI"] = arm64.COND_HI - register["LS"] = arm64.COND_LS - register["GE"] = arm64.COND_GE - register["LT"] = arm64.COND_LT - register["GT"] = arm64.COND_GT - register["LE"] = arm64.COND_LE - register["AL"] = arm64.COND_AL - register["NV"] = arm64.COND_NV // Pseudo-registers. register["SB"] = RSB register["FP"] = RFP diff --git a/src/cmd/asm/internal/arch/arm64.go b/src/cmd/asm/internal/arch/arm64.go index 24689c5ab1..591c4d35db 100644 --- a/src/cmd/asm/internal/arch/arm64.go +++ b/src/cmd/asm/internal/arch/arm64.go @@ -12,6 +12,7 @@ import ( "cmd/internal/obj" "cmd/internal/obj/arm64" "errors" + "fmt" ) var arm64LS = map[string]uint8{ @@ -52,7 +53,35 @@ func jumpArm64(word string) bool { return arm64Jump[word] } -// IsARM64CMP reports whether the op (as defined by an arm.A* constant) is +var arm64SpecialOperand map[string]arm64.SpecialOperand + +// GetARM64SpecialOperand returns the internal representation of a special operand. +func GetARM64SpecialOperand(name string) arm64.SpecialOperand { + if arm64SpecialOperand == nil { + // Generate the mapping automatically when the first time the function is called. + arm64SpecialOperand = map[string]arm64.SpecialOperand{} + for opd := arm64.SPOP_BEGIN; opd < arm64.SPOP_END; opd++ { + s := fmt.Sprintf("%s", opd) + arm64SpecialOperand[s] = opd + } + + // Handle some special cases. + specialMapping := map[string]arm64.SpecialOperand{ + // The internal representation of CS(CC) and HS(LO) are the same. + "CS": arm64.SPOP_HS, + "CC": arm64.SPOP_LO, + } + for s, opd := range specialMapping { + arm64SpecialOperand[s] = opd + } + } + if opd, ok := arm64SpecialOperand[name]; ok { + return opd + } + return arm64.SPOP_END +} + +// IsARM64CMP reports whether the op (as defined by an arm64.A* constant) is // one of the comparison instructions that require special handling. func IsARM64CMP(op obj.As) bool { switch op { diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go index 4cddcf48a4..59aedbf0cc 100644 --- a/src/cmd/asm/internal/asm/parse.go +++ b/src/cmd/asm/internal/asm/parse.go @@ -19,6 +19,7 @@ import ( "cmd/asm/internal/flags" "cmd/asm/internal/lex" "cmd/internal/obj" + "cmd/internal/obj/arm64" "cmd/internal/obj/x86" "cmd/internal/src" "cmd/internal/sys" @@ -389,8 +390,19 @@ func (p *Parser) operand(a *obj.Addr) { tok := p.next() name := tok.String() if tok.ScanToken == scanner.Ident && !p.atStartOfRegister(name) { - // We have a symbol. Parse $sym±offset(symkind) - p.symbolReference(a, name, prefix) + switch p.arch.Family { + case sys.ARM64: + // arm64 special operands. + if opd := arch.GetARM64SpecialOperand(name); opd != arm64.SPOP_END { + a.Type = obj.TYPE_SPECIAL + a.Offset = int64(opd) + break + } + fallthrough + default: + // We have a symbol. Parse $sym±offset(symkind) + p.symbolReference(a, name, prefix) + } // fmt.Printf("SYM %s\n", obj.Dconv(&emptyProg, 0, a)) if p.peek() == scanner.EOF { return @@ -843,7 +855,6 @@ func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, pr // // Anything else beginning with "<" logs an error if issueError is // true, otherwise returns (false, obj.ABI0). -// func (p *Parser) symRefAttrs(name string, issueError bool) (bool, obj.ABI) { abi := obj.ABI0 isStatic := false diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s index 8a7dd299aa..0e5799a022 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64.s +++ b/src/cmd/asm/internal/asm/testdata/arm64.s @@ -628,7 +628,8 @@ again: CSELW LT, R2, R3, R4 // 44b0831a CSINC GT, R1, ZR, R3 // 23c49f9a CSNEG MI, R1, R2, R3 // 234482da - CSINV CS, R1, R2, R3 // CSINV HS, R1, R2, R3 // 232082da + CSINV CS, R1, R2, R3 // CSINV HS, R1, R2, R3 // 232082da + CSINV HS, R1, R2, R3 // 232082da CSINVW MI, R2, ZR, R2 // 42409f5a CINC EQ, R4, R9 // 8914849a CINCW PL, R2, ZR // 5f44821a @@ -1627,4 +1628,116 @@ again: MSR R13, ZCR_EL1 // 0d1218d5 MRS ZCR_EL1, R23 // 171238d5 MSR R17, ZCR_EL1 // 111218d5 + SYS $32768, R1 // 018008d5 + SYS $32768 // 1f8008d5 + +// TLBI instruction + TLBI VMALLE1IS // 1f8308d5 + TLBI VMALLE1 // 1f8708d5 + TLBI ALLE2IS // 1f830cd5 + TLBI ALLE1IS // 9f830cd5 + TLBI VMALLS12E1IS // df830cd5 + TLBI ALLE2 // 1f870cd5 + TLBI ALLE1 // 9f870cd5 + TLBI VMALLS12E1 // df870cd5 + TLBI ALLE3IS // 1f830ed5 + TLBI ALLE3 // 1f870ed5 + TLBI VMALLE1OS // 1f8108d5 + TLBI ALLE2OS // 1f810cd5 + TLBI ALLE1OS // 9f810cd5 + TLBI VMALLS12E1OS // df810cd5 + TLBI ALLE3OS // 1f810ed5 + TLBI VAE1IS, R0 // 208308d5 + TLBI ASIDE1IS, R1 // 418308d5 + TLBI VAAE1IS, R2 // 628308d5 + TLBI VALE1IS, R3 // a38308d5 + TLBI VAALE1IS, R4 // e48308d5 + TLBI VAE1, R5 // 258708d5 + TLBI ASIDE1, R6 // 468708d5 + TLBI VAAE1, R7 // 678708d5 + TLBI VALE1, R8 // a88708d5 + TLBI VAALE1, R9 // e98708d5 + TLBI IPAS2E1IS, R10 // 2a800cd5 + TLBI IPAS2LE1IS, R11 // ab800cd5 + TLBI VAE2IS, R12 // 2c830cd5 + TLBI VALE2IS, R13 // ad830cd5 + TLBI IPAS2E1, R14 // 2e840cd5 + TLBI IPAS2LE1, R15 // af840cd5 + TLBI VAE2, R16 // 30870cd5 + TLBI VALE2, R17 // b1870cd5 + TLBI VAE3IS, ZR // 3f830ed5 + TLBI VALE3IS, R19 // b3830ed5 + TLBI VAE3, R20 // 34870ed5 + TLBI VALE3, R21 // b5870ed5 + TLBI VAE1OS, R22 // 368108d5 + TLBI ASIDE1OS, R23 // 578108d5 + TLBI VAAE1OS, R24 // 788108d5 + TLBI VALE1OS, R25 // b98108d5 + TLBI VAALE1OS, R26 // fa8108d5 + TLBI RVAE1IS, R27 // 3b8208d5 + TLBI RVAAE1IS, ZR // 7f8208d5 + TLBI RVALE1IS, R29 // bd8208d5 + TLBI RVAALE1IS, R30 // fe8208d5 + TLBI RVAE1OS, ZR // 3f8508d5 + TLBI RVAAE1OS, R0 // 608508d5 + TLBI RVALE1OS, R1 // a18508d5 + TLBI RVAALE1OS, R2 // e28508d5 + TLBI RVAE1, R3 // 238608d5 + TLBI RVAAE1, R4 // 648608d5 + TLBI RVALE1, R5 // a58608d5 + TLBI RVAALE1, R6 // e68608d5 + TLBI RIPAS2E1IS, R7 // 47800cd5 + TLBI RIPAS2LE1IS, R8 // c8800cd5 + TLBI VAE2OS, R9 // 29810cd5 + TLBI VALE2OS, R10 // aa810cd5 + TLBI RVAE2IS, R11 // 2b820cd5 + TLBI RVALE2IS, R12 // ac820cd5 + TLBI IPAS2E1OS, R13 // 0d840cd5 + TLBI RIPAS2E1, R14 // 4e840cd5 + TLBI RIPAS2E1OS, R15 // 6f840cd5 + TLBI IPAS2LE1OS, R16 // 90840cd5 + TLBI RIPAS2LE1, R17 // d1840cd5 + TLBI RIPAS2LE1OS, ZR // ff840cd5 + TLBI RVAE2OS, R19 // 33850cd5 + TLBI RVALE2OS, R20 // b4850cd5 + TLBI RVAE2, R21 // 35860cd5 + TLBI RVALE2, R22 // b6860cd5 + TLBI VAE3OS, R23 // 37810ed5 + TLBI VALE3OS, R24 // b8810ed5 + TLBI RVAE3IS, R25 // 39820ed5 + TLBI RVALE3IS, R26 // ba820ed5 + TLBI RVAE3OS, R27 // 3b850ed5 + TLBI RVALE3OS, ZR // bf850ed5 + TLBI RVAE3, R29 // 3d860ed5 + TLBI RVALE3, R30 // be860ed5 + +// DC instruction + DC IVAC, R0 // 207608d5 + DC ISW, R1 // 417608d5 + DC CSW, R2 // 427a08d5 + DC CISW, R3 // 437e08d5 + DC ZVA, R4 // 24740bd5 + DC CVAC, R5 // 257a0bd5 + DC CVAU, R6 // 267b0bd5 + DC CIVAC, R7 // 277e0bd5 + DC IGVAC, R8 // 687608d5 + DC IGSW, R9 // 897608d5 + DC IGDVAC, R10 // aa7608d5 + DC IGDSW, R11 // cb7608d5 + DC CGSW, R12 // 8c7a08d5 + DC CGDSW, R13 // cd7a08d5 + DC CIGSW, R14 // 8e7e08d5 + DC CIGDSW, R15 // cf7e08d5 + DC GVA, R16 // 70740bd5 + DC GZVA, R17 // 91740bd5 + DC CGVAC, ZR // 7f7a0bd5 + DC CGDVAC, R19 // b37a0bd5 + DC CGVAP, R20 // 747c0bd5 + DC CGDVAP, R21 // b57c0bd5 + DC CGVADP, R22 // 767d0bd5 + DC CGDVADP, R23 // b77d0bd5 + DC CIGVAC, R24 // 787e0bd5 + DC CIGDVAC, R25 // b97e0bd5 + DC CVAP, R26 // 3a7c0bd5 + DC CVADP, R27 // 3b7d0bd5 END diff --git a/src/cmd/asm/internal/asm/testdata/arm64enc.s b/src/cmd/asm/internal/asm/testdata/arm64enc.s index a29862822d..eff48ae8e7 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64enc.s +++ b/src/cmd/asm/internal/asm/testdata/arm64enc.s @@ -134,7 +134,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 CSINV LO, R2, R11, R14 // 4e308bda CSNEGW HS, R16, R29, R10 // 0a269d5a CSNEG NE, R21, R19, R11 // ab1693da - //TODO DC + DC IVAC, R1 // 217608d5 DCPS1 $11378 // 418ea5d4 DCPS2 $10699 // 6239a5d4 DCPS3 $24415 // e3ebabd4 @@ -397,7 +397,7 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$-8 SXTH R17, R25 // 393e4093 SXTW R0, R27 // 1b7c4093 SYSL $285440, R12 // 0c5b2cd5 - //TODO TLBI + TLBI VAE1IS, R1 // 218308d5 TSTW $0x80000007, R9 // TSTW $2147483655, R9 // 3f0d0172 TST $0xfffffff0, LR // TST $4294967280, R30 // df6f7cf2 TSTW R10@>21, R2 // 5f54ca6a diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s index 033c4cda6c..52f01e16a6 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64error.s +++ b/src/cmd/asm/internal/asm/testdata/arm64error.s @@ -432,4 +432,14 @@ TEXT errors(SB),$0 STP (R26, R27), 700(R2) // ERROR "cannot use REGTMP as source" MOVK $0, R10 // ERROR "zero shifts cannot be handled correctly" MOVK $(0<<32), R10 // ERROR "zero shifts cannot be handled correctly" + TLBI PLDL1KEEP // ERROR "illegal argument" + TLBI VMALLE1IS, R0 // ERROR "extraneous register at operand 2" + TLBI ALLE3OS, ZR // ERROR "extraneous register at operand 2" + TLBI VAE1IS // ERROR "missing register at operand 2" + TLBI RVALE3 // ERROR "missing register at operand 2" + DC PLDL1KEEP // ERROR "illegal argument" + DC VMALLE1IS // ERROR "illegal argument" + DC VAE1IS // ERROR "illegal argument" + DC VAE1IS, R0 // ERROR "illegal argument" + DC IVAC // ERROR "missing register at operand 2" RET diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s index fe911a74f5..79d6054869 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64.s @@ -145,6 +145,19 @@ start: SRLW X5, X6, X7 // bb535300 SUBW X5, X6, X7 // bb035340 SRAW X5, X6, X7 // bb535340 + ADDIW $1, X6 // 1b031300 + SLLIW $1, X6 // 1b131300 + SRLIW $1, X6 // 1b531300 + SRAIW $1, X6 // 1b531340 + ADDW X5, X7 // bb835300 + SLLW X5, X7 // bb935300 + SRLW X5, X7 // bbd35300 + SUBW X5, X7 // bb835340 + SRAW X5, X7 // bbd35340 + ADDW $1, X6 // 1b031300 + SLLW $1, X6 // 1b131300 + SRLW $1, X6 // 1b531300 + SRAW $1, X6 // 1b531340 // 5.3: Load and Store Instructions (RV64I) LD (X5), X6 // 03b30200 diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s index 238552565b..d3e43e721d 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64error.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s @@ -22,5 +22,9 @@ TEXT errors(SB),$0 MOVBU X5, (X6) // ERROR "unsupported unsigned store" MOVHU X5, (X6) // ERROR "unsupported unsigned store" MOVWU X5, (X6) // ERROR "unsupported unsigned store" + MOVF F0, F1, F2 // ERROR "illegal MOV instruction" + MOVD F0, F1, F2 // ERROR "illegal MOV instruction" + MOV X10, X11, X12 // ERROR "illegal MOV instruction" + MOVW X10, X11, X12 // ERROR "illegal MOV instruction" RET diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 997a830994..9877182fc4 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -119,7 +119,6 @@ func (p *Package) addToFlag(flag string, args []string) { // Would be parsed as: // // []string{"a", "b:c d", "ef", `g"`} -// func splitQuoted(s string) (r []string, err error) { var args []string arg := make([]rune, len(s)) diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go index c0d59aee01..3a27b31bfb 100644 --- a/src/cmd/cgo/godefs.go +++ b/src/cmd/cgo/godefs.go @@ -16,11 +16,11 @@ import ( ) // godefs returns the output for -godefs mode. -func (p *Package) godefs(f *File) string { +func (p *Package) godefs(f *File, args []string) string { var buf bytes.Buffer fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n") - fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " ")) + fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(args[0]), strings.Join(args[1:], " ")) fmt.Fprintf(&buf, "\n") override := make(map[string]string) diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 14642b7576..364d8b81fb 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -291,6 +291,10 @@ func main() { usage() } + // Save original command line arguments for the godefs generated comment. Relative file + // paths in os.Args will be rewritten to absolute file paths in the loop below. + osArgs := make([]string, len(os.Args)) + copy(osArgs, os.Args[:]) goFiles := args[i:] for _, arg := range args[:i] { @@ -390,7 +394,7 @@ func main() { p.PackagePath = f.Package p.Record(f) if *godefs { - os.Stdout.WriteString(p.godefs(f)) + os.Stdout.WriteString(p.godefs(f, osArgs)) } else { p.writeOutput(f, input) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 4968f7059d..8ead173e64 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -57,18 +57,19 @@ func (p *Package) writeDefs() { fflg.Close() // Write C main file for using gcc to resolve imports. + fmt.Fprintf(fm, "#include \n") // For size_t below. fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), __SIZE_TYPE__ ctxt __attribute__((unused))) { }\n") - fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void) { return 0; }\n") - fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt __attribute__((unused))) { }\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), size_t ctxt __attribute__((unused))) { }\n") + fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void) { return 0; }\n") + fmt.Fprintf(fm, "void _cgo_release_context(size_t ctxt __attribute__((unused))) { }\n") fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides these functions. We just need a prototype. - fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt);\n") - fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") - fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, size_t ctxt);\n") + fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void);\n") + fmt.Fprintf(fm, "void _cgo_release_context(size_t);\n") } fmt.Fprintf(fm, "void _cgo_allocate(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n") fmt.Fprintf(fm, "void _cgo_panic(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n") @@ -886,9 +887,9 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Wpragmas\"\n") fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Waddress-of-packed-member\"\n") - fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *), void *, int, __SIZE_TYPE__);\n") - fmt.Fprintf(fgcc, "extern __SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n") - fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n") + fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *), void *, int, size_t);\n") + fmt.Fprintf(fgcc, "extern size_t _cgo_wait_runtime_init_done(void);\n") + fmt.Fprintf(fgcc, "extern void _cgo_release_context(size_t);\n\n") fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);") fmt.Fprintf(fgcc, "%s\n", tsanProlog) fmt.Fprintf(fgcc, "%s\n", msanProlog) @@ -992,7 +993,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD") fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") - fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n") + fmt.Fprintf(fgcc, "\tsize_t _cgo_ctxt = _cgo_wait_runtime_init_done();\n") // The results part of the argument structure must be // initialized to 0 so the write barriers generated by // the assignments to these fields in Go are safe. @@ -1561,7 +1562,7 @@ var msanProlog = noMsanProlog const builtinProlog = ` #line 1 "cgo-builtin-prolog" -#include /* for ptrdiff_t and size_t below */ +#include /* Define intgo when compiling with GCC. */ typedef ptrdiff_t intgo; @@ -1604,6 +1605,7 @@ const goStringDef = ` //go:linkname _cgo_runtime_gostring runtime.gostring func _cgo_runtime_gostring(*_Ctype_char) string +// GoString converts the C string p into a Go string. func _Cfunc_GoString(p *_Ctype_char) string { return _cgo_runtime_gostring(p) } @@ -1613,6 +1615,7 @@ const goStringNDef = ` //go:linkname _cgo_runtime_gostringn runtime.gostringn func _cgo_runtime_gostringn(*_Ctype_char, int) string +// GoStringN converts the C data p with explicit length l to a Go string. func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string { return _cgo_runtime_gostringn(p, int(l)) } @@ -1622,12 +1625,19 @@ const goBytesDef = ` //go:linkname _cgo_runtime_gobytes runtime.gobytes func _cgo_runtime_gobytes(unsafe.Pointer, int) []byte +// GoBytes converts the C data p with explicit length l to a Go []byte. func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte { return _cgo_runtime_gobytes(p, int(l)) } ` const cStringDef = ` +// CString converts the Go string s to a C string. +// +// The C string is allocated in the C heap using malloc. +// It is the caller's responsibility to arrange for it to be +// freed, such as by calling C.free (be sure to include stdlib.h +// if C.free is needed). func _Cfunc_CString(s string) *_Ctype_char { p := _cgo_cmalloc(uint64(len(s)+1)) pp := (*[1<<30]byte)(p) @@ -1638,6 +1648,12 @@ func _Cfunc_CString(s string) *_Ctype_char { ` const cBytesDef = ` +// CBytes converts the Go []byte slice b to a C array. +// +// The C array is allocated in the C heap using malloc. +// It is the caller's responsibility to arrange for it to be +// freed, such as by calling C.free (be sure to include stdlib.h +// if C.free is needed). func _Cfunc_CBytes(b []byte) unsafe.Pointer { p := _cgo_cmalloc(uint64(len(b))) pp := (*[1<<30]byte)(p) @@ -1830,7 +1846,7 @@ void localCgoCheckResult(Eface val) { const builtinExportProlog = ` #line 1 "cgo-builtin-export-prolog" -#include /* for ptrdiff_t below */ +#include #ifndef GO_CGO_EXPORT_PROLOGUE_H #define GO_CGO_EXPORT_PROLOGUE_H @@ -1876,7 +1892,7 @@ typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoIntGOINTBITS GoInt; typedef GoUintGOINTBITS GoUint; -typedef __SIZE_TYPE__ GoUintptr; +typedef size_t GoUintptr; typedef float GoFloat32; typedef double GoFloat64; typedef float _Complex GoComplex64; @@ -1926,5 +1942,5 @@ static void GoInit(void) { runtime_iscgo = 1; } -extern __SIZE_TYPE__ _cgo_wait_runtime_init_done(void) __attribute__ ((weak)); +extern size_t _cgo_wait_runtime_init_done(void) __attribute__ ((weak)); ` diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go index 529150a390..07ece87c41 100644 --- a/src/cmd/compile/internal/abi/abiutils.go +++ b/src/cmd/compile/internal/abi/abiutils.go @@ -788,12 +788,12 @@ func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, is // field. For things that are not structs (or structs without padding) // it returns a list of zeros. Example: // -// type small struct { -// x uint16 -// y uint8 -// z int32 -// w int32 -// } +// type small struct { +// x uint16 +// y uint8 +// z int32 +// w int32 +// } // // For this struct we would return a list [0, 1, 0, 0], meaning that // we have one byte of padding after the second field, and no bytes of diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index b0e5c34030..d34fdc611b 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -265,8 +265,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpAMD64BLSIQ, ssa.OpAMD64BLSIL, ssa.OpAMD64BLSMSKQ, ssa.OpAMD64BLSMSKL, - ssa.OpAMD64BLSRQ, ssa.OpAMD64BLSRL, - ssa.OpAMD64TZCNTQ, ssa.OpAMD64TZCNTL: + ssa.OpAMD64BLSRQ, ssa.OpAMD64BLSRL: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() @@ -281,6 +280,23 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() p.SetFrom3Reg(v.Args[1].Reg()) + case ssa.OpAMD64SHLXLload, ssa.OpAMD64SHLXQload, + ssa.OpAMD64SHRXLload, ssa.OpAMD64SHRXQload: + p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg()) + m := obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[0].Reg()} + ssagen.AddAux(&m, v) + p.SetFrom3(m) + + case ssa.OpAMD64SHLXLloadidx1, ssa.OpAMD64SHLXLloadidx4, ssa.OpAMD64SHLXLloadidx8, + ssa.OpAMD64SHRXLloadidx1, ssa.OpAMD64SHRXLloadidx4, ssa.OpAMD64SHRXLloadidx8, + ssa.OpAMD64SHLXQloadidx1, ssa.OpAMD64SHLXQloadidx8, + ssa.OpAMD64SHRXQloadidx1, ssa.OpAMD64SHRXQloadidx8: + p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[2].Reg()) + m := obj.Addr{Type: obj.TYPE_MEM} + memIdx(&m, v) + ssagen.AddAux(&m, v) + p.SetFrom3(m) + case ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU: // Arg[0] (the dividend) is in AX. // Arg[1] (the divisor) can be in any other register. @@ -289,11 +305,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { r := v.Args[1].Reg() // Zero extend dividend. - c := s.Prog(x86.AXORL) - c.From.Type = obj.TYPE_REG - c.From.Reg = x86.REG_DX - c.To.Type = obj.TYPE_REG - c.To.Reg = x86.REG_DX + opregreg(s, x86.AXORL, x86.REG_DX, x86.REG_DX) // Issue divide. p := s.Prog(v.Op.Asm()) @@ -363,11 +375,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { n1.To.Reg = x86.REG_AX // n % -1 == 0 - n2 := s.Prog(x86.AXORL) - n2.From.Type = obj.TYPE_REG - n2.From.Reg = x86.REG_DX - n2.To.Type = obj.TYPE_REG - n2.To.Reg = x86.REG_DX + opregreg(s, x86.AXORL, x86.REG_DX, x86.REG_DX) // TODO(khr): issue only the -1 fixup code we need. // For instance, if only the quotient is used, no point in zeroing the remainder. @@ -745,11 +753,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // If flags aren't live (indicated by v.Aux == nil), // then we can rewrite MOV $0, AX into XOR AX, AX. if v.AuxInt == 0 && v.Aux == nil { - p := s.Prog(x86.AXORL) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = x + opregreg(s, x86.AXORL, x, x) break } @@ -791,7 +795,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore, ssa.OpAMD64ADDQmodify, ssa.OpAMD64SUBQmodify, ssa.OpAMD64ANDQmodify, ssa.OpAMD64ORQmodify, ssa.OpAMD64XORQmodify, ssa.OpAMD64ADDLmodify, ssa.OpAMD64SUBLmodify, ssa.OpAMD64ANDLmodify, ssa.OpAMD64ORLmodify, ssa.OpAMD64XORLmodify, - ssa.OpAMD64MOVBEQstore, ssa.OpAMD64MOVBELstore: + ssa.OpAMD64MOVBEQstore, ssa.OpAMD64MOVBELstore, ssa.OpAMD64MOVBEWstore: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[1].Reg() @@ -1137,15 +1141,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.SetFrom3Reg(v.Args[0].Reg()) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() - case ssa.OpAMD64POPCNTQ, ssa.OpAMD64POPCNTL: + case ssa.OpAMD64POPCNTQ, ssa.OpAMD64POPCNTL, + ssa.OpAMD64TZCNTQ, ssa.OpAMD64TZCNTL, + ssa.OpAMD64LZCNTQ, ssa.OpAMD64LZCNTL: if v.Args[0].Reg() != v.Reg() { - // POPCNT on Intel has a false dependency on the destination register. + // POPCNT/TZCNT/LZCNT have a false dependency on the destination register on Intel cpus. + // TZCNT/LZCNT problem affects pre-Skylake models. See discussion at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62011#c7. // Xor register with itself to break the dependency. - p := s.Prog(x86.AXORL) - p.From.Type = obj.TYPE_REG - p.From.Reg = v.Reg() - p.To.Type = obj.TYPE_REG - p.To.Reg = v.Reg() + opregreg(s, x86.AXORL, v.Reg(), v.Reg()) } p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG diff --git a/src/cmd/compile/internal/amd64/versions_test.go b/src/cmd/compile/internal/amd64/versions_test.go index 6c2617d844..e28aefbd3c 100644 --- a/src/cmd/compile/internal/amd64/versions_test.go +++ b/src/cmd/compile/internal/amd64/versions_test.go @@ -244,6 +244,7 @@ var featureToOpcodes = map[string][]string{ "sse41": {"roundsd"}, "fma": {"vfmadd231sd"}, "movbe": {"movbeqq", "movbeq", "movbell", "movbel", "movbe"}, + "lzcnt": {"lzcntq", "lzcntl", "lzcnt"}, } // Test to use POPCNT instruction, if available diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go index 96a29224bf..48eb2190b2 100644 --- a/src/cmd/compile/internal/arm64/ssa.go +++ b/src/cmd/compile/internal/arm64/ssa.go @@ -728,8 +728,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p4.To.Type = obj.TYPE_BRANCH p4.To.SetTarget(p) p5 := s.Prog(arm64.ACSET) - p5.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p5.From.Reg = arm64.COND_EQ + p5.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + p5.From.Offset = int64(arm64.SPOP_EQ) p5.To.Type = obj.TYPE_REG p5.To.Reg = out p2.To.SetTarget(p5) @@ -778,8 +778,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // CSET EQ, Rout p3 := s.Prog(arm64.ACSET) - p3.From.Type = obj.TYPE_REG - p3.From.Reg = arm64.COND_EQ + p3.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + p3.From.Offset = int64(arm64.SPOP_EQ) p3.To.Type = obj.TYPE_REG p3.To.Reg = out @@ -978,24 +978,27 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { r1 = v.Args[1].Reg() } p := s.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[ssa.Op(v.AuxInt)] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[ssa.Op(v.AuxInt)] + p.From.Offset = int64(condCode) p.Reg = v.Args[0].Reg() p.SetFrom3Reg(r1) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64CSINC, ssa.OpARM64CSINV, ssa.OpARM64CSNEG: p := s.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[ssa.Op(v.AuxInt)] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[ssa.Op(v.AuxInt)] + p.From.Offset = int64(condCode) p.Reg = v.Args[0].Reg() p.SetFrom3Reg(v.Args[1].Reg()) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64CSETM: p := s.Prog(arm64.ACSETM) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[ssa.Op(v.AuxInt)] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[ssa.Op(v.AuxInt)] + p.From.Offset = int64(condCode) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64DUFFZERO: @@ -1107,8 +1110,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64NotGreaterEqualF: // generate boolean values using CSET p := s.Prog(arm64.ACSET) - p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg - p.From.Reg = condBits[v.Op] + p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset + condCode := condBits[v.Op] + p.From.Offset = int64(condCode) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpARM64PRFM: @@ -1173,27 +1177,27 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { } } -var condBits = map[ssa.Op]int16{ - ssa.OpARM64Equal: arm64.COND_EQ, - ssa.OpARM64NotEqual: arm64.COND_NE, - ssa.OpARM64LessThan: arm64.COND_LT, - ssa.OpARM64LessThanU: arm64.COND_LO, - ssa.OpARM64LessEqual: arm64.COND_LE, - ssa.OpARM64LessEqualU: arm64.COND_LS, - ssa.OpARM64GreaterThan: arm64.COND_GT, - ssa.OpARM64GreaterThanU: arm64.COND_HI, - ssa.OpARM64GreaterEqual: arm64.COND_GE, - ssa.OpARM64GreaterEqualU: arm64.COND_HS, - ssa.OpARM64LessThanF: arm64.COND_MI, // Less than - ssa.OpARM64LessEqualF: arm64.COND_LS, // Less than or equal to - ssa.OpARM64GreaterThanF: arm64.COND_GT, // Greater than - ssa.OpARM64GreaterEqualF: arm64.COND_GE, // Greater than or equal to +var condBits = map[ssa.Op]arm64.SpecialOperand{ + ssa.OpARM64Equal: arm64.SPOP_EQ, + ssa.OpARM64NotEqual: arm64.SPOP_NE, + ssa.OpARM64LessThan: arm64.SPOP_LT, + ssa.OpARM64LessThanU: arm64.SPOP_LO, + ssa.OpARM64LessEqual: arm64.SPOP_LE, + ssa.OpARM64LessEqualU: arm64.SPOP_LS, + ssa.OpARM64GreaterThan: arm64.SPOP_GT, + ssa.OpARM64GreaterThanU: arm64.SPOP_HI, + ssa.OpARM64GreaterEqual: arm64.SPOP_GE, + ssa.OpARM64GreaterEqualU: arm64.SPOP_HS, + ssa.OpARM64LessThanF: arm64.SPOP_MI, // Less than + ssa.OpARM64LessEqualF: arm64.SPOP_LS, // Less than or equal to + ssa.OpARM64GreaterThanF: arm64.SPOP_GT, // Greater than + ssa.OpARM64GreaterEqualF: arm64.SPOP_GE, // Greater than or equal to // The following condition codes have unordered to handle comparisons related to NaN. - ssa.OpARM64NotLessThanF: arm64.COND_PL, // Greater than, equal to, or unordered - ssa.OpARM64NotLessEqualF: arm64.COND_HI, // Greater than or unordered - ssa.OpARM64NotGreaterThanF: arm64.COND_LE, // Less than, equal to or unordered - ssa.OpARM64NotGreaterEqualF: arm64.COND_LT, // Less than or unordered + ssa.OpARM64NotLessThanF: arm64.SPOP_PL, // Greater than, equal to, or unordered + ssa.OpARM64NotLessEqualF: arm64.SPOP_HI, // Greater than or unordered + ssa.OpARM64NotGreaterThanF: arm64.SPOP_LE, // Less than, equal to or unordered + ssa.OpARM64NotGreaterEqualF: arm64.SPOP_LT, // Less than or unordered } var blockJump = map[ssa.BlockKind]struct { diff --git a/src/cmd/compile/internal/importer/exportdata.go b/src/cmd/compile/internal/importer/exportdata.go index 6a672be9c1..42fc5c9a57 100644 --- a/src/cmd/compile/internal/importer/exportdata.go +++ b/src/cmd/compile/internal/importer/exportdata.go @@ -41,7 +41,9 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { // start of the file before calling this function. The hdr result // is the string before the export data, either "$$" or "$$B". // -func FindExportData(r *bufio.Reader) (hdr string, err error) { +// If size is non-negative, it's the number of bytes of export data +// still available to read from r. +func FindExportData(r *bufio.Reader) (hdr string, size int, err error) { // Read first line to make sure this is an object file. line, err := r.ReadSlice('\n') if err != nil { @@ -52,7 +54,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { if string(line) == "!\n" { // Archive file. Scan to __.PKGDEF. var name string - if name, _, err = readGopackHeader(r); err != nil { + if name, size, err = readGopackHeader(r); err != nil { return } @@ -76,6 +78,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("not a Go object file") return } + size -= len(line) // Skip over object header to export data. // Begins after first line starting with $$. @@ -84,6 +87,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("can't find export data (%v)", err) return } + size -= len(line) } hdr = string(line) diff --git a/src/cmd/compile/internal/importer/gcimporter.go b/src/cmd/compile/internal/importer/gcimporter.go index ff40be65bb..bcf0480cfc 100644 --- a/src/cmd/compile/internal/importer/gcimporter.go +++ b/src/cmd/compile/internal/importer/gcimporter.go @@ -10,6 +10,7 @@ import ( "cmd/compile/internal/types2" "fmt" "go/build" + "internal/pkgbits" "io" "io/ioutil" "os" @@ -27,7 +28,6 @@ var pkgExts = [...]string{".a", ".o"} // the build.Default build.Context). A relative srcDir is interpreted // relative to the current working directory. // If no file was found, an empty filename is returned. -// func FindPkg(path, srcDir string) (filename, id string) { if path == "" { return @@ -83,7 +83,6 @@ func FindPkg(path, srcDir string) (filename, id string) { // Import imports a gc-generated package given its import path and srcDir, adds // the corresponding package object to the packages map, and returns the object. // The packages map must contain all packages already imported. -// func Import(packages map[string]*types2.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types2.Package, err error) { var rc io.ReadCloser var id string @@ -134,9 +133,9 @@ func Import(packages map[string]*types2.Package, path, srcDir string, lookup fun } defer rc.Close() - var hdr string buf := bufio.NewReader(rc) - if hdr, err = FindExportData(buf); err != nil { + hdr, size, err := FindExportData(buf) + if err != nil { return } @@ -146,17 +145,33 @@ func Import(packages map[string]*types2.Package, path, srcDir string, lookup fun case "$$B\n": var data []byte - data, err = ioutil.ReadAll(buf) + var r io.Reader = buf + if size >= 0 { + r = io.LimitReader(r, int64(size)) + } + data, err = ioutil.ReadAll(r) if err != nil { break } + if len(data) == 0 { + err = fmt.Errorf("import %q: missing export data", path) + break + } + exportFormat := data[0] + s := string(data[1:]) + // The indexed export format starts with an 'i'; the older // binary export format starts with a 'c', 'd', or 'v' // (from "version"). Select appropriate importer. - if len(data) > 0 && data[0] == 'i' { - pkg, err = ImportData(packages, string(data[1:]), id) - } else { + switch exportFormat { + case 'u': + s = s[:strings.Index(s, "\n$$\n")] + input := pkgbits.NewPkgDecoder(id, s) + pkg = ReadPackage(nil, packages, input) + case 'i': + pkg, err = ImportData(packages, s, id) + default: err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path) } diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go index 9fecf742fb..3b6d77747b 100644 --- a/src/cmd/compile/internal/importer/gcimporter_test.go +++ b/src/cmd/compile/internal/importer/gcimporter_test.go @@ -115,10 +115,14 @@ func TestImportTestdata(t *testing.T) { } testfiles := map[string][]string{ - "exports.go": {"go/ast", "go/token"}, + "exports.go": {"go/ast", "go/token"}, + "generics.go": nil, } - if !goexperiment.Unified { - testfiles["generics.go"] = nil + if goexperiment.Unified { + // TODO(mdempsky): Fix test below to flatten the transitive + // Package.Imports graph. Unified IR is more precise about + // recreating the package import graph. + testfiles["exports.go"] = []string{"go/ast"} } for testfile, wantImports := range testfiles { @@ -326,6 +330,14 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) { return // not an interface } + // The unified IR importer always sets interface method receiver + // parameters to point to the Interface type, rather than the Named. + // See #49906. + var want types2.Type = named + if goexperiment.Unified { + want = iface + } + // check explicitly declared methods for i := 0; i < iface.NumExplicitMethods(); i++ { m := iface.ExplicitMethod(i) @@ -334,7 +346,7 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) { t.Errorf("%s: missing receiver type", m) continue } - if recv.Type() != named { + if recv.Type() != want { t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) } } diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index 82132005f9..b5c0983d6a 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -951,11 +951,11 @@ var IsIntrinsicCall = func(*CallExpr) bool { return false } // instead of computing both. SameSafeExpr assumes that l and r are // used in the same statement or expression. In order for it to be // safe to reuse l or r, they must: -// * be the same expression -// * not have side-effects (no function calls, no channel ops); -// however, panics are ok -// * not cause inappropriate aliasing; e.g. two string to []byte -// conversions, must result in two distinct slices +// * be the same expression +// * not have side-effects (no function calls, no channel ops); +// however, panics are ok +// * not cause inappropriate aliasing; e.g. two string to []byte +// conversions, must result in two distinct slices // // The handling of OINDEXMAP is subtle. OINDEXMAP can occur both // as an lvalue (map assignment) and an rvalue (map access). This is diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go index 12a463c8a4..a00667b309 100644 --- a/src/cmd/compile/internal/ir/fmt.go +++ b/src/cmd/compile/internal/ir/fmt.go @@ -105,7 +105,6 @@ func (o Op) GoString() string { // // %v Go syntax ("+", "<-", "print") // %+v Debug syntax ("ADD", "RECV", "PRINT") -// func (o Op) Format(s fmt.State, verb rune) { switch verb { default: @@ -129,7 +128,6 @@ func (o Op) Format(s fmt.State, verb rune) { // %v Go syntax // %L Go syntax followed by " (type T)" if type is known. // %+v Debug syntax, as in Dump. -// func fmtNode(n Node, s fmt.State, verb rune) { // %+v prints Dump. // Otherwise we print Go syntax. @@ -926,7 +924,6 @@ func ellipsisIf(b bool) string { // %v Go syntax, semicolon-separated // %.v Go syntax, comma-separated // %+v Debug syntax, as in DumpList. -// func (l Nodes) Format(s fmt.State, verb rune) { if s.Flag('+') && verb == 'v' { // %+v is DumpList output diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go index 801a48a78e..98c0ffa5b0 100644 --- a/src/cmd/compile/internal/ir/mini.go +++ b/src/cmd/compile/internal/ir/mini.go @@ -27,7 +27,6 @@ import ( // The embedding struct should also fill in n.op in its constructor, // for more useful panic messages when invalid methods are called, // instead of implementing Op itself. -// type miniNode struct { pos src.XPos // uint32 op Op // uint8 diff --git a/src/cmd/compile/internal/noder/export.go b/src/cmd/compile/internal/noder/export.go index 1a296e22c8..263cdc262b 100644 --- a/src/cmd/compile/internal/noder/export.go +++ b/src/cmd/compile/internal/noder/export.go @@ -14,52 +14,22 @@ import ( "cmd/internal/bio" ) -// writeNewExportFunc is a hook that can be added to append extra -// export data after the normal export data section. It allows -// experimenting with new export data format designs without requiring -// immediate support in the go/internal or x/tools importers. -var writeNewExportFunc func(out io.Writer) - func WriteExports(out *bio.Writer) { - // When unified IR exports are enable, we simply append it to the - // end of the normal export data (with compiler extensions - // disabled), and write an extra header giving its size. - // - // If the compiler sees this header, it knows to read the new data - // instead; meanwhile the go/types importers will silently ignore it - // and continue processing the old export instead. - // - // This allows us to experiment with changes to the new export data - // format without needing to update the go/internal/gcimporter or - // (worse) x/tools/go/gcexportdata. + var data bytes.Buffer - useNewExport := writeNewExportFunc != nil - - var old, new bytes.Buffer - - typecheck.WriteExports(&old, !useNewExport) - - if useNewExport { - writeNewExportFunc(&new) - } - - oldLen := old.Len() - newLen := new.Len() - - if useNewExport { - fmt.Fprintf(out, "\nnewexportsize %v\n", newLen) + if base.Debug.Unified != 0 { + data.WriteByte('u') + writeUnifiedExport(&data) + } else { + typecheck.WriteExports(&data, true) } // The linker also looks for the $$ marker - use char after $$ to distinguish format. out.WriteString("\n$$B\n") // indicate binary export format - io.Copy(out, &old) + io.Copy(out, &data) out.WriteString("\n$$\n") - io.Copy(out, &new) if base.Debug.Export != 0 { - fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, oldLen) - if useNewExport { - fmt.Printf("BenchmarkNewExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, newLen) - } + fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, data.Len()) } } diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go index 7ba1b23d12..2cef9f75e8 100644 --- a/src/cmd/compile/internal/noder/import.go +++ b/src/cmd/compile/internal/noder/import.go @@ -8,10 +8,10 @@ import ( "errors" "fmt" "internal/buildcfg" + "internal/pkgbits" "os" pathpkg "path" "runtime" - "strconv" "strings" "unicode" "unicode/utf8" @@ -28,22 +28,6 @@ import ( "cmd/internal/objabi" ) -// haveLegacyImports records whether we've imported any packages -// without a new export data section. This is useful for experimenting -// with new export data format designs, when you need to support -// existing tests that manually compile files with inconsistent -// compiler flags. -var haveLegacyImports = false - -// newReadImportFunc is an extension hook for experimenting with new -// export data formats. If a new export data payload was written out -// for an imported package by overloading writeNewExportFunc, then -// that payload will be mapped into memory and passed to -// newReadImportFunc. -var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { - panic("unexpected new export data payload") -} - type gcimports struct { ctxt *types2.Context packages map[string]*types2.Package @@ -220,7 +204,7 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag } defer f.Close() - r, end, newsize, err := findExportData(f) + r, end, err := findExportData(f) if err != nil { return } @@ -229,41 +213,40 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag fmt.Printf("importing %s (%s)\n", path, f.Name()) } - if newsize != 0 { - // We have unified IR data. Map it, and feed to the importers. - end -= newsize - var data string - data, err = base.MapFile(r.File(), end, newsize) - if err != nil { - return + c, err := r.ReadByte() + if err != nil { + return + } + + pos := r.Offset() + + // Map export data section into memory as a single large + // string. This reduces heap fragmentation and allows returning + // individual substrings very efficiently. + var data string + data, err = base.MapFile(r.File(), pos, end-pos) + if err != nil { + return + } + + switch c { + case 'u': + if !buildcfg.Experiment.Unified { + base.Fatalf("unexpected export data format") } - pkg2, err = newReadImportFunc(data, pkg1, env, packages) - } else { - // We only have old data. Oh well, fall back to the legacy importers. - haveLegacyImports = true + // TODO(mdempsky): This seems a bit clunky. + data = strings.TrimSuffix(data, "\n$$\n") - var c byte - switch c, err = r.ReadByte(); { - case err != nil: - return + pr := pkgbits.NewPkgDecoder(pkg1.Path, data) - case c != 'i': - // Indexed format is distinguished by an 'i' byte, - // whereas previous export formats started with 'c', 'd', or 'v'. - err = fmt.Errorf("unexpected package format byte: %v", c) - return - } + // Read package descriptors for both types2 and compiler backend. + readPackage(newPkgReader(pr), pkg1) + pkg2 = importer.ReadPackage(env, packages, pr) - pos := r.Offset() - - // Map string (and data) section into memory as a single large - // string. This reduces heap fragmentation and allows - // returning individual substrings very efficiently. - var data string - data, err = base.MapFile(r.File(), pos, end-pos) - if err != nil { - return + case 'i': + if buildcfg.Experiment.Unified { + base.Fatalf("unexpected export data format") } typecheck.ReadImports(pkg1, data) @@ -274,6 +257,12 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag return } } + + default: + // Indexed format is distinguished by an 'i' byte, + // whereas previous export formats started with 'c', 'd', or 'v'. + err = fmt.Errorf("unexpected package format byte: %v", c) + return } err = addFingerprint(path, f, end) @@ -283,7 +272,7 @@ func readImportFile(path string, target *ir.Package, env *types2.Context, packag // findExportData returns a *bio.Reader positioned at the start of the // binary export data section, and a file offset for where to stop // reading. -func findExportData(f *os.File) (r *bio.Reader, end, newsize int64, err error) { +func findExportData(f *os.File) (r *bio.Reader, end int64, err error) { r = bio.NewReader(f) // check object header @@ -326,14 +315,6 @@ func findExportData(f *os.File) (r *bio.Reader, end, newsize int64, err error) { // process header lines for !strings.HasPrefix(line, "$$") { - if strings.HasPrefix(line, "newexportsize ") { - fields := strings.Fields(line) - newsize, err = strconv.ParseInt(fields[1], 10, 64) - if err != nil { - return - } - } - line, err = r.ReadString('\n') if err != nil { return diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index f8f802936b..77ca642183 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -27,7 +27,7 @@ import ( func LoadPackage(filenames []string) { base.Timer.Start("fe", "parse") - mode := syntax.CheckBranches | syntax.AllowGenerics + mode := syntax.CheckBranches // Limit the number of simultaneously open files. sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 01e795183d..1350c22467 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -442,9 +442,21 @@ func (r *reader) doTyp() *types.Type { return r.structType() case pkgbits.TypeInterface: return r.interfaceType() + case pkgbits.TypeUnion: + return r.unionType() } } +func (r *reader) unionType() *types.Type { + terms := make([]*types.Type, r.Len()) + tildes := make([]bool, len(terms)) + for i := range terms { + tildes[i] = r.Bool() + terms[i] = r.typ() + } + return types.NewUnion(terms, tildes) +} + func (r *reader) interfaceType() *types.Type { tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone. @@ -577,10 +589,6 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node if pri, ok := objReader[sym]; ok { return pri.pr.objIdx(pri.idx, nil, explicits) } - if haveLegacyImports { - assert(len(explicits) == 0) - return typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) - } base.Fatalf("unresolved stub: %v", sym) } @@ -1960,12 +1968,6 @@ func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExp pri, ok := bodyReader[fn] if !ok { - // Assume it's an imported function or something that we don't - // have access to in quirks mode. - if haveLegacyImports { - return nil - } - base.FatalfAt(call.Pos(), "missing function body for call to %v", fn) } diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index ca01c0da95..2c1f2362ad 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -16,7 +16,6 @@ import ( "sort" "cmd/compile/internal/base" - "cmd/compile/internal/importer" "cmd/compile/internal/inline" "cmd/compile/internal/ir" "cmd/compile/internal/typecheck" @@ -34,38 +33,38 @@ var localPkgReader *pkgReader // // The pipeline contains 2 steps: // -// (1) Generate package export data "stub". +// 1) Generate package export data "stub". // -// (2) Generate package IR from package export data. +// 2) Generate package IR from package export data. // // The package data "stub" at step (1) contains everything from the local package, // but nothing that have been imported. When we're actually writing out export data // to the output files (see writeNewExport function), we run the "linker", which does // a few things: // -// + Updates compiler extensions data (e.g., inlining cost, escape analysis results). +// + Updates compiler extensions data (e.g., inlining cost, escape analysis results). // -// + Handles re-exporting any transitive dependencies. +// + Handles re-exporting any transitive dependencies. // -// + Prunes out any unnecessary details (e.g., non-inlineable functions, because any -// downstream importers only care about inlinable functions). +// + Prunes out any unnecessary details (e.g., non-inlineable functions, because any +// downstream importers only care about inlinable functions). // // The source files are typechecked twice, once before writing export data // using types2 checker, once after read export data using gc/typecheck. // This duplication of work will go away once we always use types2 checker, // we can remove the gc/typecheck pass. The reason it is still here: // -// + It reduces engineering costs in maintaining a fork of typecheck -// (e.g., no need to backport fixes like CL 327651). +// + It reduces engineering costs in maintaining a fork of typecheck +// (e.g., no need to backport fixes like CL 327651). // -// + It makes it easier to pass toolstash -cmp. +// + It makes it easier to pass toolstash -cmp. // -// + Historically, we would always re-run the typechecker after import, even though -// we know the imported data is valid. It's not ideal, but also not causing any -// problem either. +// + Historically, we would always re-run the typechecker after import, even though +// we know the imported data is valid. It's not ideal, but also not causing any +// problem either. // -// + There's still transformation that being done during gc/typecheck, like rewriting -// multi-valued function call, or transform ir.OINDEX -> ir.OINDEXMAP. +// + There's still transformation that being done during gc/typecheck, like rewriting +// multi-valued function call, or transform ir.OINDEX -> ir.OINDEXMAP. // // Using syntax+types2 tree, which already has a complete representation of generics, // the unified IR has the full typed AST for doing introspection during step (1). @@ -74,17 +73,6 @@ var localPkgReader *pkgReader func unified(noders []*noder) { inline.NewInline = InlineCall - writeNewExportFunc = writeNewExport - - newReadImportFunc = func(data string, pkg1 *types.Pkg, ctxt *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { - pr := pkgbits.NewPkgDecoder(pkg1.Path, data) - - // Read package descriptors for both types2 and compiler backend. - readPackage(newPkgReader(pr), pkg1) - pkg2 = importer.ReadPackage(ctxt, packages, pr) - return - } - data := writePkgStub(noders) // We already passed base.Flag.Lang to types2 to handle validating @@ -266,7 +254,7 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg) { } } -func writeNewExport(out io.Writer) { +func writeUnifiedExport(out io.Writer) { l := linker{ pw: pkgbits.NewPkgEncoder(base.Debug.SyncFrames), @@ -332,5 +320,5 @@ func writeNewExport(out io.Writer) { w.Flush() } - l.pw.DumpTo(out) + base.Ctxt.Fingerprint = l.pw.DumpTo(out) } diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index 98316c16fa..da74cacd95 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -733,6 +733,41 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() + case ssa.OpPPC64ADDC, ssa.OpPPC64ADDE, ssa.OpPPC64SUBC, ssa.OpPPC64SUBE: + r := v.Reg0() // CA is the first, implied argument. + r1 := v.Args[0].Reg() + r2 := v.Args[1].Reg() + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = r2 + p.Reg = r1 + p.To.Type = obj.TYPE_REG + p.To.Reg = r + + case ssa.OpPPC64ADDZEzero, ssa.OpPPC64SUBZEzero: + p := s.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_REG + p.From.Reg = ppc64.REG_R0 + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg() + + case ssa.OpPPC64ADDCconst: + p := s.Prog(v.Op.Asm()) + p.Reg = v.Args[0].Reg() + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.AuxInt + p.To.Type = obj.TYPE_REG + // Output is a pair, the second is the CA, which is implied. + p.To.Reg = v.Reg0() + + case ssa.OpPPC64SUBCconst: + p := s.Prog(v.Op.Asm()) + p.SetFrom3Const(v.AuxInt) + p.From.Type = obj.TYPE_REG + p.From.Reg = v.Args[0].Reg() + p.To.Type = obj.TYPE_REG + p.To.Reg = v.Reg0() + case ssa.OpPPC64SUBFCconst: p := s.Prog(v.Op.Asm()) p.SetFrom3Const(v.AuxInt) diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 633700afb0..1137101650 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -667,10 +667,10 @@ var kinds = []int{ // tflag is documented in reflect/type.go. // // tflag values must be kept in sync with copies in: -// cmd/compile/internal/reflectdata/reflect.go -// cmd/link/internal/ld/decodesym.go -// reflect/type.go -// runtime/type.go +// - cmd/compile/internal/reflectdata/reflect.go +// - cmd/link/internal/ld/decodesym.go +// - reflect/type.go +// - runtime/type.go const ( tflagUncommon = 1 << 0 tflagExtraStar = 1 << 1 @@ -1527,7 +1527,6 @@ func (a typesByString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // use bitmaps for objects up to 64 kB in size. // // Also known to reflect/type.go. -// const maxPtrmaskBytes = 2048 // GCSym returns a data symbol containing GC information for type t, along diff --git a/src/cmd/compile/internal/ssa/addressingmodes.go b/src/cmd/compile/internal/ssa/addressingmodes.go index 1baf143869..28fa86cd64 100644 --- a/src/cmd/compile/internal/ssa/addressingmodes.go +++ b/src/cmd/compile/internal/ssa/addressingmodes.go @@ -340,6 +340,22 @@ var combine = map[[2]Op]Op{ [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ1}: OpAMD64DIVSDloadidx1, [2]Op{OpAMD64DIVSDload, OpAMD64LEAQ8}: OpAMD64DIVSDloadidx8, + [2]Op{OpAMD64SHLXLload, OpAMD64ADDQ}: OpAMD64SHLXLloadidx1, + [2]Op{OpAMD64SHLXQload, OpAMD64ADDQ}: OpAMD64SHLXQloadidx1, + [2]Op{OpAMD64SHRXLload, OpAMD64ADDQ}: OpAMD64SHRXLloadidx1, + [2]Op{OpAMD64SHRXQload, OpAMD64ADDQ}: OpAMD64SHRXQloadidx1, + + [2]Op{OpAMD64SHLXLload, OpAMD64LEAQ1}: OpAMD64SHLXLloadidx1, + [2]Op{OpAMD64SHLXLload, OpAMD64LEAQ4}: OpAMD64SHLXLloadidx4, + [2]Op{OpAMD64SHLXLload, OpAMD64LEAQ8}: OpAMD64SHLXLloadidx8, + [2]Op{OpAMD64SHLXQload, OpAMD64LEAQ1}: OpAMD64SHLXQloadidx1, + [2]Op{OpAMD64SHLXQload, OpAMD64LEAQ8}: OpAMD64SHLXQloadidx8, + [2]Op{OpAMD64SHRXLload, OpAMD64LEAQ1}: OpAMD64SHRXLloadidx1, + [2]Op{OpAMD64SHRXLload, OpAMD64LEAQ4}: OpAMD64SHRXLloadidx4, + [2]Op{OpAMD64SHRXLload, OpAMD64LEAQ8}: OpAMD64SHRXLloadidx8, + [2]Op{OpAMD64SHRXQload, OpAMD64LEAQ1}: OpAMD64SHRXQloadidx1, + [2]Op{OpAMD64SHRXQload, OpAMD64LEAQ8}: OpAMD64SHRXQloadidx8, + // 386 [2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1, [2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1, diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go index 6ff3188f9b..4d21ade3e3 100644 --- a/src/cmd/compile/internal/ssa/block.go +++ b/src/cmd/compile/internal/ssa/block.go @@ -76,10 +76,10 @@ type Block struct { // d.Preds = [?, {b,1}, ?] // These indexes allow us to edit the CFG in constant time. // In addition, it informs phi ops in degenerate cases like: -// b: -// if k then c else c -// c: -// v = Phi(x, y) +// b: +// if k then c else c +// c: +// v = Phi(x, y) // Then the indexes tell you whether x is chosen from // the if or else branch from b. // b.Succs = [{c,0},{c,1}] @@ -105,6 +105,7 @@ func (e Edge) String() string { return fmt.Sprintf("{%v,%d}", e.b, e.i) } +// BlockKind is the kind of SSA block. // kind controls successors // ------------------------------------------ // Exit [return mem] [] diff --git a/src/cmd/compile/internal/ssa/branchelim.go b/src/cmd/compile/internal/ssa/branchelim.go index be5f9e0a8b..59773ef31b 100644 --- a/src/cmd/compile/internal/ssa/branchelim.go +++ b/src/cmd/compile/internal/ssa/branchelim.go @@ -11,11 +11,11 @@ import "cmd/internal/src" // // Search for basic blocks that look like // -// bb0 bb0 -// | \ / \ -// | bb1 or bb1 bb2 <- trivial if/else blocks -// | / \ / -// bb2 bb3 +// bb0 bb0 +// | \ / \ +// | bb1 or bb1 bb2 <- trivial if/else blocks +// | / \ / +// bb2 bb3 // // where the intermediate blocks are mostly empty (with no side-effects); // rewrite Phis in the postdominator as CondSelects. diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index f87ea5b893..f95140eaf9 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -24,10 +24,10 @@ import ( // Compile is the main entry point for this package. // Compile modifies f so that on return: -// · all Values in f map to 0 or 1 assembly instructions of the target architecture -// · the order of f.Blocks is the order to emit the Blocks -// · the order of b.Values is the order to emit the Values in each Block -// · f has a non-nil regAlloc field +// - all Values in f map to 0 or 1 assembly instructions of the target architecture +// - the order of f.Blocks is the order to emit the Blocks +// - the order of b.Values is the order to emit the Values in each Block +// - f has a non-nil regAlloc field func Compile(f *Func) { // TODO: debugging - set flags to control verbosity of compiler, // which phases to dump IR before/after, etc. @@ -250,8 +250,8 @@ var GenssaDump map[string]bool = make(map[string]bool) // names of functions to // version is used as a regular expression to match the phase name(s). // // Special cases that have turned out to be useful: -// ssa/check/on enables checking after each phase -// ssa/all/time enables time reporting for all phases +// - ssa/check/on enables checking after each phase +// - ssa/all/time enables time reporting for all phases // // See gc/lex.go for dissection of the option string. // Example uses: @@ -259,7 +259,6 @@ var GenssaDump map[string]bool = make(map[string]bool) // names of functions to // GO_GCFLAGS=-d=ssa/generic_cse/time,ssa/generic_cse/stats,ssa/generic_cse/debug=3 ./make.bash // // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash -// func PhaseOption(phase, flag string, val int, valString string) string { switch phase { case "", "help": diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index fa5b02b325..b9c98bdba9 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -239,6 +239,7 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo c.registers = registersPPC64[:] c.gpRegMask = gpRegMaskPPC64 c.fpRegMask = fpRegMaskPPC64 + c.specialRegMask = specialRegMaskPPC64 c.intParamRegs = paramIntRegPPC64 c.floatParamRegs = paramFloatRegPPC64 c.FPReg = framepointerRegPPC64 diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go index aad59fa24e..08dc5c468e 100644 --- a/src/cmd/compile/internal/ssa/debug.go +++ b/src/cmd/compile/internal/ssa/debug.go @@ -428,7 +428,6 @@ func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot { // This function examines the live OpArg{Int,Float}Reg values and // synthesizes new (dead) values for the non-live params or the // non-live pieces of partially live params. -// func PopulateABIInRegArgOps(f *Func) { pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType()) diff --git a/src/cmd/compile/internal/ssa/debug_test.go b/src/cmd/compile/internal/ssa/debug_test.go index b20041c1b5..2fc12557c0 100644 --- a/src/cmd/compile/internal/ssa/debug_test.go +++ b/src/cmd/compile/internal/ssa/debug_test.go @@ -93,7 +93,6 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog // go test debug_test.go -args -u // (for Delve) // go test debug_test.go -args -u -d -// func TestNexting(t *testing.T) { testenv.SkipFlaky(t, 37404) diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 7728a395e0..0b5392f0f0 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -820,12 +820,12 @@ func (f *Func) invalidateCFG() { } // DebugHashMatch reports whether environment variable evname -// 1) is empty (this is a special more-quickly implemented case of 3) -// 2) is "y" or "Y" -// 3) is a suffix of the sha1 hash of name -// 4) is a suffix of the environment variable -// fmt.Sprintf("%s%d", evname, n) -// provided that all such variables are nonempty for 0 <= i <= n +// 1) is empty (this is a special more-quickly implemented case of 3) +// 2) is "y" or "Y" +// 3) is a suffix of the sha1 hash of name +// 4) is a suffix of the environment variable +// fmt.Sprintf("%s%d", evname, n) +// provided that all such variables are nonempty for 0 <= i <= n // Otherwise it returns false. // When true is returned the message // "%s triggered %s\n", evname, name diff --git a/src/cmd/compile/internal/ssa/func_test.go b/src/cmd/compile/internal/ssa/func_test.go index 276c444b9a..bbb228d8a5 100644 --- a/src/cmd/compile/internal/ssa/func_test.go +++ b/src/cmd/compile/internal/ssa/func_test.go @@ -34,7 +34,7 @@ package ssa // TODO(matloob): Choose better names for Fun, Bloc, Goto, etc. // TODO(matloob): Write a parser for the Func disassembly. Maybe -// the parser can be used instead of Fun. +// the parser can be used instead of Fun. import ( "cmd/compile/internal/types" diff --git a/src/cmd/compile/internal/ssa/fuse_branchredirect.go b/src/cmd/compile/internal/ssa/fuse_branchredirect.go index 751dca7468..27449db55a 100644 --- a/src/cmd/compile/internal/ssa/fuse_branchredirect.go +++ b/src/cmd/compile/internal/ssa/fuse_branchredirect.go @@ -8,21 +8,21 @@ package ssa // of an If block can be derived from its predecessor If block, in // some such cases, we can redirect the predecessor If block to the // corresponding successor block directly. For example: -// p: -// v11 = Less64 v10 v8 -// If v11 goto b else u -// b: <- p ... -// v17 = Leq64 v10 v8 -// If v17 goto s else o +// p: +// v11 = Less64 v10 v8 +// If v11 goto b else u +// b: <- p ... +// v17 = Leq64 v10 v8 +// If v17 goto s else o // We can redirect p to s directly. // // The implementation here borrows the framework of the prove pass. -// 1, Traverse all blocks of function f to find If blocks. -// 2, For any If block b, traverse all its predecessors to find If blocks. -// 3, For any If block predecessor p, update relationship p->b. -// 4, Traverse all successors of b. -// 5, For any successor s of b, try to update relationship b->s, if a -// contradiction is found then redirect p to another successor of b. +// 1, Traverse all blocks of function f to find If blocks. +// 2, For any If block b, traverse all its predecessors to find If blocks. +// 3, For any If block predecessor p, update relationship p->b. +// 4, Traverse all successors of b. +// 5, For any successor s of b, try to update relationship b->s, if a +// contradiction is found then redirect p to another successor of b. func fuseBranchRedirect(f *Func) bool { ft := newFactsTable(f) ft.checkpoint() diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index f4c89b0bb3..8ec9c68d7f 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -306,7 +306,7 @@ func init() { {name: "BSRL", argLength: 1, reg: gp11, asm: "BSRL", clobberFlags: true}, // arg0 # of high-order zeroes ; undef if zero {name: "BSRW", argLength: 1, reg: gp11, asm: "BSRW", clobberFlags: true}, // arg0 # of high-order zeroes ; undef if zero - {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes + {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true}, // arg0 swap bytes {name: "SQRTSD", argLength: 1, reg: fp11, asm: "SQRTSD"}, // sqrt(arg0) {name: "SQRTSS", argLength: 1, reg: fp11, asm: "SQRTSS"}, // sqrt(arg0), float32 diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 87fe0fbee1..d50bdf2a17 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -98,10 +98,14 @@ // However, for zero-extended values, we can cheat a bit, and calculate // BSR(x<<1 + 1), which is guaranteed to be non-zero, and which conveniently // places the index of the highest set bit where we want it. -(BitLen64 x) => (ADDQconst [1] (CMOVQEQ (Select0 (BSRQ x)) (MOVQconst [-1]) (Select1 (BSRQ x)))) -(BitLen32 x) => (Select0 (BSRQ (LEAQ1 [1] (MOVLQZX x) (MOVLQZX x)))) -(BitLen16 x) => (BSRL (LEAL1 [1] (MOVWQZX x) (MOVWQZX x))) -(BitLen8 x) => (BSRL (LEAL1 [1] (MOVBQZX x) (MOVBQZX x))) +// For GOAMD64>=3, BitLen can be calculated by OperandSize - LZCNT(x). +(BitLen64 x) && buildcfg.GOAMD64 < 3 => (ADDQconst [1] (CMOVQEQ (Select0 (BSRQ x)) (MOVQconst [-1]) (Select1 (BSRQ x)))) +(BitLen32 x) && buildcfg.GOAMD64 < 3 => (Select0 (BSRQ (LEAQ1 [1] (MOVLQZX x) (MOVLQZX x)))) +(BitLen16 x) && buildcfg.GOAMD64 < 3 => (BSRL (LEAL1 [1] (MOVWQZX x) (MOVWQZX x))) +(BitLen8 x) && buildcfg.GOAMD64 < 3 => (BSRL (LEAL1 [1] (MOVBQZX x) (MOVBQZX x))) +(BitLen64 x) && buildcfg.GOAMD64 >= 3 => (NEGQ (ADDQconst [-64] (LZCNTQ x))) +// Use 64-bit version to allow const-fold remove unnecessary arithmetic. +(BitLen(32|16|8) x) && buildcfg.GOAMD64 >= 3 => (NEGQ (ADDQconst [-32] (LZCNTL x))) (Bswap(64|32) ...) => (BSWAP(Q|L) ...) @@ -2225,6 +2229,8 @@ (BSWAP(Q|L) x:(MOV(Q|L)load [i] {s} p mem)) && x.Uses == 1 && buildcfg.GOAMD64 >= 3 => (MOVBE(Q|L)load [i] {s} p mem) (BSWAP(Q|L) (MOVBE(Q|L)load [i] {s} p m)) => (MOV(Q|L)load [i] {s} p m) (MOVBE(Q|L)store [i] {s} p (BSWAP(Q|L) x) m) => (MOV(Q|L)store [i] {s} p x m) +(MOVWstore [i] {s} p x:(ROLWconst [8] w) mem) && x.Uses == 1 && buildcfg.GOAMD64 >= 3 => (MOVBEWstore [i] {s} p w mem) +(MOVBEWstore [i] {s} p x:(ROLWconst [8] w) mem) && x.Uses == 1 => (MOVWstore [i] {s} p w mem) (ORQ x0:(MOVBELload [i0] {s} p mem) sh:(SHLQconst [32] x1:(MOVBELload [i1] {s} p mem))) @@ -2245,3 +2251,6 @@ && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh) => @mergePoint(b,x0,x1) (MOVBEQload [i] {s} p1 mem) + +(SHL(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) => (SHLX(Q|L)load [off] {sym} ptr x mem) +(SHR(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) => (SHRX(Q|L)load [off] {sym} ptr x mem) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index a6906bec7c..d760d7d79e 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -141,11 +141,13 @@ func init() { readflags = regInfo{inputs: nil, outputs: gponly} flagsgpax = regInfo{inputs: nil, clobbers: ax, outputs: []regMask{gp &^ ax}} - gpload = regInfo{inputs: []regMask{gpspsbg, 0}, outputs: gponly} - gp21load = regInfo{inputs: []regMask{gp, gpspsbg, 0}, outputs: gponly} - gploadidx = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}, outputs: gponly} - gp21loadidx = regInfo{inputs: []regMask{gp, gpspsbg, gpsp, 0}, outputs: gponly} - gp21pax = regInfo{inputs: []regMask{gp &^ ax, gp}, outputs: []regMask{gp &^ ax}, clobbers: ax} + gpload = regInfo{inputs: []regMask{gpspsbg, 0}, outputs: gponly} + gp21load = regInfo{inputs: []regMask{gp, gpspsbg, 0}, outputs: gponly} + gploadidx = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}, outputs: gponly} + gp21loadidx = regInfo{inputs: []regMask{gp, gpspsbg, gpsp, 0}, outputs: gponly} + gp21pax = regInfo{inputs: []regMask{gp &^ ax, gp}, outputs: []regMask{gp &^ ax}, clobbers: ax} + gp21shxload = regInfo{inputs: []regMask{gpspsbg, gp, 0}, outputs: gponly} + gp21shxloadidx = regInfo{inputs: []regMask{gpspsbg, gpsp, gp, 0}, outputs: gponly} gpstore = regInfo{inputs: []regMask{gpspsbg, gpsp, 0}} gpstoreconst = regInfo{inputs: []regMask{gpspsbg, 0}} @@ -577,8 +579,8 @@ func init() { {name: "CMOVWGTF", argLength: 3, reg: gp21, asm: "CMOVWHI", resultInArg0: true}, {name: "CMOVWGEF", argLength: 3, reg: gp21, asm: "CMOVWCC", resultInArg0: true}, - {name: "BSWAPQ", argLength: 1, reg: gp11, asm: "BSWAPQ", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes - {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes + {name: "BSWAPQ", argLength: 1, reg: gp11, asm: "BSWAPQ", resultInArg0: true}, // arg0 swap bytes + {name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true}, // arg0 swap bytes // POPCNT instructions aren't guaranteed to be on the target platform (they are SSE4). // Any use must be preceded by a successful check of runtime.x86HasPOPCNT. @@ -923,11 +925,35 @@ func init() { {name: "TZCNTQ", argLength: 1, reg: gp11, asm: "TZCNTQ", clobberFlags: true}, {name: "TZCNTL", argLength: 1, reg: gp11, asm: "TZCNTL", clobberFlags: true}, + // CPUID feature: LZCNT. + // count the number of leading zero bits. + {name: "LZCNTQ", argLength: 1, reg: gp11, asm: "LZCNTQ", typ: "UInt64", clobberFlags: true}, + {name: "LZCNTL", argLength: 1, reg: gp11, asm: "LZCNTL", typ: "UInt32", clobberFlags: true}, + // CPUID feature: MOVBE + // MOVBEWload does not satisfy zero extended, so only use MOVBEWstore + {name: "MOVBEWstore", argLength: 3, reg: gpstore, asm: "MOVBEW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem {name: "MOVBELload", argLength: 2, reg: gpload, asm: "MOVBEL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load and swap 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend. {name: "MOVBELstore", argLength: 3, reg: gpstore, asm: "MOVBEL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem {name: "MOVBEQload", argLength: 2, reg: gpload, asm: "MOVBEQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load and swap 8 bytes from arg0+auxint+aux. arg1=mem {name: "MOVBEQstore", argLength: 3, reg: gpstore, asm: "MOVBEQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem + + // CPUID feature: BMI2. + {name: "SHLXLload", argLength: 3, reg: gp21shxload, asm: "SHLXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+auxint+aux) << arg1, arg2=mem, shift amount is mod 32 + {name: "SHLXQload", argLength: 3, reg: gp21shxload, asm: "SHLXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+auxint+aux) << arg1, arg2=mem, shift amount is mod 64 + {name: "SHRXLload", argLength: 3, reg: gp21shxload, asm: "SHRXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 32 + {name: "SHRXQload", argLength: 3, reg: gp21shxload, asm: "SHRXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 64 + + {name: "SHLXLloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 1, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+1*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32 + {name: "SHLXLloadidx4", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 4, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+4*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32 + {name: "SHLXLloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 8, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+8*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32 + {name: "SHLXQloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHLXQ", scale: 1, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+1*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 64 + {name: "SHLXQloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHLXQ", scale: 8, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+8*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 64 + {name: "SHRXLloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHRXL", scale: 1, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SHRXLloadidx4", argLength: 4, reg: gp21shxloadidx, asm: "SHRXL", scale: 4, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+4*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SHRXLloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHRXL", scale: 8, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32 + {name: "SHRXQloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHRXQ", scale: 1, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64 + {name: "SHRXQloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHRXQ", scale: 8, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64 } var AMD64blocks = []blockData{ diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index ff78be263e..e3b79200fe 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -98,7 +98,6 @@ var regNamesPPC64 = []string{ // "CR7", // "CR", - // "XER", // "LR", // "CTR", } @@ -125,11 +124,12 @@ func init() { } var ( - gp = buildReg("R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29") - fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30") - sp = buildReg("SP") - sb = buildReg("SB") - gr = buildReg("g") + gp = buildReg("R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29") + fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30") + sp = buildReg("SP") + sb = buildReg("SB") + gr = buildReg("g") + xer = buildReg("XER") // cr = buildReg("CR") // ctr = buildReg("CTR") // lr = buildReg("LR") @@ -139,8 +139,14 @@ func init() { // tls = buildReg("R13") gp01 = regInfo{inputs: nil, outputs: []regMask{gp}} gp11 = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}} + xergp = regInfo{inputs: []regMask{xer}, outputs: []regMask{gp}, clobbers: xer} + gp11cxer = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}, clobbers: xer} + gp11xer = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp, xer}} gp21 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}} gp21a0 = regInfo{inputs: []regMask{gp, gp | sp | sb}, outputs: []regMask{gp}} + gp21cxer = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}, clobbers: xer} + gp21xer = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp, xer}, clobbers: xer} + gp2xer1xer = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, xer}, outputs: []regMask{gp, xer}, clobbers: xer} gp31 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}} gp22 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp, gp}} gp32 = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp, gp}} @@ -168,21 +174,21 @@ func init() { fploadidx = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{fp}} fpstore = regInfo{inputs: []regMask{gp | sp | sb, fp}} fpstoreidx = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, fp}} - callerSave = regMask(gp | fp | gr) + callerSave = regMask(gp | fp | gr | xer) r3 = buildReg("R3") r4 = buildReg("R4") r5 = buildReg("R5") r6 = buildReg("R6") ) ops := []opData{ - {name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1 - {name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "Int64"}, // arg0 + auxInt - {name: "FADD", argLength: 2, reg: fp21, asm: "FADD", commutative: true}, // arg0+arg1 - {name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0+arg1 - {name: "SUB", argLength: 2, reg: gp21, asm: "SUB"}, // arg0-arg1 - {name: "SUBFCconst", argLength: 1, reg: gp11, asm: "SUBC", aux: "Int64"}, // auxInt - arg0 (with carry) - {name: "FSUB", argLength: 2, reg: fp21, asm: "FSUB"}, // arg0-arg1 - {name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"}, // arg0-arg1 + {name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1 + {name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "Int64"}, // arg0 + auxInt + {name: "FADD", argLength: 2, reg: fp21, asm: "FADD", commutative: true}, // arg0+arg1 + {name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0+arg1 + {name: "SUB", argLength: 2, reg: gp21, asm: "SUB"}, // arg0-arg1 + {name: "SUBFCconst", argLength: 1, reg: gp11cxer, asm: "SUBC", aux: "Int64"}, // auxInt - arg0 (carry is ignored) + {name: "FSUB", argLength: 2, reg: fp21, asm: "FSUB"}, // arg0-arg1 + {name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"}, // arg0-arg1 {name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true}, // arg0*arg1 (signed 64-bit) {name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true}, // arg0*arg1 (signed 32-bit) @@ -204,12 +210,12 @@ func init() { {name: "FMSUB", argLength: 3, reg: fp31, asm: "FMSUB"}, // arg0*arg1 - arg2 {name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS"}, // arg0*arg1 - arg2 - {name: "SRAD", argLength: 2, reg: gp21, asm: "SRAD"}, // signed arg0 >> (arg1&127), 64 bit width (note: 127, not 63!) - {name: "SRAW", argLength: 2, reg: gp21, asm: "SRAW"}, // signed arg0 >> (arg1&63), 32 bit width - {name: "SRD", argLength: 2, reg: gp21, asm: "SRD"}, // unsigned arg0 >> (arg1&127), 64 bit width - {name: "SRW", argLength: 2, reg: gp21, asm: "SRW"}, // unsigned arg0 >> (arg1&63), 32 bit width - {name: "SLD", argLength: 2, reg: gp21, asm: "SLD"}, // arg0 << (arg1&127), 64 bit width - {name: "SLW", argLength: 2, reg: gp21, asm: "SLW"}, // arg0 << (arg1&63), 32 bit width + {name: "SRAD", argLength: 2, reg: gp21cxer, asm: "SRAD"}, // signed arg0 >> (arg1&127), 64 bit width (note: 127, not 63!) + {name: "SRAW", argLength: 2, reg: gp21cxer, asm: "SRAW"}, // signed arg0 >> (arg1&63), 32 bit width + {name: "SRD", argLength: 2, reg: gp21, asm: "SRD"}, // unsigned arg0 >> (arg1&127), 64 bit width + {name: "SRW", argLength: 2, reg: gp21, asm: "SRW"}, // unsigned arg0 >> (arg1&63), 32 bit width + {name: "SLD", argLength: 2, reg: gp21, asm: "SLD"}, // arg0 << (arg1&127), 64 bit width + {name: "SLW", argLength: 2, reg: gp21, asm: "SLW"}, // arg0 << (arg1&63), 32 bit width {name: "ROTL", argLength: 2, reg: gp21, asm: "ROTL"}, // arg0 rotate left by arg1 mod 64 {name: "ROTLW", argLength: 2, reg: gp21, asm: "ROTLW"}, // uint32(arg0) rotate left by arg1 mod 32 @@ -221,12 +227,22 @@ func init() { {name: "LoweredAdd64Carry", argLength: 3, reg: gp32, resultNotInArgs: true}, // arg0 + arg1 + carry, returns (sum, carry) - {name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width - {name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width - {name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width - {name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width - {name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 64, 64 bit width - {name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 32, 32 bit width + // Operations which consume or generate the CA (xer) + {name: "ADDC", argLength: 2, reg: gp21xer, asm: "ADDC", commutative: true, typ: "(UInt64, UInt64)"}, // arg0 + arg1 -> out, CA + {name: "SUBC", argLength: 2, reg: gp21xer, asm: "SUBC", typ: "(UInt64, UInt64)"}, // arg0 - arg1 -> out, CA + {name: "ADDCconst", argLength: 1, reg: gp11xer, asm: "ADDC", typ: "(UInt64, UInt64)", aux: "Int64"}, // arg0 + imm16 -> out, CA + {name: "SUBCconst", argLength: 1, reg: gp11xer, asm: "SUBC", typ: "(UInt64, UInt64)", aux: "Int64"}, // imm16 - arg0 -> out, CA + {name: "ADDE", argLength: 3, reg: gp2xer1xer, asm: "ADDE", typ: "(UInt64, UInt64)", commutative: true}, // arg0 + arg1 + CA (arg2) -> out, CA + {name: "SUBE", argLength: 3, reg: gp2xer1xer, asm: "SUBE", typ: "(UInt64, UInt64)"}, // arg0 - arg1 - CA (arg2) -> out, CA + {name: "ADDZEzero", argLength: 1, reg: xergp, asm: "ADDZE", typ: "UInt64"}, // CA (arg0) + $0 -> out + {name: "SUBZEzero", argLength: 1, reg: xergp, asm: "SUBZE", typ: "UInt64"}, // $0 - CA (arg0) -> out + + {name: "SRADconst", argLength: 1, reg: gp11cxer, asm: "SRAD", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width + {name: "SRAWconst", argLength: 1, reg: gp11cxer, asm: "SRAW", aux: "Int64"}, // signed arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width + {name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 64, 64 bit width + {name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int64"}, // unsigned arg0 >> auxInt, 0 <= auxInt < 32, 32 bit width + {name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 64, 64 bit width + {name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int64"}, // arg0 << auxInt, 0 <= auxInt < 32, 32 bit width {name: "ROTLconst", argLength: 1, reg: gp11, asm: "ROTL", aux: "Int64"}, // arg0 rotate left by auxInt bits {name: "ROTLWconst", argLength: 1, reg: gp11, asm: "ROTLW", aux: "Int64"}, // uint32(arg0) rotate left by auxInt bits @@ -722,6 +738,7 @@ func init() { ParamFloatRegNames: "F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12", gpregmask: gp, fpregmask: fp, + specialregmask: xer, framepointerreg: -1, linkreg: -1, // not used }) diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go index b575febd72..d69db404ed 100644 --- a/src/cmd/compile/internal/ssa/location.go +++ b/src/cmd/compile/internal/ssa/location.go @@ -46,19 +46,19 @@ func (r *Register) GCNum() int16 { // variable that has been decomposed into multiple stack slots. // As an example, a string could have the following configurations: // -// stack layout LocalSlots +// stack layout LocalSlots // -// Optimizations are disabled. s is on the stack and represented in its entirety. -// [ ------- s string ---- ] { N: s, Type: string, Off: 0 } +// Optimizations are disabled. s is on the stack and represented in its entirety. +// [ ------- s string ---- ] { N: s, Type: string, Off: 0 } // -// s was not decomposed, but the SSA operates on its parts individually, so -// there is a LocalSlot for each of its fields that points into the single stack slot. -// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8} +// s was not decomposed, but the SSA operates on its parts individually, so +// there is a LocalSlot for each of its fields that points into the single stack slot. +// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8} // -// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot. -// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0}, -// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8} -// parent = &{N: s, Type: string} +// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot. +// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0}, +// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8} +// parent = &{N: s, Type: string} type LocalSlot struct { N *ir.Name // an ONAME *ir.Name representing a stack location. Type *types.Type // type of slot diff --git a/src/cmd/compile/internal/ssa/loopbce.go b/src/cmd/compile/internal/ssa/loopbce.go index 5a4bc1d60a..206aab2c5e 100644 --- a/src/cmd/compile/internal/ssa/loopbce.go +++ b/src/cmd/compile/internal/ssa/loopbce.go @@ -66,18 +66,18 @@ func parseIndVar(ind *Value) (min, inc, nxt *Value) { // // Look for variables and blocks that satisfy the following // -// loop: -// ind = (Phi min nxt), -// if ind < max -// then goto enter_loop -// else goto exit_loop +// loop: +// ind = (Phi min nxt), +// if ind < max +// then goto enter_loop +// else goto exit_loop // -// enter_loop: -// do something -// nxt = inc + ind -// goto loop +// enter_loop: +// do something +// nxt = inc + ind +// goto loop // -// exit_loop: +// exit_loop: // // // TODO: handle 32 bit operations diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 5b379a7fd3..005a033a40 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1043,10 +1043,27 @@ const ( OpAMD64BLSRL OpAMD64TZCNTQ OpAMD64TZCNTL + OpAMD64LZCNTQ + OpAMD64LZCNTL + OpAMD64MOVBEWstore OpAMD64MOVBELload OpAMD64MOVBELstore OpAMD64MOVBEQload OpAMD64MOVBEQstore + OpAMD64SHLXLload + OpAMD64SHLXQload + OpAMD64SHRXLload + OpAMD64SHRXQload + OpAMD64SHLXLloadidx1 + OpAMD64SHLXLloadidx4 + OpAMD64SHLXLloadidx8 + OpAMD64SHLXQloadidx1 + OpAMD64SHLXQloadidx8 + OpAMD64SHRXLloadidx1 + OpAMD64SHRXLloadidx4 + OpAMD64SHRXLloadidx8 + OpAMD64SHRXQloadidx1 + OpAMD64SHRXQloadidx8 OpARMADD OpARMADDconst @@ -1911,6 +1928,14 @@ const ( OpPPC64CLRLSLWI OpPPC64CLRLSLDI OpPPC64LoweredAdd64Carry + OpPPC64ADDC + OpPPC64SUBC + OpPPC64ADDCconst + OpPPC64SUBCconst + OpPPC64ADDE + OpPPC64SUBE + OpPPC64ADDZEzero + OpPPC64SUBZEzero OpPPC64SRADconst OpPPC64SRAWconst OpPPC64SRDconst @@ -4807,7 +4832,6 @@ var opcodeTable = [...]opInfo{ name: "BSWAPL", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ABSWAPL, reg: regInfo{ inputs: []inputInfo{ @@ -11498,7 +11522,6 @@ var opcodeTable = [...]opInfo{ name: "BSWAPQ", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ABSWAPQ, reg: regInfo{ inputs: []inputInfo{ @@ -11513,7 +11536,6 @@ var opcodeTable = [...]opInfo{ name: "BSWAPL", argLen: 1, resultInArg0: true, - clobberFlags: true, asm: x86.ABSWAPL, reg: regInfo{ inputs: []inputInfo{ @@ -13786,6 +13808,48 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LZCNTQ", + argLen: 1, + clobberFlags: true, + asm: x86.ALZCNTQ, + reg: regInfo{ + inputs: []inputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "LZCNTL", + argLen: 1, + clobberFlags: true, + asm: x86.ALZCNTL, + reg: regInfo{ + inputs: []inputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "MOVBEWstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: x86.AMOVBEW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + }, + }, { name: "MOVBELload", auxType: auxSymOff, @@ -13846,6 +13910,264 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "SHLXLload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, + reg: regInfo{ + inputs: []inputInfo{ + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHLXQload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXQ, + reg: regInfo{ + inputs: []inputInfo{ + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHRXLload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, + reg: regInfo{ + inputs: []inputInfo{ + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHRXQload", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXQ, + reg: regInfo{ + inputs: []inputInfo{ + {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHLXLloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHLXLloadidx4", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, + scale: 4, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHLXLloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXL, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHLXQloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXQ, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHLXQloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHLXQ, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHRXLloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHRXLloadidx4", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, + scale: 4, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHRXLloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXL, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHRXQloadidx1", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXQ, + scale: 1, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, + { + name: "SHRXQloadidx8", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymRead, + asm: x86.ASHRXQ, + scale: 8, + reg: regInfo{ + inputs: []inputInfo{ + {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 + {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB + }, + outputs: []outputInfo{ + {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15 + }, + }, + }, { name: "ADD", @@ -25134,6 +25456,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -25414,6 +25737,7 @@ var opcodeTable = [...]opInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -25428,6 +25752,7 @@ var opcodeTable = [...]opInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -25575,6 +25900,132 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ADDC", + argLen: 2, + commutative: true, + asm: ppc64.AADDC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBC", + argLen: 2, + asm: ppc64.ASUBC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "ADDCconst", + auxType: auxInt64, + argLen: 1, + asm: ppc64.AADDC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBCconst", + auxType: auxInt64, + argLen: 1, + asm: ppc64.ASUBC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "ADDE", + argLen: 3, + commutative: true, + asm: ppc64.AADDE, + reg: regInfo{ + inputs: []inputInfo{ + {2, 9223372036854775808}, // XER + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBE", + argLen: 3, + asm: ppc64.ASUBE, + reg: regInfo{ + inputs: []inputInfo{ + {2, 9223372036854775808}, // XER + {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + {1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {1, 9223372036854775808}, // XER + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "ADDZEzero", + argLen: 1, + asm: ppc64.AADDZE, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372036854775808}, // XER + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, + { + name: "SUBZEzero", + argLen: 1, + asm: ppc64.ASUBZE, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372036854775808}, // XER + }, + clobbers: 9223372036854775808, // XER + outputs: []outputInfo{ + {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 + }, + }, + }, { name: "SRADconst", auxType: auxInt64, @@ -25584,6 +26035,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -25598,6 +26050,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, + clobbers: 9223372036854775808, // XER outputs: []outputInfo{ {0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 }, @@ -27461,7 +27914,7 @@ var opcodeTable = [...]opInfo{ clobberFlags: true, call: true, reg: regInfo{ - clobbers: 9223372034707283960, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { @@ -27472,7 +27925,7 @@ var opcodeTable = [...]opInfo{ call: true, tailCall: true, reg: regInfo{ - clobbers: 9223372034707283960, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { @@ -27486,7 +27939,7 @@ var opcodeTable = [...]opInfo{ {0, 4096}, // R12 {1, 2048}, // R11 }, - clobbers: 9223372034707283960, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { @@ -27499,7 +27952,7 @@ var opcodeTable = [...]opInfo{ inputs: []inputInfo{ {0, 4096}, // R12 }, - clobbers: 9223372034707283960, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + clobbers: 18446744071562059768, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { @@ -27882,7 +28335,7 @@ var opcodeTable = [...]opInfo{ {0, 1048576}, // R20 {1, 2097152}, // R21 }, - clobbers: 9223372035777632256, // R11 R12 R18 R19 R22 R23 R24 R25 R26 R27 R28 R29 R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 + clobbers: 18446744072632408064, // R11 R12 R18 R19 R22 R23 R24 R25 R26 R27 R28 R29 R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 XER }, }, { @@ -37287,7 +37740,7 @@ var paramIntRegPPC64 = []int8{3, 4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17} var paramFloatRegPPC64 = []int8{33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44} var gpRegMaskPPC64 = regMask(1073733624) var fpRegMaskPPC64 = regMask(9223372032559808512) -var specialRegMaskPPC64 = regMask(0) +var specialRegMaskPPC64 = regMask(9223372036854775808) var framepointerRegPPC64 = int8(-1) var linkRegPPC64 = int8(-1) var registersRISCV64 = [...]Register{ diff --git a/src/cmd/compile/internal/ssa/phiopt.go b/src/cmd/compile/internal/ssa/phiopt.go index 745c61cb86..0357442ae9 100644 --- a/src/cmd/compile/internal/ssa/phiopt.go +++ b/src/cmd/compile/internal/ssa/phiopt.go @@ -15,12 +15,12 @@ package ssa // // In SSA code this appears as // -// b0 -// If b -> b1 b2 -// b1 -// Plain -> b2 -// b2 -// x = (OpPhi (ConstBool [true]) (ConstBool [false])) +// b0 +// If b -> b1 b2 +// b1 +// Plain -> b2 +// b2 +// x = (OpPhi (ConstBool [true]) (ConstBool [false])) // // In this case we can replace x with a copy of b. func phiopt(f *Func) { diff --git a/src/cmd/compile/internal/ssa/poset.go b/src/cmd/compile/internal/ssa/poset.go index ee884ca761..200106e66d 100644 --- a/src/cmd/compile/internal/ssa/poset.go +++ b/src/cmd/compile/internal/ssa/poset.go @@ -145,7 +145,6 @@ type posetNode struct { // I extra // / \ // J K -// type poset struct { lastidx uint32 // last generated dense index flags uint8 // internal flags diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index 98af586cab..d0c9a190ad 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -27,17 +27,17 @@ const ( // // E.g. // -// r := relation(...) +// r := relation(...) // -// if v < w { -// newR := r & lt -// } -// if v >= w { -// newR := r & (eq|gt) -// } -// if v != w { -// newR := r & (lt|gt) -// } +// if v < w { +// newR := r & lt +// } +// if v >= w { +// newR := r & (eq|gt) +// } +// if v != w { +// newR := r & (lt|gt) +// } type relation uint const ( diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 64792d0c80..02faf8a7bd 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -1237,7 +1237,7 @@ func (s *regAllocState) regalloc(f *Func) { desired.clobber(j.regs) desired.add(v.Args[j.idx].ID, pickReg(j.regs)) } - if opcodeTable[v.Op].resultInArg0 { + if opcodeTable[v.Op].resultInArg0 || v.Op == OpAMD64ADDQconst || v.Op == OpAMD64ADDLconst || v.Op == OpSelect0 { if opcodeTable[v.Op].commutative { desired.addList(v.Args[1].ID, prefs) } @@ -1598,11 +1598,13 @@ func (s *regAllocState) regalloc(f *Func) { } } } - for _, r := range dinfo[idx].out { - if r != noRegister && (mask&^s.used)>>r&1 != 0 { - // Desired register is allowed and unused. - mask = regMask(1) << r - break + if out.idx == 0 { // desired registers only apply to the first element of a tuple result + for _, r := range dinfo[idx].out { + if r != noRegister && (mask&^s.used)>>r&1 != 0 { + // Desired register is allowed and unused. + mask = regMask(1) << r + break + } } } // Avoid registers we're saving for other values. @@ -2581,7 +2583,12 @@ func (s *regAllocState) computeLive() { desired.add(v.Args[j.idx].ID, pickReg(j.regs)) } // Set desired register of input 0 if this is a 2-operand instruction. - if opcodeTable[v.Op].resultInArg0 { + if opcodeTable[v.Op].resultInArg0 || v.Op == OpAMD64ADDQconst || v.Op == OpAMD64ADDLconst || v.Op == OpSelect0 { + // ADDQconst is added here because we want to treat it as resultInArg0 for + // the purposes of desired registers, even though it is not an absolute requirement. + // This is because we'd rather implement it as ADDQ instead of LEAQ. + // Same for ADDLconst + // Select0 is added here to propagate the desired register to the tuple-generating instruction. if opcodeTable[v.Op].commutative { desired.addList(v.Args[1].ID, prefs) } @@ -2706,6 +2713,8 @@ type desiredStateEntry struct { ID ID // Registers it would like to be in, in priority order. // Unused slots are filled with noRegister. + // For opcodes that return tuples, we track desired registers only + // for the first element of the tuple. regs [4]register } diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index c17d8b03e2..addfaaa3a8 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -226,6 +226,8 @@ func rewriteValueAMD64(v *Value) bool { return rewriteValueAMD64_OpAMD64MOVBELstore(v) case OpAMD64MOVBEQstore: return rewriteValueAMD64_OpAMD64MOVBEQstore(v) + case OpAMD64MOVBEWstore: + return rewriteValueAMD64_OpAMD64MOVBEWstore(v) case OpAMD64MOVBQSX: return rewriteValueAMD64_OpAMD64MOVBQSX(v) case OpAMD64MOVBQSXload: @@ -9542,6 +9544,34 @@ func rewriteValueAMD64_OpAMD64MOVBEQstore(v *Value) bool { } return false } +func rewriteValueAMD64_OpAMD64MOVBEWstore(v *Value) bool { + v_2 := v.Args[2] + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (MOVBEWstore [i] {s} p x:(ROLWconst [8] w) mem) + // cond: x.Uses == 1 + // result: (MOVWstore [i] {s} p w mem) + for { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + x := v_1 + if x.Op != OpAMD64ROLWconst || auxIntToInt8(x.AuxInt) != 8 { + break + } + w := x.Args[0] + mem := v_2 + if !(x.Uses == 1) { + break + } + v.reset(OpAMD64MOVWstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, w, mem) + return true + } + return false +} func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value) bool { v_0 := v.Args[0] b := v.Block @@ -14466,6 +14496,28 @@ func rewriteValueAMD64_OpAMD64MOVWstore(v *Value) bool { v.AddArg3(p, v0, mem) return true } + // match: (MOVWstore [i] {s} p x:(ROLWconst [8] w) mem) + // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3 + // result: (MOVBEWstore [i] {s} p w mem) + for { + i := auxIntToInt32(v.AuxInt) + s := auxToSym(v.Aux) + p := v_0 + x := v_1 + if x.Op != OpAMD64ROLWconst || auxIntToInt8(x.AuxInt) != 8 { + break + } + w := x.Args[0] + mem := v_2 + if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64MOVBEWstore) + v.AuxInt = int32ToAuxInt(i) + v.Aux = symToAux(s) + v.AddArg3(p, w, mem) + return true + } return false } func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value) bool { @@ -24589,6 +24641,28 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (SHLL l:(MOVLload [off] {sym} ptr mem) x) + // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) + // result: (SHLXLload [off] {sym} ptr x mem) + for { + l := v_0 + if l.Op != OpAMD64MOVLload { + break + } + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SHLXLload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } return false } func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool { @@ -24823,6 +24897,28 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (SHLQ l:(MOVQload [off] {sym} ptr mem) x) + // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) + // result: (SHLXQload [off] {sym} ptr x mem) + for { + l := v_0 + if l.Op != OpAMD64MOVQload { + break + } + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SHLXQload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } return false } func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool { @@ -25152,6 +25248,28 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (SHRL l:(MOVLload [off] {sym} ptr mem) x) + // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) + // result: (SHRXLload [off] {sym} ptr x mem) + for { + l := v_0 + if l.Op != OpAMD64MOVLload { + break + } + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SHRXLload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } return false } func rewriteValueAMD64_OpAMD64SHRLconst(v *Value) bool { @@ -25374,6 +25492,28 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { v.AddArg2(x, v0) return true } + // match: (SHRQ l:(MOVQload [off] {sym} ptr mem) x) + // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) + // result: (SHRXQload [off] {sym} ptr x mem) + for { + l := v_0 + if l.Op != OpAMD64MOVQload { + break + } + off := auxIntToInt32(l.AuxInt) + sym := auxToSym(l.Aux) + mem := l.Args[1] + ptr := l.Args[0] + x := v_1 + if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) { + break + } + v.reset(OpAMD64SHRXQload) + v.AuxInt = int32ToAuxInt(off) + v.Aux = symToAux(sym) + v.AddArg3(ptr, x, mem) + return true + } return false } func rewriteValueAMD64_OpAMD64SHRQconst(v *Value) bool { @@ -27974,9 +28114,13 @@ func rewriteValueAMD64_OpBitLen16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (BitLen16 x) + // cond: buildcfg.GOAMD64 < 3 // result: (BSRL (LEAL1 [1] (MOVWQZX x) (MOVWQZX x))) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpAMD64BSRL) v0 := b.NewValue0(v.Pos, OpAMD64LEAL1, typ.UInt32) v0.AuxInt = int32ToAuxInt(1) @@ -27986,15 +28130,38 @@ func rewriteValueAMD64_OpBitLen16(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen16 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-32] (LZCNTL x))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-32) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTL, typ.UInt32) + v1.AddArg(x) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpBitLen32(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (BitLen32 x) + // cond: buildcfg.GOAMD64 < 3 // result: (Select0 (BSRQ (LEAQ1 [1] (MOVLQZX x) (MOVLQZX x)))) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpSelect0) v0 := b.NewValue0(v.Pos, OpAMD64BSRQ, types.NewTuple(typ.UInt64, types.TypeFlags)) v1 := b.NewValue0(v.Pos, OpAMD64LEAQ1, typ.UInt64) @@ -28006,16 +28173,39 @@ func rewriteValueAMD64_OpBitLen32(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen32 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-32] (LZCNTL x))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-32) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTL, typ.UInt32) + v1.AddArg(x) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpBitLen64(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (BitLen64 x) + // cond: buildcfg.GOAMD64 < 3 // result: (ADDQconst [1] (CMOVQEQ (Select0 (BSRQ x)) (MOVQconst [-1]) (Select1 (BSRQ x)))) for { t := v.Type x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpAMD64ADDQconst) v.AuxInt = int32ToAuxInt(1) v0 := b.NewValue0(v.Pos, OpAMD64CMOVQEQ, t) @@ -28031,15 +28221,38 @@ func rewriteValueAMD64_OpBitLen64(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen64 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-64] (LZCNTQ x))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-64) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTQ, typ.UInt64) + v1.AddArg(x) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpBitLen8(v *Value) bool { v_0 := v.Args[0] b := v.Block typ := &b.Func.Config.Types // match: (BitLen8 x) + // cond: buildcfg.GOAMD64 < 3 // result: (BSRL (LEAL1 [1] (MOVBQZX x) (MOVBQZX x))) for { x := v_0 + if !(buildcfg.GOAMD64 < 3) { + break + } v.reset(OpAMD64BSRL) v0 := b.NewValue0(v.Pos, OpAMD64LEAL1, typ.UInt32) v0.AuxInt = int32ToAuxInt(1) @@ -28049,6 +28262,25 @@ func rewriteValueAMD64_OpBitLen8(v *Value) bool { v.AddArg(v0) return true } + // match: (BitLen8 x) + // cond: buildcfg.GOAMD64 >= 3 + // result: (NEGQ (ADDQconst [-32] (LZCNTL x))) + for { + t := v.Type + x := v_0 + if !(buildcfg.GOAMD64 >= 3) { + break + } + v.reset(OpAMD64NEGQ) + v0 := b.NewValue0(v.Pos, OpAMD64ADDQconst, t) + v0.AuxInt = int32ToAuxInt(-32) + v1 := b.NewValue0(v.Pos, OpAMD64LZCNTL, typ.UInt32) + v1.AddArg(x) + v0.AddArg(v1) + v.AddArg(v0) + return true + } + return false } func rewriteValueAMD64_OpCeil(v *Value) bool { v_0 := v.Args[0] diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go index be914c8644..732bb8e321 100644 --- a/src/cmd/compile/internal/ssa/sparsetree.go +++ b/src/cmd/compile/internal/ssa/sparsetree.go @@ -207,8 +207,8 @@ func (t SparseTree) isAncestor(x, y *Block) bool { // domorder returns a value for dominator-oriented sorting. // Block domination does not provide a total ordering, // but domorder two has useful properties. -// (1) If domorder(x) > domorder(y) then x does not dominate y. -// (2) If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y, +// 1. If domorder(x) > domorder(y) then x does not dominate y. +// 2. If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y, // then x does not dominate z. // Property (1) means that blocks sorted by domorder always have a maximal dominant block first. // Property (2) allows searches for dominated blocks to exit early. diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 5120cd1086..21eee12c85 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -80,11 +80,11 @@ func needwb(v *Value, zeroes map[ID]ZeroRegion) bool { // when necessary (the condition above). It rewrites store ops to branches // and runtime calls, like // -// if writeBarrier.enabled { -// gcWriteBarrier(ptr, val) // Not a regular Go call -// } else { -// *ptr = val -// } +// if writeBarrier.enabled { +// gcWriteBarrier(ptr, val) // Not a regular Go call +// } else { +// *ptr = val +// } // // A sequence of WB stores for many pointer fields of a single type will // be emitted together, with a single branch. diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go index 3a653e46b4..3d3cba7dd3 100644 --- a/src/cmd/compile/internal/ssagen/abi.go +++ b/src/cmd/compile/internal/ssagen/abi.go @@ -14,7 +14,6 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" - "cmd/compile/internal/staticdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" @@ -243,17 +242,6 @@ func InitLSym(f *ir.Func, hasBody bool) { if f.Pragma&ir.Systemstack != 0 { f.LSym.Set(obj.AttrCFunc, true) } - if f.ABI == obj.ABIInternal || !buildcfg.Experiment.RegabiWrappers { - // Function values can only point to - // ABIInternal entry points. This will create - // the funcsym for either the defining - // function or its wrapper as appropriate. - // - // If we're not using ABI wrappers, we only - // InitLSym for the defining ABI of a function, - // so we make the funcsym when we see that. - staticdata.NeedFuncSym(f) - } } if hasBody { setupTextLSym(f, 0) diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index a214a1e8f9..883772b341 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -4496,9 +4496,6 @@ func InitTables() { sys.ARM64) addF("math/bits", "Reverse", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { - if s.config.PtrSize == 4 { - return s.newValue1(ssa.OpBitRev32, types.Types[types.TINT], args[0]) - } return s.newValue1(ssa.OpBitRev64, types.Types[types.TINT], args[0]) }, sys.ARM64) diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go index 57c15a34a0..b114bb2df6 100644 --- a/src/cmd/compile/internal/staticdata/data.go +++ b/src/cmd/compile/internal/staticdata/data.go @@ -8,7 +8,6 @@ import ( "crypto/sha256" "fmt" "go/constant" - "internal/buildcfg" "io" "io/ioutil" "os" @@ -236,15 +235,9 @@ func FuncLinksym(n *ir.Name) *obj.LSym { // except for the types package, which is protected separately. // Reusing funcsymsmu to also cover this package lookup // avoids a general, broader, expensive package lookup mutex. - // Note NeedFuncSym also does package look-up of func sym names, - // but that it is only called serially, from the front end. funcsymsmu.Lock() sf, existed := s.Pkg.LookupOK(ir.FuncSymName(s)) - // Don't export s·f when compiling for dynamic linking. - // When dynamically linking, the necessary function - // symbols will be created explicitly with NeedFuncSym. - // See the NeedFuncSym comment for details. - if !base.Ctxt.Flag_dynlink && !existed { + if !existed { funcsyms = append(funcsyms, n) } funcsymsmu.Unlock() @@ -259,48 +252,6 @@ func GlobalLinksym(n *ir.Name) *obj.LSym { return n.Linksym() } -// NeedFuncSym ensures that fn·f is exported, if needed. -// It is only used with -dynlink. -// When not compiling for dynamic linking, -// the funcsyms are created as needed by -// the packages that use them. -// Normally we emit the fn·f stubs as DUPOK syms, -// but DUPOK doesn't work across shared library boundaries. -// So instead, when dynamic linking, we only create -// the fn·f stubs in fn's package. -func NeedFuncSym(fn *ir.Func) { - if base.Ctxt.InParallel { - // The append below probably just needs to lock - // funcsymsmu, like in FuncSym. - base.Fatalf("NeedFuncSym must be called in serial") - } - if fn.ABI != obj.ABIInternal && buildcfg.Experiment.RegabiWrappers { - // Function values must always reference ABIInternal - // entry points, so it doesn't make sense to create a - // funcsym for other ABIs. - // - // (If we're not using ABI wrappers, it doesn't matter.) - base.Fatalf("expected ABIInternal: %v has %v", fn.Nname, fn.ABI) - } - if ir.IsBlank(fn.Nname) { - // Blank functions aren't unique, so we can't make a - // funcsym for them. - base.Fatalf("NeedFuncSym called for _") - } - if !base.Ctxt.Flag_dynlink { - return - } - s := fn.Nname.Sym() - if base.Flag.CompilingRuntime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") || - (base.Ctxt.Pkgpath == "internal/abi" && (s.Name == "FuncPCABI0" || s.Name == "FuncPCABIInternal")) { - // runtime.getg(), getclosureptr(), getcallerpc(), getcallersp(), - // and internal/abi.FuncPCABIxxx() are not real functions and so - // do not get funcsyms. - return - } - funcsyms = append(funcsyms, fn.Nname) -} - func WriteFuncSyms() { sort.Slice(funcsyms, func(i, j int) bool { return funcsyms[i].Linksym().Name < funcsyms[j].Linksym().Name diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go index 033283a352..1ba85cc8d9 100644 --- a/src/cmd/compile/internal/syntax/dumper_test.go +++ b/src/cmd/compile/internal/syntax/dumper_test.go @@ -13,7 +13,7 @@ func TestDump(t *testing.T) { t.Skip("skipping test in short mode") } - ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, CheckBranches|AllowGenerics) + ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, CheckBranches) if ast != nil { Fdump(testOut(), ast) diff --git a/src/cmd/compile/internal/syntax/error_test.go b/src/cmd/compile/internal/syntax/error_test.go index d87e8eaee3..724ca0eb98 100644 --- a/src/cmd/compile/internal/syntax/error_test.go +++ b/src/cmd/compile/internal/syntax/error_test.go @@ -128,10 +128,6 @@ func testSyntaxErrors(t *testing.T, filename string) { } defer f.Close() - var mode Mode - if strings.HasSuffix(filename, ".go2") { - mode = AllowGenerics - } ParseFile(filename, func(err error) { e, ok := err.(Error) if !ok { @@ -166,7 +162,7 @@ func testSyntaxErrors(t *testing.T, filename string) { } else { t.Errorf("%s:%s: unexpected error: %s", filename, orig, e.Msg) } - }, nil, mode) + }, nil, 0) if *print { fmt.Println() diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go index 2f9b43edef..b0a0918e77 100644 --- a/src/cmd/compile/internal/syntax/nodes.go +++ b/src/cmd/compile/internal/syntax/nodes.go @@ -462,7 +462,7 @@ func (simpleStmt) aSimpleStmt() {} // Comments // TODO(gri) Consider renaming to CommentPos, CommentPlacement, etc. -// Kind = Above doesn't make much sense. +// Kind = Above doesn't make much sense. type CommentKind uint const ( diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index a75a3b1a2e..f18d526877 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -87,8 +87,6 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm p.indent = nil } -func (p *parser) allowGenerics() bool { return p.mode&AllowGenerics != 0 } - // takePragma returns the current parsed pragmas // and clears them from the parser state. func (p *parser) takePragma() Pragma { @@ -473,8 +471,7 @@ func isEmptyFuncDecl(dcl Decl) bool { // elements are accepted. list returns the position of the closing token. // // list = [ f { sep f } [sep] ] close . -// -func (p *parser) list(sep, close token, f func() bool) Pos { +func (p *parser) list(context string, sep, close token, f func() bool) Pos { if debug && (sep != _Comma && sep != _Semi || close != _Rparen && close != _Rbrace && close != _Rbrack) { panic("invalid sep or close argument for list") } @@ -484,7 +481,7 @@ func (p *parser) list(sep, close token, f func() bool) Pos { done = f() // sep is optional before close if !p.got(sep) && p.tok != close { - p.syntaxError(fmt.Sprintf("expecting %s or %s", tokstring(sep), tokstring(close))) + p.syntaxError(fmt.Sprintf("in %s; possibly missing %s or %s", context, tokstring(sep), tokstring(close))) p.advance(_Rparen, _Rbrack, _Rbrace) if p.tok != close { // position could be better but we had an error so we don't care @@ -504,7 +501,7 @@ func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl { g := new(Group) p.clearPragma() p.next() // must consume "(" after calling clearPragma! - p.list(_Semi, _Rparen, func() bool { + p.list("grouped declaration", _Semi, _Rparen, func() bool { if x := f(g); x != nil { list = append(list, x) } @@ -586,7 +583,7 @@ func (p *parser) typeDecl(group *Group) Decl { d.Pragma = p.takePragma() d.Name = p.name() - if p.allowGenerics() && p.tok == _Lbrack { + if p.tok == _Lbrack { // d.Name "[" ... // array/slice type or type parameter list pos := p.pos() @@ -762,7 +759,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl { f.Name = p.name() context := "" - if f.Recv != nil && p.mode&AllowMethodTypeParams == 0 { + if f.Recv != nil { context = "method" // don't permit (method) type parameters in funcType } f.TParamList, f.Type = p.funcType(context) @@ -1024,22 +1021,24 @@ func (p *parser) operand(keep_parens bool) Expr { // as well (operand is only called from pexpr). } -// PrimaryExpr = -// Operand | -// Conversion | -// PrimaryExpr Selector | -// PrimaryExpr Index | -// PrimaryExpr Slice | -// PrimaryExpr TypeAssertion | -// PrimaryExpr Arguments . +// pexpr parses a PrimaryExpr. // -// Selector = "." identifier . -// Index = "[" Expression "]" . -// Slice = "[" ( [ Expression ] ":" [ Expression ] ) | -// ( [ Expression ] ":" Expression ":" Expression ) -// "]" . -// TypeAssertion = "." "(" Type ")" . -// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . +// PrimaryExpr = +// Operand | +// Conversion | +// PrimaryExpr Selector | +// PrimaryExpr Index | +// PrimaryExpr Slice | +// PrimaryExpr TypeAssertion | +// PrimaryExpr Arguments . +// +// Selector = "." identifier . +// Index = "[" Expression "]" . +// Slice = "[" ( [ Expression ] ":" [ Expression ] ) | +// ( [ Expression ] ":" Expression ":" Expression ) +// "]" . +// TypeAssertion = "." "(" Type ")" . +// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . func (p *parser) pexpr(x Expr, keep_parens bool) Expr { if trace { defer p.trace("pexpr")() @@ -1098,45 +1097,25 @@ loop: var i Expr if p.tok != _Colon { - if p.mode&AllowGenerics == 0 { - p.xnest++ - i = p.expr() - p.xnest-- - if p.got(_Rbrack) { - // x[i] - t := new(IndexExpr) - t.pos = pos - t.X = x - t.Index = i - x = t - break - } - } else { - var comma bool - i, comma = p.typeList() - if comma || p.tok == _Rbrack { - p.want(_Rbrack) - // x[i,] or x[i, j, ...] - t := new(IndexExpr) - t.pos = pos - t.X = x - t.Index = i - x = t - break - } + var comma bool + i, comma = p.typeList() + if comma || p.tok == _Rbrack { + p.want(_Rbrack) + // x[i,] or x[i, j, ...] + t := new(IndexExpr) + t.pos = pos + t.X = x + t.Index = i + x = t + break } } // x[i:... // For better error message, don't simply use p.want(_Colon) here (issue #47704). if !p.got(_Colon) { - if p.mode&AllowGenerics == 0 { - p.syntaxError("expecting : or ]") - p.advance(_Colon, _Rbrack) - } else { - p.syntaxError("expecting comma, : or ]") - p.advance(_Comma, _Colon, _Rbrack) - } + p.syntaxError("expecting comma, : or ]") + p.advance(_Comma, _Colon, _Rbrack) } p.xnest++ t := new(SliceExpr) @@ -1255,7 +1234,7 @@ func (p *parser) complitexpr() *CompositeLit { p.xnest++ p.want(_Lbrace) - x.Rbrace = p.list(_Comma, _Rbrace, func() bool { + x.Rbrace = p.list("composite literal", _Comma, _Rbrace, func() bool { // value e := p.bare_complitexpr() if p.tok == _Colon { @@ -1305,10 +1284,10 @@ func newIndirect(pos Pos, typ Expr) Expr { // typeOrNil is like type_ but it returns nil if there was no type // instead of reporting an error. // -// Type = TypeName | TypeLit | "(" Type ")" . -// TypeName = identifier | QualifiedIdent . -// TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType | -// SliceType | MapType | Channel_Type . +// Type = TypeName | TypeLit | "(" Type ")" . +// TypeName = identifier | QualifiedIdent . +// TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType | +// SliceType | MapType | Channel_Type . func (p *parser) typeOrNil() Expr { if trace { defer p.trace("typeOrNil")() @@ -1418,7 +1397,7 @@ func (p *parser) funcType(context string) ([]*Field, *FuncType) { typ.pos = p.pos() var tparamList []*Field - if p.allowGenerics() && p.got(_Lbrack) { + if p.got(_Lbrack) { if context != "" { // accept but complain p.syntaxErrorAt(typ.pos, context+" must have no type parameters") @@ -1499,7 +1478,7 @@ func (p *parser) structType() *StructType { p.want(_Struct) p.want(_Lbrace) - p.list(_Semi, _Rbrace, func() bool { + p.list("struct type", _Semi, _Rbrace, func() bool { p.fieldDecl(typ) return false }) @@ -1509,7 +1488,6 @@ func (p *parser) structType() *StructType { // InterfaceType = "interface" "{" { ( MethodDecl | EmbeddedElem | TypeList ) ";" } "}" . // TypeList = "type" Type { "," Type } . -// TODO(gri) remove TypeList syntax if we accept #45346 func (p *parser) interfaceType() *InterfaceType { if trace { defer p.trace("interfaceType")() @@ -1520,18 +1498,17 @@ func (p *parser) interfaceType() *InterfaceType { p.want(_Interface) p.want(_Lbrace) - p.list(_Semi, _Rbrace, func() bool { + p.list("interface type", _Semi, _Rbrace, func() bool { switch p.tok { case _Name: f := p.methodDecl() - if f.Name == nil && p.allowGenerics() { + if f.Name == nil { f = p.embeddedElem(f) } typ.MethodList = append(typ.MethodList, f) return false case _Lparen: - // TODO(gri) Need to decide how to adjust this restriction. p.syntaxError("cannot parenthesize embedded type") f := new(Field) f.pos = p.pos() @@ -1542,31 +1519,23 @@ func (p *parser) interfaceType() *InterfaceType { return false case _Operator: - if p.op == Tilde && p.allowGenerics() { + if p.op == Tilde { typ.MethodList = append(typ.MethodList, p.embeddedElem(nil)) return false } default: - if p.allowGenerics() { - pos := p.pos() - if t := p.typeOrNil(); t != nil { - f := new(Field) - f.pos = pos - f.Type = t - typ.MethodList = append(typ.MethodList, p.embeddedElem(f)) - return false - } + pos := p.pos() + if t := p.typeOrNil(); t != nil { + f := new(Field) + f.pos = pos + f.Type = t + typ.MethodList = append(typ.MethodList, p.embeddedElem(f)) + return false } } - if p.allowGenerics() { - p.syntaxError("expecting method or embedded element") - p.advance(_Semi, _Rbrace) - return false - } - - p.syntaxError("expecting method or interface name") + p.syntaxError("expecting method or embedded element") p.advance(_Semi, _Rbrace) return false }) @@ -1640,7 +1609,7 @@ func (p *parser) fieldDecl(styp *StructType) { // Careful dance: We don't know if we have an embedded instantiated // type T[P1, P2, ...] or a field T of array/slice type [P]E or []E. - if p.allowGenerics() && len(names) == 1 && p.tok == _Lbrack { + if len(names) == 1 && p.tok == _Lbrack { typ = p.arrayOrTArgs() if typ, ok := typ.(*IndexExpr); ok { // embedded type T[P1, P2, ...] @@ -1757,20 +1726,6 @@ func (p *parser) methodDecl() *Field { f.pos = p.pos() name := p.name() - // accept potential name list but complain - // TODO(gri) We probably don't need this special check anymore. - // Nobody writes this kind of code. It's from ancient - // Go beginnings. - hasNameList := false - for p.got(_Comma) { - p.name() - hasNameList = true - } - if hasNameList { - p.syntaxError("name list not allowed in interface type") - // already progressed, no need to advance - } - const context = "interface method" switch p.tok { @@ -1780,79 +1735,72 @@ func (p *parser) methodDecl() *Field { _, f.Type = p.funcType(context) case _Lbrack: - if p.allowGenerics() { - // Careful dance: We don't know if we have a generic method m[T C](x T) - // or an embedded instantiated type T[P1, P2] (we accept generic methods - // for generality and robustness of parsing). + // Careful dance: We don't know if we have a generic method m[T C](x T) + // or an embedded instantiated type T[P1, P2] (we accept generic methods + // for generality and robustness of parsing but complain with an error). + pos := p.pos() + p.next() + + // Empty type parameter or argument lists are not permitted. + // Treat as if [] were absent. + if p.tok == _Rbrack { + // name[] pos := p.pos() p.next() - - // Empty type parameter or argument lists are not permitted. - // Treat as if [] were absent. - if p.tok == _Rbrack { - // name[] - pos := p.pos() - p.next() - if p.tok == _Lparen { - // name[]( - p.errorAt(pos, "empty type parameter list") - f.Name = name - _, f.Type = p.funcType(context) - } else { - p.errorAt(pos, "empty type argument list") - f.Type = name - } - break - } - - // A type argument list looks like a parameter list with only - // types. Parse a parameter list and decide afterwards. - list := p.paramList(nil, nil, _Rbrack, false) - if len(list) == 0 { - // The type parameter list is not [] but we got nothing - // due to other errors (reported by paramList). Treat - // as if [] were absent. - if p.tok == _Lparen { - f.Name = name - _, f.Type = p.funcType(context) - } else { - f.Type = name - } - break - } - - // len(list) > 0 - if list[0].Name != nil { - // generic method + if p.tok == _Lparen { + // name[]( + p.errorAt(pos, "empty type parameter list") f.Name = name _, f.Type = p.funcType(context) - // TODO(gri) Record list as type parameter list with f.Type - // if we want to type-check the generic method. - // For now, report an error so this is not a silent event. - p.errorAt(pos, "interface method must have no type parameters") - break - } - - // embedded instantiated type - t := new(IndexExpr) - t.pos = pos - t.X = name - if len(list) == 1 { - t.Index = list[0].Type } else { - // len(list) > 1 - l := new(ListExpr) - l.pos = list[0].Pos() - l.ElemList = make([]Expr, len(list)) - for i := range list { - l.ElemList[i] = list[i].Type - } - t.Index = l + p.errorAt(pos, "empty type argument list") + f.Type = name } - f.Type = t break } - fallthrough + + // A type argument list looks like a parameter list with only + // types. Parse a parameter list and decide afterwards. + list := p.paramList(nil, nil, _Rbrack, false) + if len(list) == 0 { + // The type parameter list is not [] but we got nothing + // due to other errors (reported by paramList). Treat + // as if [] were absent. + if p.tok == _Lparen { + f.Name = name + _, f.Type = p.funcType(context) + } else { + f.Type = name + } + break + } + + // len(list) > 0 + if list[0].Name != nil { + // generic method + f.Name = name + _, f.Type = p.funcType(context) + p.errorAt(pos, "interface method must have no type parameters") + break + } + + // embedded instantiated type + t := new(IndexExpr) + t.pos = pos + t.X = name + if len(list) == 1 { + t.Index = list[0].Type + } else { + // len(list) > 1 + l := new(ListExpr) + l.pos = list[0].Pos() + l.ElemList = make([]Expr, len(list)) + for i := range list { + l.ElemList[i] = list[i].Type + } + t.Index = l + } + f.Type = t default: // embedded type @@ -1938,7 +1886,7 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field { name = p.name() } - if p.allowGenerics() && p.tok == _Lbrack { + if p.tok == _Lbrack { // name "[" ... f.Type = p.arrayOrTArgs() if typ, ok := f.Type.(*IndexExpr); ok { @@ -2033,7 +1981,7 @@ func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) var named int // number of parameters that have an explicit name and type var typed int // number of parameters that have an explicit type - end := p.list(_Comma, close, func() bool { + end := p.list("parameter list", _Comma, close, func() bool { var par *Field if typ != nil { if debug && name == nil { @@ -2572,11 +2520,13 @@ func (p *parser) commClause() *CommClause { return c } -// Statement = -// Declaration | LabeledStmt | SimpleStmt | -// GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | -// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | -// DeferStmt . +// stmtOrNil parses a statement if one is present, or else returns nil. +// +// Statement = +// Declaration | LabeledStmt | SimpleStmt | +// GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | +// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | +// DeferStmt . func (p *parser) stmtOrNil() Stmt { if trace { defer p.trace("stmt " + p.tok.String())() @@ -2713,7 +2663,7 @@ func (p *parser) argList() (list []Expr, hasDots bool) { } p.xnest++ - p.list(_Comma, _Rparen, func() bool { + p.list("argument list", _Comma, _Rparen, func() bool { list = append(list, p.expr()) hasDots = p.got(_DotDotDot) return hasDots @@ -2787,7 +2737,7 @@ func (p *parser) qualifiedName(name *Name) Expr { x = s } - if p.allowGenerics() && p.tok == _Lbrack { + if p.tok == _Lbrack { x = p.typeInstance(x) } diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index 66690a527a..b3d4573935 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -27,31 +27,17 @@ var ( ) func TestParse(t *testing.T) { - ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics) + ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) } func TestVerify(t *testing.T) { - ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics) + ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) if err != nil { return // error already reported } verifyPrint(t, *src_, ast) } -func TestParseGo2(t *testing.T) { - dir := filepath.Join(testdata, "go2") - list, err := ioutil.ReadDir(dir) - if err != nil { - t.Fatal(err) - } - for _, fi := range list { - name := fi.Name() - if !fi.IsDir() && !strings.HasPrefix(name, ".") { - ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics|AllowMethodTypeParams) - } - } -} - func TestStdLib(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode") @@ -94,7 +80,7 @@ func TestStdLib(t *testing.T) { if debug { fmt.Printf("parsing %s\n", filename) } - ast, err := ParseFile(filename, nil, nil, AllowGenerics) + ast, err := ParseFile(filename, nil, nil, 0) if err != nil { t.Error(err) return diff --git a/src/cmd/compile/internal/syntax/pos.go b/src/cmd/compile/internal/syntax/pos.go index 1494c0989f..b5e53d268b 100644 --- a/src/cmd/compile/internal/syntax/pos.go +++ b/src/cmd/compile/internal/syntax/pos.go @@ -24,7 +24,7 @@ type Pos struct { func MakePos(base *PosBase, line, col uint) Pos { return Pos{base, sat32(line), sat32(col)} } // TODO(gri) IsKnown makes an assumption about linebase < 1. -// Maybe we should check for Base() != nil instead. +// Maybe we should check for Base() != nil instead. func (pos Pos) Pos() Pos { return pos } func (pos Pos) IsKnown() bool { return pos.line > 0 } diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index 941af0aeb4..3eca2316a7 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -18,7 +18,7 @@ func TestPrint(t *testing.T) { t.Skip("skipping test in short mode") } - ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics) + ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) if ast != nil { Fprint(testOut(), ast, LineForm) @@ -117,7 +117,7 @@ var stringTests = [][2]string{ func TestPrintString(t *testing.T) { for _, test := range stringTests { - ast, err := Parse(nil, strings.NewReader(test[0]), nil, nil, AllowGenerics) + ast, err := Parse(nil, strings.NewReader(test[0]), nil, nil, 0) if err != nil { t.Error(err) continue @@ -237,7 +237,7 @@ var exprTests = [][2]string{ func TestShortString(t *testing.T) { for _, test := range exprTests { src := "package p; var _ = " + test[0] - ast, err := Parse(nil, strings.NewReader(src), nil, nil, AllowGenerics) + ast, err := Parse(nil, strings.NewReader(src), nil, nil, 0) if err != nil { t.Errorf("%s: %s", test[0], err) continue diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go index 25c8116206..83b102da9f 100644 --- a/src/cmd/compile/internal/syntax/syntax.go +++ b/src/cmd/compile/internal/syntax/syntax.go @@ -16,8 +16,6 @@ type Mode uint // Modes supported by the parser. const ( CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements - AllowGenerics - AllowMethodTypeParams // does not support interface methods yet; ignored if AllowGenerics is not set ) // Error describes a syntax error. Error implements the error interface. @@ -65,7 +63,6 @@ type PragmaHandler func(pos Pos, blank bool, text string, current Pragma) Pragma // error, and the returned syntax tree is nil. // // If pragh != nil, it is called with each pragma encountered. -// func Parse(base *PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) { defer func() { if p := recover(); p != nil { diff --git a/src/cmd/compile/internal/syntax/testdata/go2/chans.go2 b/src/cmd/compile/internal/syntax/testdata/chans.go similarity index 89% rename from src/cmd/compile/internal/syntax/testdata/go2/chans.go2 rename to src/cmd/compile/internal/syntax/testdata/chans.go index fad2bcec9d..d4c4207a4c 100644 --- a/src/cmd/compile/internal/syntax/testdata/go2/chans.go2 +++ b/src/cmd/compile/internal/syntax/testdata/chans.go @@ -1,3 +1,7 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package chans import "runtime" @@ -22,7 +26,7 @@ func Ranger[T any]() (*Sender[T], *Receiver[T]) { // A sender is used to send values to a Receiver. type Sender[T any] struct { values chan<- T - done <-chan bool + done <-chan bool } // Send sends a value to the receiver. It returns whether any more @@ -45,7 +49,7 @@ func (s *Sender[T]) Close() { // A Receiver receives values from a Sender. type Receiver[T any] struct { values <-chan T - done chan<- bool + done chan<- bool } // Next returns the next value from the channel. The bool result diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2 deleted file mode 100644 index a422d5e568..0000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2 +++ /dev/null @@ -1,60 +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. - -package p - -type myInt int - -// Parameterized type declarations - -type T1[P any] P - -type T2[P any] struct { - f P - g int // int should still be in scope chain -} - -type List[P any] []P - -// Alias type declarations cannot have type parameters. Syntax error. -// TODO(gri) Disabled for now as we don't check syntax error here. -// type A1[P any] = /* ERROR cannot be alias */ P - -// But an alias may refer to a generic, uninstantiated type. -type A2 = List -var _ A2[int] -var _ A2 /* ERROR without instantiation */ - -type A3 = List[int] -var _ A3 - -// Parameterized type instantiations - -var x int -type _ x /* ERROR not a type */ [int] - -type _ int /* ERROR not a generic type */ [int] -type _ myInt /* ERROR not a generic type */ [int] - -// TODO(gri) better error messages -type _ T1[int] -type _ T1[x /* ERROR not a type */ ] -type _ T1 /* ERROR got 2 arguments but 1 type parameters */ [int, float32] - -var _ T2[int] = T2[int]{} - -var _ List[int] = []int{1, 2, 3} -var _ List[[]int] = [][]int{{1, 2, 3}} -var _ List[List[List[int]]] - -// Parameterized types containing parameterized types - -type T3[P any] List[P] - -var _ T3[int] = T3[int](List[int]{1, 2, 3}) - -// Self-recursive generic types are not permitted - -type self1[P any] self1 /* ERROR illegal cycle */ [P] -type self2[P any] *self2[P] // this is ok diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2 deleted file mode 100644 index 76b8d5591f..0000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2 +++ /dev/null @@ -1,232 +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. - -package p - -type List[E any] []E -var _ List[List[List[int]]] -var _ List[List[List[int]]] = []List[List[int]]{} - -type ( - T1[P1 any] struct { - f1 T2[P1, float32] - } - - T2[P2, P3 any] struct { - f2 P2 - f3 P3 - } -) - -func _() { - var x1 T1[int] - var x2 T2[int, float32] - - x1.f1.f2 = 0 - x1.f1 = x2 -} - -type T3[P any] T1[T2[P, P]] - -func _() { - var x1 T3[int] - var x2 T2[int, int] - x1.f1.f2 = x2 -} - -func f[P any] (x P) List[P] { - return List[P]{x} -} - -var ( - _ []int = f(0) - _ []float32 = f[float32](10) - _ List[complex128] = f(1i) - _ []List[int] = f(List[int]{}) - _ List[List[int]] = []List[int]{} - _ = []List[int]{} -) - -// Parameterized types with methods - -func (l List[E]) Head() (_ E, _ bool) { - if len(l) > 0 { - return l[0], true - } - return -} - -// A test case for instantiating types with other types (extracted from map.go2) - -type Pair[K any] struct { - key K -} - -type Receiver[T any] struct { - values T -} - -type Iterator[K any] struct { - r Receiver[Pair[K]] -} - -func Values [T any] (r Receiver[T]) T { - return r.values -} - -func (it Iterator[K]) Next() K { - return Values[Pair[K]](it.r).key -} - -// A more complex test case testing type bounds (extracted from linalg.go2 and reduced to essence) - -type NumericAbs[T any] interface { - Abs() T -} - -func AbsDifference[T NumericAbs[T]](x T) - -type OrderedAbs[T any] T - -func (a OrderedAbs[T]) Abs() OrderedAbs[T] - -func OrderedAbsDifference[T any](x T) { - AbsDifference(OrderedAbs[T](x)) -} - -// same code, reduced to essence - -func g[P interface{ m() P }](x P) - -type T4[P any] P - -func (_ T4[P]) m() T4[P] - -func _[Q any](x Q) { - g(T4[Q](x)) -} - -// Another test case that caused problems in the past - -type T5[_ interface { a() }, _ interface{}] struct{} - -type A[P any] struct{ x P } - -func (_ A[P]) a() {} - -var _ T5[A[int], int] - -// Invoking methods with parameterized receiver types uses -// type inference to determine the actual type arguments matching -// the receiver type parameters from the actual receiver argument. -// Go does implicit address-taking and dereferenciation depending -// on the actual receiver and the method's receiver type. To make -// type inference work, the type-checker matches "pointer-ness" -// of the actual receiver and the method's receiver type. -// The following code tests this mechanism. - -type R1[A any] struct{} -func (_ R1[A]) vm() -func (_ *R1[A]) pm() - -func _[T any](r R1[T], p *R1[T]) { - r.vm() - r.pm() - p.vm() - p.pm() -} - -type R2[A, B any] struct{} -func (_ R2[A, B]) vm() -func (_ *R2[A, B]) pm() - -func _[T any](r R2[T, int], p *R2[string, T]) { - r.vm() - r.pm() - p.vm() - p.pm() -} - -// Interface type constraints can contain any type, incl. *Named types. -// Verify that we use the underlying type to compute the operational type. -type MyInt int -func add1[T interface{ ~MyInt }](x T) T { - return x + 1 -} - -type MyString string -func double[T interface{ ~MyInt | ~MyString }](x T) T { - return x + x -} - -// Embedding of interfaces with type constraints leads to interfaces -// with type constraints that are the intersection of the embedded -// type constraints. - -type E0 interface { - ~int | ~bool | ~string -} - -type E1 interface { - ~int | ~float64 | ~string -} - -type E2 interface { - ~float64 -} - -type I0 interface { - E0 -} - -func f0[T I0]() -var _ = f0[int] -var _ = f0[bool] -var _ = f0[string] -var _ = f0[float64 /* ERROR does not satisfy I0 */ ] - -type I01 interface { - E0 - E1 -} - -func f01[T I01]() -var _ = f01[int] -var _ = f01[bool /* ERROR does not satisfy I0 */ ] -var _ = f01[string] -var _ = f01[float64 /* ERROR does not satisfy I0 */ ] - -type I012 interface { - E0 - E1 - E2 -} - -func f012[T I012]() -var _ = f012[int /* ERROR does not satisfy I012 */ ] -var _ = f012[bool /* ERROR does not satisfy I012 */ ] -var _ = f012[string /* ERROR does not satisfy I012 */ ] -var _ = f012[float64 /* ERROR does not satisfy I012 */ ] - -type I12 interface { - E1 - E2 -} - -func f12[T I12]() -var _ = f12[int /* ERROR does not satisfy I12 */ ] -var _ = f12[bool /* ERROR does not satisfy I12 */ ] -var _ = f12[string /* ERROR does not satisfy I12 */ ] -var _ = f12[float64] - -type I0_ interface { - E0 - ~int -} - -func f0_[T I0_]() -var _ = f0_[int] -var _ = f0_[bool /* ERROR does not satisfy I0_ */ ] -var _ = f0_[string /* ERROR does not satisfy I0_ */ ] -var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ] diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2 deleted file mode 100644 index 111f7c1004..0000000000 --- a/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2 +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -// import "io" // for type assertion tests - -// The predeclared identifier "any" is only visible as a constraint -// in a type parameter list. -var _ any // ERROR undeclared -func _[_ any /* ok here */ , _ interface{any /* ERROR undeclared */ }](any /* ERROR undeclared */ ) { - var _ any /* ERROR undeclared */ -} - -func identity[T any](x T) T { return x } - -func _[_ any](x int) int -func _[T any](T /* ERROR redeclared */ T)() -func _[T, T /* ERROR redeclared */ any]() - -func reverse[T any](list []T) []T { - rlist := make([]T, len(list)) - i := len(list) - for _, x := range list { - i-- - rlist[i] = x - } - return rlist -} - -var _ = reverse /* ERROR cannot use generic function reverse */ -var _ = reverse[int, float32 /* ERROR got 2 type arguments */ ] ([]int{1, 2, 3}) -var _ = reverse[int]([ /* ERROR cannot use */ ]float32{1, 2, 3}) -var f = reverse[chan int] -var _ = f(0 /* ERROR cannot convert 0 .* to \[\]chan int */ ) - -func swap[A, B any](a A, b B) (B, A) { return b, a } - -var _ = swap /* ERROR single value is expected */ [int, float32](1, 2) -var f32, i = swap[int, float32](swap(float32, int)(1, 2)) -var _ float32 = f32 -var _ int = i - -func swapswap[A, B any](a A, b B) (A, B) { - return swap[B, A](b, a) -} - -type F[A, B any] func(A, B) (B, A) - -func min[T interface{ ~int }](x, y T) T { - if x < y { - return x - } - return y -} - -func _[T interface{ ~int | ~float32 }](x, y T) bool { return x < y } -func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y } -func _[T interface{ ~int | ~float32 | ~bool }](x, y T) bool { return x /* ERROR cannot compare */ < y } - -func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y } -func _[T C2[T]](x, y T) bool { return x < y } - -type C1[T any] interface{} -type C2[T any] interface{ ~int | ~float32 } - -func new[T any]() *T { - var x T - return &x -} - -var _ = new /* ERROR cannot use generic function new */ -var _ *int = new[int]() - -func _[T any](map[T /* ERROR invalid map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable - -func f1[T1 any](struct{T1}) int -var _ = f1(int)(struct{T1}{}) -type T1 = int - -func f2[t1 any](struct{t1; x float32}) int -var _ = f2(t1)(struct{t1; x float32}{}) -type t1 = int - - -func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int - -var _ = f3[int, rune, bool](1, struct{x rune}{}, nil) - -// indexing - -func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ ~string }] (x T, i int) { _ = x[i] } -func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] } -func _[T interface{ ~[10]int | ~*[20]int | ~map[string]int }] (x T, i int) { _ = x[i] } -func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] } -func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } -func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] } - -// slicing -// TODO(gri) implement this - -func _[T interface{ ~string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] } - -// len/cap built-ins - -func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) } -func _[T interface{ ~string }](x T) { _ = len(x) } -func _[T interface{ ~[10]int }](x T) { _ = len(x) } -func _[T interface{ ~[]byte }](x T) { _ = len(x) } -func _[T interface{ ~map[int]int }](x T) { _ = len(x) } -func _[T interface{ ~chan int }](x T) { _ = len(x) } -func _[T interface{ ~string | ~[]byte | ~chan int }](x T) { _ = len(x) } - -func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ ~string }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ ~[10]int }](x T) { _ = cap(x) } -func _[T interface{ ~[]byte }](x T) { _ = cap(x) } -func _[T interface{ ~map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) } -func _[T interface{ ~chan int }](x T) { _ = cap(x) } -func _[T interface{ ~[]byte | ~chan int }](x T) { _ = cap(x) } - -// range iteration - -func _[T interface{}](x T) { - for range x /* ERROR cannot range */ {} -} - -func _[T interface{ ~string | ~[]string }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } - for i, e := range x /* ERROR must have the same element type */ { _ = i } - for _, e := range x /* ERROR must have the same element type */ {} - var e rune - _ = e - for _, (e) = range x /* ERROR must have the same element type */ {} -} - - -func _[T interface{ ~string | ~[]rune | ~map[int]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x { _ = i; _ = e } -} - -func _[T interface{ ~string | ~[]rune | ~map[string]rune }](x T) { - for _, e := range x { _ = e } - for i, e := range x /* ERROR must have the same key type */ { _ = e } -} - -func _[T interface{ ~string | ~chan int }](x T) { - for range x {} - for i := range x { _ = i } - for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value -} - -func _[T interface{ ~string | ~chan<-int }](x T) { - for i := range x /* ERROR send-only channel */ { _ = i } -} - -// type inference checks - -var _ = new() /* ERROR cannot infer T */ - -func f4[A, B, C any](A, B) C - -var _ = f4(1, 2) /* ERROR cannot infer C */ -var _ = f4[int, float32, complex128](1, 2) - -func f5[A, B, C any](A, []*B, struct{f []C}) int - -var _ = f5[int, float32, complex128](0, nil, struct{f []complex128}{}) -var _ = f5(0, nil, struct{f []complex128}{}) // ERROR cannot infer -var _ = f5(0, []*float32{new[float32]()}, struct{f []complex128}{}) - -func f6[A any](A, []A) int - -var _ = f6(0, nil) - -func f6nil[A any](A) int - -var _ = f6nil(nil) // ERROR cannot infer - -// type inference with variadic functions - -func f7[T any](...T) T - -var _ int = f7() /* ERROR cannot infer T */ -var _ int = f7(1) -var _ int = f7(1, 2) -var _ int = f7([]int{}...) -var _ int = f7 /* ERROR cannot use */ ([]float64{}...) -var _ float64 = f7([]float64{}...) -var _ = f7[float64](1, 2.3) -var _ = f7(float64(1), 2.3) -var _ = f7(1, 2.3 /* ERROR does not match */ ) -var _ = f7(1.2, 3 /* ERROR does not match */ ) - -func f8[A, B any](A, B, ...B) int - -var _ = f8(1) /* ERROR not enough arguments */ -var _ = f8(1, 2.3) -var _ = f8(1, 2.3, 3.4, 4.5) -var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ ) -var _ = f8(int, float64)(1, 2.3, 3.4, 4) - -var _ = f8(int, float64)(0, 0, nil...) // test case for #18268 - -// init functions cannot have type parameters - -func init() {} -func init[/* ERROR func init must have no type parameters */ _ any]() {} -func init[/* ERROR func init must have no type parameters */ P any]() {} - -type T struct {} - -func (T) m1() {} -// The type checker accepts method type parameters if configured accordingly. -func (T) m2[_ any]() {} -func (T) m3[P any]() {} - -// type inference across parameterized types - -type S1[P any] struct { f P } - -func f9[P any](x S1[P]) - -func _() { - f9[int](S1[int]{42}) - f9(S1[int]{42}) -} - -type S2[A, B, C any] struct{} - -func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool]) - -func _[P any]() { - f10[int, float32, string](S2[int, int, string]{}, S2[int, float32, bool]{}) - f10(S2[int, int, string]{}, S2[int, float32, bool]{}) - f10(S2[P, int, P]{}, S2[P, float32, bool]{}) -} - -// corner case for type inference -// (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic) - -func f11[T any]() - -func _() { - f11[int]() -} - -// the previous example was extracted from - -func f12[T interface{m() T}]() - -type A[T any] T - -func (a A[T]) m() A[T] - -func _[T any]() { - f12(A[T])() -} - -// method expressions - -func (_ S1[P]) m() - -func _() { - m := S1[int].m - m(struct { f int }{42}) -} - -func _[T any] (x T) { - m := S1[T].m - m(S1[T]{x}) -} - -// type parameters in methods (generalization) - -type R0 struct{} - -func (R0) _[T any](x T) -func (R0 /* ERROR invalid receiver */ ) _[R0 any]() // scope of type parameters starts at "func" - -type R1[A, B any] struct{} - -func (_ R1[A, B]) m0(A, B) -func (_ R1[A, B]) m1[T any](A, B, T) T -func (_ R1 /* ERROR not a generic type */ [R1, _]) _() -func (_ R1[A, B]) _[A /* ERROR redeclared */ any](B) - -func _() { - var r R1[int, string] - r.m1[rune](42, "foo", 'a') - r.m1[rune](42, "foo", 1.2 /* ERROR truncated to rune */) - r.m1(42, "foo", 1.2) // using type inference - var _ float64 = r.m1(42, "foo", 1.2) -} - -type I1[A any] interface { - m1(A) -} - -var _ I1[int] = r1[int]{} - -type r1[T any] struct{} - -func (_ r1[T]) m1(T) - -type I2[A, B any] interface { - m1(A) - m2(A) B -} - -var _ I2[int, float32] = R2[int, float32]{} - -type R2[P, Q any] struct{} - -func (_ R2[X, Y]) m1(X) -func (_ R2[X, Y]) m2(X) Y - -// type assertions and type switches over generic types -// NOTE: These are currently disabled because it's unclear what the correct -// approach is, and one can always work around by assigning the variable to -// an interface first. - -// // ReadByte1 corresponds to the ReadByte example in the draft design. -// func ReadByte1[T io.Reader](r T) (byte, error) { -// if br, ok := r.(io.ByteReader); ok { -// return br.ReadByte() -// } -// var b [1]byte -// _, err := r.Read(b[:]) -// return b[0], err -// } -// -// // ReadBytes2 is like ReadByte1 but uses a type switch instead. -// func ReadByte2[T io.Reader](r T) (byte, error) { -// switch br := r.(type) { -// case io.ByteReader: -// return br.ReadByte() -// } -// var b [1]byte -// _, err := r.Read(b[:]) -// return b[0], err -// } -// -// // type assertions and type switches over generic types are strict -// type I3 interface { -// m(int) -// } -// -// type I4 interface { -// m() int // different signature from I3.m -// } -// -// func _[T I3](x I3, p T) { -// // type assertions and type switches over interfaces are not strict -// _ = x.(I4) -// switch x.(type) { -// case I4: -// } -// -// // type assertions and type switches over generic types are strict -// _ = p /* ERROR cannot have dynamic type I4 */.(I4) -// switch p.(type) { -// case I4 /* ERROR cannot have dynamic type I4 */ : -// } -// } - -// type assertions and type switches over generic types lead to errors for now - -func _[T any](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { - } - - // work-around - var t interface{} = x - _ = t.(int) - switch t.(type) { - } -} - -func _[T interface{ ~int }](x T) { - _ = x /* ERROR not an interface */ .(int) - switch x /* ERROR not an interface */ .(type) { - } - - // work-around - var t interface{} = x - _ = t.(int) - switch t.(type) { - } -} - -// error messages related to type bounds mention those bounds -type C[P any] interface{} - -func _[P C[P]] (x P) { - x.m /* ERROR x.m undefined */ () -} - -type I interface {} - -func _[P I] (x P) { - x.m /* ERROR interface I has no method m */ () -} - -func _[P interface{}] (x P) { - x.m /* ERROR type bound for P has no method m */ () -} - -func _[P any] (x P) { - x.m /* ERROR type bound for P has no method m */ () -} - -// automatic distinguishing between array and generic types -// NOTE: Disabled when using unified parameter list syntax. -/* -const P = 10 -type A1 [P]byte -func _(a A1) { - assert(len(a) == 10) -} - -type A2 [P]struct{ - f [P]byte -} -func _(a A2) { - assert(len(a) == 10) - assert(len(a[0].f) == 10) -} - -type A3 [P]func(x [P]A3) -func _(a A3) { - assert(len(a) == 10) -} - -type T2[P] struct{ P } -var _ T2[int] - -type T3[P] func(P) -var _ T3[int] -*/ \ No newline at end of file diff --git a/src/cmd/compile/internal/syntax/testdata/interface.go2 b/src/cmd/compile/internal/syntax/testdata/interface.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/interface.go2 rename to src/cmd/compile/internal/syntax/testdata/interface.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue20789.src b/src/cmd/compile/internal/syntax/testdata/issue20789.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue20789.src rename to src/cmd/compile/internal/syntax/testdata/issue20789.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue23385.src b/src/cmd/compile/internal/syntax/testdata/issue23385.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue23385.src rename to src/cmd/compile/internal/syntax/testdata/issue23385.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue23434.src b/src/cmd/compile/internal/syntax/testdata/issue23434.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue23434.src rename to src/cmd/compile/internal/syntax/testdata/issue23434.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue31092.src b/src/cmd/compile/internal/syntax/testdata/issue31092.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue31092.src rename to src/cmd/compile/internal/syntax/testdata/issue31092.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue43527.go2 b/src/cmd/compile/internal/syntax/testdata/issue43527.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue43527.go2 rename to src/cmd/compile/internal/syntax/testdata/issue43527.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue43674.src b/src/cmd/compile/internal/syntax/testdata/issue43674.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue43674.src rename to src/cmd/compile/internal/syntax/testdata/issue43674.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue46558.src b/src/cmd/compile/internal/syntax/testdata/issue46558.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue46558.src rename to src/cmd/compile/internal/syntax/testdata/issue46558.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue47704.go2 b/src/cmd/compile/internal/syntax/testdata/issue47704.go similarity index 89% rename from src/cmd/compile/internal/syntax/testdata/issue47704.go2 rename to src/cmd/compile/internal/syntax/testdata/issue47704.go index 4e65857f3b..2f2e29b693 100644 --- a/src/cmd/compile/internal/syntax/testdata/issue47704.go2 +++ b/src/cmd/compile/internal/syntax/testdata/issue47704.go @@ -4,7 +4,6 @@ package p -// error messages for parser in generic mode func _() { _ = m[] // ERROR expecting operand _ = m[x,] diff --git a/src/cmd/compile/internal/syntax/testdata/issue47704.src b/src/cmd/compile/internal/syntax/testdata/issue47704.src deleted file mode 100644 index 0156af7d8d..0000000000 --- a/src/cmd/compile/internal/syntax/testdata/issue47704.src +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package p - -// error messages for parser in non-generic mode -func _() { - _ = m[] // ERROR expecting operand - _ = m[x,] // ERROR unexpected comma, expecting \: or \] - _ = m[x /* ERROR unexpected a */ a b c d] -} - -// test case from the issue -func f(m map[int]int) int { - return m[0 // ERROR expecting \: or \] - ] -} diff --git a/src/cmd/compile/internal/syntax/testdata/issue48382.go2 b/src/cmd/compile/internal/syntax/testdata/issue48382.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue48382.go2 rename to src/cmd/compile/internal/syntax/testdata/issue48382.go diff --git a/src/cmd/compile/internal/syntax/testdata/issue49205.go b/src/cmd/compile/internal/syntax/testdata/issue49205.go new file mode 100644 index 0000000000..bbcc950c5c --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue49205.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// test case from issue + +type _ interface{ + m /* ERROR unexpected int in interface type; possibly missing semicolon or newline or } */ int +} + +// other cases where the fix for this issue affects the error message + +const ( + x int = 10 /* ERROR unexpected literal "foo" in grouped declaration; possibly missing semicolon or newline or \) */ "foo" +) + +var _ = []int{1, 2, 3 /* ERROR unexpected int in composite literal; possibly missing comma or } */ int } + +type _ struct { + x y /* ERROR syntax error: unexpected comma in struct type; possibly missing semicolon or newline or } */ , +} + +func f(a, b c /* ERROR unexpected d in parameter list; possibly missing comma or \) */ d) { + f(a, b, c /* ERROR unexpected d in argument list; possibly missing comma or \) */ d) +} diff --git a/src/cmd/compile/internal/syntax/testdata/issue49482.go2 b/src/cmd/compile/internal/syntax/testdata/issue49482.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/issue49482.go2 rename to src/cmd/compile/internal/syntax/testdata/issue49482.go diff --git a/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2 b/src/cmd/compile/internal/syntax/testdata/linalg.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/go2/linalg.go2 rename to src/cmd/compile/internal/syntax/testdata/linalg.go diff --git a/src/cmd/compile/internal/syntax/testdata/go2/map.go2 b/src/cmd/compile/internal/syntax/testdata/map.go similarity index 97% rename from src/cmd/compile/internal/syntax/testdata/go2/map.go2 rename to src/cmd/compile/internal/syntax/testdata/map.go index 814d9539fd..a508d214b8 100644 --- a/src/cmd/compile/internal/syntax/testdata/go2/map.go2 +++ b/src/cmd/compile/internal/syntax/testdata/map.go @@ -5,8 +5,7 @@ // Package orderedmap provides an ordered map, implemented as a binary tree. package orderedmap -// TODO(gri) fix imports for tests -import "chans" // ERROR could not import +import "chans" // Map is an ordered map. type Map[K, V any] struct { diff --git a/src/cmd/compile/internal/syntax/testdata/go2/map2.go2 b/src/cmd/compile/internal/syntax/testdata/map2.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/go2/map2.go2 rename to src/cmd/compile/internal/syntax/testdata/map2.go diff --git a/src/cmd/compile/internal/syntax/testdata/sample.src b/src/cmd/compile/internal/syntax/testdata/sample.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/sample.src rename to src/cmd/compile/internal/syntax/testdata/sample.go diff --git a/src/cmd/compile/internal/syntax/testdata/go2/slices.go2 b/src/cmd/compile/internal/syntax/testdata/slices.go similarity index 94% rename from src/cmd/compile/internal/syntax/testdata/go2/slices.go2 rename to src/cmd/compile/internal/syntax/testdata/slices.go index 2bacd1c2aa..9265109556 100644 --- a/src/cmd/compile/internal/syntax/testdata/go2/slices.go2 +++ b/src/cmd/compile/internal/syntax/testdata/slices.go @@ -56,7 +56,7 @@ func reducer(x float64, y int) float64 { } var reduced1 = Reduce[int, float64](input, 0, reducer) -var reduced2 = Reduce(input, 1i /* ERROR overflows */, reducer) // using type inference +var reduced2 = Reduce(input, 1i, reducer) // using type inference var reduced3 = Reduce(input, 1, reducer) // using type inference func filter(x int) bool { diff --git a/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2 b/src/cmd/compile/internal/syntax/testdata/smoketest.go similarity index 73% rename from src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2 rename to src/cmd/compile/internal/syntax/testdata/smoketest.go index 42efb42527..6b3593ac7a 100644 --- a/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2 +++ b/src/cmd/compile/internal/syntax/testdata/smoketest.go @@ -28,12 +28,6 @@ func _[P interface{}]() func _[P B]() func _[P B[P]]() -// in methods -func (T) _[P any]() -func (T) _[P interface{}]() -func (T) _[P B]() -func (T) _[P B[P]]() - // type instantiations type _ T[int] @@ -44,18 +38,18 @@ var _ = T[int]{} type _ struct{ T[int] } // interfaces -type _ interface{ +type _ interface { m() ~int } -type _ interface{ +type _ interface { ~int | ~float | ~string ~complex128 underlying(underlying underlying) underlying } -type _ interface{ +type _ interface { T T[int] } @@ -64,20 +58,16 @@ type _ interface{ func _(T[P], T[P1, P2]) func _(a [N]T) -type _ struct{ +type _ struct { T[P] T[P1, P2] - f [N] + f[N] } -type _ interface{ +type _ interface { m() - // generic methods - disabled for now - // m[] /* ERROR empty type parameter list */ () - // m[ /* ERROR cannot have type parameters */ P any](P) - // instantiated types - // T[] /* ERROR empty type argument list */ + T[ /* ERROR empty type argument list */ ] T[P] T[P1, P2] } diff --git a/src/cmd/compile/internal/syntax/testdata/tparams.go2 b/src/cmd/compile/internal/syntax/testdata/tparams.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/tparams.go2 rename to src/cmd/compile/internal/syntax/testdata/tparams.go diff --git a/src/cmd/compile/internal/syntax/testdata/typeset.go2 b/src/cmd/compile/internal/syntax/testdata/typeset.go similarity index 100% rename from src/cmd/compile/internal/syntax/testdata/typeset.go2 rename to src/cmd/compile/internal/syntax/testdata/typeset.go diff --git a/src/cmd/compile/internal/syntax/tokens.go b/src/cmd/compile/internal/syntax/tokens.go index 60eae36ec9..6dece1aa5b 100644 --- a/src/cmd/compile/internal/syntax/tokens.go +++ b/src/cmd/compile/internal/syntax/tokens.go @@ -93,8 +93,8 @@ func contains(tokset uint64, tok token) bool { type LitKind uint8 // TODO(gri) With the 'i' (imaginary) suffix now permitted on integer -// and floating-point numbers, having a single ImagLit does -// not represent the literal kind well anymore. Remove it? +// and floating-point numbers, having a single ImagLit does +// not represent the literal kind well anymore. Remove it? const ( IntLit LitKind = iota FloatLit diff --git a/src/cmd/compile/internal/syntax/walk.go b/src/cmd/compile/internal/syntax/walk.go index b025844204..8f1d566155 100644 --- a/src/cmd/compile/internal/syntax/walk.go +++ b/src/cmd/compile/internal/syntax/walk.go @@ -52,7 +52,7 @@ func Crawl(root Node, f func(Node) bool) { // field lists such as type T in "a, b, c T"). Such shared nodes are // walked multiple times. // TODO(gri) Revisit this design. It may make sense to walk those nodes -// only once. A place where this matters is types2.TestResolveIdents. +// only once. A place where this matters is types2.TestResolveIdents. func Walk(root Node, v Visitor) { walker{v}.node(root) } diff --git a/src/cmd/compile/internal/test/zerorange_test.go b/src/cmd/compile/internal/test/zerorange_test.go index ec87136157..e92b5d342f 100644 --- a/src/cmd/compile/internal/test/zerorange_test.go +++ b/src/cmd/compile/internal/test/zerorange_test.go @@ -170,7 +170,6 @@ func triggerZerorangeSmall(f, g, h uint64) (rv0 uint64) { // depending on the size of the thing that needs to be zeroed out // (I've verified at the time of the writing of this test that it // exercises the various cases). -// func TestZerorange45372(t *testing.T) { if r := triggerZerorangeLarge(101, 303, 505); r != 1010 { t.Errorf("large: wanted %d got %d", 1010, r) diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go index 16e24a0491..60bac77d19 100644 --- a/src/cmd/compile/internal/typecheck/stmt.go +++ b/src/cmd/compile/internal/typecheck/stmt.go @@ -78,7 +78,7 @@ func typecheckrangeExpr(n *ir.RangeStmt) { base.ErrorfAt(n.Pos(), "cannot assign type %v to %L in range%s", t, nn, why) } } - checkassign(n, nn) + checkassign(nn) } } do(n.Key, tk) @@ -137,7 +137,7 @@ func assign(stmt ir.Node, lhs, rhs []ir.Node) { if lhs[i].Typecheck() == 0 { lhs[i] = AssignExpr(lhs[i]) } - checkassign(stmt, lhs[i]) + checkassign(lhs[i]) } assignType := func(i int, typ *types.Type) { diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index e9690a5551..d4ec52adf9 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -1476,14 +1476,14 @@ func getShapes(t *types.Type, listp *[]*types.Type) { // For now, we only consider two types to have the same shape, if they have exactly // the same underlying type or they are both pointer types. // -// tparam is the associated typeparam - it must be TTYPEPARAM type. If there is a -// structural type for the associated type param (not common), then a pointer type t -// is mapped to its underlying type, rather than being merged with other pointers. +// tparam is the associated typeparam - it must be TTYPEPARAM type. If there is a +// structural type for the associated type param (not common), then a pointer type t +// is mapped to its underlying type, rather than being merged with other pointers. // -// Shape types are also distinguished by the index of the type in a type param/arg -// list. We need to do this so we can distinguish and substitute properly for two -// type params in the same function that have the same shape for a particular -// instantiation. +// Shape types are also distinguished by the index of the type in a type param/arg +// list. We need to do this so we can distinguish and substitute properly for two +// type params in the same function that have the same shape for a particular +// instantiation. func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type { assert(!t.IsShape()) if t.HasShape() { diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index b5108eab84..85de653a82 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -488,7 +488,7 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.OASOP: n := n.(*ir.AssignOpStmt) n.X, n.Y = Expr(n.X), Expr(n.Y) - checkassign(n, n.X) + checkassign(n.X) if n.IncDec && !okforarith[n.X.Type().Kind()] { base.Errorf("invalid operation: %v (non-numeric type %v)", n, n.X.Type()) return n @@ -1562,7 +1562,7 @@ func checklvalue(n ir.Node, verb string) { } } -func checkassign(stmt ir.Node, n ir.Node) { +func checkassign(n ir.Node) { // have already complained about n being invalid if n.Type() == nil { if base.Errors() == 0 { diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index a42d97cd31..bdd3ca1d28 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -83,7 +83,6 @@ const ( // %v Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols. // %+v Debug syntax: always include PkgName. prefix even for local names. // %S Short syntax: Name only, no matter what. -// func (s *Sym) Format(f fmt.State, verb rune) { mode := fmtGo switch verb { @@ -241,7 +240,6 @@ var fmtBufferPool = sync.Pool{ // %L Go syntax for underlying type if t is named // %S short Go syntax: drop leading "func" in function type // %-S special case for method receiver symbol -// func (t *Type) Format(s fmt.State, verb rune) { mode := fmtGo switch verb { diff --git a/src/cmd/compile/internal/types/size.go b/src/cmd/compile/internal/types/size.go index fc9907b85f..9fa3e49e34 100644 --- a/src/cmd/compile/internal/types/size.go +++ b/src/cmd/compile/internal/types/size.go @@ -17,18 +17,18 @@ var RegSize int // Slices in the runtime are represented by three components: // -// type slice struct { -// ptr unsafe.Pointer -// len int -// cap int -// } +// type slice struct { +// ptr unsafe.Pointer +// len int +// cap int +// } // // Strings in the runtime are represented by two components: // -// type string struct { -// ptr unsafe.Pointer -// len int -// } +// type string struct { +// ptr unsafe.Pointer +// len int +// } // // These variables are the offsets of fields and sizes of these structs. var ( diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index c8d11b5bb9..147194c369 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -1121,9 +1121,9 @@ func (t *Type) SimpleString() string { } // Cmp is a comparison between values a and b. -// -1 if a < b -// 0 if a == b -// 1 if a > b +// -1 if a < b +// 0 if a == b +// 1 if a > b type Cmp int8 const ( diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index d864c96fb6..34bb29cadc 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -285,7 +285,6 @@ type Info struct { // TypeOf returns the type of expression e, or nil if not found. // Precondition: the Types, Uses and Defs maps are populated. -// func (info *Info) TypeOf(e syntax.Expr) Type { if t, ok := info.Types[e]; ok { return t.Type @@ -305,7 +304,6 @@ func (info *Info) TypeOf(e syntax.Expr) Type { // it defines, not the type (*TypeName) it uses. // // Precondition: the Uses and Defs maps are populated. -// func (info *Info) ObjectOf(id *syntax.Name) Object { if obj := info.Defs[id]; obj != nil { return obj diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index d433ed1bdf..528beaacea 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -26,7 +26,7 @@ const brokenPkg = "package broken_" func parseSrc(path, src string) (*syntax.File, error) { errh := func(error) {} // dummy error handler so that parsing continues in presence of errors - return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, syntax.AllowGenerics|syntax.AllowMethodTypeParams) + return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, 0) } func pkgFor(path, source string, info *Info) (*Package, error) { @@ -436,36 +436,6 @@ type T[P any] []P {`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`, []testInst{{`f`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}}, }, - // we don't know how to translate these but we can type-check them - {`package q0; type T struct{}; func (T) m[P any](P) {}; func _(x T) { x.m(42) }`, - []testInst{{`m`, []string{`int`}, `func(int)`}}, - }, - {`package q1; type T struct{}; func (T) m[P any](P) P { panic(0) }; func _(x T) { x.m(42) }`, - []testInst{{`m`, []string{`int`}, `func(int) int`}}, - }, - {`package q2; type T struct{}; func (T) m[P any](...P) P { panic(0) }; func _(x T) { x.m(42) }`, - []testInst{{`m`, []string{`int`}, `func(...int) int`}}, - }, - {`package q3; type T struct{}; func (T) m[A, B, C any](A, *B, []C) {}; func _(x T) { x.m(1.2, new(string), []byte{}) }`, - []testInst{{`m`, []string{`float64`, `string`, `byte`}, `func(float64, *string, []byte)`}}, - }, - {`package q4; type T struct{}; func (T) m[A, B any](A, *B, ...[]B) {}; func _(x T) { x.m(1.2, new(byte)) }`, - []testInst{{`m`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}}, - }, - - {`package r0; type T[P1 any] struct{}; func (_ T[P2]) m[Q any](Q) {}; func _[P3 any](x T[P3]) { x.m(42) }`, - []testInst{ - {`T`, []string{`P2`}, `struct{}`}, - {`T`, []string{`P3`}, `struct{}`}, - {`m`, []string{`int`}, `func(int)`}, - }, - }, - // TODO(gri) record method type parameters in syntax.FuncType so we can check this - // {`package r1; type T interface{ m[P any](P) }; func _(x T) { x.m(4.2) }`, - // `x.m`, - // []string{`float64`}, - // `func(float64)`, - // }, {`package s1; func f[T any, P interface{*T}](x T) {}; func _(x string) { f(x) }`, []testInst{{`f`, []string{`string`, `*string`}, `func(x string)`}}, @@ -2305,6 +2275,103 @@ func TestInstanceIdentity(t *testing.T) { } } +// TestInstantiatedObjects verifies properties of instantiated objects. +func TestInstantiatedObjects(t *testing.T) { + const src = ` +package p + +type T[P any] struct { + field P +} + +func (recv *T[Q]) concreteMethod() {} + +type FT[P any] func(ftp P) (ftrp P) + +func F[P any](fp P) (frp P){ return } + +type I[P any] interface { + interfaceMethod(P) +} + +var ( + t T[int] + ft FT[int] + f = F[int] + i I[int] +) +` + info := &Info{ + Defs: make(map[*syntax.Name]Object), + } + f, err := parseSrc("p.go", src) + if err != nil { + t.Fatal(err) + } + conf := Config{} + pkg, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, info) + if err != nil { + t.Fatal(err) + } + + lookup := func(name string) Type { return pkg.Scope().Lookup(name).Type() } + tests := []struct { + ident string + obj Object + }{ + {"field", lookup("t").Underlying().(*Struct).Field(0)}, + {"concreteMethod", lookup("t").(*Named).Method(0)}, + {"recv", lookup("t").(*Named).Method(0).Type().(*Signature).Recv()}, + {"ftp", lookup("ft").Underlying().(*Signature).Params().At(0)}, + {"ftrp", lookup("ft").Underlying().(*Signature).Results().At(0)}, + {"fp", lookup("f").(*Signature).Params().At(0)}, + {"frp", lookup("f").(*Signature).Results().At(0)}, + {"interfaceMethod", lookup("i").Underlying().(*Interface).Method(0)}, + } + + // Collect all identifiers by name. + idents := make(map[string][]*syntax.Name) + syntax.Inspect(f, func(n syntax.Node) bool { + if id, ok := n.(*syntax.Name); ok { + idents[id.Value] = append(idents[id.Value], id) + } + return true + }) + + for _, test := range tests { + test := test + t.Run(test.ident, func(t *testing.T) { + if got := len(idents[test.ident]); got != 1 { + t.Fatalf("found %d identifiers named %s, want 1", got, test.ident) + } + ident := idents[test.ident][0] + def := info.Defs[ident] + if def == test.obj { + t.Fatalf("info.Defs[%s] contains the test object", test.ident) + } + if def.Pkg() != test.obj.Pkg() { + t.Errorf("Pkg() = %v, want %v", def.Pkg(), test.obj.Pkg()) + } + if def.Name() != test.obj.Name() { + t.Errorf("Name() = %v, want %v", def.Name(), test.obj.Name()) + } + if def.Pos() != test.obj.Pos() { + t.Errorf("Pos() = %v, want %v", def.Pos(), test.obj.Pos()) + } + if def.Parent() != test.obj.Parent() { + t.Fatalf("Parent() = %v, want %v", def.Parent(), test.obj.Parent()) + } + if def.Exported() != test.obj.Exported() { + t.Fatalf("Exported() = %v, want %v", def.Exported(), test.obj.Exported()) + } + if def.Id() != test.obj.Id() { + t.Fatalf("Id() = %v, want %v", def.Id(), test.obj.Id()) + } + // String and Type are expected to differ. + }) + } +} + func TestImplements(t *testing.T) { const src = ` package p diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go index 49f4e2d2ab..f766c0b31d 100644 --- a/src/cmd/compile/internal/types2/assignments.go +++ b/src/cmd/compile/internal/types2/assignments.go @@ -441,8 +441,8 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) { // unpack unpacks a *syntax.ListExpr into a list of syntax.Expr. // Helper introduced for the go/types -> types2 port. // TODO(gri) Should find a more efficient solution that doesn't -// require introduction of a new slice for simple -// expressions. +// require introduction of a new slice for simple +// expressions. func unpackExpr(x syntax.Expr) []syntax.Expr { if x, _ := x.(*syntax.ListExpr); x != nil { return x.ElemList diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index 428897c628..1bd2fdce06 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -16,7 +16,6 @@ import ( // reports whether the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. -// func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] diff --git a/src/cmd/compile/internal/types2/builtins_test.go b/src/cmd/compile/internal/types2/builtins_test.go index e07a7794f6..ad8873a7d4 100644 --- a/src/cmd/compile/internal/types2/builtins_test.go +++ b/src/cmd/compile/internal/types2/builtins_test.go @@ -159,7 +159,7 @@ func TestBuiltinSignatures(t *testing.T) { func parseGenericSrc(path, src string) (*syntax.File, error) { errh := func(error) {} // dummy error handler so that parsing continues in presence of errors - return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, syntax.AllowGenerics) + return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, 0) } func testBuiltinSignature(t *testing.T, name, src0, want string) { diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go index cdf440f9be..ec242c5e22 100644 --- a/src/cmd/compile/internal/types2/check_test.go +++ b/src/cmd/compile/internal/types2/check_test.go @@ -135,7 +135,7 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) { t.Fatal(err) } - files, errlist := parseFiles(t, filenames, syntax.AllowGenerics|syntax.AllowMethodTypeParams) + files, errlist := parseFiles(t, filenames, 0) pkgName := "" if len(files) > 0 { diff --git a/src/cmd/compile/internal/types2/context.go b/src/cmd/compile/internal/types2/context.go index 7abea6b654..ae39c7b830 100644 --- a/src/cmd/compile/internal/types2/context.go +++ b/src/cmd/compile/internal/types2/context.go @@ -12,11 +12,32 @@ import ( "sync" ) -// An Context is an opaque type checking context. It may be used to share -// identical type instances across type-checked packages or calls to -// Instantiate. +// This file contains a definition of the type-checking context; an opaque type +// that may be supplied by users during instantiation. // -// It is safe for concurrent use. +// Contexts serve two purposes: +// - reduce the duplication of identical instances +// - short-circuit instantiation cycles +// +// For the latter purpose, we must always have a context during instantiation, +// whether or not it is supplied by the user. For both purposes, it must be the +// case that hashing a pointer-identical type produces consistent results +// (somewhat obviously). +// +// However, neither of these purposes require that our hash is perfect, and so +// this was not an explicit design goal of the context type. In fact, due to +// concurrent use it is convenient not to guarantee de-duplication. +// +// Nevertheless, in the future it could be helpful to allow users to leverage +// contexts to canonicalize instances, and it would probably be possible to +// achieve such a guarantee. + +// A Context is an opaque type checking context. It may be used to share +// identical type instances across type-checked packages or calls to +// Instantiate. Contexts are safe for concurrent use. +// +// The use of a shared context does not guarantee that identical instances are +// deduplicated in all cases. type Context struct { mu sync.Mutex typeMap map[string][]ctxtEntry // type hash -> instances entries diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 23225c8d0d..1ecb4ff54b 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -1211,7 +1211,6 @@ const ( // If hint != nil, it is the type of a composite literal element. // If allowGeneric is set, the operand type may be an uninstantiated // parameterized type or function value. -// func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind { if check.conf.Trace { check.trace(e.Pos(), "-- expr %s", e) @@ -1259,7 +1258,6 @@ func (check *Checker) nonGeneric(x *operand) { // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. -// func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was issue 5770) @@ -1764,7 +1762,6 @@ func (check *Checker) typeAssertion(e syntax.Expr, x *operand, T Type, typeSwitc // expr typechecks expression e and initializes x with the expression value. // The result must be a single value. // If an error occurred, x.mode is set to invalid. -// func (check *Checker) expr(x *operand, e syntax.Expr) { check.rawExpr(x, e, nil, false) check.exclude(x, 1<= 0 && v >= max { - if check.conf.CompilerErrorMessages { - check.errorf(&x, invalidArg+"array index %s out of bounds [0:%d]", x.val.String(), max) - } else { - check.errorf(&x, invalidArg+"index %s is out of bounds", &x) - } + check.errorf(&x, invalidArg+"index %s out of bounds [0:%d]", x.val.String(), max) return } diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index 78fc35b72a..9f7e593eeb 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -21,9 +21,8 @@ const useConstraintTypeInference = true // If successful, infer returns the complete list of type arguments, one for each type parameter. // Otherwise the result is nil and appropriate errors will be reported. // -// Inference proceeds as follows: +// Inference proceeds as follows. Starting with given type arguments: // -// Starting with given type arguments // 1) apply FTI (function type inference) with typed arguments, // 2) apply CTI (constraint type inference), // 3) apply FTI with untyped function arguments, diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index a511538ccc..a69a26ba64 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -24,7 +24,8 @@ import ( // previous instances with the same identity. As a special case, generic // *Signature origin types are only considered identical if they are pointer // equivalent, so that instantiating distinct (but possibly identical) -// signatures will yield different instances. +// signatures will yield different instances. The use of a shared context does +// not guarantee that identical instances are deduplicated in all cases. // // If validate is set, Instantiate verifies that the number of type arguments // and parameters match, and that the type arguments satisfy their diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go index b8bf88dc62..431b91f270 100644 --- a/src/cmd/compile/internal/types2/interface.go +++ b/src/cmd/compile/internal/types2/interface.go @@ -132,11 +132,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType // We have a method with name f.Name. name := f.Name.Value if name == "_" { - if check.conf.CompilerErrorMessages { - check.error(f.Name, "methods must have a unique non-blank name") - } else { - check.error(f.Name, "invalid method name _") - } + check.error(f.Name, "methods must have a unique non-blank name") continue // ignore } diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 0832877226..93defd6618 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -41,7 +41,6 @@ import ( // - If indirect is set, a method with a pointer receiver type was found // but there was no pointer on the path from the actual receiver type to // the method's formal receiver base type, nor was the receiver addressable. -// func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { if T == nil { panic("LookupFieldOrMethod on nil type") @@ -83,9 +82,9 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o } // TODO(gri) The named type consolidation and seen maps below must be -// indexed by unique keys for a given type. Verify that named -// types always have only one representation (even when imported -// indirectly via different packages.) +// indexed by unique keys for a given type. Verify that named +// types always have only one representation (even when imported +// indirectly via different packages.) // lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod. // If foldCase is true, the lookup for methods will include looking for any method @@ -281,7 +280,6 @@ func lookupType(m map[Type]int, typ Type) (int, bool) { // is not set), MissingMethod only checks that methods of T which are also // present in V have matching types (e.g., for a type assertion x.(T) where // x is of interface type V). -// func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { m, alt := (*Checker)(nil).missingMethod(V, T, static) // Only report a wrong type if the alternative method has the same name as m. diff --git a/src/cmd/compile/internal/types2/mono_test.go b/src/cmd/compile/internal/types2/mono_test.go index 19d0e95637..4511110691 100644 --- a/src/cmd/compile/internal/types2/mono_test.go +++ b/src/cmd/compile/internal/types2/mono_test.go @@ -16,7 +16,7 @@ import ( func checkMono(t *testing.T, body string) error { src := "package x; import `unsafe`; var _ unsafe.Pointer;\n" + body - file, err := syntax.Parse(syntax.NewFileBase("x.go"), strings.NewReader(src), nil, nil, syntax.AllowGenerics) + file, err := syntax.Parse(syntax.NewFileBase("x.go"), strings.NewReader(src), nil, nil, 0) if err != nil { t.Fatal(err) } diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 89d24d4e0b..1d3703ffd9 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -107,7 +107,7 @@ func (t *Named) Obj() *TypeName { return t.orig.obj } // for non-instances this func (t *Named) Origin() *Named { return t.orig } // TODO(gri) Come up with a better representation and API to distinguish -// between parameterized instantiated and non-instantiated types. +// between parameterized instantiated and non-instantiated types. // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) generic type even if it is instantiated. @@ -191,7 +191,7 @@ func (t *Named) instantiateMethod(i int) *Func { rtyp = t } - sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp) + sig.recv = substVar(origSig.recv, rtyp) return NewFunc(origm.pos, origm.pkg, origm.name, sig) } diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index 08d37cb256..9043b372ea 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -16,7 +16,6 @@ import ( // An Object describes a named language entity such as a package, // constant, type, variable, function (incl. methods), or label. // All objects implement the Object interface. -// type Object interface { Parent() *Scope // scope in which this object is declared; nil for methods and struct fields Pos() syntax.Pos // position of object identifier in declaration @@ -343,7 +342,7 @@ func NewParam(pos syntax.Pos, pkg *Package, name string, typ Type) *Var { // NewField returns a new variable representing a struct field. // For embedded fields, the name is the unqualified type name -/// under which the field is accessible. +// under which the field is accessible. func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool) *Var { return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, embedded: embedded, isField: true} } diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go index fce9a11ffa..548244e64d 100644 --- a/src/cmd/compile/internal/types2/operand.go +++ b/src/cmd/compile/internal/types2/operand.go @@ -52,7 +52,6 @@ var operandModeString = [...]string{ // the operand, the operand's type, a value for constants, and an id // for built-in functions. // The zero value of operand is a ready to use invalid operand. -// type operand struct { mode operandMode expr syntax.Expr @@ -63,7 +62,6 @@ type operand struct { // Pos returns the position of the expression corresponding to x. // If x is invalid the position is nopos. -// func (x *operand) Pos() syntax.Pos { // x.expr may not be set if x is invalid if x.expr == nil { @@ -108,7 +106,6 @@ func (x *operand) Pos() syntax.Pos { // // cgofunc ( ) // cgofunc ( of type ) -// func operandString(x *operand, qf Qualifier) string { // special-case nil if x.mode == nilvalue { diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go index 61963cb043..5c64ecdfc8 100644 --- a/src/cmd/compile/internal/types2/resolver.go +++ b/src/cmd/compile/internal/types2/resolver.go @@ -179,8 +179,9 @@ func (check *Checker) importPackage(pos syntax.Pos, path, dir string) *Package { // package should be complete or marked fake, but be cautious if imp.complete || imp.fake { check.impMap[key] = imp - // Once we've formatted an error message once, keep the pkgPathMap - // up-to-date on subsequent imports. + // Once we've formatted an error message, keep the pkgPathMap + // up-to-date on subsequent imports. It is used for package + // qualification in error messages. if check.pkgPathMap != nil { check.markImports(imp) } @@ -268,7 +269,7 @@ func (check *Checker) collectObjects() { if s.LocalPkgName != nil { name = s.LocalPkgName.Value if path == "C" { - // match cmd/compile (not prescribed by spec) + // match 1.17 cmd/compile (not prescribed by spec) check.error(s.LocalPkgName, `cannot rename import "C"`) continue } @@ -295,8 +296,8 @@ func (check *Checker) collectObjects() { check.recordImplicit(s, pkgName) } - if path == "C" { - // match cmd/compile (not prescribed by spec) + if imp.fake { + // match 1.17 cmd/compile (not prescribed by spec) pkgName.used = true } @@ -700,7 +701,7 @@ func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // unusedImports checks for unused imports. func (check *Checker) unusedImports() { - // if function bodies are not checked, packages' uses are likely missing - don't check + // If function bodies are not checked, packages' uses are likely missing - don't check. if check.conf.IgnoreFuncBodies { return } diff --git a/src/cmd/compile/internal/types2/return.go b/src/cmd/compile/internal/types2/return.go index 6c3e1842ce..7cdea99e08 100644 --- a/src/cmd/compile/internal/types2/return.go +++ b/src/cmd/compile/internal/types2/return.go @@ -99,8 +99,8 @@ func (check *Checker) isTerminatingSwitch(body []*syntax.CaseClause, label strin } // TODO(gri) For nested breakable statements, the current implementation of hasBreak -// will traverse the same subtree repeatedly, once for each label. Replace -// with a single-pass label/break matching phase. +// will traverse the same subtree repeatedly, once for each label. Replace +// with a single-pass label/break matching phase. // hasBreak reports if s is or contains a break statement // referring to the label-ed statement or implicit-ly the diff --git a/src/cmd/compile/internal/types2/selection.go b/src/cmd/compile/internal/types2/selection.go index 8128aeee2e..ee63214407 100644 --- a/src/cmd/compile/internal/types2/selection.go +++ b/src/cmd/compile/internal/types2/selection.go @@ -36,7 +36,6 @@ const ( // p.x FieldVal T x int {0} true // p.m MethodVal *T m func() {1, 0} true // T.m MethodExpr T m func(T) {1, 0} false -// type Selection struct { kind SelectionKind recv Type // type of x @@ -115,7 +114,6 @@ func (s *Selection) String() string { return SelectionString(s, nil) } // "field (T) f int" // "method (T) f(X) Y" // "method expr (T) f(X) Y" -// func SelectionString(s *Selection, qf Qualifier) string { var k string switch s.kind { diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index c98024f924..2dc4dd43f3 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -199,62 +199,48 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] // Delay validation of receiver type as it may cause premature expansion // of types the receiver type is dependent on (see issues #51232, #51233). check.later(func() { - rtyp, _ := deref(recv.typ) - // spec: "The receiver type must be of the form T or *T where T is a type name." - // (ignore invalid types - error was reported before) - if rtyp != Typ[Invalid] { - var err string - switch T := rtyp.(type) { - case *Named: - T.resolve(check.bestContext(nil)) - // The receiver type may be an instantiated type referred to - // by an alias (which cannot have receiver parameters for now). - if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { - check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ) - break - } - // spec: "The type denoted by T is called the receiver base type; it must not - // be a pointer or interface type and it must be declared in the same package - // as the method." - if T.obj.pkg != check.pkg { - err = "type not defined in this package" - if check.conf.CompilerErrorMessages { - check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ) - err = "" - } - } else { - // The underlying type of a receiver base type can be a type parameter; - // e.g. for methods with a generic receiver T[P] with type T[P any] P. - // TODO(gri) Such declarations are currently disallowed. - // Revisit the need for underIs. - underIs(T, func(u Type) bool { - switch u := u.(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - err = "unsafe.Pointer" - return false - } - case *Pointer, *Interface: - err = "pointer or interface type" - return false - } - return true - }) - } + rtyp, _ := deref(recv.typ) + if rtyp == Typ[Invalid] { + return // error was reported before + } + // spec: "The type denoted by T is called the receiver base type; it must not + // be a pointer or interface type and it must be declared in the same package + // as the method." + switch T := rtyp.(type) { + case *Named: + T.resolve(check.bestContext(nil)) + // The receiver type may be an instantiated type referred to + // by an alias (which cannot have receiver parameters for now). + if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { + check.errorf(recv, "cannot define new methods on instantiated type %s", rtyp) + break + } + if T.obj.pkg != check.pkg { + check.errorf(recv, "cannot define new methods on non-local type %s", rtyp) + break + } + var cause string + switch u := T.under().(type) { case *Basic: - err = "basic or unnamed type" - if check.conf.CompilerErrorMessages { - check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ) - err = "" + // unsafe.Pointer is treated like a regular pointer + if u.kind == UnsafePointer { + cause = "unsafe.Pointer" } - default: - check.errorf(recv.pos, "invalid receiver type %s", recv.typ) + case *Pointer, *Interface: + cause = "pointer or interface type" + case *TypeParam: + // The underlying type of a receiver base type cannot be a + // type parameter: "type T[P any] P" is not a valid declaration. + unreachable() } - if err != "" { - check.errorf(recv.pos, "invalid receiver type %s (%s)", recv.typ, err) + if cause != "" { + check.errorf(recv, "invalid receiver type %s (%s)", rtyp, cause) } + case *Basic: + check.errorf(recv, "cannot define new methods on non-local type %s", rtyp) + default: + check.errorf(recv, "invalid receiver type %s", recv.typ) } }).describef(recv, "validate receiver %s", recv) } diff --git a/src/cmd/compile/internal/types2/sizes.go b/src/cmd/compile/internal/types2/sizes.go index 6f981964be..7a34b6474c 100644 --- a/src/cmd/compile/internal/types2/sizes.go +++ b/src/cmd/compile/internal/types2/sizes.go @@ -39,7 +39,6 @@ type Sizes interface { // types are naturally aligned with a maximum alignment MaxAlign. // // *StdSizes implements Sizes. -// type StdSizes struct { WordSize int64 // word size in bytes - must be >= 4 (32bits) MaxAlign int64 // maximum alignment in bytes - must be >= 1 diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go index fda78e20d1..4e54056e74 100644 --- a/src/cmd/compile/internal/types2/stdlib_test.go +++ b/src/cmd/compile/internal/types2/stdlib_test.go @@ -220,7 +220,7 @@ func typecheck(t *testing.T, path string, filenames []string) { var files []*syntax.File for _, filename := range filenames { errh := func(err error) { t.Error(err) } - file, err := syntax.ParseFile(filename, errh, nil, syntax.AllowGenerics) + file, err := syntax.ParseFile(filename, errh, nil, 0) if err != nil { return } diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index 2b6abbde7e..e00f73ce99 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -312,7 +312,7 @@ L: } // TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead. -// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.) +// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.) // // func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []syntax.Expr, seen map[string]syntax.Expr) (T Type) { // var dummy operand diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 037f04797b..c9e8f9676d 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -290,14 +290,18 @@ func (subst *subster) typOrNil(typ Type) Type { func (subst *subster) var_(v *Var) *Var { if v != nil { if typ := subst.typ(v.typ); typ != v.typ { - copy := *v - copy.typ = typ - return © + return substVar(v, typ) } } return v } +func substVar(v *Var, typ Type) *Var { + copy := *v + copy.typ = typ + return © +} + func (subst *subster) tuple(t *Tuple) *Tuple { if t != nil { if vars, copied := subst.varList(t.vars); copied { @@ -410,9 +414,8 @@ func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) { copied = true } newsig := *sig - sig = &newsig - sig.recv = NewVar(sig.recv.pos, sig.recv.pkg, "", new) - out[i] = NewFunc(method.pos, method.pkg, method.name, sig) + newsig.recv = substVar(sig.recv, new) + out[i] = NewFunc(method.pos, method.pkg, method.name, &newsig) } } return diff --git a/src/cmd/compile/internal/types2/testdata/check/decls0.go b/src/cmd/compile/internal/types2/testdata/check/decls0.go index aa98480b99..927c2d353d 100644 --- a/src/cmd/compile/internal/types2/testdata/check/decls0.go +++ b/src/cmd/compile/internal/types2/testdata/check/decls0.go @@ -194,8 +194,8 @@ func (S0) m4() (x *S0 /* ERROR illegal cycle in method declaration */ .m4) { ret // interfaces may not have any blank methods type BlankI interface { - _ /* ERROR "invalid method name" */ () - _ /* ERROR "invalid method name" */ (float32) int + _ /* ERROR "methods must have a unique non-blank name" */ () + _ /* ERROR "methods must have a unique non-blank name" */ (float32) int m() } diff --git a/src/cmd/compile/internal/types2/testdata/check/decls2/decls2a.go b/src/cmd/compile/internal/types2/testdata/check/decls2/decls2a.go index d077db55dd..66ca6ee515 100644 --- a/src/cmd/compile/internal/types2/testdata/check/decls2/decls2a.go +++ b/src/cmd/compile/internal/types2/testdata/check/decls2/decls2a.go @@ -93,10 +93,10 @@ func (a, b /* ERROR "multiple receivers" */ T3) _() {} func (a, b, c /* ERROR "multiple receivers" */ T3) _() {} // Methods associated with non-local or unnamed types. -func (int /* ERROR "invalid receiver" */ ) m() {} +func (int /* ERROR "cannot define new methods on non-local type int" */ ) m() {} func ([ /* ERROR "invalid receiver" */ ]int) m() {} -func (time /* ERROR "invalid receiver" */ .Time) m() {} -func (* /* ERROR "invalid receiver" */ time.Time) m() {} +func (time /* ERROR "cannot define new methods on non-local type time\.Time" */ .Time) m() {} +func (* /* ERROR "cannot define new methods on non-local type time\.Time" */ time.Time) m() {} func (x /* ERROR "invalid receiver" */ interface{}) m() {} // Unsafe.Pointer is treated like a pointer when used as receiver type. diff --git a/src/cmd/compile/internal/types2/testdata/check/decls4.go b/src/cmd/compile/internal/types2/testdata/check/decls4.go index eb08421bee..384bcd9b89 100644 --- a/src/cmd/compile/internal/types2/testdata/check/decls4.go +++ b/src/cmd/compile/internal/types2/testdata/check/decls4.go @@ -59,7 +59,7 @@ var ( ) // alias receiver types -func (Ai /* ERROR "invalid receiver" */) m1() {} +func (Ai /* ERROR "cannot define new methods on non-local type int" */) m1() {} func (T0) m1() {} func (A0) m1 /* ERROR already declared */ () {} func (A0) m2 () {} @@ -115,8 +115,8 @@ type ( B2 = int ) -func (B0 /* ERROR invalid receiver */ ) m() {} -func (B1 /* ERROR invalid receiver */ ) n() {} +func (B0 /* ERROR cannot define new methods on non-local type int */ ) m() {} +func (B1 /* ERROR cannot define new methods on non-local type int */ ) n() {} // cycles type ( diff --git a/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go b/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go deleted file mode 100644 index 1b406593f8..0000000000 --- a/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// If types2.Config.AcceptMethodTypeParams is set, -// the type checker accepts methods that have their -// own type parameter list. - -package p - -type S struct{} - -func (S) m[T any](v T) {} - -// TODO(gri) Once we collect interface method type parameters -// in the parser, we can enable these tests again. -/* -type I interface { - m[T any](v T) -} - -type J interface { - m[T any](v T) -} - -var _ I = S{} -var _ I = J(nil) - -type C interface{ n() } - -type Sc struct{} - -func (Sc) m[T C](v T) - -type Ic interface { - m[T C](v T) -} - -type Jc interface { - m[T C](v T) -} - -var _ Ic = Sc{} -var _ Ic = Jc(nil) - -// TODO(gri) These should fail because the constraints don't match. -var _ I = Sc{} -var _ I = Jc(nil) - -var _ Ic = S{} -var _ Ic = J(nil) -*/ \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go b/src/cmd/compile/internal/types2/testdata/check/typeparams.go index 68b1f0f5c5..498d6f2d26 100644 --- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go +++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go @@ -329,9 +329,8 @@ func init[P /* ERROR func init must have no type parameters */ any]() {} type T struct {} func (T) m1() {} -// The type checker accepts method type parameters if configured accordingly. -func (T) m2[_ any]() {} -func (T) m3[P any]() {} +func (T) m2[ /* ERROR method must have no type parameters */ _ any]() {} +func (T) m3[ /* ERROR method must have no type parameters */ P any]() {} // type inference across parameterized types @@ -390,28 +389,6 @@ func _[T any] (x T) { m(S1[T]{x}) } -// type parameters in methods (generalization) - -type R0 struct{} - -func (R0) _[T any](x T) {} -func (R0 /* ERROR invalid receiver */ ) _[R0 any]() {} // scope of type parameters starts at "func" - -type R1[A, B any] struct{} - -func (_ R1[A, B]) m0(A, B) -func (_ R1[A, B]) m1[T any](A, B, T) T { panic(0) } -func (_ R1 /* ERROR not a generic type */ [R1, _]) _() -func (_ R1[A, B]) _[A /* ERROR redeclared */ any](B) {} - -func _() { - var r R1[int, string] - r.m1[rune](42, "foo", 'a') - r.m1[rune](42, "foo", 1.2 /* ERROR cannot use .* as rune .* \(truncated\) */) - r.m1(42, "foo", 1.2) // using type inference - var _ float64 = r.m1(42, "foo", 1.2) -} - type I1[A any] interface { m1(A) } diff --git a/src/cmd/compile/internal/types2/testdata/examples/constraints.go b/src/cmd/compile/internal/types2/testdata/examples/constraints.go index 4d7f70313a..5b144893ce 100644 --- a/src/cmd/compile/internal/types2/testdata/examples/constraints.go +++ b/src/cmd/compile/internal/types2/testdata/examples/constraints.go @@ -24,7 +24,8 @@ type ( _ interface{int|any} _ interface{int|~string|union} _ interface{int|~string|interface{int}} - _ interface{union|union /* ERROR overlapping terms p.union and p.union */ } + _ interface{union|int} // interfaces (here: union) are ignored when checking for overlap + _ interface{union|union} // ditto // For now we do not permit interfaces with methods in unions. _ interface{~ /* ERROR invalid use of ~ */ any} @@ -44,9 +45,9 @@ type ( type ( _[T interface{ *T } ] struct{} // ok _[T interface{ int | *T } ] struct{} // ok - _[T interface{ T /* ERROR cannot embed a type parameter */ } ] struct{} - _[T interface{ ~T /* ERROR cannot embed a type parameter */ } ] struct{} - _[T interface{ int|T /* ERROR cannot embed a type parameter */ }] struct{} + _[T interface{ T /* ERROR term cannot be a type parameter */ } ] struct{} + _[T interface{ ~T /* ERROR type in term ~T cannot be a type parameter */ } ] struct{} + _[T interface{ int|T /* ERROR term cannot be a type parameter */ }] struct{} ) // Multiple embedded union elements are intersected. The order in which they diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go index e38e57268d..c893cc049e 100644 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go @@ -5,5 +5,5 @@ package p type T[P any] interface{ - P // ERROR cannot embed a type parameter + P // ERROR term cannot be a type parameter } diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43109.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43109.go new file mode 100644 index 0000000000..a4533c9bf7 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43109.go @@ -0,0 +1,10 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Ensure there is no "imported but not used" error +// if a package wasn't imported in the first place. + +package p + +import . "/foo" // ERROR could not import \/foo diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47127.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47127.go index 108d600a38..bb4b487eb2 100644 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47127.go +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47127.go @@ -8,30 +8,30 @@ package p type ( _[P any] interface{ *P | []P | chan P | map[string]P } - _[P any] interface{ P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ ~P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | ~P /* ERROR "cannot embed a type parameter" */ } + _[P any] interface{ P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _[P any] interface{ int | P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } ) func _[P any]() { type ( _[P any] interface{ *P | []P | chan P | map[string]P } - _[P any] interface{ P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ ~P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | ~P /* ERROR "cannot embed a type parameter" */ } + _[P any] interface{ P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _[P any] interface{ int | P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } _ interface{ *P | []P | chan P | map[string]P } - _ interface{ P /* ERROR "cannot embed a type parameter" */ } - _ interface{ ~P /* ERROR "cannot embed a type parameter" */ } - _ interface{ int | P /* ERROR "cannot embed a type parameter" */ } - _ interface{ int | ~P /* ERROR "cannot embed a type parameter" */ } + _ interface{ P /* ERROR term cannot be a type parameter */ } + _ interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _ interface{ int | P /* ERROR term cannot be a type parameter */ } + _ interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } ) } func _[P any, Q interface{ *P | []P | chan P | map[string]P }]() {} -func _[P any, Q interface{ P /* ERROR "cannot embed a type parameter" */ }]() {} -func _[P any, Q interface{ ~P /* ERROR "cannot embed a type parameter" */ }]() {} -func _[P any, Q interface{ int | P /* ERROR "cannot embed a type parameter" */ }]() {} -func _[P any, Q interface{ int | ~P /* ERROR "cannot embed a type parameter" */ }]() {} +func _[P any, Q interface{ P /* ERROR term cannot be a type parameter */ }]() {} +func _[P any, Q interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ }]() {} +func _[P any, Q interface{ int | P /* ERROR term cannot be a type parameter */ }]() {} +func _[P any, Q interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ }]() {} diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go index 711e50a55a..3dd303957c 100644 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go @@ -14,8 +14,8 @@ func (A1[P]) m2() {} type A2 = T[int] -func (A2 /* ERROR cannot define methods on instantiated type T\[int\] */) m3() {} -func (_ /* ERROR cannot define methods on instantiated type T\[int\] */ A2) m4() {} +func (A2 /* ERROR cannot define new methods on instantiated type T\[int\] */) m3() {} +func (_ /* ERROR cannot define new methods on instantiated type T\[int\] */ A2) m4() {} func (T[int]) m5() {} // int is the type parameter name, not an instantiation func (T[* /* ERROR must be an identifier */ int]) m6() {} // syntax error diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue51607.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue51607.go new file mode 100644 index 0000000000..d8df143627 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue51607.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// Interface types must be ignored during overlap test. + +type ( + T1 interface{int} + T2 interface{~int} + T3 interface{T1 | bool | string} + T4 interface{T2 | ~bool | ~string} +) + +type ( + // overlap errors for non-interface terms + // (like the interface terms, but explicitly inlined) + _ interface{int | int /* ERROR overlapping terms int and int */ } + _ interface{int | ~ /* ERROR overlapping terms ~int and int */ int} + _ interface{~int | int /* ERROR overlapping terms int and ~int */ } + _ interface{~int | ~ /* ERROR overlapping terms ~int and ~int */ int} + + _ interface{T1 | bool | string | T1 | bool /* ERROR overlapping terms bool and bool */ | string /* ERROR overlapping terms string and string */ } + _ interface{T1 | bool | string | T2 | ~ /* ERROR overlapping terms ~bool and bool */ bool | ~ /* ERROR overlapping terms ~string and string */ string} + + // no errors for interface terms + _ interface{T1 | T1} + _ interface{T1 | T2} + _ interface{T2 | T1} + _ interface{T2 | T2} + + _ interface{T3 | T3 | int} + _ interface{T3 | T4 | bool } + _ interface{T4 | T3 | string } + _ interface{T4 | T4 | float64 } +) + +func _[_ T1 | bool | string | T1 | bool /* ERROR overlapping terms */ ]() {} +func _[_ T1 | bool | string | T2 | ~ /* ERROR overlapping terms */ bool ]() {} +func _[_ T2 | ~bool | ~string | T1 | bool /* ERROR overlapping terms */ ]() {} +func _[_ T2 | ~bool | ~string | T2 | ~ /* ERROR overlapping terms */ bool ]() {} + +func _[_ T3 | T3 | int]() {} +func _[_ T3 | T4 | bool]() {} +func _[_ T4 | T3 | string]() {} +func _[_ T4 | T4 | float64]() {} + +// test cases from issue + +type _ interface { + interface {bool | int} | interface {bool | string} +} + +type _ interface { + interface {bool | int} ; interface {bool | string} +} + +type _ interface { + interface {bool; int} ; interface {bool; string} +} + +type _ interface { + interface {bool; int} | interface {bool; string} +} \ No newline at end of file diff --git a/src/cmd/compile/internal/types2/typeset_test.go b/src/cmd/compile/internal/types2/typeset_test.go index 68e5d8ad62..69eaff741f 100644 --- a/src/cmd/compile/internal/types2/typeset_test.go +++ b/src/cmd/compile/internal/types2/typeset_test.go @@ -47,7 +47,7 @@ func TestTypeSetString(t *testing.T) { // parse errh := func(error) {} // dummy error handler so that parsing continues in presence of errors src := "package p; type T interface" + body - file, err := syntax.Parse(nil, strings.NewReader(src), errh, nil, syntax.AllowGenerics) + file, err := syntax.Parse(nil, strings.NewReader(src), errh, nil, 0) if err != nil { t.Fatalf("%s: %v (invalid test case)", body, err) } diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index ada0529929..e0f36fcec4 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -25,7 +25,6 @@ import ( // // Using a nil Qualifier is equivalent to using (*Package).Path: the // object is qualified by the import path, e.g., "encoding/json.Marshal". -// type Qualifier func(*Package) string // RelativeTo returns a Qualifier that fully qualifies members of diff --git a/src/cmd/compile/internal/types2/typeterm.go b/src/cmd/compile/internal/types2/typeterm.go index 1d7223f13c..3d82a37ab8 100644 --- a/src/cmd/compile/internal/types2/typeterm.go +++ b/src/cmd/compile/internal/types2/typeterm.go @@ -10,7 +10,6 @@ package types2 // 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) // T: &term{false, T} == {T} // set of type T // ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t -// type term struct { tilde bool // valid if typ != nil typ Type diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index afbea06032..8b9976da79 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -17,7 +17,6 @@ import ( // If an error occurred, x.mode is set to invalid. // For the meaning of def, see Checker.definedType, below. // If wantType is set, the identifier e is expected to denote a type. -// func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType bool) { x.mode = invalid x.expr = e @@ -181,7 +180,6 @@ func (check *Checker) validVarType(e syntax.Expr, typ Type) { // If def != nil, e is the type specification for the defined type def, declared // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. -// func (check *Checker) definedType(e syntax.Expr, def *Named) Type { typ := check.typInternal(e, def) assert(isTyped(typ)) @@ -216,7 +214,6 @@ func goTypeName(typ Type) string { // typInternal drives type checking of types. // Must only be called by definedType or genericType. -// func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) { if check.conf.Trace { check.trace(e0.Pos(), "-- type %s", e0) diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go index 132e73098a..0ed125fb29 100644 --- a/src/cmd/compile/internal/types2/union.go +++ b/src/cmd/compile/internal/types2/union.go @@ -113,14 +113,12 @@ func parseUnion(check *Checker, uexpr syntax.Expr) Type { switch { case tset.NumMethods() != 0: check.errorf(tlist[i], "cannot use %s in union (%s contains methods)", t, t) - continue case t.typ == universeComparable.Type(): check.error(tlist[i], "cannot use comparable in union") - continue case tset.comparable: check.errorf(tlist[i], "cannot use %s in union (%s embeds comparable)", t, t) - continue } + continue // terms with interface types are not subject to the no-overlap rule } // Report overlapping (non-disjoint) terms such as @@ -148,7 +146,11 @@ func parseTilde(check *Checker, tx syntax.Expr) *Term { // simply use its underlying type (like we do for other named, embedded interfaces), // and since the underlying type is an interface the embedding is well defined. if isTypeParam(typ) { - check.error(x, "cannot embed a type parameter") + if tilde { + check.errorf(x, "type in term %s cannot be a type parameter", tx) + } else { + check.error(x, "term cannot be a type parameter") + } typ = Typ[Invalid] } term := NewTerm(tilde, typ) @@ -160,10 +162,16 @@ func parseTilde(check *Checker, tx syntax.Expr) *Term { // overlappingTerm reports the index of the term x in terms which is // overlapping (not disjoint) from y. The result is < 0 if there is no -// such term. +// such term. The type of term y must not be an interface, and terms +// with an interface type are ignored in the terms list. func overlappingTerm(terms []*Term, y *Term) int { + assert(!IsInterface(y.typ)) for i, x := range terms { - // disjoint requires non-nil, non-top arguments + if IsInterface(x.typ) { + continue + } + // disjoint requires non-nil, non-top arguments, + // and non-interface types as term types. if debug { if x == nil || x.typ == nil || y == nil || y.typ == nil { panic("empty or top union term") diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go index 1deff3961f..9292924f23 100644 --- a/src/cmd/compile/internal/types2/universe.go +++ b/src/cmd/compile/internal/types2/universe.go @@ -245,7 +245,6 @@ func init() { // Objects with names containing blanks are internal and not entered into // a scope. Objects with exported names are inserted in the unsafe package // scope; other objects are inserted in the universe scope. -// func def(obj Object) { assert(obj.color() == black) name := obj.Name() diff --git a/src/cmd/compile/internal/types2/validtype.go b/src/cmd/compile/internal/types2/validtype.go index c7d42551dd..d495c6788e 100644 --- a/src/cmd/compile/internal/types2/validtype.go +++ b/src/cmd/compile/internal/types2/validtype.go @@ -141,8 +141,8 @@ func (env *tparamEnv) push(typ *Named) *tparamEnv { } // TODO(gri) Alternative implementation: -// We may not need to build a stack of environments to -// look up the type arguments for type parameters. The -// same information should be available via the path: -// We should be able to just walk the path backwards -// and find the type arguments in the instance objects. +// We may not need to build a stack of environments to +// look up the type arguments for type parameters. The +// same information should be available via the path: +// We should be able to just walk the path backwards +// and find the type arguments in the instance objects. diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index b25627bd22..7ec5494d99 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -125,15 +125,14 @@ func walkClose(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { // Lower copy(a, b) to a memmove call or a runtime call. // -// init { -// n := len(a) -// if n > len(b) { n = len(b) } -// if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) } -// } -// n; +// init { +// n := len(a) +// if n > len(b) { n = len(b) } +// if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) } +// } +// n; // // Also works if b is a string. -// func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node { if n.X.Type().Elem().HasPointers() { ir.CurFunc.SetWBPos(n.Pos()) diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go index 6edff4fbba..72631e7dfb 100644 --- a/src/cmd/compile/internal/walk/convert.go +++ b/src/cmd/compile/internal/walk/convert.go @@ -411,7 +411,7 @@ func soleComponent(init *ir.Nodes, n ir.Node) ir.Node { // Treat blank fields as the zero value as the Go language requires. n = typecheck.Temp(n.Type().Field(0).Type) appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil)) - return n + continue } n = typecheck.Expr(ir.NewSelectorExpr(n.Pos(), ir.OXDOT, n, n.Type().Field(0).Sym)) case n.Type().IsArray(): diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index aa8c548963..dcf7a786e7 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -316,9 +316,9 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // isMapClear checks if n is of the form: // -// for k := range m { -// delete(m, k) -// } +// for k := range m { +// delete(m, k) +// } // // where == for keys of map m is reflexive. func isMapClear(n *ir.RangeStmt) bool { @@ -374,9 +374,9 @@ func mapClear(m ir.Node) ir.Node { // fast zeroing of slices and arrays (issue 5373). // Look for instances of // -// for i := range a { -// a[i] = zero -// } +// for i := range a { +// a[i] = zero +// } // // in which the evaluation of a is side-effect-free. // diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go index 86c95d15c5..8bd31514a0 100644 --- a/src/cmd/cover/cover_test.go +++ b/src/cmd/cover/cover_test.go @@ -165,7 +165,6 @@ func buildCover(t *testing.T) { // go build -o testcover // testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go // go run ./testdata/main.go ./testdata/test.go -// func TestCover(t *testing.T) { t.Parallel() testenv.MustHaveGoRun(t) diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 4dfaf83ef7..db2ac1f2a6 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -316,34 +316,6 @@ func chomp(s string) string { return strings.TrimRight(s, " \t\r\n") } -func branchtag(branch string) (tag string, precise bool) { - log := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch) - tag = branch - for row, line := range strings.Split(log, "\n") { - // Each line is either blank, or looks like - // (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4) - // We need to find an element starting with refs/tags/. - const s = " refs/tags/" - i := strings.Index(line, s) - if i < 0 { - continue - } - // Trim off known prefix. - line = line[i+len(s):] - // The tag name ends at a comma or paren. - j := strings.IndexAny(line, ",)") - if j < 0 { - continue // malformed line; ignore it - } - tag = line[:j] - if row == 0 { - precise = true // tag denotes HEAD - } - break - } - return -} - // findgoversion determines the Go version to use in the version string. func findgoversion() string { // The $GOROOT/VERSION file takes priority, for distributions @@ -395,42 +367,26 @@ func findgoversion() string { } // Otherwise, use Git. - // What is the current branch? - branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD")) - - // What are the tags along the current branch? - tag := "devel" - precise := false - - // If we're on a release branch, use the closest matching tag - // that is on the release branch (and not on the master branch). - if strings.HasPrefix(branch, "release-branch.") { - tag, precise = branchtag(branch) - } - - if !precise { - // Tag does not point at HEAD; add 1.x base version, hash, and date to version. - // - // Note that we lightly parse internal/goversion/goversion.go to - // obtain the base version. We can't just import the package, - // because cmd/dist is built with a bootstrap GOROOT which could - // be an entirely different version of Go, like 1.4. We assume - // that the file contains "const Version = ". - - goversionSource := readfile(pathf("%s/src/internal/goversion/goversion.go", goroot)) - m := regexp.MustCompile(`(?m)^const Version = (\d+)`).FindStringSubmatch(goversionSource) - if m == nil { - fatalf("internal/goversion/goversion.go does not contain 'const Version = ...'") - } - tag += fmt.Sprintf(" go1.%s-", m[1]) - - tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format:%h %cd", "HEAD")) + // + // Include 1.x base version, hash, and date in the version. + // + // Note that we lightly parse internal/goversion/goversion.go to + // obtain the base version. We can't just import the package, + // because cmd/dist is built with a bootstrap GOROOT which could + // be an entirely different version of Go, like 1.4. We assume + // that the file contains "const Version = ". + goversionSource := readfile(pathf("%s/src/internal/goversion/goversion.go", goroot)) + m := regexp.MustCompile(`(?m)^const Version = (\d+)`).FindStringSubmatch(goversionSource) + if m == nil { + fatalf("internal/goversion/goversion.go does not contain 'const Version = ...'") } + version := fmt.Sprintf("devel go1.%s-", m[1]) + version += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format:%h %cd", "HEAD")) // Cache version. - writefile(tag, path, 0) + writefile(version, path, 0) - return tag + return version } // isGitRepo reports whether the working directory is inside a Git repository. @@ -776,6 +732,8 @@ func runInstall(pkg string, ch chan struct{}) { pathf("%s/src/runtime/funcdata.h", goroot), 0) copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot), pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0) + copyfile(pathf("%s/pkg/include/asm_amd64.h", goroot), + pathf("%s/src/runtime/asm_amd64.h", goroot), 0) } // Generate any missing files; regenerate existing ones. diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go index caafc13da8..520dde7050 100644 --- a/src/cmd/dist/buildgo.go +++ b/src/cmd/dist/buildgo.go @@ -97,7 +97,7 @@ func mkzosarch(dir, file string) { // mkzcgo writes zcgo.go for the go/build package: // // package build -// var cgoEnabled = map[string]bool{} +// var cgoEnabled = map[string]bool{} // // It is invoked to write go/build/zcgo.go. func mkzcgo(dir, file string) { diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go index fdc1d25774..e66a9f009f 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -20,7 +20,6 @@ import ( // package sys // // const StackGuardMultiplier = -// func mkzversion(dir, file string) { var buf bytes.Buffer fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") @@ -80,7 +79,6 @@ func mkbuildcfg(file string) { // package objabi // // const stackGuardMultiplierDefault = -// func mkobjabi(file string) { var buf bytes.Buffer fmt.Fprintf(&buf, "// Code generated by go tool dist; DO NOT EDIT.\n") diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index a540a2abda..9118c133e5 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -38,6 +38,9 @@ func cmdtest() { flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"), "run only those tests matching the regular expression; empty means to run all. "+ "Special exception: if the string begins with '!', the match is inverted.") + flag.BoolVar(&t.msan, "msan", false, "run in memory sanitizer builder mode") + flag.BoolVar(&t.asan, "asan", false, "run in address sanitizer builder mode") + xflagparse(-1) // any number of args if noRebuild { t.rebuild = false @@ -49,6 +52,8 @@ func cmdtest() { // tester executes cmdtest. type tester struct { race bool + msan bool + asan bool listMode bool rebuild bool failed bool @@ -404,6 +409,12 @@ func (t *tester) registerStdTest(pkg string) { if t.race { args = append(args, "-race") } + if t.msan { + args = append(args, "-msan") + } + if t.asan { + args = append(args, "-asan") + } if t.compileOnly { args = append(args, "-run=^$") } diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 84fef6db77..b4387dc078 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -1567,7 +1567,7 @@ func TestCgoHandlesWlORIGIN(t *testing.T) { defer tg.cleanup() tg.parallel() tg.tempFile("src/origin/origin.go", `package origin - // #cgo !darwin LDFLAGS: -Wl,-rpath,$ORIGIN + // #cgo !darwin,!windows LDFLAGS: -Wl,-rpath,$ORIGIN // void f(void) {} import "C" func f() { C.f() }`) diff --git a/src/cmd/go/internal/base/signal_unix.go b/src/cmd/go/internal/base/signal_unix.go index 657ff38584..f198df6abc 100644 --- a/src/cmd/go/internal/base/signal_unix.go +++ b/src/cmd/go/internal/base/signal_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || js || linux || netbsd || openbsd || solaris +//go:build unix || js package base diff --git a/src/cmd/go/internal/cache/cache.go b/src/cmd/go/internal/cache/cache.go index 93d7c25658..4ac2b818ff 100644 --- a/src/cmd/go/internal/cache/cache.go +++ b/src/cmd/go/internal/cache/cache.go @@ -47,7 +47,6 @@ type Cache struct { // to share a cache directory (for example, if the directory were stored // in a network file system). File locking is notoriously unreliable in // network file systems and may not suffice to protect the cache. -// func Open(dir string) (*Cache, error) { info, err := os.Stat(dir) if err != nil { diff --git a/src/cmd/go/internal/imports/build.go b/src/cmd/go/internal/imports/build.go index ff6bea6777..10e90fc216 100644 --- a/src/cmd/go/internal/imports/build.go +++ b/src/cmd/go/internal/imports/build.go @@ -66,7 +66,6 @@ func isGoBuildComment(line []byte) bool { // the purpose of satisfying build tags, in order to estimate // (conservatively) whether a file could ever possibly be used // in any build. -// func ShouldBuild(content []byte, tags map[string]bool) bool { // Identify leading run of // comments and blank lines, // which must be followed by a blank line. diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go index 491bec39af..c18dbdf850 100644 --- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go +++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !plan9 && !solaris && !windows +//go:build !unix && !plan9 && !windows package filelock diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index d4847efb98..c170699535 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -2151,7 +2151,6 @@ func (ld *loader) buildStacks() { // other2 tested by // other2.test imports // pkg -// func (pkg *loadPkg) stackText() string { var stack []*loadPkg for p := pkg; p != nil; p = p.stack { diff --git a/src/cmd/go/internal/modload/stat_unix.go b/src/cmd/go/internal/modload/stat_unix.go index 8a3653ba80..a0d5f4d247 100644 --- a/src/cmd/go/internal/modload/stat_unix.go +++ b/src/cmd/go/internal/modload/stat_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package modload diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go index 3551a5997c..8a55e9cca0 100644 --- a/src/cmd/go/internal/vet/vetflag.go +++ b/src/cmd/go/internal/vet/vetflag.go @@ -35,7 +35,6 @@ import ( // implementation. It is also used by tests. // // The default behavior (vetTool=="") runs 'go tool vet'. -// var vetTool string // -vettool func init() { diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 6d6837aa8a..4252209f10 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -1948,7 +1948,6 @@ func mayberemovefile(s string) { // // fmtcmd replaces the name of the current directory with dot (.) // but only when it is at the beginning of a space-separated token. -// func (b *Builder) fmtcmd(dir string, format string, args ...any) string { cmd := fmt.Sprintf(format, args...) if dir != "" && dir != "/" { @@ -2005,7 +2004,6 @@ func (b *Builder) Showcmd(dir string, format string, args ...any) { // // If a is not nil and a.output is not nil, showOutput appends to that slice instead of // printing to b.Print. -// func (b *Builder) showOutput(a *Action, dir, desc, out string) { prefix := "# " + desc suffix := "\n" + out diff --git a/src/cmd/go/internal/workcmd/use.go b/src/cmd/go/internal/workcmd/use.go index e20041f79f..6da64b3f09 100644 --- a/src/cmd/go/internal/workcmd/use.go +++ b/src/cmd/go/internal/workcmd/use.go @@ -12,7 +12,6 @@ import ( "cmd/go/internal/modload" "cmd/go/internal/str" "context" - "errors" "fmt" "io/fs" "os" @@ -85,13 +84,14 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) { lookDir := func(dir string) { absDir, dir := pathRel(workDir, dir) - fi, err := os.Stat(filepath.Join(absDir, "go.mod")) + fi, err := fsys.Stat(filepath.Join(absDir, "go.mod")) if err != nil { if os.IsNotExist(err) { keepDirs[absDir] = "" - return + } else { + base.Errorf("go: %v", err) } - base.Errorf("go: %v", err) + return } if !fi.Mode().IsRegular() { @@ -108,13 +108,33 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) { base.Fatalf("go: 'go work use' requires one or more directory arguments") } for _, useDir := range args { + absArg, _ := pathRel(workDir, useDir) + + info, err := fsys.Stat(absArg) + if err != nil { + // Errors raised from os.Stat are formatted to be more user-friendly. + if os.IsNotExist(err) { + base.Errorf("go: directory %v does not exist", absArg) + } else { + base.Errorf("go: %v", err) + } + continue + } else if !info.IsDir() { + base.Errorf("go: %s is not a directory", absArg) + continue + } + if !*useR { lookDir(useDir) continue } // Add or remove entries for any subdirectories that still exist. - err := fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error { + fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { if info.Mode()&fs.ModeSymlink != 0 { if target, err := fsys.Stat(path); err == nil && target.IsDir() { @@ -126,13 +146,9 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) { lookDir(path) return nil }) - if err != nil && !errors.Is(err, os.ErrNotExist) { - base.Errorf("go: %v", err) - } // Remove entries for subdirectories that no longer exist. // Because they don't exist, they will be skipped by Walk. - absArg, _ := pathRel(workDir, useDir) for absDir, _ := range haveDirs { if str.HasFilePathPrefix(absDir, absArg) { if _, ok := keepDirs[absDir]; !ok { diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go index bffbe32220..76c542f32a 100644 --- a/src/cmd/go/script_test.go +++ b/src/cmd/go/script_test.go @@ -490,7 +490,6 @@ func isCaseSensitive(t *testing.T) bool { // Keep list and the implementations below sorted by name. // // NOTE: If you make changes here, update testdata/script/README too! -// var scriptCmds = map[string]func(*testScript, simpleStatus, []string){ "addcrlf": (*testScript).cmdAddcrlf, "cc": (*testScript).cmdCc, @@ -1373,7 +1372,9 @@ type command struct { // parse parses a single line as a list of space-separated arguments // subject to environment variable expansion (but not resplitting). // Single quotes around text disable splitting and expansion. -// To embed a single quote, double it: 'Don''t communicate by sharing memory.' +// To embed a single quote, double it: +// +// 'Don''t communicate by sharing memory.' func (ts *testScript) parse(line string) command { ts.line = line diff --git a/src/cmd/go/stop_other_test.go b/src/cmd/go/stop_other_test.go index 35c12858c1..cb4569b91d 100644 --- a/src/cmd/go/stop_other_test.go +++ b/src/cmd/go/stop_other_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !(aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris) +//go:build !(unix || (js && wasm)) package main_test diff --git a/src/cmd/go/stop_unix_test.go b/src/cmd/go/stop_unix_test.go index 5939f0d40d..baa1427465 100644 --- a/src/cmd/go/stop_unix_test.go +++ b/src/cmd/go/stop_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package main_test diff --git a/src/cmd/go/testdata/script/mod_dot.txt b/src/cmd/go/testdata/script/mod_dot.txt index cb60e988b6..aa24986c72 100644 --- a/src/cmd/go/testdata/script/mod_dot.txt +++ b/src/cmd/go/testdata/script/mod_dot.txt @@ -1,3 +1,4 @@ +env GOWORK=off env GO111MODULE=on # golang.org/issue/32917 and golang.org/issue/28459: 'go build' and 'go test' diff --git a/src/cmd/go/testdata/script/test_chatty_parallel_success_run.txt b/src/cmd/go/testdata/script/test_chatty_parallel_success_run.txt new file mode 100644 index 0000000000..4e0f239a66 --- /dev/null +++ b/src/cmd/go/testdata/script/test_chatty_parallel_success_run.txt @@ -0,0 +1,41 @@ +# Run parallel chatty tests. Assert on CONT lines. This test makes sure that +# multiple parallel outputs have the appropriate CONT lines between them. +go test -parallel 3 chatty_parallel -v + +stdout '=== RUN TestInterruptor/interruption\n=== CONT TestLog\n chatty_parallel_test.go:28: this is the second TestLog log\n--- PASS: Test(Log|Interruptor) \([0-9.]{4}s\)' + +-- go.mod -- +module chatty_parallel + +go 1.18 +-- chatty_parallel_test.go -- +package chatty_parallel_test + +import ( + "testing" +) + +var ( + afterFirstLog = make(chan struct{}) + afterSubTest = make(chan struct{}) + afterSecondLog = make(chan struct{}) +) + +func TestInterruptor(t *testing.T) { + t.Parallel() + + <-afterFirstLog + t.Run("interruption", func (t *testing.T) {}) + close(afterSubTest) + <-afterSecondLog // Delay the "PASS: TestInterruptor" line until after "CONT TestLog". +} + +func TestLog(t *testing.T) { + t.Parallel() + + t.Logf("this is the first TestLog log") + close(afterFirstLog) + <-afterSubTest + t.Logf("this is the second TestLog log") + close(afterSecondLog) +} diff --git a/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt b/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt deleted file mode 100644 index e651a7ed24..0000000000 --- a/src/cmd/go/testdata/script/test_chatty_parallel_success_sleepy.txt +++ /dev/null @@ -1,39 +0,0 @@ -# Run parallel chatty tests. Assert on CONT lines. This test makes sure that -# multiple parallel outputs have the appropriate CONT lines between them. -go test -parallel 3 chatty_parallel_test.go -v - -stdout '--- PASS: TestFast \([0-9.]{4}s\)\n=== CONT TestSlow\n chatty_parallel_test.go:31: this is the second TestSlow log\n--- PASS: TestSlow \([0-9.]{4}s\)' - --- chatty_parallel_test.go -- -package chatty_parallel_test - -import ( - "testing" - "time" -) - -var ( - run = make(chan struct{}) - afterFirstLog = make(chan struct{}) - afterPass = make(chan struct{}) -) - -func TestFast(t *testing.T) { - t.Parallel() - - <-afterFirstLog - t.Cleanup(func() { - close(afterPass) - }) -} - -func TestSlow(t *testing.T) { - t.Parallel() - - t.Logf("this is the first TestSlow log") - close(afterFirstLog) - - <-afterPass - time.Sleep(100 * time.Millisecond) - t.Logf("this is the second TestSlow log") -} diff --git a/src/cmd/go/testdata/script/work_use_only_dirs.txt b/src/cmd/go/testdata/script/work_use_only_dirs.txt new file mode 100644 index 0000000000..aa6dd78a6a --- /dev/null +++ b/src/cmd/go/testdata/script/work_use_only_dirs.txt @@ -0,0 +1,17 @@ +! go work use foo bar baz + +stderr '^go: '$WORK'[/\\]gopath[/\\]src[/\\]foo is not a directory' +stderr '^go: directory '$WORK'[/\\]gopath[/\\]src[/\\]baz does not exist' +cmp go.work go.work_want + +! go work use -r qux +stderr '^go: '$WORK'[/\\]gopath[/\\]src[/\\]qux is not a directory' + +-- go.work -- +go 1.18 +-- go.work_want -- +go 1.18 +-- foo -- +-- qux -- +-- bar/go.mod -- +module bar \ No newline at end of file diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go index ff225bedd7..af2a0df338 100644 --- a/src/cmd/internal/goobj/objfile.go +++ b/src/cmd/internal/goobj/objfile.go @@ -264,15 +264,15 @@ func (p *ImportedPkg) Write(w *Writer) { // Symbol definition. // // Serialized format: -// Sym struct { -// Name string -// ABI uint16 -// Type uint8 -// Flag uint8 -// Flag2 uint8 -// Siz uint32 -// Align uint32 -// } +// Sym struct { +// Name string +// ABI uint16 +// Type uint8 +// Flag uint8 +// Flag2 uint8 +// Siz uint32 +// Align uint32 +// } type Sym [SymSize]byte const SymSize = stringRefSize + 2 + 1 + 1 + 1 + 4 + 4 @@ -371,13 +371,13 @@ const HashSize = sha1.Size // Relocation. // // Serialized format: -// Reloc struct { -// Off int32 -// Siz uint8 -// Type uint16 -// Add int64 -// Sym SymRef -// } +// Reloc struct { +// Off int32 +// Siz uint8 +// Type uint16 +// Add int64 +// Sym SymRef +// } type Reloc [RelocSize]byte const RelocSize = 4 + 1 + 2 + 8 + 8 @@ -415,10 +415,10 @@ func (r *Reloc) fromBytes(b []byte) { copy(r[:], b) } // Aux symbol info. // // Serialized format: -// Aux struct { -// Type uint8 -// Sym SymRef -// } +// Aux struct { +// Type uint8 +// Sym SymRef +// } type Aux [AuxSize]byte const AuxSize = 1 + 8 @@ -458,11 +458,11 @@ func (a *Aux) fromBytes(b []byte) { copy(a[:], b) } // Referenced symbol flags. // // Serialized format: -// RefFlags struct { -// Sym symRef -// Flag uint8 -// Flag2 uint8 -// } +// RefFlags struct { +// Sym symRef +// Flag uint8 +// Flag2 uint8 +// } type RefFlags [RefFlagsSize]byte const RefFlagsSize = 8 + 1 + 1 @@ -490,10 +490,10 @@ const huge = (1<<31 - 1) / RelocSize // Referenced symbol name. // // Serialized format: -// RefName struct { -// Sym symRef -// Name string -// } +// RefName struct { +// Sym symRef +// Name string +// } type RefName [RefNameSize]byte const RefNameSize = 8 + stringRefSize diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go index a63ac71a16..053cb8f548 100644 --- a/src/cmd/internal/moddeps/moddeps_test.go +++ b/src/cmd/internal/moddeps/moddeps_test.go @@ -55,7 +55,7 @@ func TestAllDependencies(t *testing.T) { // dependencies are vendored. If any imported package is missing, // 'go list -deps' will fail when attempting to load it. cmd := exec.Command(goBin, "list", "-mod=vendor", "-deps", "./...") - cmd.Env = append(os.Environ(), "GO111MODULE=on") + cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOWORK=off") cmd.Dir = m.Dir cmd.Stderr = new(strings.Builder) _, err := cmd.Output() @@ -69,7 +69,7 @@ func TestAllDependencies(t *testing.T) { // There is no vendor directory, so the module must have no dependencies. // Check that the list of active modules contains only the main module. cmd := exec.Command(goBin, "list", "-mod=readonly", "-m", "all") - cmd.Env = append(os.Environ(), "GO111MODULE=on") + cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOWORK=off") cmd.Dir = m.Dir cmd.Stderr = new(strings.Builder) out, err := cmd.Output() @@ -197,6 +197,7 @@ func TestAllDependencies(t *testing.T) { // Add GOROOTcopy/bin and bundleDir to front of PATH. "PATH="+filepath.Join(gorootCopyDir, "bin")+string(filepath.ListSeparator)+ bundleDir+string(filepath.ListSeparator)+os.Getenv("PATH"), + "GOWORK=off", ), } goBinCopy := filepath.Join(gorootCopyDir, "bin", "go") @@ -463,7 +464,7 @@ func findGorootModules(t *testing.T) []gorootModule { // Use 'go list' to describe the module contained in this directory (but // not its dependencies). cmd := exec.Command(goBin, "list", "-json", "-m") - cmd.Env = append(os.Environ(), "GO111MODULE=on") + cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOWORK=off") cmd.Dir = dir cmd.Stderr = new(strings.Builder) out, err := cmd.Output() diff --git a/src/cmd/internal/obj/addrtype_string.go b/src/cmd/internal/obj/addrtype_string.go index 71f0dd97a8..e6277d39b0 100644 --- a/src/cmd/internal/obj/addrtype_string.go +++ b/src/cmd/internal/obj/addrtype_string.go @@ -4,9 +4,30 @@ package obj import "strconv" -const _AddrType_name = "TYPE_NONETYPE_BRANCHTYPE_TEXTSIZETYPE_MEMTYPE_CONSTTYPE_FCONSTTYPE_SCONSTTYPE_REGTYPE_ADDRTYPE_SHIFTTYPE_REGREGTYPE_REGREG2TYPE_INDIRTYPE_REGLIST" +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[TYPE_NONE-0] + _ = x[TYPE_BRANCH-1] + _ = x[TYPE_TEXTSIZE-2] + _ = x[TYPE_MEM-3] + _ = x[TYPE_CONST-4] + _ = x[TYPE_FCONST-5] + _ = x[TYPE_SCONST-6] + _ = x[TYPE_REG-7] + _ = x[TYPE_ADDR-8] + _ = x[TYPE_SHIFT-9] + _ = x[TYPE_REGREG-10] + _ = x[TYPE_REGREG2-11] + _ = x[TYPE_INDIR-12] + _ = x[TYPE_REGLIST-13] + _ = x[TYPE_SPECIAL-14] +} -var _AddrType_index = [...]uint8{0, 9, 20, 33, 41, 51, 62, 73, 81, 90, 100, 111, 123, 133, 145} +const _AddrType_name = "TYPE_NONETYPE_BRANCHTYPE_TEXTSIZETYPE_MEMTYPE_CONSTTYPE_FCONSTTYPE_SCONSTTYPE_REGTYPE_ADDRTYPE_SHIFTTYPE_REGREGTYPE_REGREG2TYPE_INDIRTYPE_REGLISTTYPE_SPECIAL" + +var _AddrType_index = [...]uint8{0, 9, 20, 33, 41, 51, 62, 73, 81, 90, 100, 111, 123, 133, 145, 157} func (i AddrType) String() string { if i >= AddrType(len(_AddrType_index)-1) { diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index f3480e0f5e..d6522f5738 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -143,26 +143,6 @@ const ( REG_V30 REG_V31 - // The EQ in - // CSET EQ, R0 - // is encoded as TYPE_REG, even though it's not really a register. - COND_EQ - COND_NE - COND_HS - COND_LO - COND_MI - COND_PL - COND_VS - COND_VC - COND_HI - COND_LS - COND_GE - COND_LT - COND_GT - COND_LE - COND_AL - COND_NV - REG_RSP = REG_V31 + 32 // to differentiate ZR/SP, REG_RSP&0x1f = 31 ) @@ -197,28 +177,10 @@ const ( // a special register and the low bits select the register. // SYSREG_END is the last item in the automatically generated system register // declaration, and it is defined in the sysRegEnc.go file. +// Define the special register after REG_SPECIAL, the first value of it should be +// REG_{name} = SYSREG_END + iota. const ( REG_SPECIAL = obj.RBaseARM64 + 1<<12 - REG_DAIFSet = SYSREG_END + iota - REG_DAIFClr - REG_PLDL1KEEP - REG_PLDL1STRM - REG_PLDL2KEEP - REG_PLDL2STRM - REG_PLDL3KEEP - REG_PLDL3STRM - REG_PLIL1KEEP - REG_PLIL1STRM - REG_PLIL2KEEP - REG_PLIL2STRM - REG_PLIL3KEEP - REG_PLIL3STRM - REG_PSTL1KEEP - REG_PSTL1STRM - REG_PSTL2KEEP - REG_PSTL2STRM - REG_PSTL3KEEP - REG_PSTL3STRM ) // Register assignments: @@ -388,7 +350,8 @@ const ( C_SHIFT // Rn<<2 C_EXTREG // Rn.UXTB[<<3] C_SPR // REG_NZCV - C_COND // EQ, NE, etc + C_COND // condition code, EQ, NE, etc. + C_SPOP // special operand, PLDL1KEEP, VMALLE1IS, etc. C_ARNG // Vn. C_ELEM // Vn.[index] C_LIST // [V1, V2, V3] @@ -1085,3 +1048,164 @@ const ( ARNG_S ARNG_D ) + +//go:generate stringer -type SpecialOperand -trimprefix SPOP_ +type SpecialOperand int + +const ( + // PRFM + SPOP_PLDL1KEEP SpecialOperand = iota // must be the first one + SPOP_BEGIN SpecialOperand = iota - 1 // set as the lower bound + SPOP_PLDL1STRM + SPOP_PLDL2KEEP + SPOP_PLDL2STRM + SPOP_PLDL3KEEP + SPOP_PLDL3STRM + SPOP_PLIL1KEEP + SPOP_PLIL1STRM + SPOP_PLIL2KEEP + SPOP_PLIL2STRM + SPOP_PLIL3KEEP + SPOP_PLIL3STRM + SPOP_PSTL1KEEP + SPOP_PSTL1STRM + SPOP_PSTL2KEEP + SPOP_PSTL2STRM + SPOP_PSTL3KEEP + SPOP_PSTL3STRM + + // TLBI + SPOP_VMALLE1IS + SPOP_VAE1IS + SPOP_ASIDE1IS + SPOP_VAAE1IS + SPOP_VALE1IS + SPOP_VAALE1IS + SPOP_VMALLE1 + SPOP_VAE1 + SPOP_ASIDE1 + SPOP_VAAE1 + SPOP_VALE1 + SPOP_VAALE1 + SPOP_IPAS2E1IS + SPOP_IPAS2LE1IS + SPOP_ALLE2IS + SPOP_VAE2IS + SPOP_ALLE1IS + SPOP_VALE2IS + SPOP_VMALLS12E1IS + SPOP_IPAS2E1 + SPOP_IPAS2LE1 + SPOP_ALLE2 + SPOP_VAE2 + SPOP_ALLE1 + SPOP_VALE2 + SPOP_VMALLS12E1 + SPOP_ALLE3IS + SPOP_VAE3IS + SPOP_VALE3IS + SPOP_ALLE3 + SPOP_VAE3 + SPOP_VALE3 + SPOP_VMALLE1OS + SPOP_VAE1OS + SPOP_ASIDE1OS + SPOP_VAAE1OS + SPOP_VALE1OS + SPOP_VAALE1OS + SPOP_RVAE1IS + SPOP_RVAAE1IS + SPOP_RVALE1IS + SPOP_RVAALE1IS + SPOP_RVAE1OS + SPOP_RVAAE1OS + SPOP_RVALE1OS + SPOP_RVAALE1OS + SPOP_RVAE1 + SPOP_RVAAE1 + SPOP_RVALE1 + SPOP_RVAALE1 + SPOP_RIPAS2E1IS + SPOP_RIPAS2LE1IS + SPOP_ALLE2OS + SPOP_VAE2OS + SPOP_ALLE1OS + SPOP_VALE2OS + SPOP_VMALLS12E1OS + SPOP_RVAE2IS + SPOP_RVALE2IS + SPOP_IPAS2E1OS + SPOP_RIPAS2E1 + SPOP_RIPAS2E1OS + SPOP_IPAS2LE1OS + SPOP_RIPAS2LE1 + SPOP_RIPAS2LE1OS + SPOP_RVAE2OS + SPOP_RVALE2OS + SPOP_RVAE2 + SPOP_RVALE2 + SPOP_ALLE3OS + SPOP_VAE3OS + SPOP_VALE3OS + SPOP_RVAE3IS + SPOP_RVALE3IS + SPOP_RVAE3OS + SPOP_RVALE3OS + SPOP_RVAE3 + SPOP_RVALE3 + + // DC + SPOP_IVAC + SPOP_ISW + SPOP_CSW + SPOP_CISW + SPOP_ZVA + SPOP_CVAC + SPOP_CVAU + SPOP_CIVAC + SPOP_IGVAC + SPOP_IGSW + SPOP_IGDVAC + SPOP_IGDSW + SPOP_CGSW + SPOP_CGDSW + SPOP_CIGSW + SPOP_CIGDSW + SPOP_GVA + SPOP_GZVA + SPOP_CGVAC + SPOP_CGDVAC + SPOP_CGVAP + SPOP_CGDVAP + SPOP_CGVADP + SPOP_CGDVADP + SPOP_CIGVAC + SPOP_CIGDVAC + SPOP_CVAP + SPOP_CVADP + + // PSTATE fields + SPOP_DAIFSet + SPOP_DAIFClr + + // Condition code, EQ, NE, etc. Their relative order to EQ is matter. + SPOP_EQ + SPOP_NE + SPOP_HS + SPOP_LO + SPOP_MI + SPOP_PL + SPOP_VS + SPOP_VC + SPOP_HI + SPOP_LS + SPOP_GE + SPOP_LT + SPOP_GT + SPOP_LE + SPOP_AL + SPOP_NV + // Condition code end. + + SPOP_END +) diff --git a/src/cmd/internal/obj/arm64/anames7.go b/src/cmd/internal/obj/arm64/anames7.go index 2ecd8164b6..54fc939c01 100644 --- a/src/cmd/internal/obj/arm64/anames7.go +++ b/src/cmd/internal/obj/arm64/anames7.go @@ -15,6 +15,7 @@ var cnames7 = []string{ "SHIFT", "EXTREG", "SPR", + "SPOP", "COND", "ARNG", "ELEM", diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 244430eb8f..72c4cd48ed 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -838,13 +838,16 @@ var optab = []Optab{ {AMSR, C_REG, C_NONE, C_NONE, C_SPR, 36, 4, 0, 0, 0}, {AMOVD, C_VCON, C_NONE, C_NONE, C_SPR, 37, 4, 0, 0, 0}, {AMSR, C_VCON, C_NONE, C_NONE, C_SPR, 37, 4, 0, 0, 0}, - {APRFM, C_UOREG32K, C_NONE, C_NONE, C_SPR, 91, 4, 0, 0, 0}, + {AMSR, C_VCON, C_NONE, C_NONE, C_SPOP, 37, 4, 0, 0, 0}, + {APRFM, C_UOREG32K, C_NONE, C_NONE, C_SPOP, 91, 4, 0, 0, 0}, {APRFM, C_UOREG32K, C_NONE, C_NONE, C_LCON, 91, 4, 0, 0, 0}, {ADMB, C_VCON, C_NONE, C_NONE, C_NONE, 51, 4, 0, 0, 0}, {AHINT, C_VCON, C_NONE, C_NONE, C_NONE, 52, 4, 0, 0, 0}, {ASYS, C_VCON, C_NONE, C_NONE, C_NONE, 50, 4, 0, 0, 0}, - {ASYS, C_VCON, C_REG, C_NONE, C_NONE, 50, 4, 0, 0, 0}, + {ASYS, C_VCON, C_NONE, C_NONE, C_REG, 50, 4, 0, 0, 0}, {ASYSL, C_VCON, C_NONE, C_NONE, C_REG, 50, 4, 0, 0, 0}, + {ATLBI, C_SPOP, C_NONE, C_NONE, C_NONE, 107, 4, 0, 0, 0}, + {ATLBI, C_SPOP, C_NONE, C_NONE, C_REG, 107, 4, 0, 0, 0}, /* encryption instructions */ {AAESD, C_VREG, C_NONE, C_NONE, C_VREG, 29, 4, 0, 0, 0}, // for compatibility with old code @@ -873,40 +876,157 @@ var optab = []Optab{ {obj.AXXX, C_NONE, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0, 0}, } -/* - * valid pstate field values, and value to use in instruction - */ +// Valid pstate field values, and value to use in instruction. +// Doesn't include special registers. var pstatefield = []struct { - reg int16 + opd SpecialOperand enc uint32 }{ - {REG_SPSel, 0<<16 | 4<<12 | 5<<5}, - {REG_DAIFSet, 3<<16 | 4<<12 | 6<<5}, - {REG_DAIFClr, 3<<16 | 4<<12 | 7<<5}, + {SPOP_DAIFSet, 3<<16 | 4<<12 | 6<<5}, + {SPOP_DAIFClr, 3<<16 | 4<<12 | 7<<5}, } -var prfopfield = []struct { - reg int16 - enc uint32 +var prfopfield = map[SpecialOperand]uint32{ + SPOP_PLDL1KEEP: 0, + SPOP_PLDL1STRM: 1, + SPOP_PLDL2KEEP: 2, + SPOP_PLDL2STRM: 3, + SPOP_PLDL3KEEP: 4, + SPOP_PLDL3STRM: 5, + SPOP_PLIL1KEEP: 8, + SPOP_PLIL1STRM: 9, + SPOP_PLIL2KEEP: 10, + SPOP_PLIL2STRM: 11, + SPOP_PLIL3KEEP: 12, + SPOP_PLIL3STRM: 13, + SPOP_PSTL1KEEP: 16, + SPOP_PSTL1STRM: 17, + SPOP_PSTL2KEEP: 18, + SPOP_PSTL2STRM: 19, + SPOP_PSTL3KEEP: 20, + SPOP_PSTL3STRM: 21, +} + +// sysInstFields helps convert SYS alias instructions to SYS instructions. +// For example, the format of TLBI is: TLBI {, }. +// It's equivalent to: SYS #, C8, , #{, }. +// The field hasOperand2 indicates whether Xt is required. It helps to check +// some combinations that may be undefined, such as TLBI VMALLE1IS, R0. +var sysInstFields = map[SpecialOperand]struct { + op1 uint8 + cn uint8 + cm uint8 + op2 uint8 + hasOperand2 bool }{ - {REG_PLDL1KEEP, 0}, - {REG_PLDL1STRM, 1}, - {REG_PLDL2KEEP, 2}, - {REG_PLDL2STRM, 3}, - {REG_PLDL3KEEP, 4}, - {REG_PLDL3STRM, 5}, - {REG_PLIL1KEEP, 8}, - {REG_PLIL1STRM, 9}, - {REG_PLIL2KEEP, 10}, - {REG_PLIL2STRM, 11}, - {REG_PLIL3KEEP, 12}, - {REG_PLIL3STRM, 13}, - {REG_PSTL1KEEP, 16}, - {REG_PSTL1STRM, 17}, - {REG_PSTL2KEEP, 18}, - {REG_PSTL2STRM, 19}, - {REG_PSTL3KEEP, 20}, - {REG_PSTL3STRM, 21}, + // TLBI + SPOP_VMALLE1IS: {0, 8, 3, 0, false}, + SPOP_VAE1IS: {0, 8, 3, 1, true}, + SPOP_ASIDE1IS: {0, 8, 3, 2, true}, + SPOP_VAAE1IS: {0, 8, 3, 3, true}, + SPOP_VALE1IS: {0, 8, 3, 5, true}, + SPOP_VAALE1IS: {0, 8, 3, 7, true}, + SPOP_VMALLE1: {0, 8, 7, 0, false}, + SPOP_VAE1: {0, 8, 7, 1, true}, + SPOP_ASIDE1: {0, 8, 7, 2, true}, + SPOP_VAAE1: {0, 8, 7, 3, true}, + SPOP_VALE1: {0, 8, 7, 5, true}, + SPOP_VAALE1: {0, 8, 7, 7, true}, + SPOP_IPAS2E1IS: {4, 8, 0, 1, true}, + SPOP_IPAS2LE1IS: {4, 8, 0, 5, true}, + SPOP_ALLE2IS: {4, 8, 3, 0, false}, + SPOP_VAE2IS: {4, 8, 3, 1, true}, + SPOP_ALLE1IS: {4, 8, 3, 4, false}, + SPOP_VALE2IS: {4, 8, 3, 5, true}, + SPOP_VMALLS12E1IS: {4, 8, 3, 6, false}, + SPOP_IPAS2E1: {4, 8, 4, 1, true}, + SPOP_IPAS2LE1: {4, 8, 4, 5, true}, + SPOP_ALLE2: {4, 8, 7, 0, false}, + SPOP_VAE2: {4, 8, 7, 1, true}, + SPOP_ALLE1: {4, 8, 7, 4, false}, + SPOP_VALE2: {4, 8, 7, 5, true}, + SPOP_VMALLS12E1: {4, 8, 7, 6, false}, + SPOP_ALLE3IS: {6, 8, 3, 0, false}, + SPOP_VAE3IS: {6, 8, 3, 1, true}, + SPOP_VALE3IS: {6, 8, 3, 5, true}, + SPOP_ALLE3: {6, 8, 7, 0, false}, + SPOP_VAE3: {6, 8, 7, 1, true}, + SPOP_VALE3: {6, 8, 7, 5, true}, + SPOP_VMALLE1OS: {0, 8, 1, 0, false}, + SPOP_VAE1OS: {0, 8, 1, 1, true}, + SPOP_ASIDE1OS: {0, 8, 1, 2, true}, + SPOP_VAAE1OS: {0, 8, 1, 3, true}, + SPOP_VALE1OS: {0, 8, 1, 5, true}, + SPOP_VAALE1OS: {0, 8, 1, 7, true}, + SPOP_RVAE1IS: {0, 8, 2, 1, true}, + SPOP_RVAAE1IS: {0, 8, 2, 3, true}, + SPOP_RVALE1IS: {0, 8, 2, 5, true}, + SPOP_RVAALE1IS: {0, 8, 2, 7, true}, + SPOP_RVAE1OS: {0, 8, 5, 1, true}, + SPOP_RVAAE1OS: {0, 8, 5, 3, true}, + SPOP_RVALE1OS: {0, 8, 5, 5, true}, + SPOP_RVAALE1OS: {0, 8, 5, 7, true}, + SPOP_RVAE1: {0, 8, 6, 1, true}, + SPOP_RVAAE1: {0, 8, 6, 3, true}, + SPOP_RVALE1: {0, 8, 6, 5, true}, + SPOP_RVAALE1: {0, 8, 6, 7, true}, + SPOP_RIPAS2E1IS: {4, 8, 0, 2, true}, + SPOP_RIPAS2LE1IS: {4, 8, 0, 6, true}, + SPOP_ALLE2OS: {4, 8, 1, 0, false}, + SPOP_VAE2OS: {4, 8, 1, 1, true}, + SPOP_ALLE1OS: {4, 8, 1, 4, false}, + SPOP_VALE2OS: {4, 8, 1, 5, true}, + SPOP_VMALLS12E1OS: {4, 8, 1, 6, false}, + SPOP_RVAE2IS: {4, 8, 2, 1, true}, + SPOP_RVALE2IS: {4, 8, 2, 5, true}, + SPOP_IPAS2E1OS: {4, 8, 4, 0, true}, + SPOP_RIPAS2E1: {4, 8, 4, 2, true}, + SPOP_RIPAS2E1OS: {4, 8, 4, 3, true}, + SPOP_IPAS2LE1OS: {4, 8, 4, 4, true}, + SPOP_RIPAS2LE1: {4, 8, 4, 6, true}, + SPOP_RIPAS2LE1OS: {4, 8, 4, 7, true}, + SPOP_RVAE2OS: {4, 8, 5, 1, true}, + SPOP_RVALE2OS: {4, 8, 5, 5, true}, + SPOP_RVAE2: {4, 8, 6, 1, true}, + SPOP_RVALE2: {4, 8, 6, 5, true}, + SPOP_ALLE3OS: {6, 8, 1, 0, false}, + SPOP_VAE3OS: {6, 8, 1, 1, true}, + SPOP_VALE3OS: {6, 8, 1, 5, true}, + SPOP_RVAE3IS: {6, 8, 2, 1, true}, + SPOP_RVALE3IS: {6, 8, 2, 5, true}, + SPOP_RVAE3OS: {6, 8, 5, 1, true}, + SPOP_RVALE3OS: {6, 8, 5, 5, true}, + SPOP_RVAE3: {6, 8, 6, 1, true}, + SPOP_RVALE3: {6, 8, 6, 5, true}, + // DC + SPOP_IVAC: {0, 7, 6, 1, true}, + SPOP_ISW: {0, 7, 6, 2, true}, + SPOP_CSW: {0, 7, 10, 2, true}, + SPOP_CISW: {0, 7, 14, 2, true}, + SPOP_ZVA: {3, 7, 4, 1, true}, + SPOP_CVAC: {3, 7, 10, 1, true}, + SPOP_CVAU: {3, 7, 11, 1, true}, + SPOP_CIVAC: {3, 7, 14, 1, true}, + SPOP_IGVAC: {0, 7, 6, 3, true}, + SPOP_IGSW: {0, 7, 6, 4, true}, + SPOP_IGDVAC: {0, 7, 6, 5, true}, + SPOP_IGDSW: {0, 7, 6, 6, true}, + SPOP_CGSW: {0, 7, 10, 4, true}, + SPOP_CGDSW: {0, 7, 10, 6, true}, + SPOP_CIGSW: {0, 7, 14, 4, true}, + SPOP_CIGDSW: {0, 7, 14, 6, true}, + SPOP_GVA: {3, 7, 4, 3, true}, + SPOP_GZVA: {3, 7, 4, 4, true}, + SPOP_CGVAC: {3, 7, 10, 3, true}, + SPOP_CGDVAC: {3, 7, 10, 5, true}, + SPOP_CGVAP: {3, 7, 12, 3, true}, + SPOP_CGDVAP: {3, 7, 12, 5, true}, + SPOP_CGVADP: {3, 7, 13, 3, true}, + SPOP_CGDVADP: {3, 7, 13, 5, true}, + SPOP_CIGVAC: {3, 7, 14, 3, true}, + SPOP_CIGDVAC: {3, 7, 14, 5, true}, + SPOP_CVAP: {3, 7, 12, 1, true}, + SPOP_CVADP: {3, 7, 13, 1, true}, } // Used for padinng NOOP instruction @@ -1676,8 +1796,6 @@ func rclass(r int16) int { return C_FREG case REG_V0 <= r && r <= REG_V31: return C_VREG - case COND_EQ <= r && r <= COND_NV: - return C_COND case r == REGSP: return C_RSP case r >= REG_ARNG && r < REG_ELEM: @@ -1953,8 +2071,14 @@ func (c *ctxt7) aclass(a *obj.Addr) int { case obj.TYPE_BRANCH: return C_SBRA - } + case obj.TYPE_SPECIAL: + opd := SpecialOperand(a.Offset) + if SPOP_EQ <= opd && opd <= SPOP_NV { + return C_COND + } + return C_SPOP + } return C_GOK } @@ -2868,9 +2992,10 @@ func buildop(ctxt *obj.Link) { case ASYS: oprangeset(AAT, t) - oprangeset(ADC, t) oprangeset(AIC, t) - oprangeset(ATLBI, t) + + case ATLBI: + oprangeset(ADC, t) case ASYSL, AHINT: break @@ -3526,12 +3651,11 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 18: /* csel cond,Rn,Rm,Rd; cinc/cinv/cneg cond,Rn,Rd; cset cond,Rd */ o1 = c.oprrr(p, p.As) - cond := int(p.From.Reg) - // AL and NV are not allowed for CINC/CINV/CNEG/CSET/CSETM instructions - if cond < COND_EQ || cond > COND_NV || (cond == COND_AL || cond == COND_NV) && p.From3Type() == obj.TYPE_NONE { + cond := SpecialOperand(p.From.Offset) + if cond < SPOP_EQ || cond > SPOP_NV || (cond == SPOP_AL || cond == SPOP_NV) && p.From3Type() == obj.TYPE_NONE { c.ctxt.Diag("invalid condition: %v", p) } else { - cond -= COND_EQ + cond -= SPOP_EQ } r := int(p.Reg) @@ -3554,11 +3678,11 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 19: /* CCMN cond, (Rm|uimm5),Rn, uimm4 -> ccmn Rn,Rm,uimm4,cond */ nzcv := int(p.To.Offset) - cond := int(p.From.Reg) - if cond < COND_EQ || cond > COND_NV { + cond := SpecialOperand(p.From.Offset) + if cond < SPOP_EQ || cond > SPOP_NV { c.ctxt.Diag("invalid condition\n%v", p) } else { - cond -= COND_EQ + cond -= SPOP_EQ } var rf int if p.GetFrom3().Type == obj.TYPE_REG { @@ -3919,10 +4043,16 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 = c.opirr(p, AMSR) o1 |= uint32((p.From.Offset & 0xF) << 8) /* Crm */ v := uint32(0) - for i := 0; i < len(pstatefield); i++ { - if pstatefield[i].reg == p.To.Reg { - v = pstatefield[i].enc - break + // PSTATEfield can be special registers and special operands. + if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SPSel { + v = 0<<16 | 4<<12 | 5<<5 + } else if p.To.Type == obj.TYPE_SPECIAL { + opd := SpecialOperand(p.To.Offset) + for _, pf := range pstatefield { + if pf.opd == opd { + v = pf.enc + break + } } } @@ -4133,8 +4263,6 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= uint32(p.From.Offset) if p.To.Type == obj.TYPE_REG { o1 |= uint32(p.To.Reg & 31) - } else if p.Reg != 0 { - o1 |= uint32(p.Reg & 31) } else { o1 |= 0x1F } @@ -4220,11 +4348,11 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 57: /* floating point conditional compare */ o1 = c.oprrr(p, p.As) - cond := int(p.From.Reg) - if cond < COND_EQ || cond > COND_NV { + cond := SpecialOperand(p.From.Offset) + if cond < SPOP_EQ || cond > SPOP_NV { c.ctxt.Diag("invalid condition\n%v", p) } else { - cond -= COND_EQ + cond -= SPOP_EQ } nzcv := int(p.To.Offset) @@ -4976,22 +5104,16 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 91: /* prfm imm(Rn), */ imm := uint32(p.From.Offset) r := p.From.Reg - v := uint32(0xff) + var v uint32 + var ok bool if p.To.Type == obj.TYPE_CONST { v = uint32(p.To.Offset) - if v > 31 { - c.ctxt.Diag("illegal prefetch operation\n%v", p) - } + ok = v <= 31 } else { - for i := 0; i < len(prfopfield); i++ { - if prfopfield[i].reg == p.To.Reg { - v = prfopfield[i].enc - break - } - } - if v == 0xff { - c.ctxt.Diag("illegal prefetch operation:\n%v", p) - } + v, ok = prfopfield[SpecialOperand(p.To.Offset)] + } + if !ok { + c.ctxt.Diag("illegal prefetch operation:\n%v", p) } o1 = c.opirr(p, p.As) @@ -5514,6 +5636,26 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { c.ctxt.Diag("illegal destination register: %v\n", p) } o1 |= enc | uint32(rs&31)<<16 | uint32(rb&31)<<5 | uint32(rt&31) + + case 107: /* tlbi, dc */ + op, ok := sysInstFields[SpecialOperand(p.From.Offset)] + if !ok || (p.As == ATLBI && op.cn != 8) || (p.As == ADC && op.cn != 7) { + c.ctxt.Diag("illegal argument: %v\n", p) + break + } + o1 = c.opirr(p, p.As) + if op.hasOperand2 { + if p.To.Reg == 0 { + c.ctxt.Diag("missing register at operand 2: %v\n", p) + } + o1 |= uint32(p.To.Reg & 0x1F) + } else { + if p.To.Reg != 0 || p.Reg != 0 { + c.ctxt.Diag("extraneous register at operand 2: %v\n", p) + } + o1 |= uint32(0x1F) + } + o1 |= uint32(SYSARG4(int(op.op1), int(op.cn), int(op.cm), int(op.op2))) } out[0] = o1 out[1] = o2 diff --git a/src/cmd/internal/obj/arm64/doc.go b/src/cmd/internal/obj/arm64/doc.go index 1234a3e818..2763cf4139 100644 --- a/src/cmd/internal/obj/arm64/doc.go +++ b/src/cmd/internal/obj/arm64/doc.go @@ -11,7 +11,7 @@ Instructions mnemonics mapping rules 1. Most instructions use width suffixes of instruction names to indicate operand width rather than using different register names. - Examples: +Examples: ADC R24, R14, R12 <=> adc x12, x24 ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24 FCMPS F2, F3 <=> fcmp s3, s2 @@ -20,7 +20,7 @@ using different register names. 2. Go uses .P and .W suffixes to indicate post-increment and pre-increment. - Examples: +Examples: MOVD.P -8(R10), R8 <=> ldr x8, [x10],#-8 MOVB.W 16(R16), R10 <=> ldrsb x10, [x16,#16]! MOVBU.W 16(R16), R10 <=> ldrb x10, [x16,#16]! @@ -39,7 +39,7 @@ ldrsh, sturh, strh => MOVH. 5. Go adds a V prefix for most floating-point and SIMD instructions, except cryptographic extension instructions and floating-point(scalar) instructions. - Examples: +Examples: VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h VLD1.P (R6)(R11), [V31.D1] <=> ld1 {v31.1d}, [x6], x11 VFMLA V29.S2, V20.S2, V14.S2 <=> fmla v14.2s, v20.2s, v29.2s @@ -52,7 +52,7 @@ Go asm supports the PCALIGN directive, which indicates that the next instruction to a specified boundary by padding with NOOP instruction. The alignment value supported on arm64 must be a power of 2 and in the range of [8, 2048]. - Examples: +Examples: PCALIGN $16 MOVD $2, R0 // This instruction is aligned with 16 bytes. PCALIGN $1024 @@ -63,7 +63,8 @@ its address will be aligned to the same or coarser boundary, which is the maximu alignment values. In the following example, the function Add is aligned with 128 bytes. - Examples: + +Examples: TEXT ·Add(SB),$40-16 MOVD $2, R0 PCALIGN $32 @@ -79,7 +80,7 @@ have the same alignment as the first hand-written instruction. In the following example, PCALIGN at the entry of the function Add will align its address to 2048 bytes. - Examples: +Examples: TEXT ·Add(SB),NOSPLIT|NOFRAME,$0 PCALIGN $2048 MOVD $1, R0 @@ -91,7 +92,7 @@ In the following example, PCALIGN at the entry of the function Add will align it Go asm uses VMOVQ/VMOVD/VMOVS to move 128-bit, 64-bit and 32-bit constants into vector registers, respectively. And for a 128-bit interger, it take two 64-bit operands, for the low and high parts separately. - Examples: +Examples: VMOVS $0x11223344, V0 VMOVD $0x1122334455667788, V1 VMOVQ $0x1122334455667788, $0x99aabbccddeeff00, V2 // V2=0x99aabbccddeeff001122334455667788 @@ -104,7 +105,7 @@ is the 16-bit unsigned immediate, in the range 0 to 65535; For the 32-bit varian The current Go assembler does not accept zero shifts, such as "op $0, Rd" and "op $(0<<(16|32|48)), Rd" instructions. - Examples: +Examples: MOVK $(10<<32), R20 <=> movk x20, #10, lsl #32 MOVZW $(20<<16), R8 <=> movz w8, #20, lsl #16 MOVK $(0<<16), R10 will be reported as an error by the assembler. @@ -121,7 +122,7 @@ Special Cases. related to real ARM64 instruction. NOOP serves for the hardware nop instruction. NOOP is an alias of HINT $0. - Examples: +Examples: VMOV V13.B[1], R20 <=> mov x20, v13.b[1] VMOV V13.H[1], R20 <=> mov w20, v13.h[1] JMP (R3) <=> br x3 @@ -146,7 +147,7 @@ Argument mapping rules Go reverses the arguments of most instructions. - Examples: +Examples: ADD R11.SXTB<<1, RSP, R25 <=> add x25, sp, w11, sxtb #1 VADD V16, V19, V14 <=> add d14, d19, d16 @@ -155,32 +156,32 @@ Special Cases. (1) Argument order is the same as in the GNU ARM64 syntax: cbz, cbnz and some store instructions, such as str, stur, strb, sturb, strh, sturh stlr, stlrb. stlrh, st1. - Examples: +Examples: MOVD R29, 384(R19) <=> str x29, [x19,#384] MOVB.P R30, 30(R4) <=> strb w30, [x4],#30 STLRH R21, (R19) <=> stlrh w21, [x19] (2) MADD, MADDW, MSUB, MSUBW, SMADDL, SMSUBL, UMADDL, UMSUBL , , , - Examples: +Examples: MADD R2, R30, R22, R6 <=> madd x6, x22, x2, x30 SMSUBL R10, R3, R17, R27 <=> smsubl x27, w17, w10, x3 (3) FMADDD, FMADDS, FMSUBD, FMSUBS, FNMADDD, FNMADDS, FNMSUBD, FNMSUBS , , , - Examples: +Examples: FMADDD F30, F20, F3, F29 <=> fmadd d29, d3, d30, d20 FNMSUBS F7, F25, F7, F22 <=> fnmsub s22, s7, s7, s25 (4) BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX $, , $, - Examples: +Examples: BFIW $16, R20, $6, R0 <=> bfi w0, w20, #16, #6 UBFIZ $34, R26, $5, R20 <=> ubfiz x20, x26, #34, #5 (5) FCCMPD, FCCMPS, FCCMPED, FCCMPES , Fm. Fn, $ - Examples: +Examples: FCCMPD AL, F8, F26, $0 <=> fccmp d26, d8, #0x0, al FCCMPS VS, F29, F4, $4 <=> fccmp s4, s29, #0x4, vs FCCMPED LE, F20, F5, $13 <=> fccmpe d5, d20, #0xd, le @@ -188,20 +189,20 @@ such as str, stur, strb, sturb, strh, sturh stlr, stlrb. stlrh, st1. (6) CCMN, CCMNW, CCMP, CCMPW , , $, $ - Examples: +Examples: CCMP MI, R22, $12, $13 <=> ccmp x22, #0xc, #0xd, mi CCMNW AL, R1, $11, $8 <=> ccmn w1, #0xb, #0x8, al (7) CCMN, CCMNW, CCMP, CCMPW , , , $ - Examples: +Examples: CCMN VS, R13, R22, $10 <=> ccmn x13, x22, #0xa, vs CCMPW HS, R19, R14, $11 <=> ccmp w19, w14, #0xb, cs (9) CSEL, CSELW, CSNEG, CSNEGW, CSINC, CSINCW , , , ; FCSELD, FCSELS , , , - Examples: +Examples: CSEL GT, R0, R19, R1 <=> csel x1, x0, x19, gt CSNEGW GT, R7, R17, R8 <=> csneg w8, w7, w17, gt FCSELD EQ, F15, F18, F16 <=> fcsel d16, d15, d18, eq @@ -211,13 +212,13 @@ FCSELD, FCSELS , , , (11) STLXR, STLXRW, STXR, STXRW, STLXRB, STLXRH, STXRB, STXRH , (), - Examples: +Examples: STLXR ZR, (R15), R16 <=> stlxr w16, xzr, [x15] STXRB R9, (R21), R19 <=> stxrb w19, w9, [x21] (12) STLXP, STLXPW, STXP, STXPW (, ), (), - Examples: +Examples: STLXP (R17, R19), (R4), R5 <=> stlxp w5, x17, x19, [x4] STXPW (R30, R25), (R22), R13 <=> stxp w13, w30, w25, [x22] @@ -227,28 +228,28 @@ FCSELD, FCSELS , , , Optionally-shifted immediate. - Examples: +Examples: ADD $(3151<<12), R14, R20 <=> add x20, x14, #0xc4f, lsl #12 ADDW $1864, R25, R6 <=> add w6, w25, #0x748 Optionally-shifted registers are written as {}. The can be <<(lsl), >>(lsr), ->(asr), @>(ror). - Examples: +Examples: ADD R19>>30, R10, R24 <=> add x24, x10, x19, lsr #30 ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24 Extended registers are written as {.{<<}}. can be UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW or SXTX. - Examples: +Examples: ADDS R19.UXTB<<4, R9, R26 <=> adds x26, x9, w19, uxtb #4 ADDSW R14.SXTX, R14, R6 <=> adds w6, w14, w14, sxtx Memory references: [{,#0}] is written as (Rn|RSP), a base register and an immediate offset is written as imm(Rn|RSP), a base register and an offset register is written as (Rn|RSP)(Rm). - Examples: +Examples: LDAR (R22), R9 <=> ldar x9, [x22] LDP 28(R17), (R15, R23) <=> ldp x15, x23, [x17,#28] MOVWU (R4)(R12<<2), R8 <=> ldr w8, [x4, x12, lsl #2] @@ -257,12 +258,12 @@ offset is written as imm(Rn|RSP), a base register and an offset register is writ Register pairs are written as (Rt1, Rt2). - Examples: +Examples: LDP.P -240(R11), (R12, R26) <=> ldp x12, x26, [x11],#-240 Register with arrangement and register with arrangement and index. - Examples: +Examples: VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h VLD1 (R2), [V21.B16] <=> ld1 {v21.16b}, [x2] VST1.P V9.S[1], (R16)(R21) <=> st1 {v9.s}[1], [x16], x28 diff --git a/src/cmd/internal/obj/arm64/list7.go b/src/cmd/internal/obj/arm64/list7.go index e63a4815f9..0187ad3341 100644 --- a/src/cmd/internal/obj/arm64/list7.go +++ b/src/cmd/internal/obj/arm64/list7.go @@ -59,6 +59,7 @@ func init() { obj.RegisterOpcode(obj.ABaseARM64, Anames) obj.RegisterRegisterList(obj.RegListARM64Lo, obj.RegListARM64Hi, rlconv) obj.RegisterOpSuffix("arm64", obj.CConvARM) + obj.RegisterSpecialOperands(int64(SPOP_BEGIN), int64(SPOP_END), SPCconv) } func arrange(a int) string { @@ -108,50 +109,8 @@ func rconv(r int) string { return fmt.Sprintf("F%d", r-REG_F0) case REG_V0 <= r && r <= REG_V31: return fmt.Sprintf("V%d", r-REG_V0) - case COND_EQ <= r && r <= COND_NV: - return strcond[r-COND_EQ] case r == REGSP: return "RSP" - case r == REG_DAIFSet: - return "DAIFSet" - case r == REG_DAIFClr: - return "DAIFClr" - case r == REG_PLDL1KEEP: - return "PLDL1KEEP" - case r == REG_PLDL1STRM: - return "PLDL1STRM" - case r == REG_PLDL2KEEP: - return "PLDL2KEEP" - case r == REG_PLDL2STRM: - return "PLDL2STRM" - case r == REG_PLDL3KEEP: - return "PLDL3KEEP" - case r == REG_PLDL3STRM: - return "PLDL3STRM" - case r == REG_PLIL1KEEP: - return "PLIL1KEEP" - case r == REG_PLIL1STRM: - return "PLIL1STRM" - case r == REG_PLIL2KEEP: - return "PLIL2KEEP" - case r == REG_PLIL2STRM: - return "PLIL2STRM" - case r == REG_PLIL3KEEP: - return "PLIL3KEEP" - case r == REG_PLIL3STRM: - return "PLIL3STRM" - case r == REG_PSTL1KEEP: - return "PSTL1KEEP" - case r == REG_PSTL1STRM: - return "PSTL1STRM" - case r == REG_PSTL2KEEP: - return "PSTL2KEEP" - case r == REG_PSTL2STRM: - return "PSTL2STRM" - case r == REG_PSTL3KEEP: - return "PSTL3KEEP" - case r == REG_PSTL3STRM: - return "PSTL3STRM" case REG_UXTB <= r && r < REG_UXTH: if ext != 0 { return fmt.Sprintf("%s.UXTB<<%d", regname(r), ext) @@ -223,6 +182,14 @@ func DRconv(a int) string { return "C_??" } +func SPCconv(a int64) string { + spc := SpecialOperand(a) + if spc >= SPOP_BEGIN && spc < SPOP_END { + return fmt.Sprintf("%s", spc) + } + return "SPC_??" +} + func rlconv(list int64) string { str := "" diff --git a/src/cmd/internal/obj/arm64/specialoperand_string.go b/src/cmd/internal/obj/arm64/specialoperand_string.go new file mode 100644 index 0000000000..0a73c69b12 --- /dev/null +++ b/src/cmd/internal/obj/arm64/specialoperand_string.go @@ -0,0 +1,166 @@ +// Code generated by "stringer -type SpecialOperand -trimprefix SPOP_"; DO NOT EDIT. + +package arm64 + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[SPOP_PLDL1KEEP-0] + _ = x[SPOP_BEGIN-0] + _ = x[SPOP_PLDL1STRM-1] + _ = x[SPOP_PLDL2KEEP-2] + _ = x[SPOP_PLDL2STRM-3] + _ = x[SPOP_PLDL3KEEP-4] + _ = x[SPOP_PLDL3STRM-5] + _ = x[SPOP_PLIL1KEEP-6] + _ = x[SPOP_PLIL1STRM-7] + _ = x[SPOP_PLIL2KEEP-8] + _ = x[SPOP_PLIL2STRM-9] + _ = x[SPOP_PLIL3KEEP-10] + _ = x[SPOP_PLIL3STRM-11] + _ = x[SPOP_PSTL1KEEP-12] + _ = x[SPOP_PSTL1STRM-13] + _ = x[SPOP_PSTL2KEEP-14] + _ = x[SPOP_PSTL2STRM-15] + _ = x[SPOP_PSTL3KEEP-16] + _ = x[SPOP_PSTL3STRM-17] + _ = x[SPOP_VMALLE1IS-18] + _ = x[SPOP_VAE1IS-19] + _ = x[SPOP_ASIDE1IS-20] + _ = x[SPOP_VAAE1IS-21] + _ = x[SPOP_VALE1IS-22] + _ = x[SPOP_VAALE1IS-23] + _ = x[SPOP_VMALLE1-24] + _ = x[SPOP_VAE1-25] + _ = x[SPOP_ASIDE1-26] + _ = x[SPOP_VAAE1-27] + _ = x[SPOP_VALE1-28] + _ = x[SPOP_VAALE1-29] + _ = x[SPOP_IPAS2E1IS-30] + _ = x[SPOP_IPAS2LE1IS-31] + _ = x[SPOP_ALLE2IS-32] + _ = x[SPOP_VAE2IS-33] + _ = x[SPOP_ALLE1IS-34] + _ = x[SPOP_VALE2IS-35] + _ = x[SPOP_VMALLS12E1IS-36] + _ = x[SPOP_IPAS2E1-37] + _ = x[SPOP_IPAS2LE1-38] + _ = x[SPOP_ALLE2-39] + _ = x[SPOP_VAE2-40] + _ = x[SPOP_ALLE1-41] + _ = x[SPOP_VALE2-42] + _ = x[SPOP_VMALLS12E1-43] + _ = x[SPOP_ALLE3IS-44] + _ = x[SPOP_VAE3IS-45] + _ = x[SPOP_VALE3IS-46] + _ = x[SPOP_ALLE3-47] + _ = x[SPOP_VAE3-48] + _ = x[SPOP_VALE3-49] + _ = x[SPOP_VMALLE1OS-50] + _ = x[SPOP_VAE1OS-51] + _ = x[SPOP_ASIDE1OS-52] + _ = x[SPOP_VAAE1OS-53] + _ = x[SPOP_VALE1OS-54] + _ = x[SPOP_VAALE1OS-55] + _ = x[SPOP_RVAE1IS-56] + _ = x[SPOP_RVAAE1IS-57] + _ = x[SPOP_RVALE1IS-58] + _ = x[SPOP_RVAALE1IS-59] + _ = x[SPOP_RVAE1OS-60] + _ = x[SPOP_RVAAE1OS-61] + _ = x[SPOP_RVALE1OS-62] + _ = x[SPOP_RVAALE1OS-63] + _ = x[SPOP_RVAE1-64] + _ = x[SPOP_RVAAE1-65] + _ = x[SPOP_RVALE1-66] + _ = x[SPOP_RVAALE1-67] + _ = x[SPOP_RIPAS2E1IS-68] + _ = x[SPOP_RIPAS2LE1IS-69] + _ = x[SPOP_ALLE2OS-70] + _ = x[SPOP_VAE2OS-71] + _ = x[SPOP_ALLE1OS-72] + _ = x[SPOP_VALE2OS-73] + _ = x[SPOP_VMALLS12E1OS-74] + _ = x[SPOP_RVAE2IS-75] + _ = x[SPOP_RVALE2IS-76] + _ = x[SPOP_IPAS2E1OS-77] + _ = x[SPOP_RIPAS2E1-78] + _ = x[SPOP_RIPAS2E1OS-79] + _ = x[SPOP_IPAS2LE1OS-80] + _ = x[SPOP_RIPAS2LE1-81] + _ = x[SPOP_RIPAS2LE1OS-82] + _ = x[SPOP_RVAE2OS-83] + _ = x[SPOP_RVALE2OS-84] + _ = x[SPOP_RVAE2-85] + _ = x[SPOP_RVALE2-86] + _ = x[SPOP_ALLE3OS-87] + _ = x[SPOP_VAE3OS-88] + _ = x[SPOP_VALE3OS-89] + _ = x[SPOP_RVAE3IS-90] + _ = x[SPOP_RVALE3IS-91] + _ = x[SPOP_RVAE3OS-92] + _ = x[SPOP_RVALE3OS-93] + _ = x[SPOP_RVAE3-94] + _ = x[SPOP_RVALE3-95] + _ = x[SPOP_IVAC-96] + _ = x[SPOP_ISW-97] + _ = x[SPOP_CSW-98] + _ = x[SPOP_CISW-99] + _ = x[SPOP_ZVA-100] + _ = x[SPOP_CVAC-101] + _ = x[SPOP_CVAU-102] + _ = x[SPOP_CIVAC-103] + _ = x[SPOP_IGVAC-104] + _ = x[SPOP_IGSW-105] + _ = x[SPOP_IGDVAC-106] + _ = x[SPOP_IGDSW-107] + _ = x[SPOP_CGSW-108] + _ = x[SPOP_CGDSW-109] + _ = x[SPOP_CIGSW-110] + _ = x[SPOP_CIGDSW-111] + _ = x[SPOP_GVA-112] + _ = x[SPOP_GZVA-113] + _ = x[SPOP_CGVAC-114] + _ = x[SPOP_CGDVAC-115] + _ = x[SPOP_CGVAP-116] + _ = x[SPOP_CGDVAP-117] + _ = x[SPOP_CGVADP-118] + _ = x[SPOP_CGDVADP-119] + _ = x[SPOP_CIGVAC-120] + _ = x[SPOP_CIGDVAC-121] + _ = x[SPOP_CVAP-122] + _ = x[SPOP_CVADP-123] + _ = x[SPOP_DAIFSet-124] + _ = x[SPOP_DAIFClr-125] + _ = x[SPOP_EQ-126] + _ = x[SPOP_NE-127] + _ = x[SPOP_HS-128] + _ = x[SPOP_LO-129] + _ = x[SPOP_MI-130] + _ = x[SPOP_PL-131] + _ = x[SPOP_VS-132] + _ = x[SPOP_VC-133] + _ = x[SPOP_HI-134] + _ = x[SPOP_LS-135] + _ = x[SPOP_GE-136] + _ = x[SPOP_LT-137] + _ = x[SPOP_GT-138] + _ = x[SPOP_LE-139] + _ = x[SPOP_AL-140] + _ = x[SPOP_NV-141] + _ = x[SPOP_END-142] +} + +const _SpecialOperand_name = "PLDL1KEEPPLDL1STRMPLDL2KEEPPLDL2STRMPLDL3KEEPPLDL3STRMPLIL1KEEPPLIL1STRMPLIL2KEEPPLIL2STRMPLIL3KEEPPLIL3STRMPSTL1KEEPPSTL1STRMPSTL2KEEPPSTL2STRMPSTL3KEEPPSTL3STRMVMALLE1ISVAE1ISASIDE1ISVAAE1ISVALE1ISVAALE1ISVMALLE1VAE1ASIDE1VAAE1VALE1VAALE1IPAS2E1ISIPAS2LE1ISALLE2ISVAE2ISALLE1ISVALE2ISVMALLS12E1ISIPAS2E1IPAS2LE1ALLE2VAE2ALLE1VALE2VMALLS12E1ALLE3ISVAE3ISVALE3ISALLE3VAE3VALE3VMALLE1OSVAE1OSASIDE1OSVAAE1OSVALE1OSVAALE1OSRVAE1ISRVAAE1ISRVALE1ISRVAALE1ISRVAE1OSRVAAE1OSRVALE1OSRVAALE1OSRVAE1RVAAE1RVALE1RVAALE1RIPAS2E1ISRIPAS2LE1ISALLE2OSVAE2OSALLE1OSVALE2OSVMALLS12E1OSRVAE2ISRVALE2ISIPAS2E1OSRIPAS2E1RIPAS2E1OSIPAS2LE1OSRIPAS2LE1RIPAS2LE1OSRVAE2OSRVALE2OSRVAE2RVALE2ALLE3OSVAE3OSVALE3OSRVAE3ISRVALE3ISRVAE3OSRVALE3OSRVAE3RVALE3IVACISWCSWCISWZVACVACCVAUCIVACIGVACIGSWIGDVACIGDSWCGSWCGDSWCIGSWCIGDSWGVAGZVACGVACCGDVACCGVAPCGDVAPCGVADPCGDVADPCIGVACCIGDVACCVAPCVADPDAIFSetDAIFClrEQNEHSLOMIPLVSVCHILSGELTGTLEALNVEND" + +var _SpecialOperand_index = [...]uint16{0, 9, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99, 108, 117, 126, 135, 144, 153, 162, 171, 177, 185, 192, 199, 207, 214, 218, 224, 229, 234, 240, 249, 259, 266, 272, 279, 286, 298, 305, 313, 318, 322, 327, 332, 342, 349, 355, 362, 367, 371, 376, 385, 391, 399, 406, 413, 421, 428, 436, 444, 453, 460, 468, 476, 485, 490, 496, 502, 509, 519, 530, 537, 543, 550, 557, 569, 576, 584, 593, 601, 611, 621, 630, 641, 648, 656, 661, 667, 674, 680, 687, 694, 702, 709, 717, 722, 728, 732, 735, 738, 742, 745, 749, 753, 758, 763, 767, 773, 778, 782, 787, 792, 798, 801, 805, 810, 816, 821, 827, 833, 840, 846, 853, 857, 862, 869, 876, 878, 880, 882, 884, 886, 888, 890, 892, 894, 896, 898, 900, 902, 904, 906, 908, 911} + +func (i SpecialOperand) String() string { + if i < 0 || i >= SpecialOperand(len(_SpecialOperand_index)-1) { + return "SpecialOperand(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _SpecialOperand_name[_SpecialOperand_index[i]:_SpecialOperand_index[i+1]] +} diff --git a/src/cmd/internal/obj/inl.go b/src/cmd/internal/obj/inl.go index 1b1d13a679..d65a7357a6 100644 --- a/src/cmd/internal/obj/inl.go +++ b/src/cmd/internal/obj/inl.go @@ -13,19 +13,19 @@ import "cmd/internal/src" // every time a function is inlined. For example, suppose f() calls g() // and g has two calls to h(), and that f, g, and h are inlineable: // -// 1 func main() { -// 2 f() -// 3 } -// 4 func f() { -// 5 g() -// 6 } -// 7 func g() { -// 8 h() -// 9 h() -// 10 } -// 11 func h() { -// 12 println("H") -// 13 } +// 1 func main() { +// 2 f() +// 3 } +// 4 func f() { +// 5 g() +// 6 } +// 7 func g() { +// 8 h() +// 9 h() +// 10 } +// 11 func h() { +// 12 println("H") +// 13 } // // Assuming the global tree starts empty, inlining will produce the // following tree: diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index e0a3138c38..a3eba73906 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -94,6 +94,12 @@ import ( // type = TYPE_SCONST // val = string // +// +// Special symbolic constants for ARM64, such as conditional flags, tlbi_op and so on. +// Encoding: +// type = TYPE_SPECIAL +// offset = The constant value corresponding to this symbol +// // // Any register: integer, floating point, control, segment, and so on. // If looking for specific register kind, must check type and reg value range. @@ -236,6 +242,7 @@ const ( TYPE_REGREG2 TYPE_INDIR TYPE_REGLIST + TYPE_SPECIAL ) func (a *Addr) Target() *Prog { diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 7bae31f343..e9302ee642 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -448,14 +448,14 @@ func contentHash64(s *LSym) goobj.Hash64Type { // Depending on the category of the referenced symbol, we choose // different hash algorithms such that the hash is globally // consistent. -// - For referenced content-addressable symbol, its content hash -// is globally consistent. -// - For package symbol and builtin symbol, its local index is -// globally consistent. -// - For non-package symbol, its fully-expanded name is globally -// consistent. For now, we require we know the current package -// path so we can always expand symbol names. (Otherwise, -// symbols with relocations are not considered hashable.) +// - For referenced content-addressable symbol, its content hash +// is globally consistent. +// - For package symbol and builtin symbol, its local index is +// globally consistent. +// - For non-package symbol, its fully-expanded name is globally +// consistent. For now, we require we know the current package +// path so we can always expand symbol names. (Otherwise, +// symbols with relocations are not considered hashable.) // // For now, we assume there is no circular dependencies among // hashed symbols. diff --git a/src/cmd/internal/obj/pass.go b/src/cmd/internal/obj/pass.go index 01657dd4f6..b91a15da97 100644 --- a/src/cmd/internal/obj/pass.go +++ b/src/cmd/internal/obj/pass.go @@ -112,6 +112,11 @@ func checkaddr(ctxt *Link, p *Prog, a *Addr) { break } return + case TYPE_SPECIAL: + if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name != 0 || a.Class != 0 || a.Sym != nil { + break + } + return } ctxt.Diag("invalid encoding for argument %v", p) diff --git a/src/cmd/internal/obj/ppc64/doc.go b/src/cmd/internal/obj/ppc64/doc.go index a9d89c93b4..bdaeaf69e1 100644 --- a/src/cmd/internal/obj/ppc64/doc.go +++ b/src/cmd/internal/obj/ppc64/doc.go @@ -23,207 +23,212 @@ In the examples below, the Go assembly is on the left, PPC64 assembly on the rig 1. Operand ordering - In Go asm, the last operand (right) is the target operand, but with PPC64 asm, - the first operand (left) is the target. The order of the remaining operands is - not consistent: in general opcodes with 3 operands that perform math or logical - operations have their operands in reverse order. Opcodes for vector instructions - and those with more than 3 operands usually have operands in the same order except - for the target operand, which is first in PPC64 asm and last in Go asm. +In Go asm, the last operand (right) is the target operand, but with PPC64 asm, +the first operand (left) is the target. The order of the remaining operands is +not consistent: in general opcodes with 3 operands that perform math or logical +operations have their operands in reverse order. Opcodes for vector instructions +and those with more than 3 operands usually have operands in the same order except +for the target operand, which is first in PPC64 asm and last in Go asm. - Example: - ADD R3, R4, R5 <=> add r5, r4, r3 +Example: + + ADD R3, R4, R5 <=> add r5, r4, r3 2. Constant operands - In Go asm, an operand that starts with '$' indicates a constant value. If the - instruction using the constant has an immediate version of the opcode, then an - immediate value is used with the opcode if possible. +In Go asm, an operand that starts with '$' indicates a constant value. If the +instruction using the constant has an immediate version of the opcode, then an +immediate value is used with the opcode if possible. - Example: - ADD $1, R3, R4 <=> addi r4, r3, 1 +Example: + + ADD $1, R3, R4 <=> addi r4, r3, 1 3. Opcodes setting condition codes - In PPC64 asm, some instructions other than compares have variations that can set - the condition code where meaningful. This is indicated by adding '.' to the end - of the PPC64 instruction. In Go asm, these instructions have 'CC' at the end of - the opcode. The possible settings of the condition code depend on the instruction. - CR0 is the default for fixed-point instructions; CR1 for floating point; CR6 for - vector instructions. +In PPC64 asm, some instructions other than compares have variations that can set +the condition code where meaningful. This is indicated by adding '.' to the end +of the PPC64 instruction. In Go asm, these instructions have 'CC' at the end of +the opcode. The possible settings of the condition code depend on the instruction. +CR0 is the default for fixed-point instructions; CR1 for floating point; CR6 for +vector instructions. - Example: - ANDCC R3, R4, R5 <=> and. r5, r3, r4 (set CR0) +Example: + + ANDCC R3, R4, R5 <=> and. r5, r3, r4 (set CR0) 4. Loads and stores from memory - In Go asm, opcodes starting with 'MOV' indicate a load or store. When the target - is a memory reference, then it is a store; when the target is a register and the - source is a memory reference, then it is a load. +In Go asm, opcodes starting with 'MOV' indicate a load or store. When the target +is a memory reference, then it is a store; when the target is a register and the +source is a memory reference, then it is a load. - MOV{B,H,W,D} variations identify the size as byte, halfword, word, doubleword. +MOV{B,H,W,D} variations identify the size as byte, halfword, word, doubleword. - Adding 'Z' to the opcode for a load indicates zero extend; if omitted it is sign extend. - Adding 'U' to a load or store indicates an update of the base register with the offset. - Adding 'BR' to an opcode indicates byte-reversed load or store, or the order opposite - of the expected endian order. If 'BR' is used then zero extend is assumed. +Adding 'Z' to the opcode for a load indicates zero extend; if omitted it is sign extend. +Adding 'U' to a load or store indicates an update of the base register with the offset. +Adding 'BR' to an opcode indicates byte-reversed load or store, or the order opposite +of the expected endian order. If 'BR' is used then zero extend is assumed. - Memory references n(Ra) indicate the address in Ra + n. When used with an update form - of an opcode, the value in Ra is incremented by n. +Memory references n(Ra) indicate the address in Ra + n. When used with an update form +of an opcode, the value in Ra is incremented by n. - Memory references (Ra+Rb) or (Ra)(Rb) indicate the address Ra + Rb, used by indexed - loads or stores. Both forms are accepted. When used with an update then the base register - is updated by the value in the index register. +Memory references (Ra+Rb) or (Ra)(Rb) indicate the address Ra + Rb, used by indexed +loads or stores. Both forms are accepted. When used with an update then the base register +is updated by the value in the index register. - Examples: - MOVD (R3), R4 <=> ld r4,0(r3) - MOVW (R3), R4 <=> lwa r4,0(r3) - MOVWZU 4(R3), R4 <=> lwzu r4,4(r3) - MOVWZ (R3+R5), R4 <=> lwzx r4,r3,r5 - MOVHZ (R3), R4 <=> lhz r4,0(r3) - MOVHU 2(R3), R4 <=> lhau r4,2(r3) - MOVBZ (R3), R4 <=> lbz r4,0(r3) +Examples: - MOVD R4,(R3) <=> std r4,0(r3) - MOVW R4,(R3) <=> stw r4,0(r3) - MOVW R4,(R3+R5) <=> stwx r4,r3,r5 - MOVWU R4,4(R3) <=> stwu r4,4(r3) - MOVH R4,2(R3) <=> sth r4,2(r3) - MOVBU R4,(R3)(R5) <=> stbux r4,r3,r5 + MOVD (R3), R4 <=> ld r4,0(r3) + MOVW (R3), R4 <=> lwa r4,0(r3) + MOVWZU 4(R3), R4 <=> lwzu r4,4(r3) + MOVWZ (R3+R5), R4 <=> lwzx r4,r3,r5 + MOVHZ (R3), R4 <=> lhz r4,0(r3) + MOVHU 2(R3), R4 <=> lhau r4,2(r3) + MOVBZ (R3), R4 <=> lbz r4,0(r3) + + MOVD R4,(R3) <=> std r4,0(r3) + MOVW R4,(R3) <=> stw r4,0(r3) + MOVW R4,(R3+R5) <=> stwx r4,r3,r5 + MOVWU R4,4(R3) <=> stwu r4,4(r3) + MOVH R4,2(R3) <=> sth r4,2(r3) + MOVBU R4,(R3)(R5) <=> stbux r4,r3,r5 4. Compares - When an instruction does a compare or other operation that might - result in a condition code, then the resulting condition is set - in a field of the condition register. The condition register consists - of 8 4-bit fields named CR0 - CR7. When a compare instruction - identifies a CR then the resulting condition is set in that field - to be read by a later branch or isel instruction. Within these fields, - bits are set to indicate less than, greater than, or equal conditions. +When an instruction does a compare or other operation that might +result in a condition code, then the resulting condition is set +in a field of the condition register. The condition register consists +of 8 4-bit fields named CR0 - CR7. When a compare instruction +identifies a CR then the resulting condition is set in that field +to be read by a later branch or isel instruction. Within these fields, +bits are set to indicate less than, greater than, or equal conditions. - Once an instruction sets a condition, then a subsequent branch, isel or - other instruction can read the condition field and operate based on the - bit settings. +Once an instruction sets a condition, then a subsequent branch, isel or +other instruction can read the condition field and operate based on the +bit settings. - Examples: - CMP R3, R4 <=> cmp r3, r4 (CR0 assumed) - CMP R3, R4, CR1 <=> cmp cr1, r3, r4 +Examples: - Note that the condition register is the target operand of compare opcodes, so - the remaining operands are in the same order for Go asm and PPC64 asm. - When CR0 is used then it is implicit and does not need to be specified. + CMP R3, R4 <=> cmp r3, r4 (CR0 assumed) + CMP R3, R4, CR1 <=> cmp cr1, r3, r4 + +Note that the condition register is the target operand of compare opcodes, so +the remaining operands are in the same order for Go asm and PPC64 asm. +When CR0 is used then it is implicit and does not need to be specified. 5. Branches - Many branches are represented as a form of the BC instruction. There are - other extended opcodes to make it easier to see what type of branch is being - used. +Many branches are represented as a form of the BC instruction. There are +other extended opcodes to make it easier to see what type of branch is being +used. - The following is a brief description of the BC instruction and its commonly - used operands. +The following is a brief description of the BC instruction and its commonly +used operands. - BC op1, op2, op3 +BC op1, op2, op3 - op1: type of branch - 16 -> bctr (branch on ctr) - 12 -> bcr (branch if cr bit is set) - 8 -> bcr+bctr (branch on ctr and cr values) + op1: type of branch + 16 -> bctr (branch on ctr) + 12 -> bcr (branch if cr bit is set) + 8 -> bcr+bctr (branch on ctr and cr values) 4 -> bcr != 0 (branch if specified cr bit is not set) There are more combinations but these are the most common. - op2: condition register field and condition bit + op2: condition register field and condition bit This contains an immediate value indicating which condition field to read and what bits to test. Each field is 4 bits long with CR0 - at bit 0, CR1 at bit 4, etc. The value is computed as 4*CR+condition - with these condition values: + at bit 0, CR1 at bit 4, etc. The value is computed as 4*CR+condition + with these condition values: - 0 -> LT - 1 -> GT - 2 -> EQ - 3 -> OVG + 0 -> LT + 1 -> GT + 2 -> EQ + 3 -> OVG Thus 0 means test CR0 for LT, 5 means CR1 for GT, 30 means CR7 for EQ. - op3: branch target + op3: branch target - Examples: +Examples: - BC 12, 0, target <=> blt cr0, target - BC 12, 2, target <=> beq cr0, target - BC 12, 5, target <=> bgt cr1, target - BC 12, 30, target <=> beq cr7, target - BC 4, 6, target <=> bne cr1, target - BC 4, 1, target <=> ble cr1, target + BC 12, 0, target <=> blt cr0, target + BC 12, 2, target <=> beq cr0, target + BC 12, 5, target <=> bgt cr1, target + BC 12, 30, target <=> beq cr7, target + BC 4, 6, target <=> bne cr1, target + BC 4, 1, target <=> ble cr1, target - The following extended opcodes are available for ease of use and readability: + The following extended opcodes are available for ease of use and readability: - BNE CR2, target <=> bne cr2, target - BEQ CR4, target <=> beq cr4, target - BLT target <=> blt target (cr0 default) - BGE CR7, target <=> bge cr7, target + BNE CR2, target <=> bne cr2, target + BEQ CR4, target <=> beq cr4, target + BLT target <=> blt target (cr0 default) + BGE CR7, target <=> bge cr7, target - Refer to the ISA for more information on additional values for the BC instruction, - how to handle OVG information, and much more. +Refer to the ISA for more information on additional values for the BC instruction, +how to handle OVG information, and much more. 5. Align directive - Starting with Go 1.12, Go asm supports the PCALIGN directive, which indicates - that the next instruction should be aligned to the specified value. Currently - 8 and 16 are the only supported values, and a maximum of 2 NOPs will be added - to align the code. That means in the case where the code is aligned to 4 but - PCALIGN $16 is at that location, the code will only be aligned to 8 to avoid - adding 3 NOPs. +Starting with Go 1.12, Go asm supports the PCALIGN directive, which indicates +that the next instruction should be aligned to the specified value. Currently +8 and 16 are the only supported values, and a maximum of 2 NOPs will be added +to align the code. That means in the case where the code is aligned to 4 but +PCALIGN $16 is at that location, the code will only be aligned to 8 to avoid +adding 3 NOPs. - The purpose of this directive is to improve performance for cases like loops - where better alignment (8 or 16 instead of 4) might be helpful. This directive - exists in PPC64 assembler and is frequently used by PPC64 assembler writers. +The purpose of this directive is to improve performance for cases like loops +where better alignment (8 or 16 instead of 4) might be helpful. This directive +exists in PPC64 assembler and is frequently used by PPC64 assembler writers. - PCALIGN $16 - PCALIGN $8 +PCALIGN $16 +PCALIGN $8 - Functions in Go are aligned to 16 bytes, as is the case in all other compilers - for PPC64. +Functions in Go are aligned to 16 bytes, as is the case in all other compilers +for PPC64. 6. Shift instructions - The simple scalar shifts on PPC64 expect a shift count that fits in 5 bits for - 32-bit values or 6 bit for 64-bit values. If the shift count is a constant value - greater than the max then the assembler sets it to the max for that size (31 for - 32 bit values, 63 for 64 bit values). If the shift count is in a register, then - only the low 5 or 6 bits of the register will be used as the shift count. The - Go compiler will add appropriate code to compare the shift value to achieve the - the correct result, and the assembler does not add extra checking. +The simple scalar shifts on PPC64 expect a shift count that fits in 5 bits for +32-bit values or 6 bit for 64-bit values. If the shift count is a constant value +greater than the max then the assembler sets it to the max for that size (31 for +32 bit values, 63 for 64 bit values). If the shift count is in a register, then +only the low 5 or 6 bits of the register will be used as the shift count. The +Go compiler will add appropriate code to compare the shift value to achieve the +the correct result, and the assembler does not add extra checking. - Examples: +Examples: - SRAD $8,R3,R4 => sradi r4,r3,8 - SRD $8,R3,R4 => rldicl r4,r3,56,8 - SLD $8,R3,R4 => rldicr r4,r3,8,55 - SRAW $16,R4,R5 => srawi r5,r4,16 - SRW $40,R4,R5 => rlwinm r5,r4,0,0,31 - SLW $12,R4,R5 => rlwinm r5,r4,12,0,19 + SRAD $8,R3,R4 => sradi r4,r3,8 + SRD $8,R3,R4 => rldicl r4,r3,56,8 + SLD $8,R3,R4 => rldicr r4,r3,8,55 + SRAW $16,R4,R5 => srawi r5,r4,16 + SRW $40,R4,R5 => rlwinm r5,r4,0,0,31 + SLW $12,R4,R5 => rlwinm r5,r4,12,0,19 - Some non-simple shifts have operands in the Go assembly which don't map directly - onto operands in the PPC64 assembly. When an operand in a shift instruction in the - Go assembly is a bit mask, that mask is represented as a start and end bit in the - PPC64 assembly instead of a mask. See the ISA for more detail on these types of shifts. - Here are a few examples: +Some non-simple shifts have operands in the Go assembly which don't map directly +onto operands in the PPC64 assembly. When an operand in a shift instruction in the +Go assembly is a bit mask, that mask is represented as a start and end bit in the +PPC64 assembly instead of a mask. See the ISA for more detail on these types of shifts. +Here are a few examples: - RLWMI $7,R3,$65535,R6 => rlwimi r6,r3,7,16,31 - RLDMI $0,R4,$7,R6 => rldimi r6,r4,0,61 + RLWMI $7,R3,$65535,R6 => rlwimi r6,r3,7,16,31 + RLDMI $0,R4,$7,R6 => rldimi r6,r4,0,61 - More recently, Go opcodes were added which map directly onto the PPC64 opcodes. It is - recommended to use the newer opcodes to avoid confusion. +More recently, Go opcodes were added which map directly onto the PPC64 opcodes. It is +recommended to use the newer opcodes to avoid confusion. - RLDICL $0,R4,$15,R6 => rldicl r6,r4,0,15 - RLDICR $0,R4,$15,R6 => rldicr r6.r4,0,15 + RLDICL $0,R4,$15,R6 => rldicl r6,r4,0,15 + RLDICR $0,R4,$15,R6 => rldicr r6.r4,0,15 Register naming 1. Special register usage in Go asm - The following registers should not be modified by user Go assembler code. +The following registers should not be modified by user Go assembler code. R0: Go code expects this register to contain the value 0. R1: Stack pointer @@ -231,7 +236,7 @@ Register naming R13: TLS pointer R30: g (goroutine) - Register names: +Register names: Rn is used for general purpose registers. (0-31) Fn is used for floating point registers. (0-31) diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go index 8c2daf6e5b..594f8ea3fc 100644 --- a/src/cmd/internal/obj/riscv/cpu.go +++ b/src/cmd/internal/obj/riscv/cpu.go @@ -276,15 +276,11 @@ const ( ) // RISC-V mnemonics, as defined in the "opcodes" and "opcodes-pseudo" files -// from: -// -// https://github.com/riscv/riscv-opcodes +// at https://github.com/riscv/riscv-opcodes. // // As well as some pseudo-mnemonics (e.g. MOV) used only in the assembler. // -// See also "The RISC-V Instruction Set Manual" at: -// -// https://riscv.org/specifications/ +// See also "The RISC-V Instruction Set Manual" at https://riscv.org/specifications/. // // If you modify this table, you MUST run 'go generate' to regenerate anames.go! const ( diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 9f16de0c8c..956d69ee2e 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -53,6 +53,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { if p.Reg == obj.REG_NONE { switch p.As { case AADDI, ASLTI, ASLTIU, AANDI, AORI, AXORI, ASLLI, ASRLI, ASRAI, + AADDIW, ASLLIW, ASRLIW, ASRAIW, AADDW, ASUBW, ASLLW, ASRLW, ASRAW, AADD, AAND, AOR, AXOR, ASLL, ASRL, ASUB, ASRA, AMUL, AMULH, AMULHU, AMULHSU, AMULW, ADIV, ADIVU, ADIVW, ADIVUW, AREM, AREMU, AREMW, AREMUW: @@ -82,6 +83,14 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { p.As = ASRLI case ASRA: p.As = ASRAI + case AADDW: + p.As = AADDIW + case ASLLW: + p.As = ASLLIW + case ASRLW: + p.As = ASRLIW + case ASRAW: + p.As = ASRAIW } } @@ -314,10 +323,7 @@ func setPCs(p *obj.Prog, pc int64) int64 { // FixedFrameSize makes other packages aware of the space allocated for RA. // // A nicer version of this diagram can be found on slide 21 of the presentation -// attached to: -// -// https://golang.org/issue/16922#issuecomment-243748180 -// +// attached to https://golang.org/issue/16922#issuecomment-243748180. func stackOffset(a *obj.Addr, stacksize int64) { switch a.Name { case obj.NAME_AUTO: @@ -1797,6 +1803,11 @@ func instructionsForMOV(p *obj.Prog) []*instruction { ins := instructionForProg(p) inss := []*instruction{ins} + if p.Reg != 0 { + p.Ctxt.Diag("%v: illegal MOV instruction", p) + return nil + } + switch { case p.From.Type == obj.TYPE_CONST && p.To.Type == obj.TYPE_REG: // Handle constant to register moves. diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 0c9dde7965..4e1a2d19b6 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -363,6 +363,9 @@ func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) { case TYPE_REGLIST: io.WriteString(w, RLconv(a.Offset)) + + case TYPE_SPECIAL: + io.WriteString(w, SPCconv(a.Offset)) } } @@ -575,6 +578,33 @@ func RLconv(list int64) string { return fmt.Sprintf("RL???%d", list) } +// Special operands +type spcSet struct { + lo int64 + hi int64 + SPCconv func(int64) string +} + +var spcSpace []spcSet + +// RegisterSpecialOperands binds a pretty-printer (SPCconv) for special +// operand numbers to a given special operand number range. Lo is inclusive, +// hi is exclusive (valid special operands are lo through hi-1). +func RegisterSpecialOperands(lo, hi int64, rlconv func(int64) string) { + spcSpace = append(spcSpace, spcSet{lo, hi, rlconv}) +} + +// SPCconv returns the string representation of the special operand spc. +func SPCconv(spc int64) string { + for i := range spcSpace { + spcs := &spcSpace[i] + if spcs.lo <= spc && spc < spcs.hi { + return spcs.SPCconv(spc) + } + } + return fmt.Sprintf("SPC???%d", spc) +} + type opSet struct { lo As names []string diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index a508e484e4..64a0bc96b7 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -59,7 +59,6 @@ var ( // is very slight but negative, so the alignment is disabled by // setting MaxLoopPad = 0. The code is here for reference and // for future experiments. -// const ( loopAlign = 16 maxLoopPad = 0 @@ -4167,7 +4166,6 @@ func (ab *AsmBuf) asmvex(ctxt *obj.Link, rm, v, r *obj.Addr, vex, opcode uint8) // REG_X15 => 15 // REG_R9 => 9 // REG_AX => 0 -// func regIndex(r int16) int { lower3bits := reg[r] high4bit := regrex[r] & Rxr << 1 diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go index 318bd76aba..5b7b95757b 100644 --- a/src/cmd/link/elf_test.go +++ b/src/cmd/link/elf_test.go @@ -469,3 +469,31 @@ func TestPIESize(t *testing.T) { }) } } + +func TestIssue51939(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + td := t.TempDir() + goFile := filepath.Join(td, "issue51939.go") + if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil { + t.Fatal(err) + } + outFile := filepath.Join(td, "issue51939.exe") + goTool := testenv.GoToolPath(t) + cmd := exec.Command(goTool, "build", "-o", outFile, goFile) + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + ef, err := elf.Open(outFile) + if err != nil { + t.Fatal(err) + } + + for _, s := range ef.Sections { + if s.Flags&elf.SHF_ALLOC == 0 && s.Addr != 0 { + t.Errorf("section %s should not allocated with addr %x", s.Name, s.Addr) + } + } +} diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go index 6c163c801e..39179515cd 100644 --- a/src/cmd/link/internal/benchmark/bench.go +++ b/src/cmd/link/internal/benchmark/bench.go @@ -44,16 +44,16 @@ type mark struct { // // Typical usage should look like: // -// func main() { -// filename := "" // Set to enable per-phase pprof file output. -// bench := benchmark.New(benchmark.GC, filename) -// defer bench.Report(os.Stdout) -// // etc -// bench.Start("foo") -// foo() -// bench.Start("bar") -// bar() -// } +// func main() { +// filename := "" // Set to enable per-phase pprof file output. +// bench := benchmark.New(benchmark.GC, filename) +// defer bench.Report(os.Stdout) +// // etc +// bench.Start("foo") +// foo() +// bench.Start("bar") +// bar() +// } // // Note that a nil Metrics object won't cause any errors, so one could write // code like: diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index 23915f9032..125a5d6fcb 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -38,6 +38,8 @@ import ( "internal/buildcfg" "io" "os" + "path/filepath" + "strings" ) const ( @@ -65,6 +67,9 @@ type ArHdr struct { // define them. This is used for the compiler support library // libgcc.a. func hostArchive(ctxt *Link, name string) { + if ctxt.Debugvlog > 1 { + ctxt.Logf("hostArchive(%s)\n", name) + } f, err := bio.Open(name) if err != nil { if os.IsNotExist(err) { @@ -122,8 +127,12 @@ func hostArchive(ctxt *Link, name string) { pname := fmt.Sprintf("%s(%s)", name, arhdr.name) l = atolwhex(arhdr.size) - libgcc := sym.Library{Pkg: "libgcc"} - h := ldobj(ctxt, f, &libgcc, l, pname, name) + pkname := filepath.Base(name) + if i := strings.LastIndex(pkname, ".a"); i >= 0 { + pkname = pkname[:i] + } + libar := sym.Library{Pkg: pkname} + h := ldobj(ctxt, f, &libar, l, pname, name) if h.ld == nil { Errorf(nil, "%s unrecognized object file at offset %d", name, off) continue diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 2f9bf25d10..1f7b37f892 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -1469,7 +1469,6 @@ func TestIssue42484(t *testing.T) { // i1:0:1 f1:1:1 ~r0:2:2 ~r1:3:2 // // where each chunk above is of the form NAME:ORDER:INOUTCLASSIFICATION -// func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string { // Values in the returned map are of the form : // where order is the order within the child DIE list of the diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index cb094a373a..733f4ec00b 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -549,30 +549,31 @@ func elfMipsAbiFlags(sh *ElfShdr, startva uint64, resoff uint64) int { return n } -//typedef struct -//{ -// /* Version of flags structure. */ -// uint16_t version; -// /* The level of the ISA: 1-5, 32, 64. */ -// uint8_t isa_level; -// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ -// uint8_t isa_rev; -// /* The size of general purpose registers. */ -// uint8_t gpr_size; -// /* The size of co-processor 1 registers. */ -// uint8_t cpr1_size; -// /* The size of co-processor 2 registers. */ -// uint8_t cpr2_size; -// /* The floating-point ABI. */ -// uint8_t fp_abi; -// /* Processor-specific extension. */ -// uint32_t isa_ext; -// /* Mask of ASEs used. */ -// uint32_t ases; -// /* Mask of general flags. */ -// uint32_t flags1; -// uint32_t flags2; -//} Elf_Internal_ABIFlags_v0; +// Layout is given by this C definition: +// typedef struct +// { +// /* Version of flags structure. */ +// uint16_t version; +// /* The level of the ISA: 1-5, 32, 64. */ +// uint8_t isa_level; +// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ +// uint8_t isa_rev; +// /* The size of general purpose registers. */ +// uint8_t gpr_size; +// /* The size of co-processor 1 registers. */ +// uint8_t cpr1_size; +// /* The size of co-processor 2 registers. */ +// uint8_t cpr2_size; +// /* The floating-point ABI. */ +// uint8_t fp_abi; +// /* Processor-specific extension. */ +// uint32_t isa_ext; +// /* Mask of ASEs used. */ +// uint32_t ases; +// /* Mask of general flags. */ +// uint32_t flags1; +// uint32_t flags2; +// } Elf_Internal_ABIFlags_v0; func elfWriteMipsAbiFlags(ctxt *Link) int { sh := elfshname(".MIPS.abiflags") ctxt.Out.SeekSet(int64(sh.Off)) @@ -1100,16 +1101,18 @@ func elfshbits(linkmode LinkMode, sect *sym.Section) *ElfShdr { sh.Flags |= uint64(elf.SHF_TLS) sh.Type = uint32(elf.SHT_NOBITS) } + if linkmode != LinkExternal { + sh.Addr = sect.Vaddr + } + if strings.HasPrefix(sect.Name, ".debug") || strings.HasPrefix(sect.Name, ".zdebug") { sh.Flags = 0 + sh.Addr = 0 if sect.Compressed { sh.Flags |= uint64(elf.SHF_COMPRESSED) } } - if linkmode != LinkExternal { - sh.Addr = sect.Vaddr - } sh.Addralign = uint64(sect.Align) sh.Size = sect.Length if sect.Name != ".tbss" { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 6055d4327e..07cffcafff 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -614,25 +614,7 @@ func (ctxt *Link) loadlib() { *flagLibGCC = ctxt.findLibPathCmd("--print-file-name=libcompiler_rt.a", "libcompiler_rt") } if ctxt.HeadType == objabi.Hwindows { - if p := ctxt.findLibPath("libmingwex.a"); p != "none" { - hostArchive(ctxt, p) - } - if p := ctxt.findLibPath("libmingw32.a"); p != "none" { - hostArchive(ctxt, p) - } - // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol - // (see https://golang.org/issue/23649 for details). - if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { - hostArchive(ctxt, p) - } - // TODO: maybe do something similar to peimporteddlls to collect all lib names - // and try link them all to final exe just like libmingwex.a and libmingw32.a: - /* - for: - #cgo windows LDFLAGS: -lmsvcrt -lm - import: - libmsvcrt.a libm.a - */ + loadWindowsHostArchives(ctxt) } if *flagLibGCC != "none" { hostArchive(ctxt, *flagLibGCC) @@ -648,6 +630,67 @@ func (ctxt *Link) loadlib() { strictDupMsgCount = ctxt.loader.NStrictDupMsgs() } +// loadWindowsHostArchives loads in host archives and objects when +// doing internal linking on windows. Older toolchains seem to require +// just a single pass through the various archives, but some modern +// toolchains when linking a C program with mingw pass library paths +// multiple times to the linker, e.g. "... -lmingwex -lmingw32 ... +// -lmingwex -lmingw32 ...". To accommodate this behavior, we make two +// passes over the host archives below. +func loadWindowsHostArchives(ctxt *Link) { + any := true + for i := 0; any && i < 2; i++ { + // Link crt2.o (if present) to resolve "atexit" when + // using LLVM-based compilers. + isunresolved := symbolsAreUnresolved(ctxt, []string{"atexit"}) + if isunresolved[0] { + if p := ctxt.findLibPath("crt2.o"); p != "none" { + hostObject(ctxt, "crt2", p) + } + } + if p := ctxt.findLibPath("libmingwex.a"); p != "none" { + hostArchive(ctxt, p) + } + if p := ctxt.findLibPath("libmingw32.a"); p != "none" { + hostArchive(ctxt, p) + } + // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol + // (see https://golang.org/issue/23649 for details). + if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" { + hostArchive(ctxt, p) + } + any = false + undefs := ctxt.loader.UndefinedRelocTargets(1) + if len(undefs) > 0 { + any = true + } + } + // If needed, create the __CTOR_LIST__ and __DTOR_LIST__ + // symbols (referenced by some of the mingw support library + // routines). Creation of these symbols is normally done by the + // linker if not already present. + want := []string{"__CTOR_LIST__", "__DTOR_LIST__"} + isunresolved := symbolsAreUnresolved(ctxt, want) + for k, w := range want { + if isunresolved[k] { + sb := ctxt.loader.CreateSymForUpdate(w, 0) + sb.SetType(sym.SDATA) + sb.AddUint64(ctxt.Arch, 0) + sb.SetReachable(true) + ctxt.loader.SetAttrSpecial(sb.Sym(), true) + } + } + // TODO: maybe do something similar to peimporteddlls to collect + // all lib names and try link them all to final exe just like + // libmingwex.a and libmingw32.a: + /* + for: + #cgo windows LDFLAGS: -lmsvcrt -lm + import: + libmsvcrt.a libm.a + */ +} + // loadcgodirectives reads the previously discovered cgo directives, creating // symbols in preparation for host object loading or use later in the link. func (ctxt *Link) loadcgodirectives() { @@ -1311,13 +1354,52 @@ func (ctxt *Link) hostlink() { argv = append(argv, "-Wl,-bbigtoc") } - // Enable ASLR on Windows. - addASLRargs := func(argv []string) []string { - // Enable ASLR. - argv = append(argv, "-Wl,--dynamicbase") + // Enable/disable ASLR on Windows. + addASLRargs := func(argv []string, val bool) []string { + // Old/ancient versions of GCC support "--dynamicbase" and + // "--high-entropy-va" but don't enable it by default. In + // addition, they don't accept "--disable-dynamicbase" or + // "--no-dynamicbase", so the only way to disable ASLR is to + // not pass any flags at all. + // + // More modern versions of GCC (and also clang) enable ASLR + // by default. With these compilers, however you can turn it + // off if you want using "--disable-dynamicbase" or + // "--no-dynamicbase". + // + // The strategy below is to try using "--disable-dynamicbase"; + // if this succeeds, then assume we're working with more + // modern compilers and act accordingly. If it fails, assume + // an ancient compiler with ancient defaults. + var dbopt string + var heopt string + dbon := "--dynamicbase" + heon := "--high-entropy-va" + dboff := "--disable-dynamicbase" + heoff := "--disable-high-entropy-va" + if val { + dbopt = dbon + heopt = heon + } else { + // Test to see whether "--disable-dynamicbase" works. + newer := linkerFlagSupported(ctxt.Arch, argv[0], "", "-Wl,"+dboff) + if newer { + // Newer compiler, which supports both on/off options. + dbopt = dboff + heopt = heoff + } else { + // older toolchain: we have to say nothing in order to + // get a no-ASLR binary. + dbopt = "" + heopt = "" + } + } + if dbopt != "" { + argv = append(argv, "-Wl,"+dbopt) + } // enable high-entropy ASLR on 64-bit. - if ctxt.Arch.PtrSize >= 8 { - argv = append(argv, "-Wl,--high-entropy-va") + if ctxt.Arch.PtrSize >= 8 && heopt != "" { + argv = append(argv, "-Wl,"+heopt) } return argv } @@ -1334,7 +1416,7 @@ func (ctxt *Link) hostlink() { switch ctxt.HeadType { case objabi.Hdarwin, objabi.Haix: case objabi.Hwindows: - argv = addASLRargs(argv) + argv = addASLRargs(argv, *flagAslr) default: // ELF. if ctxt.UseRelro() { @@ -1351,9 +1433,7 @@ func (ctxt *Link) hostlink() { } argv = append(argv, "-shared") if ctxt.HeadType == objabi.Hwindows { - if *flagAslr { - argv = addASLRargs(argv) - } + argv = addASLRargs(argv, *flagAslr) } else { // Pass -z nodelete to mark the shared library as // non-closeable: a dlclose will do nothing. @@ -2001,6 +2081,59 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string, return nil } +// symbolsAreUnresolved scans through the loader's list of unresolved +// symbols and checks to see whether any of them match the names of the +// symbols in 'want'. Return value is a list of bools, with list[K] set +// to true if there is an unresolved reference to the symbol in want[K]. +func symbolsAreUnresolved(ctxt *Link, want []string) []bool { + returnAllUndefs := -1 + undefs := ctxt.loader.UndefinedRelocTargets(returnAllUndefs) + seen := make(map[loader.Sym]struct{}) + rval := make([]bool, len(want)) + wantm := make(map[string]int) + for k, w := range want { + wantm[w] = k + } + count := 0 + for _, s := range undefs { + if _, ok := seen[s]; ok { + continue + } + seen[s] = struct{}{} + if k, ok := wantm[ctxt.loader.SymName(s)]; ok { + rval[k] = true + count++ + if count == len(want) { + return rval + } + } + } + return rval +} + +// hostObject reads a single host object file (compare to "hostArchive"). +// This is used as part of internal linking when we need to pull in +// files such as "crt?.o". +func hostObject(ctxt *Link, objname string, path string) { + if ctxt.Debugvlog > 1 { + ctxt.Logf("hostObject(%s)\n", path) + } + objlib := sym.Library{ + Pkg: objname, + } + f, err := bio.Open(path) + if err != nil { + Exitf("cannot open host object %q file %s: %v", objname, path, err) + } + defer f.Close() + h := ldobj(ctxt, f, &objlib, 0, path, path) + if h.ld == nil { + Exitf("unrecognized object file format in %s", path) + } + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) +} + func checkFingerprint(lib *sym.Library, libfp goobj.FingerprintType, src string, srcfp goobj.FingerprintType) { if libfp != srcfp { Exitf("fingerprint mismatch: %s has %x, import from %s expecting %x", lib, libfp, src, srcfp) diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index 1d21dce9c5..1d1751ccdc 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -30,12 +30,12 @@ const outbufMode = 0775 // any system calls to read the value. // // Third, it also mmaps the output file (if available). The intended usage is: -// - Mmap the output file -// - Write the content -// - possibly apply any edits in the output buffer -// - possibly write more content to the file. These writes take place in a heap -// backed buffer that will get synced to disk. -// - Munmap the output file +// - Mmap the output file +// - Write the content +// - possibly apply any edits in the output buffer +// - possibly write more content to the file. These writes take place in a heap +// backed buffer that will get synced to disk. +// - Munmap the output file // // And finally, it provides a mechanism by which you can multithread the // writing of output files. This mechanism is accomplished by copying a OutBuf, diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index d46aa41181..2cbec5d06f 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -166,21 +166,21 @@ type symAndSize struct { // // Notes on the layout of global symbol index space: // -// - Go object files are read before host object files; each Go object -// read adds its defined package symbols to the global index space. -// Nonpackage symbols are not yet added. +// - Go object files are read before host object files; each Go object +// read adds its defined package symbols to the global index space. +// Nonpackage symbols are not yet added. // -// - In loader.LoadNonpkgSyms, add non-package defined symbols and -// references in all object files to the global index space. +// - In loader.LoadNonpkgSyms, add non-package defined symbols and +// references in all object files to the global index space. // -// - Host object file loading happens; the host object loader does a -// name/version lookup for each symbol it finds; this can wind up -// extending the external symbol index space range. The host object -// loader stores symbol payloads in loader.payloads using SymbolBuilder. +// - Host object file loading happens; the host object loader does a +// name/version lookup for each symbol it finds; this can wind up +// extending the external symbol index space range. The host object +// loader stores symbol payloads in loader.payloads using SymbolBuilder. // -// - Each symbol gets a unique global index. For duplicated and -// overwriting/overwritten symbols, the second (or later) appearance -// of the symbol gets the same global index as the first appearance. +// - Each symbol gets a unique global index. For duplicated and +// overwriting/overwritten symbols, the second (or later) appearance +// of the symbol gets the same global index as the first appearance. type Loader struct { start map[*oReader]Sym // map from object file to its start index objs []objIdx // sorted by start index (i.e. objIdx.i) @@ -2579,7 +2579,6 @@ type ErrorReporter struct { // // Logging an error means that on exit cmd/link will delete any // output file and return a non-zero error code. -// func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) { if s != 0 && reporter.ldr.SymName(s) != "" { // Note: Replace is needed here because symbol names might have % in them, diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go index 9cc7effe1f..bfe2e837c9 100644 --- a/src/cmd/link/internal/loadpe/ldpe.go +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -135,17 +135,6 @@ const ( IMAGE_REL_ARM64_REL32 = 0x0011 ) -// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe. -const ( - IMAGE_SCN_CNT_CODE = 0x00000020 - IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 - IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 - IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 - IMAGE_SCN_MEM_EXECUTE = 0x20000000 - IMAGE_SCN_MEM_READ = 0x40000000 - IMAGE_SCN_MEM_WRITE = 0x80000000 -) - // TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf // peBiobuf makes bio.Reader look like io.ReaderAt. @@ -173,14 +162,38 @@ func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loa return bld } +// peLoaderState holds various bits of useful state information needed +// while loading a PE object file. +type peLoaderState struct { + l *loader.Loader + arch *sys.Arch + f *pe.File + pn string + sectsyms map[*pe.Section]loader.Sym + defWithImp map[string]struct{} + comdats map[uint16]int64 // key is section index, val is size + sectdata map[*pe.Section][]byte + localSymVersion int +} + +// comdatDefinitions records the names of symbols for which we've +// previously seen a definition in COMDAT. Key is symbol name, value +// is symbol size (or -1 if we're using the "any" strategy). +var comdatDefinitions = make(map[string]int64) + // Load loads the PE file pn from input. // Symbols are written into syms, and a slice of the text symbols is returned. // If an .rsrc section or set of .rsrc$xx sections is found, its symbols are // returned as rsrc. func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) { - lookup := l.LookupOrCreateCgoExport - sectsyms := make(map[*pe.Section]loader.Sym) - sectdata := make(map[*pe.Section][]byte) + state := &peLoaderState{ + l: l, + arch: arch, + sectsyms: make(map[*pe.Section]loader.Sym), + sectdata: make(map[*pe.Section][]byte), + localSymVersion: localSymVersion, + pn: pn, + } // Some input files are archives containing multiple of // object files, and pe.NewFile seeks to the start of @@ -194,36 +207,37 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read return nil, nil, err } defer f.Close() + state.f = f // TODO return error if found .cormeta // create symbols for mapped sections for _, sect := range f.Sections { - if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + if sect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } - if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + if sect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { // This has been seen for .idata sections, which we // want to ignore. See issues 5106 and 5273. continue } name := fmt.Sprintf("%s(%s)", pkg, sect.Name) - s := lookup(name, localSymVersion) + s := state.l.LookupOrCreateCgoExport(name, localSymVersion) bld := l.MakeSymbolUpdater(s) - switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { - case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata + switch sect.Characteristics & (pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE | pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE) { + case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ: //.rdata bld.SetType(sym.SRODATA) - case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss + case pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.bss bld.SetType(sym.SNOPTRBSS) - case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data + case pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE: //.data bld.SetType(sym.SNOPTRDATA) - case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text + case pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.IMAGE_SCN_MEM_READ: //.text bld.SetType(sym.STEXT) default: @@ -235,41 +249,48 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read if err != nil { return nil, nil, err } - sectdata[sect] = data + state.sectdata[sect] = data bld.SetData(data) } bld.SetSize(int64(sect.Size)) - sectsyms[sect] = s + state.sectsyms[sect] = s if sect.Name == ".rsrc" || strings.HasPrefix(sect.Name, ".rsrc$") { rsrc = append(rsrc, s) } } + // Make a prepass over the symbols to detect situations where + // we have both a defined symbol X and an import symbol __imp_X + // (needed by readpesym()). + if err := state.preprocessSymbols(); err != nil { + return nil, nil, err + } + // load relocations for _, rsect := range f.Sections { - if _, found := sectsyms[rsect]; !found { + if _, found := state.sectsyms[rsect]; !found { continue } if rsect.NumberOfRelocations == 0 { continue } - if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + if rsect.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE != 0 { continue } - if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + if rsect.Characteristics&(pe.IMAGE_SCN_CNT_CODE|pe.IMAGE_SCN_CNT_INITIALIZED_DATA|pe.IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { // This has been seen for .idata sections, which we // want to ignore. See issues 5106 and 5273. continue } splitResources := strings.HasPrefix(rsect.Name, ".rsrc$") - sb := l.MakeSymbolUpdater(sectsyms[rsect]) + sb := l.MakeSymbolUpdater(state.sectsyms[rsect]) for j, r := range rsect.Relocs { if int(r.SymbolTableIndex) >= len(f.COFFSymbols) { return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols)) } pesym := &f.COFFSymbols[r.SymbolTableIndex] - _, gosym, err := readpesym(l, arch, lookup, f, pesym, sectsyms, localSymVersion) + _, gosym, err := state.readpesym(pesym) if err != nil { return nil, nil, err } @@ -292,20 +313,20 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read case sys.I386, sys.AMD64: switch r.Type { default: - return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type) + return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, state.sectsyms[rsect], r.Type) case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 IMAGE_REL_AMD64_ADDR32NB: rType = objabi.R_PCREL - rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: rType = objabi.R_ADDR // load addend from image - rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 rSize = 8 @@ -313,39 +334,39 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read rType = objabi.R_ADDR // load addend from image - rAdd = int64(binary.LittleEndian.Uint64(sectdata[rsect][rOff:])) + rAdd = int64(binary.LittleEndian.Uint64(state.sectdata[rsect][rOff:])) } case sys.ARM: switch r.Type { default: - return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type) + return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, state.sectsyms[rsect], r.Type) case IMAGE_REL_ARM_SECREL: rType = objabi.R_PCREL - rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) case IMAGE_REL_ARM_ADDR32, IMAGE_REL_ARM_ADDR32NB: rType = objabi.R_ADDR - rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) case IMAGE_REL_ARM_BRANCH24: rType = objabi.R_CALLARM - rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) } case sys.ARM64: switch r.Type { default: - return nil, nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, sectsyms[rsect], r.Type) + return nil, nil, fmt.Errorf("%s: %v: unknown ARM64 relocation type %v", pn, state.sectsyms[rsect], r.Type) case IMAGE_REL_ARM64_ADDR32, IMAGE_REL_ARM64_ADDR32NB: rType = objabi.R_ADDR - rAdd = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rOff:]))) + rAdd = int64(int32(binary.LittleEndian.Uint32(state.sectdata[rsect][rOff:]))) } } @@ -406,12 +427,12 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read var sect *pe.Section if pesym.SectionNumber > 0 { sect = f.Sections[pesym.SectionNumber-1] - if _, found := sectsyms[sect]; !found { + if _, found := state.sectsyms[sect]; !found { continue } } - bld, s, err := readpesym(l, arch, lookup, f, pesym, sectsyms, localSymVersion) + bld, s, err := state.readpesym(pesym) if err != nil { return nil, nil, err } @@ -430,7 +451,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read continue } else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) { sect = f.Sections[pesym.SectionNumber-1] - if _, found := sectsyms[sect]; !found { + if _, found := state.sectsyms[sect]; !found { return nil, nil, fmt.Errorf("%s: %v: missing sect.sym", pn, s) } } else { @@ -441,17 +462,27 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read return nil, nil, nil } + // Check for COMDAT symbol. + if sz, ok1 := state.comdats[uint16(pesym.SectionNumber-1)]; ok1 { + if psz, ok2 := comdatDefinitions[l.SymName(s)]; ok2 { + if sz == psz { + // OK to discard, we've seen an instance + // already. + continue + } + } + } if l.OuterSym(s) != 0 { if l.AttrDuplicateOK(s) { continue } outerName := l.SymName(l.OuterSym(s)) - sectName := l.SymName(sectsyms[sect]) + sectName := l.SymName(state.sectsyms[sect]) return nil, nil, fmt.Errorf("%s: duplicate symbol reference: %s in both %s and %s", pn, l.SymName(s), outerName, sectName) } bld = makeUpdater(l, bld, s) - sectsym := sectsyms[sect] + sectsym := state.sectsyms[sect] bld.SetType(l.SymType(sectsym)) l.AddInteriorSym(sectsym, s) bld.SetValue(int64(pesym.Value)) @@ -462,12 +493,20 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read } bld.SetExternal(true) } + if sz, ok := state.comdats[uint16(pesym.SectionNumber-1)]; ok { + // This is a COMDAT definition. Record that we're picking + // this instance so that we can ignore future defs. + if _, ok := comdatDefinitions[l.SymName(s)]; ok { + return nil, nil, fmt.Errorf("internal error: preexisting COMDAT definition for %q", name) + } + comdatDefinitions[l.SymName(s)] = sz + } } // Sort outer lists by address, adding to textp. // This keeps textp in increasing address order. for _, sect := range f.Sections { - s := sectsyms[sect] + s := state.sectsyms[sect] if s == 0 { continue } @@ -490,36 +529,30 @@ func issect(s *pe.COFFSymbol) bool { return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.' } -func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader.Sym, f *pe.File, pesym *pe.COFFSymbol, sectsyms map[*pe.Section]loader.Sym, localSymVersion int) (*loader.SymbolBuilder, loader.Sym, error) { - symname, err := pesym.FullName(f.StringTable) +func (state *peLoaderState) readpesym(pesym *pe.COFFSymbol) (*loader.SymbolBuilder, loader.Sym, error) { + symname, err := pesym.FullName(state.f.StringTable) if err != nil { return nil, 0, err } var name string if issect(pesym) { - name = l.SymName(sectsyms[f.Sections[pesym.SectionNumber-1]]) + name = state.l.SymName(state.sectsyms[state.f.Sections[pesym.SectionNumber-1]]) } else { name = symname - switch arch.Family { - case sys.AMD64: - if name == "__imp___acrt_iob_func" { - // Do not rename __imp___acrt_iob_func into __acrt_iob_func, - // because __imp___acrt_iob_func symbol is real - // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details). + if strings.HasPrefix(symname, "__imp_") { + orig := symname[len("__imp_"):] + if _, ok := state.defWithImp[orig]; ok { + // Don't rename __imp_XXX to XXX, since if we do this + // we'll wind up with a duplicate definition. One + // example is "__acrt_iob_func"; see commit b295099 + // from git://git.code.sf.net/p/mingw-w64/mingw-w64 + // for details. } else { name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name } - case sys.I386: - if name == "__imp____acrt_iob_func" { - // Do not rename __imp____acrt_iob_func into ___acrt_iob_func, - // because __imp____acrt_iob_func symbol is real - // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for details). - } else { - name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name - } - if name[0] == '_' { - name = name[1:] // _Name => Name - } + } + if state.arch.Family == sys.I386 && name[0] == '_' { + name = name[1:] // _Name => Name } } @@ -537,11 +570,11 @@ func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL: switch pesym.StorageClass { case IMAGE_SYM_CLASS_EXTERNAL: //global - s = lookup(name, 0) + s = state.l.LookupOrCreateCgoExport(name, 0) case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL: - s = lookup(name, localSymVersion) - bld = makeUpdater(l, bld, s) + s = state.l.LookupOrCreateCgoExport(name, state.localSymVersion) + bld = makeUpdater(state.l, bld, s) bld.SetDuplicateOK(true) default: @@ -549,14 +582,81 @@ func readpesym(l *loader.Loader, arch *sys.Arch, lookup func(string, int) loader } } - if s != 0 && l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) { - bld = makeUpdater(l, bld, s) + if s != 0 && state.l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) { + bld = makeUpdater(state.l, bld, s) bld.SetType(sym.SXREF) } if strings.HasPrefix(symname, "__imp_") { - bld = makeUpdater(l, bld, s) + bld = makeUpdater(state.l, bld, s) bld.SetGot(-2) // flag for __imp_ } return bld, s, nil } + +// preprocessSymbols walks the COFF symbols for the PE file we're +// reading and looks for cases where we have both a symbol definition +// for "XXX" and an "__imp_XXX" symbol, recording these cases in a map +// in the state struct. This information will be used in readpesym() +// above to give such symbols special treatment. This function also +// gathers information about COMDAT sections/symbols for later use +// in readpesym(). +func (state *peLoaderState) preprocessSymbols() error { + + // Locate comdat sections. + state.comdats = make(map[uint16]int64) + for i, s := range state.f.Sections { + if s.Characteristics&uint32(pe.IMAGE_SCN_LNK_COMDAT) != 0 { + state.comdats[uint16(i)] = int64(s.Size) + } + } + + // Examine symbol defs. + imp := make(map[string]struct{}) + def := make(map[string]struct{}) + for i, numaux := 0, 0; i < len(state.f.COFFSymbols); i += numaux + 1 { + pesym := &state.f.COFFSymbols[i] + numaux = int(pesym.NumberOfAuxSymbols) + if pesym.SectionNumber == 0 { // extern + continue + } + symname, err := pesym.FullName(state.f.StringTable) + if err != nil { + return err + } + def[symname] = struct{}{} + if strings.HasPrefix(symname, "__imp_") { + imp[strings.TrimPrefix(symname, "__imp_")] = struct{}{} + } + if _, isc := state.comdats[uint16(pesym.SectionNumber-1)]; !isc { + continue + } + if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) { + continue + } + // This symbol corresponds to a COMDAT section. Read the + // aux data for it. + auxsymp, err := state.f.COFFSymbolReadSectionDefAux(i) + if err != nil { + return fmt.Errorf("unable to read aux info for section def symbol %d %s: pe.COFFSymbolReadComdatInfo returns %v", i, symname, err) + } + if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_SAME_SIZE { + // This is supported. + } else if auxsymp.Selection == pe.IMAGE_COMDAT_SELECT_ANY { + // Also supported. + state.comdats[uint16(pesym.SectionNumber-1)] = int64(-1) + } else { + // We don't support any of the other strategies at the + // moment. I suspect that we may need to also support + // "associative", we'll see. + return fmt.Errorf("internal error: unsupported COMDAT selection strategy found in path=%s sec=%d strategy=%d idx=%d, please file a bug", state.pn, auxsymp.SecNum, auxsymp.Selection, i) + } + } + state.defWithImp = make(map[string]struct{}) + for n := range imp { + if _, ok := def[n]; ok { + state.defWithImp[n] = struct{}{} + } + } + return nil +} diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 1952971dcb..f26b3f3cf2 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -43,10 +43,10 @@ import ( // moduledata linked list at initialization time. This is only done if the runtime // is in a different module. // -// : -// larl %r2, -// jg -// undef +// : +// larl %r2, +// jg +// undef // // The job of appending the moduledata is delegated to runtime.addmoduledata. func gentext(ctxt *ld.Link, ldr *loader.Loader) { diff --git a/src/cmd/pprof/doc.go b/src/cmd/pprof/doc.go index 84de036610..6b8c28fe1d 100644 --- a/src/cmd/pprof/doc.go +++ b/src/cmd/pprof/doc.go @@ -4,9 +4,13 @@ // Pprof interprets and displays profiles of Go programs. // -// Usage: +// Basic usage: // // go tool pprof binary profile // -// For more information, see https://blog.golang.org/profiling-go-programs. +// For detailed usage information: +// +// go tool pprof -h +// +// For an example, see https://blog.golang.org/profiling-go-programs. package main diff --git a/src/compress/flate/huffman_code.go b/src/compress/flate/huffman_code.go index 891537ed5e..07b7827e1a 100644 --- a/src/compress/flate/huffman_code.go +++ b/src/compress/flate/huffman_code.go @@ -118,19 +118,20 @@ func (h *huffmanEncoder) bitLength(freq []int32) int { const maxBitsLimit = 16 -// Return the number of literals assigned to each bit size in the Huffman encoding -// -// This method is only called when list.length >= 3 +// bitCounts computes the number of literals assigned to each bit size in the Huffman encoding. +// It is only called when list.length >= 3. // The cases of 0, 1, and 2 literals are handled by special case code. // -// list An array of the literals with non-zero frequencies -// and their associated frequencies. The array is in order of increasing -// frequency, and has as its last element a special element with frequency -// MaxInt32 -// maxBits The maximum number of bits that should be used to encode any literal. -// Must be less than 16. -// return An integer array in which array[i] indicates the number of literals -// that should be encoded in i bits. +// list is an array of the literals with non-zero frequencies +// and their associated frequencies. The array is in order of increasing +// frequency and has as its last element a special element with frequency +// MaxInt32. +// +// maxBits is the maximum number of bits that should be used to encode any literal. +// It must be less than 16. +// +// bitCounts retruns an integer slice in which slice[i] indicates the number of literals +// that should be encoded in i bits. func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { if maxBits >= maxBitsLimit { panic("flate: maxBits too large") @@ -269,7 +270,7 @@ func (h *huffmanEncoder) assignEncodingAndSize(bitCount []int32, list []literalN // Update this Huffman Code object to be the minimum code for the specified frequency count. // -// freq An array of frequencies, in which frequency[i] gives the frequency of literal i. +// freq is an array of frequencies, in which freq[i] gives the frequency of literal i. // maxBits The maximum number of bits to use for any literal. func (h *huffmanEncoder) generate(freq []int32, maxBits int32) { if h.freqcache == nil { diff --git a/src/compress/zlib/reader.go b/src/compress/zlib/reader.go index a195b380d8..343a18bf68 100644 --- a/src/compress/zlib/reader.go +++ b/src/compress/zlib/reader.go @@ -32,7 +32,10 @@ import ( "io" ) -const zlibDeflate = 8 +const ( + zlibDeflate = 8 + zlibMaxWindow = 7 +) var ( // ErrChecksum is returned when reading ZLIB data that has an invalid checksum. @@ -143,7 +146,7 @@ func (z *reader) Reset(r io.Reader, dict []byte) error { return z.err } h := uint(z.scratch[0])<<8 | uint(z.scratch[1]) - if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) { + if (z.scratch[0]&0x0f != zlibDeflate) || (z.scratch[0]>>4 > zlibMaxWindow) || (h%31 != 0) { z.err = ErrHeader return z.err } diff --git a/src/compress/zlib/reader_test.go b/src/compress/zlib/reader_test.go index 70e33babd1..20cec696ee 100644 --- a/src/compress/zlib/reader_test.go +++ b/src/compress/zlib/reader_test.go @@ -65,7 +65,14 @@ var zlibTests = []zlibTest{ nil, }, { - "bad header", + "bad header (CINFO)", + "", + []byte{0x88, 0x98, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}, + nil, + ErrHeader, + }, + { + "bad header (FCHECK)", "", []byte{0x78, 0x9f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}, nil, diff --git a/src/container/ring/ring.go b/src/container/ring/ring.go index ce15032543..268670bc85 100644 --- a/src/container/ring/ring.go +++ b/src/container/ring/ring.go @@ -10,7 +10,6 @@ package ring // serves as reference to the entire ring. Empty rings are represented // as nil Ring pointers. The zero value for a Ring is a one-element // ring with a nil Value. -// type Ring struct { next, prev *Ring Value any // for use by client; untouched by this library @@ -40,7 +39,6 @@ func (r *Ring) Prev() *Ring { // Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0) // in the ring and returns that ring element. r must not be empty. -// func (r *Ring) Move(n int) *Ring { if r.next == nil { return r.init() @@ -89,7 +87,6 @@ func New(n int) *Ring { // them creates a single ring with the elements of s inserted // after r. The result points to the element following the // last element of s after insertion. -// func (r *Ring) Link(s *Ring) *Ring { n := r.Next() if s != nil { @@ -107,7 +104,6 @@ func (r *Ring) Link(s *Ring) *Ring { // Unlink removes n % r.Len() elements from the ring r, starting // at r.Next(). If n % r.Len() == 0, r remains unchanged. // The result is the removed subring. r must not be empty. -// func (r *Ring) Unlink(n int) *Ring { if n <= 0 { return nil @@ -117,7 +113,6 @@ func (r *Ring) Unlink(n int) *Ring { // Len computes the number of elements in ring r. // It executes in time proportional to the number of elements. -// func (r *Ring) Len() int { n := 0 if r != nil { diff --git a/src/crypto/cipher/gcm.go b/src/crypto/cipher/gcm.go index ba0af84a9d..47078ce594 100644 --- a/src/crypto/cipher/gcm.go +++ b/src/crypto/cipher/gcm.go @@ -13,7 +13,7 @@ import ( // AEAD is a cipher mode providing authenticated encryption with associated // data. For a description of the methodology, see -// https://en.wikipedia.org/wiki/Authenticated_encryption +// https://en.wikipedia.org/wiki/Authenticated_encryption. type AEAD interface { // NonceSize returns the size of the nonce that must be passed to Seal // and Open. diff --git a/src/crypto/elliptic/internal/fiat/p224_fiat64.go b/src/crypto/elliptic/internal/fiat/p224_fiat64.go index 4ece3e9220..588e9ea620 100644 --- a/src/crypto/elliptic/internal/fiat/p224_fiat64.go +++ b/src/crypto/elliptic/internal/fiat/p224_fiat64.go @@ -78,7 +78,6 @@ func p224CmovznzU64(out1 *uint64, arg1 p224Uint1, arg2 uint64, arg3 uint64) { // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p224Mul(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement, arg2 *p224MontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] @@ -383,7 +382,6 @@ func p224Mul(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m // 0 ≤ eval out1 < m -// func p224Square(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] @@ -689,7 +687,6 @@ func p224Square(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDoma // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p224Add(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement, arg2 *p224MontgomeryDomainFieldElement) { var x1 uint64 var x2 uint64 @@ -739,7 +736,6 @@ func p224Add(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p224Sub(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement, arg2 *p224MontgomeryDomainFieldElement) { var x1 uint64 var x2 uint64 @@ -777,7 +773,6 @@ func p224Sub(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = 1 mod m // 0 ≤ eval out1 < m -// func p224SetOne(out1 *p224MontgomeryDomainFieldElement) { out1[0] = 0xffffffff00000000 out1[1] = 0xffffffffffffffff @@ -792,7 +787,6 @@ func p224SetOne(out1 *p224MontgomeryDomainFieldElement) { // Postconditions: // eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m // 0 ≤ eval out1 < m -// func p224FromMontgomery(out1 *p224NonMontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement) { x1 := arg1[0] var x2 uint64 @@ -970,7 +964,6 @@ func p224FromMontgomery(out1 *p224NonMontgomeryDomainFieldElement, arg1 *p224Mon // Postconditions: // eval (from_montgomery out1) mod m = eval arg1 mod m // 0 ≤ eval out1 < m -// func p224ToMontgomery(out1 *p224MontgomeryDomainFieldElement, arg1 *p224NonMontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] diff --git a/src/crypto/elliptic/internal/fiat/p384_fiat64.go b/src/crypto/elliptic/internal/fiat/p384_fiat64.go index 493bed47e1..dc48cd38fb 100644 --- a/src/crypto/elliptic/internal/fiat/p384_fiat64.go +++ b/src/crypto/elliptic/internal/fiat/p384_fiat64.go @@ -78,7 +78,6 @@ func p384CmovznzU64(out1 *uint64, arg1 p384Uint1, arg2 uint64, arg3 uint64) { // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p384Mul(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement, arg2 *p384MontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] @@ -783,7 +782,6 @@ func p384Mul(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m // 0 ≤ eval out1 < m -// func p384Square(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] @@ -1489,7 +1487,6 @@ func p384Square(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDoma // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p384Add(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement, arg2 *p384MontgomeryDomainFieldElement) { var x1 uint64 var x2 uint64 @@ -1557,7 +1554,6 @@ func p384Add(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p384Sub(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement, arg2 *p384MontgomeryDomainFieldElement) { var x1 uint64 var x2 uint64 @@ -1609,7 +1605,6 @@ func p384Sub(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = 1 mod m // 0 ≤ eval out1 < m -// func p384SetOne(out1 *p384MontgomeryDomainFieldElement) { out1[0] = 0xffffffff00000001 out1[1] = 0xffffffff @@ -1626,7 +1621,6 @@ func p384SetOne(out1 *p384MontgomeryDomainFieldElement) { // Postconditions: // eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^6) mod m // 0 ≤ eval out1 < m -// func p384FromMontgomery(out1 *p384NonMontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement) { x1 := arg1[0] var x2 uint64 @@ -2096,7 +2090,6 @@ func p384FromMontgomery(out1 *p384NonMontgomeryDomainFieldElement, arg1 *p384Mon // Postconditions: // eval (from_montgomery out1) mod m = eval arg1 mod m // 0 ≤ eval out1 < m -// func p384ToMontgomery(out1 *p384MontgomeryDomainFieldElement, arg1 *p384NonMontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] diff --git a/src/crypto/elliptic/internal/fiat/p521_fiat64.go b/src/crypto/elliptic/internal/fiat/p521_fiat64.go index 9f4f290f4c..ea92c948fd 100644 --- a/src/crypto/elliptic/internal/fiat/p521_fiat64.go +++ b/src/crypto/elliptic/internal/fiat/p521_fiat64.go @@ -78,7 +78,6 @@ func p521CmovznzU64(out1 *uint64, arg1 p521Uint1, arg2 uint64, arg3 uint64) { // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p521Mul(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement, arg2 *p521MontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] @@ -1599,7 +1598,6 @@ func p521Mul(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m // 0 ≤ eval out1 < m -// func p521Square(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement) { x1 := arg1[1] x2 := arg1[2] @@ -3121,7 +3119,6 @@ func p521Square(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDoma // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p521Add(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement, arg2 *p521MontgomeryDomainFieldElement) { var x1 uint64 var x2 uint64 @@ -3216,7 +3213,6 @@ func p521Add(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m // 0 ≤ eval out1 < m -// func p521Sub(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement, arg2 *p521MontgomeryDomainFieldElement) { var x1 uint64 var x2 uint64 @@ -3289,7 +3285,6 @@ func p521Sub(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainF // Postconditions: // eval (from_montgomery out1) mod m = 1 mod m // 0 ≤ eval out1 < m -// func p521SetOne(out1 *p521MontgomeryDomainFieldElement) { out1[0] = 0x80000000000000 out1[1] = uint64(0x0) @@ -3309,7 +3304,6 @@ func p521SetOne(out1 *p521MontgomeryDomainFieldElement) { // Postconditions: // eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^9) mod m // 0 ≤ eval out1 < m -// func p521FromMontgomery(out1 *p521NonMontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement) { x1 := arg1[0] var x2 uint64 @@ -4263,7 +4257,6 @@ func p521FromMontgomery(out1 *p521NonMontgomeryDomainFieldElement, arg1 *p521Mon // Postconditions: // eval (from_montgomery out1) mod m = eval arg1 mod m // 0 ≤ eval out1 < m -// func p521ToMontgomery(out1 *p521MontgomeryDomainFieldElement, arg1 *p521NonMontgomeryDomainFieldElement) { var x1 uint64 var x2 uint64 diff --git a/src/crypto/elliptic/p256.go b/src/crypto/elliptic/p256.go index e1c6ff4f87..fcff1d3d39 100644 --- a/src/crypto/elliptic/p256.go +++ b/src/crypto/elliptic/p256.go @@ -51,7 +51,7 @@ func p256GetScalar(out *[32]byte, in []byte) { n := new(big.Int).SetBytes(in) var scalarBytes []byte - if n.Cmp(p256Params.N) >= 0 { + if n.Cmp(p256Params.N) >= 0 || len(in) > len(out) { n.Mod(n, p256Params.N) scalarBytes = n.Bytes() } else { @@ -282,7 +282,8 @@ var p256Zero31 = [p256Limbs]uint32{two31m3, two30m2, two31m2, two30p13m2, two31m // p256Diff sets out = in-in2. // // On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and -// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. +// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. +// // On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. func p256Diff(out, in, in2 *[p256Limbs]uint32) { var carry uint32 diff --git a/src/crypto/elliptic/p256_ppc64le.go b/src/crypto/elliptic/p256_ppc64le.go index e9a6a067a2..dda1157564 100644 --- a/src/crypto/elliptic/p256_ppc64le.go +++ b/src/crypto/elliptic/p256_ppc64le.go @@ -49,7 +49,6 @@ func (curve p256CurveFast) Params() *CurveParams { func p256MulAsm(res, in1, in2 []byte) // Montgomery square modulo P256 -// func p256Sqr(res, in []byte) { p256MulAsm(res, in, in) } @@ -98,7 +97,6 @@ func p256PointDoubleAsm(res, in *p256Point) // The result should be a slice in LE order, but the slice // from big.Bytes is in BE order. // TODO: For big endian implementation, do not reverse bytes. -// func fromBig(big *big.Int) []byte { // This could be done a lot more efficiently... res := big.Bytes() diff --git a/src/crypto/elliptic/p256_test.go b/src/crypto/elliptic/p256_test.go index c6862d9547..a607766bc6 100644 --- a/src/crypto/elliptic/p256_test.go +++ b/src/crypto/elliptic/p256_test.go @@ -136,3 +136,17 @@ func TestP256CombinedMult(t *testing.T) { t.Errorf("1×G + (-1)×G = (%d, %d), should be ∞", x, y) } } + +func TestIssue52075(t *testing.T) { + Gx, Gy := P256().Params().Gx, P256().Params().Gy + scalar := make([]byte, 33) + scalar[32] = 1 + x, y := P256().ScalarBaseMult(scalar) + if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 { + t.Errorf("unexpected output (%v,%v)", x, y) + } + x, y = P256().ScalarMult(Gx, Gy, scalar) + if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 { + t.Errorf("unexpected output (%v,%v)", x, y) + } +} diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go index 560f195d86..c9eb7d992d 100644 --- a/src/crypto/rand/rand_unix.go +++ b/src/crypto/rand/rand_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // Unix cryptographically secure pseudorandom number // generator. diff --git a/src/crypto/rand/util.go b/src/crypto/rand/util.go index 0f143a3830..11b1a28ec5 100644 --- a/src/crypto/rand/util.go +++ b/src/crypto/rand/util.go @@ -5,6 +5,7 @@ package rand import ( + "crypto/internal/randutil" "errors" "io" "math/big" @@ -17,6 +18,8 @@ func Prime(rand io.Reader, bits int) (*big.Int, error) { return nil, errors.New("crypto/rand: prime size must be at least 2-bit") } + randutil.MaybeReadByte(rand) + b := uint(bits % 8) if b == 0 { b = 8 diff --git a/src/crypto/rand/util_test.go b/src/crypto/rand/util_test.go index e76ce2018a..9caf8e91cc 100644 --- a/src/crypto/rand/util_test.go +++ b/src/crypto/rand/util_test.go @@ -38,6 +38,25 @@ func TestPrimeBitsLt2(t *testing.T) { } } +func TestPrimeNondeterministic(t *testing.T) { + r := mathrand.New(mathrand.NewSource(42)) + p0, err := rand.Prime(r, 32) + if err != nil { + t.Fatal(err) + } + for i := 0; i < 128; i++ { + r.Seed(42) + p, err := rand.Prime(r, 32) + if err != nil { + t.Fatal(err) + } + if p.Cmp(p0) != 0 { + return + } + } + t.Error("Prime always generated the same prime given the same input") +} + func TestInt(t *testing.T) { // start at 128 so the case of (max.BitLen() % 8) == 0 is covered for n := 128; n < 140; n++ { diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 76312984ab..a33107e2f4 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -269,7 +269,6 @@ var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map. // // The relative order of ECDSA and RSA cipher suites doesn't matter, // as they depend on the certificate. Pick one to get a stable order. -// var cipherSuitesPreferenceOrder = []uint16{ // AEADs w/ ECDHE TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, diff --git a/src/crypto/tls/handshake_unix_test.go b/src/crypto/tls/handshake_unix_test.go index b61e7c24ef..86a48f299b 100644 --- a/src/crypto/tls/handshake_unix_test.go +++ b/src/crypto/tls/handshake_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package tls diff --git a/src/crypto/x509/cert_pool.go b/src/crypto/x509/cert_pool.go index 873ffeee1d..ae43c84424 100644 --- a/src/crypto/x509/cert_pool.go +++ b/src/crypto/x509/cert_pool.go @@ -249,3 +249,16 @@ func (s *CertPool) Subjects() [][]byte { } return res } + +// Equal reports whether s and other are equal. +func (s *CertPool) Equal(other *CertPool) bool { + if s.systemPool != other.systemPool || len(s.haveSum) != len(other.haveSum) { + return false + } + for h := range s.haveSum { + if !other.haveSum[h] { + return false + } + } + return true +} diff --git a/src/crypto/x509/cert_pool_test.go b/src/crypto/x509/cert_pool_test.go new file mode 100644 index 0000000000..d1ec9aaefd --- /dev/null +++ b/src/crypto/x509/cert_pool_test.go @@ -0,0 +1,58 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import "testing" + +func TestCertPoolEqual(t *testing.T) { + a, b := NewCertPool(), NewCertPool() + if !a.Equal(b) { + t.Error("two empty pools not equal") + } + + tc := &Certificate{Raw: []byte{1, 2, 3}, RawSubject: []byte{2}} + a.AddCert(tc) + if a.Equal(b) { + t.Error("empty pool equals non-empty pool") + } + + b.AddCert(tc) + if !a.Equal(b) { + t.Error("two non-empty pools not equal") + } + + otherTC := &Certificate{Raw: []byte{9, 8, 7}, RawSubject: []byte{8}} + a.AddCert(otherTC) + if a.Equal(b) { + t.Error("non-equal pools equal") + } + + systemA, err := SystemCertPool() + if err != nil { + t.Fatalf("unable to load system cert pool: %s", err) + } + systemB, err := SystemCertPool() + if err != nil { + t.Fatalf("unable to load system cert pool: %s", err) + } + if !systemA.Equal(systemB) { + t.Error("two empty system pools not equal") + } + + systemA.AddCert(tc) + if systemA.Equal(systemB) { + t.Error("empty system pool equals non-empty system pool") + } + + systemB.AddCert(tc) + if !systemA.Equal(systemB) { + t.Error("two non-empty system pools not equal") + } + + systemA.AddCert(otherTC) + if systemA.Equal(systemB) { + t.Error("non-equal system pools equal") + } +} diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go index 75c212910b..eb91a5db6e 100644 --- a/src/crypto/x509/internal/macos/corefoundation.go +++ b/src/crypto/x509/internal/macos/corefoundation.go @@ -37,9 +37,12 @@ func CFDataToSlice(data CFRef) []byte { } // CFStringToString returns a Go string representation of the passed -// in CFString. +// in CFString, or an empty string if it's invalid. func CFStringToString(ref CFRef) string { - data := CFStringCreateExternalRepresentation(ref) + data, err := CFStringCreateExternalRepresentation(ref) + if err != nil { + return "" + } b := CFDataToSlice(data) CFRelease(data) return string(b) @@ -186,9 +189,12 @@ func x509_CFErrorCopyDescription_trampoline() //go:cgo_import_dynamic x509_CFStringCreateExternalRepresentation CFStringCreateExternalRepresentation "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation" -func CFStringCreateExternalRepresentation(strRef CFRef) CFRef { +func CFStringCreateExternalRepresentation(strRef CFRef) (CFRef, error) { ret := syscall(abi.FuncPCABI0(x509_CFStringCreateExternalRepresentation_trampoline), kCFAllocatorDefault, uintptr(strRef), kCFStringEncodingUTF8, 0, 0, 0) - return CFRef(ret) + if ret == 0 { + return 0, errors.New("string can't be represented as UTF-8") + } + return CFRef(ret), nil } func x509_CFStringCreateExternalRepresentation_trampoline() diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go index ef64bda49f..381d918a94 100644 --- a/src/crypto/x509/internal/macos/security.go +++ b/src/crypto/x509/internal/macos/security.go @@ -131,11 +131,16 @@ func x509_SecTrustCreateWithCertificates_trampoline() //go:cgo_import_dynamic x509_SecCertificateCreateWithData SecCertificateCreateWithData "/System/Library/Frameworks/Security.framework/Versions/A/Security" -func SecCertificateCreateWithData(b []byte) CFRef { +func SecCertificateCreateWithData(b []byte) (CFRef, error) { data := BytesToCFData(b) + defer CFRelease(data) ret := syscall(abi.FuncPCABI0(x509_SecCertificateCreateWithData_trampoline), kCFAllocatorDefault, uintptr(data), 0, 0, 0, 0) - CFRelease(data) - return CFRef(ret) + // Returns NULL if the data passed in the data parameter is not a valid + // DER-encoded X.509 certificate. + if ret == 0 { + return 0, errors.New("SecCertificateCreateWithData: invalid certificate") + } + return CFRef(ret), nil } func x509_SecCertificateCreateWithData_trampoline() diff --git a/src/crypto/x509/parser.go b/src/crypto/x509/parser.go index bb60cea7c9..333991bf14 100644 --- a/src/crypto/x509/parser.go +++ b/src/crypto/x509/parser.go @@ -941,10 +941,10 @@ func parseCertificate(der []byte) (*Certificate, error) { } if cert.Version > 1 { - if !tbs.SkipOptionalASN1(cryptobyte_asn1.Tag(1).Constructed().ContextSpecific()) { + if !tbs.SkipOptionalASN1(cryptobyte_asn1.Tag(1).ContextSpecific()) { return nil, errors.New("x509: malformed issuerUniqueID") } - if !tbs.SkipOptionalASN1(cryptobyte_asn1.Tag(2).Constructed().ContextSpecific()) { + if !tbs.SkipOptionalASN1(cryptobyte_asn1.Tag(2).ContextSpecific()) { return nil, errors.New("x509: malformed subjectUniqueID") } if cert.Version == 3 { diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go index ad365f577e..4759462653 100644 --- a/src/crypto/x509/root_darwin.go +++ b/src/crypto/x509/root_darwin.go @@ -12,8 +12,8 @@ import ( func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { certs := macOS.CFArrayCreateMutable() defer macOS.ReleaseCFArray(certs) - leaf := macOS.SecCertificateCreateWithData(c.Raw) - if leaf == 0 { + leaf, err := macOS.SecCertificateCreateWithData(c.Raw) + if err != nil { return nil, errors.New("invalid leaf certificate") } macOS.CFArrayAppendValue(certs, leaf) @@ -23,8 +23,8 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate if err != nil { return nil, err } - sc := macOS.SecCertificateCreateWithData(c.Raw) - if sc != 0 { + sc, err := macOS.SecCertificateCreateWithData(c.Raw) + if err == nil { macOS.CFArrayAppendValue(certs, sc) } } diff --git a/src/crypto/x509/root_windows_test.go b/src/crypto/x509/root_windows_test.go index ce6d9273d9..f6dafe4004 100644 --- a/src/crypto/x509/root_windows_test.go +++ b/src/crypto/x509/root_windows_test.go @@ -7,7 +7,11 @@ package x509_test import ( "crypto/tls" "crypto/x509" + "errors" "internal/testenv" + "net" + "strings" + "syscall" "testing" "time" ) @@ -17,10 +21,19 @@ func TestPlatformVerifier(t *testing.T) { t.Skip() } - getChain := func(host string) []*x509.Certificate { + getChain := func(t *testing.T, host string) []*x509.Certificate { t.Helper() c, err := tls.Dial("tcp", host+":443", &tls.Config{InsecureSkipVerify: true}) if err != nil { + // From https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2, + // matching the error string observed in https://go.dev/issue/52094. + const WSATRY_AGAIN syscall.Errno = 11002 + var errDNS *net.DNSError + if strings.HasSuffix(host, ".badssl.com") && errors.As(err, &errDNS) && strings.HasSuffix(errDNS.Err, WSATRY_AGAIN.Error()) { + t.Log(err) + testenv.SkipFlaky(t, 52094) + } + t.Fatalf("tls connection failed: %s", err) } return c.ConnectionState().PeerCertificates @@ -74,7 +87,7 @@ func TestPlatformVerifier(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - chain := getChain(tc.host) + chain := getChain(t, tc.host) var opts x509.VerifyOptions if len(chain) > 1 { opts.Intermediates = x509.NewCertPool() diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 98778fe455..71ab62a67f 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -736,6 +736,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V // list. (While this is not specified, it is common practice in order to limit // the types of certificates a CA can issue.) // +// Certificates that use SHA1WithRSA and ECDSAWithSHA1 signatures are not supported, +// and will not be used to build chains. +// // WARNING: this function doesn't do any revocation checking. func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { // Platform-specific verification needs the ASN.1 contents so diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index 837c42a3db..cb43079a9c 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -184,13 +184,13 @@ const ( MD2WithRSA // Unsupported. MD5WithRSA // Only supported for signing, not verification. - SHA1WithRSA // Only supported for signing, not verification. + SHA1WithRSA // Only supported for signing, and verification of CRLs, CSRs, and OCSP responses. SHA256WithRSA SHA384WithRSA SHA512WithRSA DSAWithSHA1 // Unsupported. DSAWithSHA256 // Unsupported. - ECDSAWithSHA1 // Only supported for signing, not verification. + ECDSAWithSHA1 // Only supported for signing, and verification of CRLs, CSRs, and OCSP responses. ECDSAWithSHA256 ECDSAWithSHA384 ECDSAWithSHA512 @@ -244,59 +244,56 @@ func (algo PublicKeyAlgorithm) String() string { // OIDs for signature algorithms // -// pkcs-1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } +// pkcs-1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } // // // RFC 3279 2.2.1 RSA Signature Algorithms // -// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } +// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } // -// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } +// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } // -// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } +// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } // -// dsaWithSha1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } +// dsaWithSha1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } // // RFC 3279 2.2.3 ECDSA Signature Algorithm // -// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-x962(10045) -// signatures(4) ecdsa-with-SHA1(1)} -// +// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-x962(10045) +// signatures(4) ecdsa-with-SHA1(1)} // // RFC 4055 5 PKCS #1 Version 1.5 // -// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } +// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } // -// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } +// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } // -// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } +// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } // // // RFC 5758 3.1 DSA Signature Algorithms // -// dsaWithSha256 OBJECT IDENTIFIER ::= { -// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) -// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} +// dsaWithSha256 OBJECT IDENTIFIER ::= { +// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) +// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} // // RFC 5758 3.2 ECDSA Signature Algorithm // -// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } +// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } // -// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } -// -// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } +// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } // +// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) +// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } // // RFC 8410 3 Curve25519 and Curve448 Algorithm Identifiers // -// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 } - +// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 } var ( oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} @@ -434,18 +431,18 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm // RFC 3279, 2.3 Public Key Algorithms // -// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// rsadsi(113549) pkcs(1) 1 } +// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) +// rsadsi(113549) pkcs(1) 1 } // // rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } // -// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// x9-57(10040) x9cm(4) 1 } +// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) +// x9-57(10040) x9cm(4) 1 } // // RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters // -// id-ecPublicKey OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } +// id-ecPublicKey OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } var ( oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} @@ -469,18 +466,18 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm // RFC 5480, 2.1.1.1. Named Curve // -// secp224r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 33 } +// secp224r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 33 } // -// secp256r1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) -// prime(1) 7 } +// secp256r1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) +// prime(1) 7 } // -// secp384r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 34 } +// secp384r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 34 } // -// secp521r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 35 } +// secp521r1 OBJECT IDENTIFIER ::= { +// iso(1) identified-organization(3) certicom(132) curve(0) 35 } // // NB: secp256r1 is equivalent to prime256v1 var ( @@ -537,16 +534,16 @@ const ( // RFC 5280, 4.2.1.12 Extended Key Usage // -// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } +// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } // -// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } +// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } // -// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } -// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } -// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } -// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } -// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } -// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } +// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } +// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } +// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } +// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } +// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } +// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } var ( oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} @@ -770,7 +767,7 @@ func (c *Certificate) hasSANExtension() bool { } // CheckSignatureFrom verifies that the signature on c is a valid signature -// from parent. +// from parent. SHA1WithRSA and ECDSAWithSHA1 signatures are not supported. func (c *Certificate) CheckSignatureFrom(parent *Certificate) error { // RFC 5280, 4.2.1.9: // "If the basic constraints extension is not present in a version 3 @@ -792,13 +789,13 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) error { // TODO(agl): don't ignore the path length constraint. - return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature) + return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature, parent.PublicKey, debugAllowSHA1) } // CheckSignature verifies that signature is a valid signature over signed from // c's public key. func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) error { - return checkSignature(algo, signed, signature, c.PublicKey) + return checkSignature(algo, signed, signature, c.PublicKey, true) } func (c *Certificate) hasNameConstraints() bool { @@ -818,9 +815,9 @@ func signaturePublicKeyAlgoMismatchError(expectedPubKeyAlgo PublicKeyAlgorithm, return fmt.Errorf("x509: signature algorithm specifies an %s public key, but have public key of type %T", expectedPubKeyAlgo.String(), pubKey) } -// CheckSignature verifies that signature is a valid signature over signed from +// checkSignature verifies that signature is a valid signature over signed from // a crypto.PublicKey. -func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) (err error) { +func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey, allowSHA1 bool) (err error) { var hashType crypto.Hash var pubKeyAlgo PublicKeyAlgorithm @@ -839,7 +836,7 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey case crypto.MD5: return InsecureAlgorithmError(algo) case crypto.SHA1: - if !debugAllowSHA1 { + if !allowSHA1 { return InsecureAlgorithmError(algo) } fallthrough @@ -1587,11 +1584,11 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv // Check the signature to ensure the crypto.Signer behaved correctly. sigAlg := getSignatureAlgorithmFromAI(signatureAlgorithm) switch sigAlg { - case MD5WithRSA, SHA1WithRSA, ECDSAWithSHA1: + case MD5WithRSA: // We skip the check if the signature algorithm is only supported for // signing, not verification. default: - if err := checkSignature(sigAlg, c.Raw, signature, key.Public()); err != nil { + if err := checkSignature(sigAlg, c.Raw, signature, key.Public(), true); err != nil { return nil, fmt.Errorf("x509: signature over certificate returned by signer is invalid: %w", err) } } @@ -2070,7 +2067,7 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error // CheckSignature reports whether the signature on c is valid. func (c *CertificateRequest) CheckSignature() error { - return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey) + return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey, true) } // RevocationList contains the fields used to create an X.509 v2 Certificate diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index 69dcd11543..d2889fc1d7 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -2924,30 +2924,15 @@ func TestCreateCertificateBrokenSigner(t *testing.T) { } func TestCreateCertificateLegacy(t *testing.T) { - ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatalf("Failed to generate ECDSA key: %s", err) + sigAlg := MD5WithRSA + template := &Certificate{ + SerialNumber: big.NewInt(10), + DNSNames: []string{"example.com"}, + SignatureAlgorithm: sigAlg, } - - for _, sigAlg := range []SignatureAlgorithm{ - MD5WithRSA, SHA1WithRSA, ECDSAWithSHA1, - } { - template := &Certificate{ - SerialNumber: big.NewInt(10), - DNSNames: []string{"example.com"}, - SignatureAlgorithm: sigAlg, - } - var k crypto.Signer - switch sigAlg { - case MD5WithRSA, SHA1WithRSA: - k = testPrivateKey - case ECDSAWithSHA1: - k = ecdsaPriv - } - _, err := CreateCertificate(rand.Reader, template, template, k.Public(), &brokenSigner{k.Public()}) - if err != nil { - t.Fatalf("CreateCertificate failed when SignatureAlgorithm = %v: %s", sigAlg, err) - } + _, err := CreateCertificate(rand.Reader, template, template, testPrivateKey.Public(), &brokenSigner{testPrivateKey.Public()}) + if err != nil { + t.Fatalf("CreateCertificate failed when SignatureAlgorithm = %v: %s", sigAlg, err) } } @@ -3348,3 +3333,114 @@ func TestLargeOID(t *testing.T) { t.Fatalf("ParseCertificate to failed to parse certificate with large OID: %s", err) } } + +const uniqueIDPEM = `-----BEGIN CERTIFICATE----- +MIIFsDCCBJigAwIBAgIIrOyC1ydafZMwDQYJKoZIhvcNAQEFBQAwgY4xgYswgYgG +A1UEAx6BgABNAGkAYwByAG8AcwBvAGYAdAAgAEYAbwByAGUAZgByAG8AbgB0ACAA +VABNAEcAIABIAFQAVABQAFMAIABJAG4AcwBwAGUAYwB0AGkAbwBuACAAQwBlAHIA +dABpAGYAaQBjAGEAdABpAG8AbgAgAEEAdQB0AGgAbwByAGkAdAB5MB4XDTE0MDEx +ODAwNDEwMFoXDTE1MTExNTA5Mzc1NlowgZYxCzAJBgNVBAYTAklEMRAwDgYDVQQI +EwdqYWthcnRhMRIwEAYDVQQHEwlJbmRvbmVzaWExHDAaBgNVBAoTE3N0aG9ub3Jl +aG90ZWxyZXNvcnQxHDAaBgNVBAsTE3N0aG9ub3JlaG90ZWxyZXNvcnQxJTAjBgNV +BAMTHG1haWwuc3Rob25vcmVob3RlbHJlc29ydC5jb20wggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCvuu0qpI+Ko2X84Twkf84cRD/rgp6vpgc5Ebejx/D4 +PEVON5edZkazrMGocK/oQqIlRxx/lefponN/chlGcllcVVPWTuFjs8k+Aat6T1qp +4iXxZekAqX+U4XZMIGJD3PckPL6G2RQSlF7/LhGCsRNRdKpMWSTbou2Ma39g52Kf +gsl3SK/GwLiWpxpcSkNQD1hugguEIsQYLxbeNwpcheXZtxbBGguPzQ7rH8c5vuKU +BkMOzaiNKLzHbBdFSrua8KWwCJg76Vdq/q36O9GlW6YgG3i+A4pCJjXWerI1lWwX +Ktk5V+SvUHGey1bkDuZKJ6myMk2pGrrPWCT7jP7WskChAgMBAAGBCQBCr1dgEleo +cKOCAfswggH3MIHDBgNVHREEgbswgbiCHG1haWwuc3Rob25vcmVob3RlbHJlc29y +dC5jb22CIGFzaGNoc3ZyLnN0aG9ub3JlaG90ZWxyZXNvcnQuY29tgiRBdXRvRGlz +Y292ZXIuc3Rob25vcmVob3RlbHJlc29ydC5jb22CHEF1dG9EaXNjb3Zlci5ob3Rl +bHJlc29ydC5jb22CCEFTSENIU1ZSghdzdGhvbm9yZWhvdGVscmVzb3J0LmNvbYIP +aG90ZWxyZXNvcnQuY29tMCEGCSsGAQQBgjcUAgQUHhIAVwBlAGIAUwBlAHIAdgBl +AHIwHQYDVR0OBBYEFMAC3UR4FwAdGekbhMgnd6lMejtbMAsGA1UdDwQEAwIFoDAT +BgNVHSUEDDAKBggrBgEFBQcDATAJBgNVHRMEAjAAMIG/BgNVHQEEgbcwgbSAFGfF +6xihk+gJJ5TfwvtWe1UFnHLQoYGRMIGOMYGLMIGIBgNVBAMegYAATQBpAGMAcgBv +AHMAbwBmAHQAIABGAG8AcgBlAGYAcgBvAG4AdAAgAFQATQBHACAASABUAFQAUABT +ACAASQBuAHMAcABlAGMAdABpAG8AbgAgAEMAZQByAHQAaQBmAGkAYwBhAHQAaQBv +AG4AIABBAHUAdABoAG8AcgBpAHQAeYIIcKhXEmBXr0IwDQYJKoZIhvcNAQEFBQAD +ggEBABlSxyCMr3+ANr+WmPSjyN5YCJBgnS0IFCwJAzIYP87bcTye/U8eQ2+E6PqG +Q7Huj7nfHEw9qnGo+HNyPp1ad3KORzXDb54c6xEoi+DeuPzYHPbn4c3hlH49I0aQ +eWW2w4RslSWpLvO6Y7Lboyz2/Thk/s2kd4RHxkkWpH2ltPqJuYYg3X6oM5+gIFHJ +WGnh+ojZ5clKvS5yXh3Wkj78M6sb32KfcBk0Hx6NkCYPt60ODYmWtvqwtw6r73u5 +TnTYWRNvo2svX69TriL+CkHY9O1Hkwf2It5zHl3gNiKTJVaak8AuEz/CKWZneovt +yYLwhUhg3PX5Co1VKYE+9TxloiE= +-----END CERTIFICATE-----` + +func TestParseUniqueID(t *testing.T) { + b, _ := pem.Decode([]byte(uniqueIDPEM)) + if b == nil { + t.Fatalf("couldn't decode test certificate") + } + cert, err := ParseCertificate(b.Bytes) + if err != nil { + t.Fatalf("ParseCertificate to failed to parse certificate with unique identifier id: %s", err) + } + if len(cert.Extensions) != 7 { + t.Fatalf("unexpected number of extensions (probably because the extension section was not parsed): got %d, want 7", len(cert.Extensions)) + } +} + +func TestDisableSHA1ForCertOnly(t *testing.T) { + defer func(old bool) { debugAllowSHA1 = old }(debugAllowSHA1) + debugAllowSHA1 = false + + tmpl := &Certificate{ + SerialNumber: big.NewInt(1), + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + SignatureAlgorithm: SHA1WithRSA, + BasicConstraintsValid: true, + IsCA: true, + KeyUsage: KeyUsageCertSign | KeyUsageCRLSign, + } + certDER, err := CreateCertificate(rand.Reader, tmpl, tmpl, rsaPrivateKey.Public(), rsaPrivateKey) + if err != nil { + t.Fatalf("failed to generate test cert: %s", err) + } + cert, err := ParseCertificate(certDER) + if err != nil { + t.Fatalf("failed to parse test cert: %s", err) + } + + err = cert.CheckSignatureFrom(cert) + if err == nil { + t.Error("expected CheckSignatureFrom to fail") + } else if _, ok := err.(InsecureAlgorithmError); !ok { + t.Errorf("expected InsecureAlgorithmError error, got %T", err) + } + + crlDER, err := CreateRevocationList(rand.Reader, &RevocationList{ + SignatureAlgorithm: SHA1WithRSA, + Number: big.NewInt(1), + ThisUpdate: time.Now().Add(-time.Hour), + NextUpdate: time.Now().Add(time.Hour), + }, cert, rsaPrivateKey) + if err != nil { + t.Fatalf("failed to generate test CRL: %s", err) + } + // TODO(rolandshoemaker): this should be ParseRevocationList once it lands + crl, err := ParseCRL(crlDER) + if err != nil { + t.Fatalf("failed to parse test CRL: %s", err) + } + + if err = cert.CheckCRLSignature(crl); err != nil { + t.Errorf("unexpected error: %s", err) + } + + // This is an unrelated OCSP response, which will fail signature verification + // but shouldn't return a InsecureAlgorithmError, since SHA1 should be allowed + // for OCSP. + ocspTBSHex := "30819fa2160414884451ff502a695e2d88f421bad90cf2cecbea7c180f32303133303631383037323434335a30743072304a300906052b0e03021a0500041448b60d38238df8456e4ee5843ea394111802979f0414884451ff502a695e2d88f421bad90cf2cecbea7c021100f78b13b946fc9635d8ab49de9d2148218000180f32303133303631383037323434335aa011180f32303133303632323037323434335a" + ocspTBS, err := hex.DecodeString(ocspTBSHex) + if err != nil { + t.Fatalf("failed to decode OCSP response TBS hex: %s", err) + } + + err = cert.CheckSignature(SHA1WithRSA, ocspTBS, nil) + if err != rsa.ErrVerification { + t.Errorf("unexpected error: %s", err) + } +} diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go index d1edcb8c48..d9b40ff53f 100644 --- a/src/database/sql/fakedb_test.go +++ b/src/database/sql/fakedb_test.go @@ -510,7 +510,7 @@ func errf(msg string, args ...any) error { // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=? // (note that where columns must always contain ? marks, -// just a limitation for fakedb) +// just a limitation for fakedb) func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (*fakeStmt, error) { if len(parts) != 3 { stmt.Close() @@ -1174,7 +1174,6 @@ func (rc *rowsCursor) NextResultSet() error { // This could be surprising behavior to retroactively apply to // driver.String now that Go1 is out, but this is convenient for // our TestPointerParamsAndScans. -// type fakeDriverString struct{} func (fakeDriverString) ConvertValue(v any) (driver.Value, error) { diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index d55cee1210..9a879464d8 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -184,7 +184,6 @@ type RawBytes []byte // } else { // // NULL value // } -// type NullString struct { String string Valid bool // Valid is true if String is not NULL diff --git a/src/debug/dwarf/entry.go b/src/debug/dwarf/entry.go index 3bc6a5454e..9f3f4971e1 100644 --- a/src/debug/dwarf/entry.go +++ b/src/debug/dwarf/entry.go @@ -381,7 +381,6 @@ func (i Class) GoString() string { // A common idiom is to merge the check for nil return with // the check that the value has the expected dynamic type, as in: // v, ok := e.Val(AttrSibling).(int64) -// func (e *Entry) Val(a Attr) any { if f := e.AttrField(a); f != nil { return f.Val @@ -974,7 +973,7 @@ func (r *Reader) SeekPC(pc uint64) (*Entry, error) { u := &r.d.unit[unit] r.b = makeBuf(r.d, u, "info", u.off, u.data) e, err := r.Next() - if err != nil { + if err != nil || e == nil || e.Tag == 0 { return nil, err } ranges, err := r.d.Ranges(e) diff --git a/src/debug/dwarf/entry_test.go b/src/debug/dwarf/entry_test.go index 393ad89f52..4e96dbfc1d 100644 --- a/src/debug/dwarf/entry_test.go +++ b/src/debug/dwarf/entry_test.go @@ -427,3 +427,18 @@ func TestIssue51758(t *testing.T) { } } } + +func TestIssue52045(t *testing.T) { + var abbrev, aranges, frame, line, pubnames, ranges, str []byte + info := []byte{0x7, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} + + // A hand-crafted input corresponding to a minimal-size + // .debug_info (header only, no DIEs) and an empty abbrev table. + data0, _ := New(abbrev, aranges, frame, info, line, pubnames, ranges, str) + reader0 := data0.Reader() + entry0, _ := reader0.SeekPC(0x0) + // main goal is to make sure we can get here without crashing + if entry0 != nil { + t.Errorf("got non-nil entry0, wanted nil") + } +} diff --git a/src/debug/elf/elf.go b/src/debug/elf/elf.go index 4c51bc4de2..5b2e6d9d3f 100644 --- a/src/debug/elf/elf.go +++ b/src/debug/elf/elf.go @@ -384,6 +384,7 @@ const ( EM_RISCV Machine = 243 /* RISC-V */ EM_LANAI Machine = 244 /* Lanai 32-bit processor */ EM_BPF Machine = 247 /* Linux BPF – in-kernel virtual machine */ + EM_LOONGARCH Machine = 258 /* LoongArch */ /* Non-standard or deprecated. */ EM_486 Machine = 6 /* Intel i486. */ @@ -575,6 +576,7 @@ var machineStrings = []intName{ {243, "EM_RISCV"}, {244, "EM_LANAI"}, {247, "EM_BPF"}, + {258, "EM_LOONGARCH"}, /* Non-standard or deprecated. */ {6, "EM_486"}, @@ -2150,6 +2152,118 @@ var rmipsStrings = []intName{ func (i R_MIPS) String() string { return stringName(uint32(i), rmipsStrings, false) } func (i R_MIPS) GoString() string { return stringName(uint32(i), rmipsStrings, true) } +// Relocation types for LARCH. +type R_LARCH int + +const ( + R_LARCH_NONE R_LARCH = 0 + R_LARCH_32 R_LARCH = 1 + R_LARCH_64 R_LARCH = 2 + R_LARCH_RELATIVE R_LARCH = 3 + R_LARCH_COPY R_LARCH = 4 + R_LARCH_JUMP_SLOT R_LARCH = 5 + R_LARCH_TLS_DTPMOD32 R_LARCH = 6 + R_LARCH_TLS_DTPMOD64 R_LARCH = 7 + R_LARCH_TLS_DTPREL32 R_LARCH = 8 + R_LARCH_TLS_DTPREL64 R_LARCH = 9 + R_LARCH_TLS_TPREL32 R_LARCH = 10 + R_LARCH_TLS_TPREL64 R_LARCH = 11 + R_LARCH_IRELATIVE R_LARCH = 12 + R_LARCH_MARK_LA R_LARCH = 20 + R_LARCH_MARK_PCREL R_LARCH = 21 + R_LARCH_SOP_PUSH_PCREL R_LARCH = 22 + R_LARCH_SOP_PUSH_ABSOLUTE R_LARCH = 23 + R_LARCH_SOP_PUSH_DUP R_LARCH = 24 + R_LARCH_SOP_PUSH_GPREL R_LARCH = 25 + R_LARCH_SOP_PUSH_TLS_TPREL R_LARCH = 26 + R_LARCH_SOP_PUSH_TLS_GOT R_LARCH = 27 + R_LARCH_SOP_PUSH_TLS_GD R_LARCH = 28 + R_LARCH_SOP_PUSH_PLT_PCREL R_LARCH = 29 + R_LARCH_SOP_ASSERT R_LARCH = 30 + R_LARCH_SOP_NOT R_LARCH = 31 + R_LARCH_SOP_SUB R_LARCH = 32 + R_LARCH_SOP_SL R_LARCH = 33 + R_LARCH_SOP_SR R_LARCH = 34 + R_LARCH_SOP_ADD R_LARCH = 35 + R_LARCH_SOP_AND R_LARCH = 36 + R_LARCH_SOP_IF_ELSE R_LARCH = 37 + R_LARCH_SOP_POP_32_S_10_5 R_LARCH = 38 + R_LARCH_SOP_POP_32_U_10_12 R_LARCH = 39 + R_LARCH_SOP_POP_32_S_10_12 R_LARCH = 40 + R_LARCH_SOP_POP_32_S_10_16 R_LARCH = 41 + R_LARCH_SOP_POP_32_S_10_16_S2 R_LARCH = 42 + R_LARCH_SOP_POP_32_S_5_20 R_LARCH = 43 + R_LARCH_SOP_POP_32_S_0_5_10_16_S2 R_LARCH = 44 + R_LARCH_SOP_POP_32_S_0_10_10_16_S2 R_LARCH = 45 + R_LARCH_SOP_POP_32_U R_LARCH = 46 + R_LARCH_ADD8 R_LARCH = 47 + R_LARCH_ADD16 R_LARCH = 48 + R_LARCH_ADD24 R_LARCH = 49 + R_LARCH_ADD32 R_LARCH = 50 + R_LARCH_ADD64 R_LARCH = 51 + R_LARCH_SUB8 R_LARCH = 52 + R_LARCH_SUB16 R_LARCH = 53 + R_LARCH_SUB24 R_LARCH = 54 + R_LARCH_SUB32 R_LARCH = 55 + R_LARCH_SUB64 R_LARCH = 56 +) + +var rlarchStrings = []intName{ + {0, "R_LARCH_NONE"}, + {1, "R_LARCH_32"}, + {2, "R_LARCH_64"}, + {3, "R_LARCH_RELATIVE"}, + {4, "R_LARCH_COPY"}, + {5, "R_LARCH_JUMP_SLOT"}, + {6, "R_LARCH_TLS_DTPMOD32"}, + {7, "R_LARCH_TLS_DTPMOD64"}, + {8, "R_LARCH_TLS_DTPREL32"}, + {9, "R_LARCH_TLS_DTPREL64"}, + {10, "R_LARCH_TLS_TPREL32"}, + {11, "R_LARCH_TLS_TPREL64"}, + {12, "R_LARCH_IRELATIVE"}, + {20, "R_LARCH_MARK_LA"}, + {21, "R_LARCH_MARK_PCREL"}, + {22, "R_LARCH_SOP_PUSH_PCREL"}, + {23, "R_LARCH_SOP_PUSH_ABSOLUTE"}, + {24, "R_LARCH_SOP_PUSH_DUP"}, + {25, "R_LARCH_SOP_PUSH_GPREL"}, + {26, "R_LARCH_SOP_PUSH_TLS_TPREL"}, + {27, "R_LARCH_SOP_PUSH_TLS_GOT"}, + {28, "R_LARCH_SOP_PUSH_TLS_GD"}, + {29, "R_LARCH_SOP_PUSH_PLT_PCREL"}, + {30, "R_LARCH_SOP_ASSERT"}, + {31, "R_LARCH_SOP_NOT"}, + {32, "R_LARCH_SOP_SUB"}, + {33, "R_LARCH_SOP_SL"}, + {34, "R_LARCH_SOP_SR"}, + {35, "R_LARCH_SOP_ADD"}, + {36, "R_LARCH_SOP_AND"}, + {37, "R_LARCH_SOP_IF_ELSE"}, + {38, "R_LARCH_SOP_POP_32_S_10_5"}, + {39, "R_LARCH_SOP_POP_32_U_10_12"}, + {40, "R_LARCH_SOP_POP_32_S_10_12"}, + {41, "R_LARCH_SOP_POP_32_S_10_16"}, + {42, "R_LARCH_SOP_POP_32_S_10_16_S2"}, + {43, "R_LARCH_SOP_POP_32_S_5_20"}, + {44, "R_LARCH_SOP_POP_32_S_0_5_10_16_S2"}, + {45, "R_LARCH_SOP_POP_32_S_0_10_10_16_S2"}, + {46, "R_LARCH_SOP_POP_32_U"}, + {47, "R_LARCH_ADD8"}, + {48, "R_LARCH_ADD16"}, + {49, "R_LARCH_ADD24"}, + {50, "R_LARCH_ADD32"}, + {51, "R_LARCH_ADD64"}, + {52, "R_LARCH_SUB8"}, + {53, "R_LARCH_SUB16"}, + {54, "R_LARCH_SUB24"}, + {55, "R_LARCH_SUB32"}, + {56, "R_LARCH_SUB64"}, +} + +func (i R_LARCH) String() string { return stringName(uint32(i), rlarchStrings, false) } +func (i R_LARCH) GoString() string { return stringName(uint32(i), rlarchStrings, true) } + // Relocation types for PowerPC. // // Values that are shared by both R_PPC and R_PPC64 are prefixed with diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index e93200a11d..6bfcd2a3f8 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -325,6 +325,13 @@ func NewFile(r io.ReaderAt) (*File, error) { shstrndx = int(hdr.Shstrndx) } + if shoff < 0 { + return nil, &FormatError{0, "invalid shoff", shoff} + } + if phoff < 0 { + return nil, &FormatError{0, "invalid phoff", phoff} + } + if shoff == 0 && shnum != 0 { return nil, &FormatError{0, "invalid ELF shnum for shoff=0", shnum} } @@ -419,6 +426,12 @@ func NewFile(r io.ReaderAt) (*File, error) { Entsize: sh.Entsize, } } + if int64(s.Offset) < 0 { + return nil, &FormatError{off, "invalid section offset", int64(s.Offset)} + } + if int64(s.FileSize) < 0 { + return nil, &FormatError{off, "invalid section size", int64(s.FileSize)} + } s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize)) if s.Flags&SHF_COMPRESSED == 0 { @@ -620,6 +633,8 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error { return f.applyRelocationsMIPS(dst, rels) case f.Class == ELFCLASS64 && f.Machine == EM_MIPS: return f.applyRelocationsMIPS64(dst, rels) + case f.Class == ELFCLASS64 && f.Machine == EM_LOONGARCH: + return f.applyRelocationsLOONG64(dst, rels) case f.Class == ELFCLASS64 && f.Machine == EM_RISCV: return f.applyRelocationsRISCV64(dst, rels) case f.Class == ELFCLASS64 && f.Machine == EM_S390: @@ -993,6 +1008,54 @@ func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error { return nil } +func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error { + // 24 is the size of Rela64. + if len(rels)%24 != 0 { + return errors.New("length of relocation section is not a multiple of 24") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + var symNo uint64 + var t R_LARCH + symNo = rela.Info >> 32 + t = R_LARCH(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + if !canApplyRelocation(sym) { + continue + } + + switch t { + case R_LARCH_64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + val64 := sym.Value + uint64(rela.Addend) + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) + case R_LARCH_32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + val32 := uint32(sym.Value) + uint32(rela.Addend) + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) + } + } + + return nil +} + func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error { // 24 is the size of Rela64. if len(rels)%24 != 0 { diff --git a/src/debug/elf/file_test.go b/src/debug/elf/file_test.go index 4c6fdeece9..c0decdd66e 100644 --- a/src/debug/elf/file_test.go +++ b/src/debug/elf/file_test.go @@ -913,14 +913,32 @@ func TestNoSectionOverlaps(t *testing.T) { if sih.Type == SHT_NOBITS { continue } + // checking for overlap in file for j, sj := range f.Sections { sjh := sj.SectionHeader - if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.Size == 0 { + if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.FileSize == 0 { continue } - if sih.Offset >= sjh.Offset && sih.Offset < sjh.Offset+sjh.Size { - t.Errorf("ld produced ELF with section %s within %s: 0x%x <= 0x%x..0x%x < 0x%x", - sih.Name, sjh.Name, sjh.Offset, sih.Offset, sih.Offset+sih.Size, sjh.Offset+sjh.Size) + if sih.Offset >= sjh.Offset && sih.Offset < sjh.Offset+sjh.FileSize { + t.Errorf("ld produced ELF with section offset %s within %s: 0x%x <= 0x%x..0x%x < 0x%x", + sih.Name, sjh.Name, sjh.Offset, sih.Offset, sih.Offset+sih.FileSize, sjh.Offset+sjh.FileSize) + } + } + + if sih.Flags&SHF_ALLOC == 0 { + continue + } + + // checking for overlap in address space + for j, sj := range f.Sections { + sjh := sj.SectionHeader + if i == j || sjh.Flags&SHF_ALLOC == 0 || sjh.Type == SHT_NOBITS || + sih.Addr == sjh.Addr && sih.Size == 0 { + continue + } + if sih.Addr >= sjh.Addr && sih.Addr < sjh.Addr+sjh.Size { + t.Errorf("ld produced ELF with section address %s within %s: 0x%x <= 0x%x..0x%x < 0x%x", + sih.Name, sjh.Name, sjh.Addr, sih.Addr, sih.Addr+sih.Size, sjh.Addr+sjh.Size) } } } diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go index d690a1e3f2..04b5fcc283 100644 --- a/src/debug/gosym/pclntab_test.go +++ b/src/debug/gosym/pclntab_test.go @@ -271,13 +271,12 @@ func TestPCLine(t *testing.T) { // read115Executable returns a hello world executable compiled by Go 1.15. // // The file was compiled in /tmp/hello.go: -// [BEGIN] -// package main // -// func main() { -// println("hello") -// } -// [END] +// package main +// +// func main() { +// println("hello") +// } func read115Executable(tb testing.TB) []byte { zippedDat, err := os.ReadFile("testdata/pcln115.gz") if err != nil { diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go index b641158ecc..ee59dedeb4 100644 --- a/src/debug/pe/section.go +++ b/src/debug/pe/section.go @@ -109,3 +109,15 @@ func (s *Section) Data() ([]byte, error) { func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } + +// Section characteristics flags. +const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_LNK_COMDAT = 0x00001000 + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 +) diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go index 7fa5948641..0dfd5d90b8 100644 --- a/src/debug/pe/symbol.go +++ b/src/debug/pe/symbol.go @@ -8,6 +8,7 @@ import ( "encoding/binary" "fmt" "io" + "unsafe" ) const COFFSymbolSize = 18 @@ -96,3 +97,61 @@ type Symbol struct { Type uint16 StorageClass uint8 } + +// COFFSymbolAuxFormat5 describes the expected form of an aux symbol +// attached to a section definition symbol. The PE format defines a +// number of different aux symbol formats: format 1 for function +// definitions, format 2 for .be and .ef symbols, and so on. Format 5 +// holds extra info associated with a section definition, including +// number of relocations + line numbers, as well as COMDAT info. See +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions +// for more on what's going on here. +type COFFSymbolAuxFormat5 struct { + Size uint32 + NumRelocs uint16 + NumLineNumbers uint16 + Checksum uint32 + SecNum uint16 + Selection uint8 + _ [3]uint8 // padding +} + +// These constants make up the possible values for the 'Selection' +// field in an AuxFormat5. +const ( + IMAGE_COMDAT_SELECT_NODUPLICATES = 1 + IMAGE_COMDAT_SELECT_ANY = 2 + IMAGE_COMDAT_SELECT_SAME_SIZE = 3 + IMAGE_COMDAT_SELECT_EXACT_MATCH = 4 + IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5 + IMAGE_COMDAT_SELECT_LARGEST = 6 +) + +// COFFSymbolReadSectionDefAux returns a blob of axiliary information +// (including COMDAT info) for a section definition symbol. Here 'idx' +// is the index of a section symbol in the main COFFSymbol array for +// the File. Return value is a pointer to the appropriate aux symbol +// struct. For more info, see: +// +// auxiliary symbols: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-symbol-records +// COMDAT sections: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#comdat-sections-object-only +// auxiliary info for section definitions: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions +// +func (f *File) COFFSymbolReadSectionDefAux(idx int) (*COFFSymbolAuxFormat5, error) { + var rv *COFFSymbolAuxFormat5 + if idx < 0 || idx > len(f.COFFSymbols) { + return rv, fmt.Errorf("invalid symbol index") + } + pesym := &f.COFFSymbols[idx] + const IMAGE_SYM_CLASS_STATIC = 3 + if pesym.StorageClass != uint8(IMAGE_SYM_CLASS_STATIC) { + return rv, fmt.Errorf("incorrect symbol storage class") + } + if pesym.NumberOfAuxSymbols == 0 || idx+1 >= len(f.COFFSymbols) { + return rv, fmt.Errorf("aux symbol unavailable") + } + // Locate and return a pointer to the successor aux symbol. + pesymn := &f.COFFSymbols[idx+1] + rv = (*COFFSymbolAuxFormat5)(unsafe.Pointer(pesymn)) + return rv, nil +} diff --git a/src/debug/pe/symbols_test.go b/src/debug/pe/symbols_test.go new file mode 100644 index 0000000000..5ccf635830 --- /dev/null +++ b/src/debug/pe/symbols_test.go @@ -0,0 +1,99 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "fmt" + "runtime" + "testing" +) + +type testpoint struct { + name string + ok bool + err string + auxstr string +} + +func TestReadCOFFSymbolAuxInfo(t *testing.T) { + + switch runtime.GOARCH { + case "mips", "mips64", "ppc64", "s390x": + t.Skipf("Skipping on %s (big endian) until issue #52079 fixed", + runtime.GOARCH) + } + + testpoints := map[int]testpoint{ + 39: testpoint{ + name: ".rdata$.refptr.__native_startup_lock", + ok: true, + auxstr: "{Size:8 NumRelocs:1 NumLineNumbers:0 Checksum:0 SecNum:16 Selection:2 _:[0 0 0]}", + }, + 81: testpoint{ + name: ".debug_line", + ok: true, + auxstr: "{Size:994 NumRelocs:1 NumLineNumbers:0 Checksum:1624223678 SecNum:32 Selection:0 _:[0 0 0]}", + }, + 155: testpoint{ + name: ".file", + ok: false, + err: "incorrect symbol storage class", + }, + } + + // The testdata PE object file below was selected from a release + // build from https://github.com/mstorsjo/llvm-mingw/releases; it + // corresponds to the mingw "crt2.o" object. The object itself was + // built using an x86_64 HOST=linux TARGET=windows clang cross + // compiler based on LLVM 13. More build details can be found at + // https://github.com/mstorsjo/llvm-mingw/releases. + f, err := Open("testdata/llvm-mingw-20211002-msvcrt-x86_64-crt2") + if err != nil { + t.Errorf("open failed with %v", err) + } + defer f.Close() + for k := range f.COFFSymbols { + tp, ok := testpoints[k] + if !ok { + continue + } + sym := &f.COFFSymbols[k] + if sym.NumberOfAuxSymbols == 0 { + t.Errorf("expected aux symbols for sym %d", k) + continue + } + name, nerr := sym.FullName(f.StringTable) + if nerr != nil { + t.Errorf("FullName(%d) failed with %v", k, nerr) + continue + } + if name != tp.name { + t.Errorf("name check for %d, got %s want %s", k, name, tp.name) + continue + } + ap, err := f.COFFSymbolReadSectionDefAux(k) + if tp.ok { + if err != nil { + t.Errorf("unexpected failure on %d, got error %v", k, err) + continue + } + got := fmt.Sprintf("%+v", *ap) + if got != tp.auxstr { + t.Errorf("COFFSymbolReadSectionDefAux on %d bad return, got:\n%s\nwant:\n%s\n", k, got, tp.auxstr) + continue + } + } else { + if err == nil { + t.Errorf("unexpected non-failure on %d", k) + continue + } + got := fmt.Sprintf("%v", err) + if got != tp.err { + t.Errorf("COFFSymbolReadSectionDefAux %d wrong error, got %q want %q", k, got, tp.err) + continue + } + } + } +} diff --git a/src/debug/pe/testdata/llvm-mingw-20211002-msvcrt-x86_64-crt2 b/src/debug/pe/testdata/llvm-mingw-20211002-msvcrt-x86_64-crt2 new file mode 100644 index 0000000000..5576c1c49e Binary files /dev/null and b/src/debug/pe/testdata/llvm-mingw-20211002-msvcrt-x86_64-crt2 differ diff --git a/src/encoding/ascii85/ascii85.go b/src/encoding/ascii85/ascii85.go index f1f7af863c..1f1fb00ffa 100644 --- a/src/encoding/ascii85/ascii85.go +++ b/src/encoding/ascii85/ascii85.go @@ -183,7 +183,6 @@ func (e CorruptInputError) Error() string { // than wait for the completion of another 32-bit block. // // NewDecoder wraps an io.Reader interface around Decode. -// func Decode(dst, src []byte, flush bool) (ndst, nsrc int, err error) { var v uint32 var nb int diff --git a/src/encoding/binary/varint.go b/src/encoding/binary/varint.go index 8fe20b5c45..bfb4dd193e 100644 --- a/src/encoding/binary/varint.go +++ b/src/encoding/binary/varint.go @@ -56,7 +56,6 @@ func PutUvarint(buf []byte, x uint64) int { // n == 0: buf too small // n < 0: value larger than 64 bits (overflow) // and -n is the number of bytes read -// func Uvarint(buf []byte) (uint64, int) { var x uint64 var s uint @@ -95,7 +94,6 @@ func PutVarint(buf []byte, x int64) int { // n == 0: buf too small // n < 0: value larger than 64 bits (overflow) // and -n is the number of bytes read -// func Varint(buf []byte) (int64, int) { ux, n := Uvarint(buf) // ok to continue in presence of error x := int64(ux >> 1) diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 555df0b7e8..ce9675a62f 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -92,7 +92,6 @@ import ( // invalid UTF-16 surrogate pairs are not treated as an error. // Instead, they are replaced by the Unicode replacement // character U+FFFD. -// func Unmarshal(data []byte, v any) error { // Check for well-formedness. // Avoids filling out half a data structure diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index 571ac094e2..fc865386ed 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -154,7 +154,6 @@ import ( // JSON cannot represent cyclic data structures and Marshal does not // handle them. Passing cyclic structures to Marshal will result in // an error. -// func Marshal(v any) ([]byte, error) { e := newEncodeState() diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go index 6362170d5d..b278ee4013 100644 --- a/src/encoding/json/stream.go +++ b/src/encoding/json/stream.go @@ -287,7 +287,6 @@ var _ Unmarshaler = (*RawMessage)(nil) // Number, for JSON numbers // string, for JSON string literals // nil, for JSON null -// type Token any const ( diff --git a/src/flag/flag.go b/src/flag/flag.go index cdea949a2f..15bcb6cea9 100644 --- a/src/flag/flag.go +++ b/src/flag/flag.go @@ -488,7 +488,7 @@ func Set(name, value string) error { // isZeroValue determines whether the string represents the zero // value for a flag. -func isZeroValue(flag *Flag, value string) bool { +func isZeroValue(flag *Flag, value string) (ok bool, err error) { // Build a zero value of the flag's Value type, and see if the // result of calling its String method equals the value passed in. // This works unless the Value type is itself an interface type. @@ -499,7 +499,18 @@ func isZeroValue(flag *Flag, value string) bool { } else { z = reflect.Zero(typ) } - return value == z.Interface().(Value).String() + // Catch panics calling the String method, which shouldn't prevent the + // usage message from being printed, but that we should report to the + // user so that they know to fix their code. + defer func() { + if e := recover(); e != nil { + if typ.Kind() == reflect.Pointer { + typ = typ.Elem() + } + err = fmt.Errorf("panic calling String method on zero %v for flag %s: %v", typ, flag.Name, e) + } + }() + return value == z.Interface().(Value).String(), nil } // UnquoteUsage extracts a back-quoted name from the usage @@ -545,6 +556,7 @@ func UnquoteUsage(flag *Flag) (name string, usage string) { // default values of all defined command-line flags in the set. See the // documentation for the global function PrintDefaults for more information. func (f *FlagSet) PrintDefaults() { + var isZeroValueErrs []error f.VisitAll(func(flag *Flag) { var b strings.Builder fmt.Fprintf(&b, " -%s", flag.Name) // Two spaces before -; see next two comments. @@ -564,7 +576,11 @@ func (f *FlagSet) PrintDefaults() { } b.WriteString(strings.ReplaceAll(usage, "\n", "\n \t")) - if !isZeroValue(flag, flag.DefValue) { + // Print the default value only if it differs to the zero value + // for this flag type. + if isZero, err := isZeroValue(flag, flag.DefValue); err != nil { + isZeroValueErrs = append(isZeroValueErrs, err) + } else if !isZero { if _, ok := flag.Value.(*stringValue); ok { // put quotes on the value fmt.Fprintf(&b, " (default %q)", flag.DefValue) @@ -574,6 +590,15 @@ func (f *FlagSet) PrintDefaults() { } fmt.Fprint(f.Output(), b.String(), "\n") }) + // If calling String on any zero flag.Values triggered a panic, print + // the messages after the full set of defaults so that the programmer + // knows to fix the panic. + if errs := isZeroValueErrs; len(errs) > 0 { + fmt.Fprintln(f.Output()) + for _, err := range errs { + fmt.Fprintln(f.Output(), err) + } + } } // PrintDefaults prints, to standard error unless configured otherwise, diff --git a/src/flag/flag_test.go b/src/flag/flag_test.go index d5c443d3c6..ca6ba5d149 100644 --- a/src/flag/flag_test.go +++ b/src/flag/flag_test.go @@ -432,6 +432,25 @@ func TestHelp(t *testing.T) { } } +// zeroPanicker is a flag.Value whose String method panics if its dontPanic +// field is false. +type zeroPanicker struct { + dontPanic bool + v string +} + +func (f *zeroPanicker) Set(s string) error { + f.v = s + return nil +} + +func (f *zeroPanicker) String() string { + if !f.dontPanic { + panic("panic!") + } + return f.v +} + const defaultOutput = ` -A for bootstrapping, allow 'any' type -Alongflagname disable bounds checking @@ -452,10 +471,19 @@ const defaultOutput = ` -A for bootstrapping, allow 'any' type a non-zero int (default 27) -O a flag multiline help string (default true) + -V list + a list of strings (default [a b]) -Z int an int that defaults to zero + -ZP0 value + a flag whose String method panics when it is zero + -ZP1 value + a flag whose String method panics when it is zero -maxT timeout set timeout for dial + +panic calling String method on zero flag_test.zeroPanicker for flag ZP0: panic! +panic calling String method on zero flag_test.zeroPanicker for flag ZP1: panic! ` func TestPrintDefaults(t *testing.T) { @@ -472,12 +500,15 @@ func TestPrintDefaults(t *testing.T) { fs.String("M", "", "a multiline\nhelp\nstring") fs.Int("N", 27, "a non-zero int") fs.Bool("O", true, "a flag\nmultiline help string") + fs.Var(&flagVar{"a", "b"}, "V", "a `list` of strings") fs.Int("Z", 0, "an int that defaults to zero") + fs.Var(&zeroPanicker{true, ""}, "ZP0", "a flag whose String method panics when it is zero") + fs.Var(&zeroPanicker{true, "something"}, "ZP1", "a flag whose String method panics when it is zero") fs.Duration("maxT", 0, "set `timeout` for dial") fs.PrintDefaults() got := buf.String() if got != defaultOutput { - t.Errorf("got %q want %q\n", got, defaultOutput) + t.Errorf("got:\n%q\nwant:\n%q", got, defaultOutput) } } diff --git a/src/fmt/doc.go b/src/fmt/doc.go index 6b49deda87..4151b39ea6 100644 --- a/src/fmt/doc.go +++ b/src/fmt/doc.go @@ -115,10 +115,11 @@ with %q, invalid Unicode code points are changed to the Unicode replacement character, U+FFFD, as in strconv.QuoteRune. Other flags: - + always print a sign for numeric values; + + '+' always print a sign for numeric values; guarantee ASCII-only output for %q (%+q) - - pad with spaces on the right rather than the left (left-justify the field) - # alternate format: add leading 0b for binary (%#b), 0 for octal (%#o), + '-' pad with spaces on the right rather than the left (left-justify the field) + '#' alternate format: add leading 0b for binary (%#b), 0 for octal (%#o), 0x or 0X for hex (%#x or %#X); suppress 0x for %p (%#p); for %q, print a raw (backquoted) string if strconv.CanBackquote returns true; @@ -127,7 +128,7 @@ Other flags: write e.g. U+0078 'x' if the character is printable for %U (%#U). ' ' (space) leave a space for elided sign in numbers (% d); put spaces between bytes printing strings or slices in hex (% x, % X) - 0 pad with leading zeros rather than spaces; + '0' pad with leading zeros rather than spaces; for numbers, this moves the padding after the sign; ignored for strings, byte slices and byte arrays diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index a74a827c8f..61855359f8 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -72,7 +72,6 @@ func (c *Comment) End() token.Pos { return token.Pos(int(c.Slash) + len(c.Text)) // A CommentGroup represents a sequence of comments // with no other tokens and no empty lines between. -// type CommentGroup struct { List []*Comment // len(List) > 0 } @@ -273,7 +272,6 @@ func (f *FieldList) NumFields() int { // An expression is represented by a tree consisting of one // or more of the following concrete expression nodes. -// type ( // A BadExpr node is a placeholder for an expression containing // syntax errors for which a correct expression node cannot be @@ -417,7 +415,6 @@ type ( // The direction of a channel type is indicated by a bit // mask including one or both of the following constants. -// type ChanDir int const ( @@ -428,7 +425,6 @@ const ( // A type is represented by a tree consisting of one // or more of the following type-specific expression // nodes. -// type ( // An ArrayType node represents an array or slice type. ArrayType struct { @@ -549,7 +545,6 @@ func (x *ChanType) End() token.Pos { return x.Value.End() } // exprNode() ensures that only expression/type nodes can be // assigned to an Expr. -// func (*BadExpr) exprNode() {} func (*Ident) exprNode() {} func (*Ellipsis) exprNode() {} @@ -580,15 +575,12 @@ func (*ChanType) exprNode() {} // NewIdent creates a new Ident without position. // Useful for ASTs generated by code other than the Go parser. -// func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} } // IsExported reports whether name starts with an upper-case letter. -// func IsExported(name string) bool { return token.IsExported(name) } // IsExported reports whether id starts with an upper-case letter. -// func (id *Ident) IsExported() bool { return token.IsExported(id.Name) } func (id *Ident) String() string { @@ -603,7 +595,6 @@ func (id *Ident) String() string { // A statement is represented by a tree consisting of one // or more of the following concrete statement nodes. -// type ( // A BadStmt node is a placeholder for statements containing // syntax errors for which no correct statement nodes can be @@ -854,7 +845,6 @@ func (s *RangeStmt) End() token.Pos { return s.Body.End() } // stmtNode() ensures that only statement nodes can be // assigned to a Stmt. -// func (*BadStmt) stmtNode() {} func (*DeclStmt) stmtNode() {} func (*EmptyStmt) stmtNode() {} @@ -882,7 +872,6 @@ func (*RangeStmt) stmtNode() {} // A Spec node represents a single (non-parenthesized) import, // constant, type, or variable declaration. -// type ( // The Spec type stands for any of *ImportSpec, *ValueSpec, and *TypeSpec. Spec interface { @@ -952,13 +941,11 @@ func (s *TypeSpec) End() token.Pos { return s.Type.End() } // specNode() ensures that only spec nodes can be // assigned to a Spec. -// func (*ImportSpec) specNode() {} func (*ValueSpec) specNode() {} func (*TypeSpec) specNode() {} // A declaration is represented by one of the following declaration nodes. -// type ( // A BadDecl node is a placeholder for a declaration containing // syntax errors for which a correct declaration node cannot be @@ -1020,7 +1007,6 @@ func (d *FuncDecl) End() token.Pos { // declNode() ensures that only declaration nodes can be // assigned to a Decl. -// func (*BadDecl) declNode() {} func (*GenDecl) declNode() {} func (*FuncDecl) declNode() {} @@ -1046,7 +1032,6 @@ func (*FuncDecl) declNode() {} // interpretation of the syntax tree by the manipulating program: Except for Doc // and Comment comments directly associated with nodes, the remaining comments // are "free-floating" (see also issues #18593, #20744). -// type File struct { Doc *CommentGroup // associated documentation; or nil Package token.Pos // position of "package" keyword @@ -1068,7 +1053,6 @@ func (f *File) End() token.Pos { // A Package node represents a set of source files // collectively building a Go package. -// type Package struct { Name string // package name Scope *Scope // package scope across all files diff --git a/src/go/ast/commentmap.go b/src/go/ast/commentmap.go index 5161ea70b7..9f81493f64 100644 --- a/src/go/ast/commentmap.go +++ b/src/go/ast/commentmap.go @@ -18,7 +18,6 @@ func (a byPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() } func (a byPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // sortComments sorts the list of comment groups in source order. -// func sortComments(list []*CommentGroup) { // TODO(gri): Does it make sense to check for sorted-ness // first (because we know that sorted-ness is @@ -31,7 +30,6 @@ func sortComments(list []*CommentGroup) { // A CommentMap maps an AST node to a list of comment groups // associated with it. See NewCommentMap for a description of // the association. -// type CommentMap map[Node][]*CommentGroup func (cmap CommentMap) addComment(n Node, c *CommentGroup) { @@ -54,7 +52,6 @@ func (a byInterval) Less(i, j int) bool { func (a byInterval) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // nodeList returns the list of nodes of the AST n in source order. -// func nodeList(n Node) []Node { var list []Node Inspect(n, func(n Node) bool { @@ -75,7 +72,6 @@ func nodeList(n Node) []Node { } // A commentListReader helps iterating through a list of comment groups. -// type commentListReader struct { fset *token.FileSet list []*CommentGroup @@ -99,12 +95,10 @@ func (r *commentListReader) next() { // A nodeStack keeps track of nested nodes. // A node lower on the stack lexically contains the nodes higher on the stack. -// type nodeStack []Node // push pops all nodes that appear lexically before n // and then pushes n on the stack. -// func (s *nodeStack) push(n Node) { s.pop(n.Pos()) *s = append((*s), n) @@ -113,7 +107,6 @@ func (s *nodeStack) push(n Node) { // pop pops all nodes that appear lexically before pos // (i.e., whose lexical extent has ended before or at pos). // It returns the last node popped. -// func (s *nodeStack) pop(pos token.Pos) (top Node) { i := len(*s) for i > 0 && (*s)[i-1].End() <= pos { @@ -139,7 +132,6 @@ func (s *nodeStack) pop(pos token.Pos) (top Node) { // node possible: For instance, if the comment is a line comment // trailing an assignment, the comment is associated with the entire // assignment rather than just the last operand in the assignment. -// func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) CommentMap { if len(comments) == 0 { return nil // no comments to map @@ -242,7 +234,6 @@ func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) Com // Update replaces an old node in the comment map with the new node // and returns the new node. Comments that were associated with the // old node are associated with the new node. -// func (cmap CommentMap) Update(old, new Node) Node { if list := cmap[old]; len(list) > 0 { delete(cmap, old) @@ -254,7 +245,6 @@ func (cmap CommentMap) Update(old, new Node) Node { // Filter returns a new comment map consisting of only those // entries of cmap for which a corresponding node exists in // the AST specified by node. -// func (cmap CommentMap) Filter(node Node) CommentMap { umap := make(CommentMap) Inspect(node, func(n Node) bool { @@ -268,7 +258,6 @@ func (cmap CommentMap) Filter(node Node) CommentMap { // Comments returns the list of comment groups in the comment map. // The result is sorted in source order. -// func (cmap CommentMap) Comments() []*CommentGroup { list := make([]*CommentGroup, 0, len(cmap)) for _, e := range cmap { diff --git a/src/go/ast/commentmap_test.go b/src/go/ast/commentmap_test.go index 38c62b01ab..281467c41f 100644 --- a/src/go/ast/commentmap_test.go +++ b/src/go/ast/commentmap_test.go @@ -73,7 +73,6 @@ func f3() { // res maps a key of the form "line number: node type" // to the associated comments' text. -// var res = map[string]string{ " 5: *ast.File": "the very first comment\npackage p\n", " 5: *ast.Ident": " the name is p\n", diff --git a/src/go/ast/filter.go b/src/go/ast/filter.go index c398e6e603..2fc73c4b99 100644 --- a/src/go/ast/filter.go +++ b/src/go/ast/filter.go @@ -24,7 +24,6 @@ func exportFilter(name string) bool { // stripped. The File.Comments list is not changed. // // FileExports reports whether there are exported declarations. -// func FileExports(src *File) bool { return filterFile(src, exportFilter, true) } @@ -35,7 +34,6 @@ func FileExports(src *File) bool { // // PackageExports reports whether there are exported declarations; // it returns false otherwise. -// func PackageExports(pkg *Package) bool { return filterPackage(pkg, exportFilter, true) } @@ -59,7 +57,6 @@ func filterIdentList(list []*Ident, f Filter) []*Ident { // fieldName assumes that x is the type of an anonymous field and // returns the corresponding field name. If x is not an acceptable // anonymous field, the result is nil. -// func fieldName(x Expr) *Ident { switch t := x.(type) { case *Ident: @@ -229,7 +226,6 @@ func filterSpecList(list []Spec, f Filter, export bool) []Spec { // // FilterDecl reports whether there are any declared names left after // filtering. -// func FilterDecl(decl Decl, f Filter) bool { return filterDecl(decl, f, false) } @@ -254,7 +250,6 @@ func filterDecl(decl Decl, f Filter, export bool) bool { // // FilterFile reports whether there are any top-level declarations // left after filtering. -// func FilterFile(src *File, f Filter) bool { return filterFile(src, f, false) } @@ -281,7 +276,6 @@ func filterFile(src *File, f Filter, export bool) bool { // // FilterPackage reports whether there are any top-level declarations // left after filtering. -// func FilterPackage(pkg *Package, f Filter) bool { return filterPackage(pkg, f, false) } @@ -315,7 +309,6 @@ const ( // nameOf returns the function (foo) or method name (foo.bar) for // the given function declaration. If the AST is incorrect for the // receiver, it assumes a function instead. -// func nameOf(f *FuncDecl) string { if r := f.Recv; r != nil && len(r.List) == 1 { // looks like a correct receiver declaration @@ -335,12 +328,10 @@ func nameOf(f *FuncDecl) string { // separator is an empty //-style comment that is interspersed between // different comment groups when they are concatenated into a single group -// var separator = &Comment{token.NoPos, "//"} // MergePackageFiles creates a file AST by merging the ASTs of the // files belonging to a package. The mode flags control merging behavior. -// func MergePackageFiles(pkg *Package, mode MergeMode) *File { // Count the number of package docs, comments and declarations across // all package files. Also, compute sorted list of filenames, so that diff --git a/src/go/ast/filter_test.go b/src/go/ast/filter_test.go index 9fd86cb467..86f396bb8b 100644 --- a/src/go/ast/filter_test.go +++ b/src/go/ast/filter_test.go @@ -38,7 +38,6 @@ func (x *t2) f2() {} // of one without, and it favors duplicate entries appearing // later in the source over ones appearing earlier. This is why // (*t2).f2 is kept and t2.f2 is eliminated in this test case. -// const golden = `package p type t1 struct{} diff --git a/src/go/ast/resolve.go b/src/go/ast/resolve.go index 126a27b18c..970aa88ad6 100644 --- a/src/go/ast/resolve.go +++ b/src/go/ast/resolve.go @@ -70,7 +70,6 @@ type Importer func(imports map[string]*Object, path string) (pkg *Object, err er // belong to different packages, one package name is selected and files with // different package names are reported and then ignored. // The result is a package node and a scanner.ErrorList if there were errors. -// func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, error) { var p pkgBuilder p.fset = fset diff --git a/src/go/ast/scope.go b/src/go/ast/scope.go index d24a5f0e00..02691f8e54 100644 --- a/src/go/ast/scope.go +++ b/src/go/ast/scope.go @@ -15,7 +15,6 @@ import ( // A Scope maintains the set of named language entities declared // in the scope and a link to the immediately surrounding (outer) // scope. -// type Scope struct { Outer *Scope Objects map[string]*Object @@ -30,7 +29,6 @@ func NewScope(outer *Scope) *Scope { // Lookup returns the object with the given name if it is // found in scope s, otherwise it returns nil. Outer scopes // are ignored. -// func (s *Scope) Lookup(name string) *Object { return s.Objects[name] } @@ -39,7 +37,6 @@ func (s *Scope) Lookup(name string) *Object { // If the scope already contains an object alt with the same name, // Insert leaves the scope unchanged and returns alt. Otherwise // it inserts obj and returns nil. -// func (s *Scope) Insert(obj *Object) (alt *Object) { if alt = s.Objects[obj.Name]; alt == nil { s.Objects[obj.Name] = obj @@ -72,7 +69,6 @@ func (s *Scope) String() string { // Kind Data type Data value // Pkg *Scope package scope // Con int iota for the respective declaration -// type Object struct { Kind ObjKind Name string // declared name diff --git a/src/go/ast/walk.go b/src/go/ast/walk.go index 308662f633..a293c99a10 100644 --- a/src/go/ast/walk.go +++ b/src/go/ast/walk.go @@ -40,14 +40,13 @@ func walkDeclList(v Visitor, list []Decl) { } // TODO(gri): Investigate if providing a closure to Walk leads to -// simpler use (and may help eliminate Inspect in turn). +// simpler use (and may help eliminate Inspect in turn). // Walk traverses an AST in depth-first order: It starts by calling // v.Visit(node); node must not be nil. If the visitor w returned by // v.Visit(node) is not nil, Walk is invoked recursively with visitor // w for each of the non-nil children of node, followed by a call of // w.Visit(nil). -// func Walk(v Visitor, node Node) { if v = v.Visit(node); v == nil { return @@ -394,7 +393,6 @@ func (f inspector) Visit(node Node) Visitor { // f(node); node must not be nil. If f returns true, Inspect invokes f // recursively for each of the non-nil children of node, followed by a // call of f(nil). -// func Inspect(node Node, f func(Node) bool) { Walk(inspector(f), node) } diff --git a/src/go/build/build.go b/src/go/build/build.go index 4ec9ed57a2..f156078dbf 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -534,7 +534,6 @@ func nameExt(name string) string { // // If an error occurs, Import returns a non-nil error and a non-nil // *Package containing partial information. -// func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) { p := &Package{ ImportPath: path, @@ -1813,7 +1812,6 @@ func safeCgoName(s string) bool { // Would be parsed as: // // []string{"a", "b:c d", "ef", `g"`} -// func splitQuoted(s string) (r []string, err error) { var args []string arg := make([]rune, len(s)) @@ -1979,6 +1977,10 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { } n := len(l) if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { + if allTags != nil { + // In case we short-circuit on l[n-1]. + allTags[l[n-2]] = true + } return ctxt.matchTag(l[n-1], allTags) && ctxt.matchTag(l[n-2], allTags) } if n >= 1 && (knownOS[l[n-1]] || knownArch[l[n-1]]) { diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go index 6cd7f9b589..36bcae179e 100644 --- a/src/go/build/build_test.go +++ b/src/go/build/build_test.go @@ -731,3 +731,39 @@ func TestCgoImportsIgnored(t *testing.T) { } } } + +// Issue #52053. Check that if there is a file x_GOOS_GOARCH.go that both +// GOOS and GOARCH show up in the Package.AllTags field. We test both the +// case where the file matches and where the file does not match. +// The latter case used to fail, incorrectly omitting GOOS. +func TestAllTags(t *testing.T) { + ctxt := Default + ctxt.GOARCH = "arm" + ctxt.GOOS = "netbsd" + p, err := ctxt.ImportDir("testdata/alltags", 0) + if err != nil { + t.Fatal(err) + } + want := []string{"arm", "netbsd"} + if !reflect.DeepEqual(p.AllTags, want) { + t.Errorf("AllTags = %v, want %v", p.AllTags, want) + } + wantFiles := []string{"alltags.go", "x_netbsd_arm.go"} + if !reflect.DeepEqual(p.GoFiles, wantFiles) { + t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) + } + + ctxt.GOARCH = "amd64" + ctxt.GOOS = "linux" + p, err = ctxt.ImportDir("testdata/alltags", 0) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(p.AllTags, want) { + t.Errorf("AllTags = %v, want %v", p.AllTags, want) + } + wantFiles = []string{"alltags.go"} + if !reflect.DeepEqual(p.GoFiles, wantFiles) { + t.Errorf("GoFiles = %v, want %v", p.GoFiles, wantFiles) + } +} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index ade6519b8d..f318f6e432 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -66,7 +66,6 @@ import ( // // All-caps names are pseudo-names for specific points // in the dependency lattice. -// var depsRules = ` # No dependencies allowed for any of these packages. NONE @@ -416,8 +415,8 @@ var depsRules = ` < crypto/internal/boring < crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4, crypto/sha1, crypto/sha256, crypto/sha512 - < crypto/rand < crypto/internal/randutil + < crypto/rand < crypto/ed25519 < golang.org/x/crypto/cryptobyte/asn1 < golang.org/x/crypto/cryptobyte diff --git a/src/go/build/testdata/alltags/alltags.go b/src/go/build/testdata/alltags/alltags.go new file mode 100644 index 0000000000..5d308550d1 --- /dev/null +++ b/src/go/build/testdata/alltags/alltags.go @@ -0,0 +1,5 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package alltags diff --git a/src/go/build/testdata/alltags/x_netbsd_arm.go b/src/go/build/testdata/alltags/x_netbsd_arm.go new file mode 100644 index 0000000000..5d308550d1 --- /dev/null +++ b/src/go/build/testdata/alltags/x_netbsd_arm.go @@ -0,0 +1,5 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package alltags diff --git a/src/go/constant/value.go b/src/go/constant/value.go index dee3bce9ee..3daa4c2860 100644 --- a/src/go/constant/value.go +++ b/src/go/constant/value.go @@ -70,9 +70,9 @@ type Value interface { const prec = 512 // TODO(gri) Consider storing "error" information in an unknownVal so clients -// can provide better error messages. For instance, if a number is -// too large (incl. infinity), that could be recorded in unknownVal. -// See also #20583 and #42695 for use cases. +// can provide better error messages. For instance, if a number is +// too large (incl. infinity), that could be recorded in unknownVal. +// See also #20583 and #42695 for use cases. // Representation of values: // @@ -578,7 +578,6 @@ func Float64Val(x Value) (float64, bool) { // Int int64 or *big.Int // Float *big.Float or *big.Rat // everything else nil -// func Val(x Value) any { switch x := x.(type) { case boolVal: @@ -609,7 +608,6 @@ func Val(x Value) any { // *big.Float Float // *big.Rat Float // anything else Unknown -// func Make(x any) Value { switch x := x.(type) { case bool: @@ -945,7 +943,6 @@ func is63bit(x int64) bool { // The operation must be defined for the operand. // If prec > 0 it specifies the ^ (xor) result size in bits. // If y is Unknown, the result is Unknown. -// func UnaryOp(op token.Token, y Value, prec uint) Value { switch op { case token.ADD: @@ -1035,7 +1032,6 @@ func ord(x Value) int { // smallest complexity for two values x and y. If one of them is // numeric, both of them must be numeric. If one of them is Unknown // or invalid (say, nil) both results are that value. -// func match(x, y Value) (_, _ Value) { switch ox, oy := ord(x), ord(y); { case ox < oy: @@ -1092,7 +1088,6 @@ func match0(x, y Value) (_, _ Value) { // To force integer division of Int operands, use op == token.QUO_ASSIGN // instead of token.QUO; the result is guaranteed to be Int in this case. // Division by zero leads to a run-time panic. -// func BinaryOp(x_ Value, op token.Token, y_ Value) Value { x, y := match(x_, y_) @@ -1272,7 +1267,6 @@ func quo(x, y Value) Value { return BinaryOp(x, token.QUO, y) } // Shift returns the result of the shift expression x op s // with op == token.SHL or token.SHR (<< or >>). x must be // an Int or an Unknown. If x is Unknown, the result is x. -// func Shift(x Value, op token.Token, s uint) Value { switch x := x.(type) { case unknownVal: @@ -1328,7 +1322,6 @@ func cmpZero(x int, op token.Token) bool { // The comparison must be defined for the operands. // If one of the operands is Unknown, the result is // false. -// func Compare(x_ Value, op token.Token, y_ Value) bool { x, y := match(x_, y_) diff --git a/src/go/doc/comment.go b/src/go/doc/comment.go index a93c05fbb7..f1aa69d974 100644 --- a/src/go/doc/comment.go +++ b/src/go/doc/comment.go @@ -28,8 +28,11 @@ var ( unicodeQuoteReplacer = strings.NewReplacer("``", ulquo, "''", urquo) ) -// Escape comment text for HTML. If nice is set, -// also turn `` into “ and '' into ”. +// Escape comment text for HTML. If nice is set, also replace: +// +// `` -> “ +// '' -> ” +// func commentEscape(w io.Writer, text string, nice bool) { if nice { // In the first pass, we convert `` and '' into their unicode equivalents. @@ -93,8 +96,10 @@ var ( // into a link). Go identifiers that appear in the words map are italicized; if // the corresponding map value is not the empty string, it is considered a URL // and the word is converted into a link. If nice is set, the remaining text's -// appearance is improved where it makes sense (e.g., `` is turned into “ -// and '' into ”). +// appearance is improved where it makes sense, such as replacing: +// +// `` -> “ +// '' -> ” func emphasize(w io.Writer, line string, words map[string]string, nice bool) { for { m := matchRx.FindStringSubmatchIndex(line) diff --git a/src/go/doc/doc.go b/src/go/doc/doc.go index 5ab854d084..f0c1b5dd32 100644 --- a/src/go/doc/doc.go +++ b/src/go/doc/doc.go @@ -114,7 +114,6 @@ const ( // New takes ownership of the AST pkg and may edit or overwrite it. // To have the Examples fields populated, use NewFromFiles and include // the package's _test.go files. -// func New(pkg *ast.Package, importPath string, mode Mode) *Package { var r reader r.readPackage(pkg, mode) @@ -156,7 +155,6 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package { // // NewFromFiles takes ownership of the AST files and may edit them, // unless the PreserveAST Mode bit is on. -// func NewFromFiles(fset *token.FileSet, files []*ast.File, importPath string, opts ...any) (*Package, error) { // Check for invalid API usage. if fset == nil { diff --git a/src/go/doc/example.go b/src/go/doc/example.go index 0a880cdefb..fcd59e100a 100644 --- a/src/go/doc/example.go +++ b/src/go/doc/example.go @@ -462,7 +462,6 @@ func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.Comm // or Foo (with a "bar" suffix). // // Examples with malformed names are not associated with anything. -// func classifyExamples(p *Package, examples []*Example) { if len(examples) == 0 { return diff --git a/src/go/doc/exports.go b/src/go/doc/exports.go index 671c622205..655e889293 100644 --- a/src/go/doc/exports.go +++ b/src/go/doc/exports.go @@ -13,7 +13,6 @@ import ( // filterIdentList removes unexported names from list in place // and returns the resulting list. -// func filterIdentList(list []*ast.Ident) []*ast.Ident { j := 0 for _, x := range list { @@ -69,7 +68,6 @@ func updateIdentList(list []*ast.Ident) (hasExported bool) { } // hasExportedName reports whether list contains any exported names. -// func hasExportedName(list []*ast.Ident) bool { for _, x := range list { if x.IsExported() { @@ -106,7 +104,6 @@ func removeAnonymousField(name string, ityp *ast.InterfaceType) { // in place and reports whether fields were removed. Anonymous fields are // recorded with the parent type. filterType is called with the types of // all remaining fields. -// func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) { if fields == nil { return @@ -159,7 +156,6 @@ func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp } // filterParamList applies filterType to each parameter type in fields. -// func (r *reader) filterParamList(fields *ast.FieldList) { if fields != nil { for _, f := range fields.List { @@ -171,7 +167,6 @@ func (r *reader) filterParamList(fields *ast.FieldList) { // filterType strips any unexported struct fields or method types from typ // in place. If fields (or methods) have been removed, the corresponding // struct or interface type has the Incomplete field set to true. -// func (r *reader) filterType(parent *namedType, typ ast.Expr) { switch t := typ.(type) { case *ast.Ident: @@ -255,7 +250,6 @@ func (r *reader) filterSpec(spec ast.Spec) bool { // copyConstType returns a copy of typ with position pos. // typ must be a valid constant type. // In practice, only (possibly qualified) identifiers are possible. -// func copyConstType(typ ast.Expr, pos token.Pos) ast.Expr { switch typ := typ.(type) { case *ast.Ident: @@ -318,7 +312,6 @@ func (r *reader) filterDecl(decl ast.Decl) bool { } // fileExports removes unexported declarations from src in place. -// func (r *reader) fileExports(src *ast.File) { j := 0 for _, d := range src.Decls { diff --git a/src/go/doc/filter.go b/src/go/doc/filter.go index 9904da150e..f8d3e1fca2 100644 --- a/src/go/doc/filter.go +++ b/src/go/doc/filter.go @@ -97,7 +97,6 @@ func filterTypes(a []*Type, f Filter) []*Type { // Filter eliminates documentation for names that don't pass through the filter f. // TODO(gri): Recognize "Type.Method" as a name. -// func (p *Package) Filter(f Filter) { p.Consts = filterValues(p.Consts, f) p.Vars = filterValues(p.Vars, f) diff --git a/src/go/doc/reader.go b/src/go/doc/reader.go index d9e721d01b..c591059e5c 100644 --- a/src/go/doc/reader.go +++ b/src/go/doc/reader.go @@ -21,12 +21,10 @@ import ( // A methodSet describes a set of methods. Entries where Decl == nil are conflict // entries (more than one method with the same name at the same embedding level). -// type methodSet map[string]*Func // recvString returns a string representation of recv of the form "T", "*T", // "T[A, ...]", "*T[A, ...]" or "BADRECV" (if not a proper receiver type). -// func recvString(recv ast.Expr) string { switch t := recv.(type) { case *ast.Ident: @@ -65,7 +63,6 @@ func recvParam(p ast.Expr) string { // If there are multiple f's with the same name, set keeps the first // one with documentation; conflicts are ignored. The boolean // specifies whether to leave the AST untouched. -// func (mset methodSet) set(f *ast.FuncDecl, preserveAST bool) { name := f.Name.Name if g := mset[name]; g != nil && g.Doc != "" { @@ -101,7 +98,6 @@ func (mset methodSet) set(f *ast.FuncDecl, preserveAST bool) { // add adds method m to the method set; m is ignored if the method set // already contains a method with the same name at the same or a higher // level than m. -// func (mset methodSet) add(m *Func) { old := mset[m.Name] if old == nil || m.Level < old.Level { @@ -122,7 +118,6 @@ func (mset methodSet) add(m *Func) { // baseTypeName returns the name of the base type of x (or "") // and whether the type is imported or not. -// func baseTypeName(x ast.Expr) (name string, imported bool) { switch t := x.(type) { case *ast.Ident: @@ -151,7 +146,6 @@ type embeddedSet map[*namedType]bool // A namedType represents a named unqualified (package local, or possibly // predeclared) type. The namedType for a type name is always found via // reader.lookupType. -// type namedType struct { doc string // doc comment for type name string // type name @@ -176,7 +170,6 @@ type namedType struct { // in the respective AST nodes so that they are not printed // twice (once when printing the documentation and once when // printing the corresponding AST node). -// type reader struct { mode Mode @@ -206,7 +199,6 @@ func (r *reader) isVisible(name string) bool { // If the base type has not been encountered yet, a new // type with the given name but no associated declaration // is added to the type map. -// func (r *reader) lookupType(name string) *namedType { if name == "" || name == "_" { return nil // no type docs for anonymous types @@ -229,7 +221,6 @@ func (r *reader) lookupType(name string) *namedType { // anonymous field in the parent type. If the field is imported // (qualified name) or the parent is nil, the field is ignored. // The function returns the field name. -// func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) { fname, imp := baseTypeName(fieldType) if parent == nil || imp { @@ -273,7 +264,6 @@ func specNames(specs []ast.Spec) []string { } // readValue processes a const or var declaration. -// func (r *reader) readValue(decl *ast.GenDecl) { // determine if decl should be associated with a type // Heuristic: For each typed entry, determine the type name, if any. @@ -347,7 +337,6 @@ func (r *reader) readValue(decl *ast.GenDecl) { } // fields returns a struct's fields or an interface's methods. -// func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) { var fields *ast.FieldList switch t := typ.(type) { @@ -364,7 +353,6 @@ func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) { } // readType processes a type declaration. -// func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) { typ := r.lookupType(spec.Name.Name) if typ == nil { @@ -400,13 +388,11 @@ func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) { } // isPredeclared reports whether n denotes a predeclared type. -// func (r *reader) isPredeclared(n string) bool { return predeclaredTypes[n] && r.types[n] == nil } // readFunc processes a func or method declaration. -// func (r *reader) readFunc(fun *ast.FuncDecl) { // strip function body if requested. if r.mode&PreserveAST == 0 { @@ -500,7 +486,6 @@ var ( ) // readNote collects a single note from a sequence of comments. -// func (r *reader) readNote(list []*ast.Comment) { text := (&ast.CommentGroup{List: list}).Text() if m := noteMarkerRx.FindStringSubmatchIndex(text); m != nil { @@ -526,7 +511,6 @@ func (r *reader) readNote(list []*ast.Comment) { // and is followed by the note body (e.g., "// BUG(gri): fix this"). // The note ends at the end of the comment group or at the start of // another note in the same comment group, whichever comes first. -// func (r *reader) readNotes(comments []*ast.CommentGroup) { for _, group := range comments { i := -1 // comment index of most recent note start, valid if >= 0 @@ -546,7 +530,6 @@ func (r *reader) readNotes(comments []*ast.CommentGroup) { } // readFile adds the AST for a source file to the reader. -// func (r *reader) readFile(src *ast.File) { // add package documentation if src.Doc != nil { @@ -696,7 +679,6 @@ func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) } // collectEmbeddedMethods collects the embedded methods of typ in mset. -// func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) { visited[typ] = true for embedded, isPtr := range typ.embedded { @@ -720,7 +702,6 @@ func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvType } // computeMethodSets determines the actual method sets for each type encountered. -// func (r *reader) computeMethodSets() { for _, t := range r.types { // collect embedded methods for t @@ -746,7 +727,6 @@ func (r *reader) computeMethodSets() { // types that have no declaration. Instead, these functions and methods // are shown at the package level. It also removes types with missing // declarations or which are not visible. -// func (r *reader) cleanupTypes() { for _, t := range r.types { visible := r.isVisible(t.name) @@ -813,7 +793,6 @@ func sortedKeys(m map[string]int) []string { } // sortingName returns the name to use when sorting d into place. -// func sortingName(d *ast.GenDecl) string { if len(d.Specs) == 1 { if s, ok := d.Specs[0].(*ast.ValueSpec); ok { @@ -906,7 +885,6 @@ func sortedFuncs(m methodSet, allMethods bool) []*Func { // noteBodies returns a list of note body strings given a list of notes. // This is only used to populate the deprecated Package.Bugs field. -// func noteBodies(notes []*Note) []string { var list []string for _, n := range notes { diff --git a/src/go/doc/synopsis.go b/src/go/doc/synopsis.go index 3fa1616cd1..ca607cc4e5 100644 --- a/src/go/doc/synopsis.go +++ b/src/go/doc/synopsis.go @@ -12,7 +12,6 @@ import ( // firstSentenceLen returns the length of the first sentence in s. // The sentence ends after the first period followed by space and // not preceded by exactly one uppercase letter. -// func firstSentenceLen(s string) int { var ppp, pp, p rune for i, q := range s { @@ -64,7 +63,6 @@ func clean(s string, flags int) string { // has no \n, \r, or \t characters and uses only single spaces between // words. If s starts with any of the IllegalPrefixes, the result // is the empty string. -// func Synopsis(s string) string { s = clean(s[0:firstSentenceLen(s)], 0) for _, prefix := range IllegalPrefixes { diff --git a/src/go/format/benchmark_test.go b/src/go/format/benchmark_test.go index ac19aa3bf5..d434d6f549 100644 --- a/src/go/format/benchmark_test.go +++ b/src/go/format/benchmark_test.go @@ -31,7 +31,6 @@ var debug = flag.Bool("debug", false, "write .src files containing formatting in // 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, // 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // ... -// func array1(buf *bytes.Buffer, n int) { buf.WriteString("var _ = [...]byte{\n") for i := 0; i < n; { diff --git a/src/go/format/format.go b/src/go/format/format.go index ea8dd20823..fb87e84a4e 100644 --- a/src/go/format/format.go +++ b/src/go/format/format.go @@ -50,7 +50,6 @@ const parserMode = parser.ParseComments // // The function may return early (before the entire result is written) // and return a formatting error, for instance due to an incorrect AST. -// func Node(dst io.Writer, fset *token.FileSet, node any) error { // Determine if we have a complete source file (file != nil). var file *ast.File @@ -99,7 +98,6 @@ func Node(dst io.Writer, fset *token.FileSet, node any) error { // is applied to the result (such that it has the same leading and trailing // space as src), and the result is indented by the same amount as the first // line of src containing code. Imports are not sorted for partial source files. -// func Source(src []byte) ([]byte, error) { fset := token.NewFileSet() file, sourceAdj, indentAdj, err := parse(fset, "", src, true) diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go index 48335fa6d8..10402fe43e 100644 --- a/src/go/internal/gccgoimporter/parser.go +++ b/src/go/internal/gccgoimporter/parser.go @@ -187,7 +187,6 @@ func (p *parser) parseQualifiedNameStr(unquotedName string) (pkgpath, name strin // getPkg returns the package for a given path. If the package is // not found but we have a package name, create the package and // add it to the p.imports map. -// func (p *parser) getPkg(pkgpath, name string) *types.Package { // package unsafe is not in the imports map - handle explicitly if pkgpath == "unsafe" { @@ -934,7 +933,6 @@ func lookupBuiltinType(typ int) types.Type { // Type = "<" "type" ( "-" int | int [ TypeSpec ] ) ">" . // // parseType updates the type map to t for all type numbers n. -// func (p *parser) parseType(pkg *types.Package, n ...any) types.Type { p.expect('<') t, _ := p.parseTypeAfterAngle(pkg, n...) diff --git a/src/go/internal/gcimporter/exportdata.go b/src/go/internal/gcimporter/exportdata.go index c12e459c3d..42e6ea9077 100644 --- a/src/go/internal/gcimporter/exportdata.go +++ b/src/go/internal/gcimporter/exportdata.go @@ -41,7 +41,7 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) { // start of the file before calling this function. The hdr result // is the string before the export data, either "$$" or "$$B". // -func FindExportData(r *bufio.Reader) (hdr string, err error) { +func FindExportData(r *bufio.Reader) (hdr string, size int, err error) { // Read first line to make sure this is an object file. line, err := r.ReadSlice('\n') if err != nil { @@ -52,7 +52,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { if string(line) == "!\n" { // Archive file. Scan to __.PKGDEF. var name string - if name, _, err = readGopackHeader(r); err != nil { + if name, size, err = readGopackHeader(r); err != nil { return } @@ -76,6 +76,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("not a Go object file") return } + size -= len(line) // Skip over object header to export data. // Begins after first line starting with $$. @@ -84,6 +85,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) { err = fmt.Errorf("can't find export data (%v)", err) return } + size -= len(line) } hdr = string(line) diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index 73cf6334fd..0b27a95404 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -11,6 +11,7 @@ import ( "go/build" "go/token" "go/types" + "internal/pkgbits" "io" "os" "path/filepath" @@ -27,7 +28,6 @@ var pkgExts = [...]string{".a", ".o"} // the build.Default build.Context). A relative srcDir is interpreted // relative to the current working directory. // If no file was found, an empty filename is returned. -// func FindPkg(path, srcDir string) (filename, id string) { if path == "" { return @@ -83,7 +83,6 @@ func FindPkg(path, srcDir string) (filename, id string) { // Import imports a gc-generated package given its import path and srcDir, adds // the corresponding package object to the packages map, and returns the object. // The packages map must contain all packages already imported. -// func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) { var rc io.ReadCloser var id string @@ -134,9 +133,9 @@ func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDi } defer rc.Close() - var hdr string buf := bufio.NewReader(rc) - if hdr, err = FindExportData(buf); err != nil { + hdr, size, err := FindExportData(buf) + if err != nil { return } @@ -146,14 +145,32 @@ func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDi case "$$B\n": var exportFormat byte - exportFormat, err = buf.ReadByte() + if exportFormat, err = buf.ReadByte(); err != nil { + return + } - // The indexed export format starts with an 'i'; the older - // binary export format starts with a 'c', 'd', or 'v' - // (from "version"). Select appropriate importer. - if err == nil && exportFormat == 'i' { + // The unified export format starts with a 'u'; the indexed export + // format starts with an 'i'; and the older binary export format + // starts with a 'c', 'd', or 'v' (from "version"). Select + // appropriate importer. + switch exportFormat { + case 'u': + var data []byte + var r io.Reader = buf + if size >= 0 { + r = io.LimitReader(r, int64(size)) + } + if data, err = io.ReadAll(r); err != nil { + return + } + s := string(data) + s = s[:strings.LastIndex(s, "\n$$\n")] + + input := pkgbits.NewPkgDecoder(id, s) + pkg = readUnifiedPackage(fset, nil, packages, input) + case 'i': pkg, err = iImportData(fset, packages, buf, id) - } else { + default: err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path) } diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 89b7fde836..c10915fdf5 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -125,10 +125,14 @@ func TestImportTestdata(t *testing.T) { } testfiles := map[string][]string{ - "exports.go": {"go/ast", "go/token"}, + "exports.go": {"go/ast", "go/token"}, + "generics.go": nil, } - if !goexperiment.Unified { - testfiles["generics.go"] = nil + if goexperiment.Unified { + // TODO(mdempsky): Fix test below to flatten the transitive + // Package.Imports graph. Unified IR is more precise about + // recreating the package import graph. + testfiles["exports.go"] = []string{"go/ast"} } for testfile, wantImports := range testfiles { @@ -153,11 +157,6 @@ func TestImportTestdata(t *testing.T) { } func TestImportTypeparamTests(t *testing.T) { - // This test doesn't yet work with the unified export format. - if goexperiment.Unified { - t.Skip("unified export data format is currently unsupported") - } - // This package only handles gc export data. if runtime.Compiler != "gc" { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) @@ -460,6 +459,14 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { return // not an interface } + // The unified IR importer always sets interface method receiver + // parameters to point to the Interface type, rather than the Named. + // See #49906. + var want types.Type = named + if goexperiment.Unified { + want = iface + } + // check explicitly declared methods for i := 0; i < iface.NumExplicitMethods(); i++ { m := iface.ExplicitMethod(i) @@ -468,8 +475,8 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { t.Errorf("%s: missing receiver type", m) continue } - if recv.Type() != named { - t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named) + if recv.Type() != want { + t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), want) } } diff --git a/src/go/internal/gcimporter/support.go b/src/go/internal/gcimporter/support.go index 61d1b46a68..8b61a417ad 100644 --- a/src/go/internal/gcimporter/support.go +++ b/src/go/internal/gcimporter/support.go @@ -13,6 +13,12 @@ import ( "sync" ) +func assert(b bool) { + if !b { + panic("assertion failed") + } +} + func errorf(format string, args ...any) { panic(fmt.Sprintf(format, args...)) } @@ -148,3 +154,13 @@ type anyType struct{} func (t anyType) Underlying() types.Type { return t } func (t anyType) String() string { return "any" } + +type derivedInfo struct { + idx int + needed bool +} + +type typeInfo struct { + idx int + derived bool +} diff --git a/src/go/internal/gcimporter/ureader.go b/src/go/internal/gcimporter/ureader.go new file mode 100644 index 0000000000..5260759c4f --- /dev/null +++ b/src/go/internal/gcimporter/ureader.go @@ -0,0 +1,590 @@ +// UNREVIEWED + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gcimporter + +import ( + "go/token" + "go/types" + "internal/pkgbits" +) + +// A pkgReader holds the shared state for reading a unified IR package +// description. +type pkgReader struct { + pkgbits.PkgDecoder + + fake fakeFileSet + + ctxt *types.Context + imports map[string]*types.Package // previously imported packages, indexed by path + + // lazily initialized arrays corresponding to the unified IR + // PosBase, Pkg, and Type sections, respectively. + posBases []string // position bases (i.e., file names) + pkgs []*types.Package + typs []types.Type + + // laterFns holds functions that need to be invoked at the end of + // import reading. + laterFns []func() +} + +// later adds a function to be invoked at the end of import reading. +func (pr *pkgReader) later(fn func()) { + pr.laterFns = append(pr.laterFns, fn) +} + +// readUnifiedPackage reads a package description from the given +// unified IR export data decoder. +func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package { + pr := pkgReader{ + PkgDecoder: input, + + fake: fakeFileSet{ + fset: fset, + files: make(map[string]*fileInfo), + }, + + ctxt: ctxt, + imports: imports, + + posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)), + pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)), + typs: make([]types.Type, input.NumElems(pkgbits.RelocType)), + } + defer pr.fake.setLines() + + r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) + pkg := r.pkg() + r.Bool() // has init + + for i, n := 0, r.Len(); i < n; i++ { + // As if r.obj(), but avoiding the Scope.Lookup call, + // to avoid eager loading of imports. + r.Sync(pkgbits.SyncObject) + assert(!r.Bool()) + r.p.objIdx(r.Reloc(pkgbits.RelocObj)) + assert(r.Len() == 0) + } + + r.Sync(pkgbits.SyncEOF) + + for _, fn := range pr.laterFns { + fn() + } + + pkg.MarkComplete() + return pkg +} + +// A reader holds the state for reading a single unified IR element +// within a package. +type reader struct { + pkgbits.Decoder + + p *pkgReader + + dict *readerDict +} + +// A readerDict holds the state for type parameters that parameterize +// the current unified IR element. +type readerDict struct { + // bounds is a slice of typeInfos corresponding to the underlying + // bounds of the element's type parameters. + bounds []typeInfo + + // tparams is a slice of the constructed TypeParams for the element. + tparams []*types.TypeParam + + // devived is a slice of types derived from tparams, which may be + // instantiated while reading the current element. + derived []derivedInfo + derivedTypes []types.Type // lazily instantiated from derived +} + +func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx int, marker pkgbits.SyncMarker) *reader { + return &reader{ + Decoder: pr.NewDecoder(k, idx, marker), + p: pr, + } +} + +// @@@ Positions + +func (r *reader) pos() token.Pos { + r.Sync(pkgbits.SyncPos) + if !r.Bool() { + return token.NoPos + } + + // TODO(mdempsky): Delta encoding. + posBase := r.posBase() + line := r.Uint() + col := r.Uint() + return r.p.fake.pos(posBase, int(line), int(col)) +} + +func (r *reader) posBase() string { + return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase)) +} + +func (pr *pkgReader) posBaseIdx(idx int) string { + if b := pr.posBases[idx]; b != "" { + return b + } + + r := pr.newReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase) + + // Within types2, position bases have a lot more details (e.g., + // keeping track of where //line directives appeared exactly). + // + // For go/types, we just track the file name. + + filename := r.String() + + if r.Bool() { // file base + // Was: "b = token.NewTrimmedFileBase(filename, true)" + } else { // line base + pos := r.pos() + line := r.Uint() + col := r.Uint() + + // Was: "b = token.NewLineBase(pos, filename, true, line, col)" + _, _, _ = pos, line, col + } + + b := filename + pr.posBases[idx] = b + return b +} + +// @@@ Packages + +func (r *reader) pkg() *types.Package { + r.Sync(pkgbits.SyncPkg) + return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg)) +} + +func (pr *pkgReader) pkgIdx(idx int) *types.Package { + // TODO(mdempsky): Consider using some non-nil pointer to indicate + // the universe scope, so we don't need to keep re-reading it. + if pkg := pr.pkgs[idx]; pkg != nil { + return pkg + } + + pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg() + pr.pkgs[idx] = pkg + return pkg +} + +func (r *reader) doPkg() *types.Package { + path := r.String() + if path == "builtin" { + return nil // universe + } + if path == "unsafe" { + return types.Unsafe + } + if path == "" { + path = r.p.PkgPath() + } + + if pkg := r.p.imports[path]; pkg != nil { + return pkg + } + + name := r.String() + height := r.Len() + + // Was: "pkg := types.NewPackageHeight(path, name, height)" + pkg, _ := types.NewPackage(path, name), height + r.p.imports[path] = pkg + + imports := make([]*types.Package, r.Len()) + for i := range imports { + imports[i] = r.pkg() + } + pkg.SetImports(imports) + + return pkg +} + +// @@@ Types + +func (r *reader) typ() types.Type { + return r.p.typIdx(r.typInfo(), r.dict) +} + +func (r *reader) typInfo() typeInfo { + r.Sync(pkgbits.SyncType) + if r.Bool() { + return typeInfo{idx: r.Len(), derived: true} + } + return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false} +} + +func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types.Type { + idx := info.idx + var where *types.Type + if info.derived { + where = &dict.derivedTypes[idx] + idx = dict.derived[idx].idx + } else { + where = &pr.typs[idx] + } + + if typ := *where; typ != nil { + return typ + } + + r := pr.newReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx) + r.dict = dict + + typ := r.doTyp() + assert(typ != nil) + + // See comment in pkgReader.typIdx explaining how this happens. + if prev := *where; prev != nil { + return prev + } + + *where = typ + return typ +} + +func (r *reader) doTyp() (res types.Type) { + switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag { + default: + errorf("unhandled type tag: %v", tag) + panic("unreachable") + + case pkgbits.TypeBasic: + return types.Typ[r.Len()] + + case pkgbits.TypeNamed: + obj, targs := r.obj() + name := obj.(*types.TypeName) + if len(targs) != 0 { + t, _ := types.Instantiate(r.p.ctxt, name.Type(), targs, false) + return t + } + return name.Type() + + case pkgbits.TypeTypeParam: + return r.dict.tparams[r.Len()] + + case pkgbits.TypeArray: + len := int64(r.Uint64()) + return types.NewArray(r.typ(), len) + case pkgbits.TypeChan: + dir := types.ChanDir(r.Len()) + return types.NewChan(dir, r.typ()) + case pkgbits.TypeMap: + return types.NewMap(r.typ(), r.typ()) + case pkgbits.TypePointer: + return types.NewPointer(r.typ()) + case pkgbits.TypeSignature: + return r.signature(nil, nil, nil) + case pkgbits.TypeSlice: + return types.NewSlice(r.typ()) + case pkgbits.TypeStruct: + return r.structType() + case pkgbits.TypeInterface: + return r.interfaceType() + case pkgbits.TypeUnion: + return r.unionType() + } +} + +func (r *reader) structType() *types.Struct { + fields := make([]*types.Var, r.Len()) + var tags []string + for i := range fields { + pos := r.pos() + pkg, name := r.selector() + ftyp := r.typ() + tag := r.String() + embedded := r.Bool() + + fields[i] = types.NewField(pos, pkg, name, ftyp, embedded) + if tag != "" { + for len(tags) < i { + tags = append(tags, "") + } + tags = append(tags, tag) + } + } + return types.NewStruct(fields, tags) +} + +func (r *reader) unionType() *types.Union { + terms := make([]*types.Term, r.Len()) + for i := range terms { + terms[i] = types.NewTerm(r.Bool(), r.typ()) + } + return types.NewUnion(terms) +} + +func (r *reader) interfaceType() *types.Interface { + methods := make([]*types.Func, r.Len()) + embeddeds := make([]types.Type, r.Len()) + implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool() + + for i := range methods { + pos := r.pos() + pkg, name := r.selector() + mtyp := r.signature(nil, nil, nil) + methods[i] = types.NewFunc(pos, pkg, name, mtyp) + } + + for i := range embeddeds { + embeddeds[i] = r.typ() + } + + iface := types.NewInterfaceType(methods, embeddeds) + if implicit { + iface.MarkImplicit() + } + return iface +} + +func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature { + r.Sync(pkgbits.SyncSignature) + + params := r.params() + results := r.params() + variadic := r.Bool() + + return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic) +} + +func (r *reader) params() *types.Tuple { + r.Sync(pkgbits.SyncParams) + + params := make([]*types.Var, r.Len()) + for i := range params { + params[i] = r.param() + } + + return types.NewTuple(params...) +} + +func (r *reader) param() *types.Var { + r.Sync(pkgbits.SyncParam) + + pos := r.pos() + pkg, name := r.localIdent() + typ := r.typ() + + return types.NewParam(pos, pkg, name, typ) +} + +// @@@ Objects + +func (r *reader) obj() (types.Object, []types.Type) { + r.Sync(pkgbits.SyncObject) + + assert(!r.Bool()) + + pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj)) + obj := pkgScope(pkg).Lookup(name) + + targs := make([]types.Type, r.Len()) + for i := range targs { + targs[i] = r.typ() + } + + return obj, targs +} + +func (pr *pkgReader) objIdx(idx int) (*types.Package, string) { + rname := pr.newReader(pkgbits.RelocName, idx, pkgbits.SyncObject1) + + objPkg, objName := rname.qualifiedIdent() + assert(objName != "") + + tag := pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj)) + + if tag == pkgbits.ObjStub { + assert(objPkg == nil || objPkg == types.Unsafe) + return objPkg, objName + } + + if objPkg.Scope().Lookup(objName) == nil { + dict := pr.objDictIdx(idx) + + r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1) + r.dict = dict + + declare := func(obj types.Object) { + objPkg.Scope().Insert(obj) + } + + switch tag { + default: + panic("weird") + + case pkgbits.ObjAlias: + pos := r.pos() + typ := r.typ() + declare(types.NewTypeName(pos, objPkg, objName, typ)) + + case pkgbits.ObjConst: + pos := r.pos() + typ := r.typ() + val := r.Value() + declare(types.NewConst(pos, objPkg, objName, typ, val)) + + case pkgbits.ObjFunc: + pos := r.pos() + tparams := r.typeParamNames() + sig := r.signature(nil, nil, tparams) + declare(types.NewFunc(pos, objPkg, objName, sig)) + + case pkgbits.ObjType: + pos := r.pos() + + obj := types.NewTypeName(pos, objPkg, objName, nil) + named := types.NewNamed(obj, nil, nil) + declare(obj) + + named.SetTypeParams(r.typeParamNames()) + + // TODO(mdempsky): Rewrite receiver types to underlying is an + // Interface? The go/types importer does this (I think because + // unit tests expected that), but cmd/compile doesn't care + // about it, so maybe we can avoid worrying about that here. + rhs := r.typ() + r.p.later(func() { + underlying := rhs.Underlying() + named.SetUnderlying(underlying) + }) + + for i, n := 0, r.Len(); i < n; i++ { + named.AddMethod(r.method()) + } + + case pkgbits.ObjVar: + pos := r.pos() + typ := r.typ() + declare(types.NewVar(pos, objPkg, objName, typ)) + } + } + + return objPkg, objName +} + +func (pr *pkgReader) objDictIdx(idx int) *readerDict { + r := pr.newReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1) + + var dict readerDict + + if implicits := r.Len(); implicits != 0 { + errorf("unexpected object with %v implicit type parameter(s)", implicits) + } + + dict.bounds = make([]typeInfo, r.Len()) + for i := range dict.bounds { + dict.bounds[i] = r.typInfo() + } + + dict.derived = make([]derivedInfo, r.Len()) + dict.derivedTypes = make([]types.Type, len(dict.derived)) + for i := range dict.derived { + dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()} + } + + // function references follow, but reader doesn't need those + + return &dict +} + +func (r *reader) typeParamNames() []*types.TypeParam { + r.Sync(pkgbits.SyncTypeParamNames) + + // Note: This code assumes it only processes objects without + // implement type parameters. This is currently fine, because + // reader is only used to read in exported declarations, which are + // always package scoped. + + if len(r.dict.bounds) == 0 { + return nil + } + + // Careful: Type parameter lists may have cycles. To allow for this, + // we construct the type parameter list in two passes: first we + // create all the TypeNames and TypeParams, then we construct and + // set the bound type. + + r.dict.tparams = make([]*types.TypeParam, len(r.dict.bounds)) + for i := range r.dict.bounds { + pos := r.pos() + pkg, name := r.localIdent() + + tname := types.NewTypeName(pos, pkg, name, nil) + r.dict.tparams[i] = types.NewTypeParam(tname, nil) + } + + typs := make([]types.Type, len(r.dict.bounds)) + for i, bound := range r.dict.bounds { + typs[i] = r.p.typIdx(bound, r.dict) + } + + // TODO(mdempsky): This is subtle, elaborate further. + // + // We have to save tparams outside of the closure, because + // typeParamNames() can be called multiple times with the same + // dictionary instance. + // + // Also, this needs to happen later to make sure SetUnderlying has + // been called. + // + // TODO(mdempsky): Is it safe to have a single "later" slice or do + // we need to have multiple passes? See comments on CL 386002 and + // go.dev/issue/52104. + tparams := r.dict.tparams + r.p.later(func() { + for i, typ := range typs { + tparams[i].SetConstraint(typ) + } + }) + + return r.dict.tparams +} + +func (r *reader) method() *types.Func { + r.Sync(pkgbits.SyncMethod) + pos := r.pos() + pkg, name := r.selector() + + rparams := r.typeParamNames() + sig := r.signature(r.param(), rparams, nil) + + _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go. + return types.NewFunc(pos, pkg, name, sig) +} + +func (r *reader) qualifiedIdent() (*types.Package, string) { return r.ident(pkgbits.SyncSym) } +func (r *reader) localIdent() (*types.Package, string) { return r.ident(pkgbits.SyncLocalIdent) } +func (r *reader) selector() (*types.Package, string) { return r.ident(pkgbits.SyncSelector) } + +func (r *reader) ident(marker pkgbits.SyncMarker) (*types.Package, string) { + r.Sync(marker) + return r.pkg(), r.String() +} + +// pkgScope returns pkg.Scope(). +// If pkg is nil, it returns types.Universe instead. +// +// TODO(mdempsky): Remove after x/tools can depend on Go 1.19. +func pkgScope(pkg *types.Package) *types.Scope { + if pkg != nil { + return pkg.Scope() + } + return types.Universe +} diff --git a/src/go/parser/error_test.go b/src/go/parser/error_test.go index bedfc265b5..c3a8ec6ad8 100644 --- a/src/go/parser/error_test.go +++ b/src/go/parser/error_test.go @@ -64,12 +64,10 @@ func getPos(fset *token.FileSet, filename string, offset int) token.Pos { // The special form /* ERROR HERE "rx" */ must be used for error // messages that appear immediately after a token, rather than at // a token's position. -// var errRx = regexp.MustCompile(`^/\* *ERROR *(HERE)? *"([^"]*)" *\*/$`) // expectedErrors collects the regular expressions of ERROR comments found // in files and returns them as a map of error positions to error messages. -// func expectedErrors(fset *token.FileSet, filename string, src []byte) map[token.Pos]string { errors := make(map[token.Pos]string) @@ -116,7 +114,6 @@ func expectedErrors(fset *token.FileSet, filename string, src []byte) map[token. // compareErrors compares the map of expected error messages with the list // of found errors and reports discrepancies. -// func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found scanner.ErrorList) { t.Helper() for _, error := range found { diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go index e4f8c281ea..e3468f481f 100644 --- a/src/go/parser/interface.go +++ b/src/go/parser/interface.go @@ -21,7 +21,6 @@ import ( // If src != nil, readSource converts src to a []byte if possible; // otherwise it returns an error. If src == nil, readSource returns // the result of reading the file specified by filename. -// func readSource(filename string, src any) ([]byte, error) { if src != nil { switch s := src.(type) { @@ -45,7 +44,6 @@ func readSource(filename string, src any) ([]byte, error) { // A Mode value is a set of flags (or 0). // They control the amount of source code parsed and other optional // parser functionality. -// type Mode uint const ( @@ -81,7 +79,6 @@ const ( // errors were found, the result is a partial AST (with ast.Bad* nodes // representing the fragments of erroneous source code). Multiple errors // are returned via a scanner.ErrorList which is sorted by source position. -// func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast.File, err error) { if fset == nil { panic("parser.ParseFile: no token.FileSet provided (fset == nil)") @@ -136,7 +133,6 @@ func ParseFile(fset *token.FileSet, filename string, src any, mode Mode) (f *ast // If the directory couldn't be read, a nil map and the respective error are // returned. If a parse error occurred, a non-nil but incomplete map and the // first error encountered are returned. -// func ParseDir(fset *token.FileSet, path string, filter func(fs.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) { list, err := os.ReadDir(path) if err != nil { @@ -187,7 +183,6 @@ func ParseDir(fset *token.FileSet, path string, filter func(fs.FileInfo) bool, m // errors were found, the result is a partial AST (with ast.Bad* nodes // representing the fragments of erroneous source code). Multiple errors // are returned via a scanner.ErrorList which is sorted by source position. -// func ParseExprFrom(fset *token.FileSet, filename string, src any, mode Mode) (expr ast.Expr, err error) { if fset == nil { panic("parser.ParseExprFrom: no token.FileSet provided (fset == nil)") @@ -232,7 +227,6 @@ func ParseExprFrom(fset *token.FileSet, filename string, src any, mode Mode) (ex // If syntax errors were found, the result is a partial AST (with ast.Bad* nodes // representing the fragments of erroneous source code). Multiple errors are // returned via a scanner.ErrorList which is sorted by source position. -// func ParseExpr(x string) (ast.Expr, error) { return ParseExprFrom(token.NewFileSet(), "", []byte(x), 0) } diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 51a3c3e67f..3eb00e9446 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -154,7 +154,6 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) { // comments list, and return it together with the line at which // the last comment in the group ends. A non-comment token or n // empty lines terminate a comment group. -// func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) { var list []*ast.Comment endline = p.file.Line(p.pos) @@ -185,7 +184,6 @@ func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline // // Lead and line comments may be considered documentation that is // stored in the AST. -// func (p *parser) next() { p.leadComment = nil p.lineComment = nil @@ -288,7 +286,6 @@ func (p *parser) expect2(tok token.Token) (pos token.Pos) { // expectClosing is like expect but provides a better error message // for the common case of a missing comma before a newline. -// func (p *parser) expectClosing(tok token.Token, context string) token.Pos { if p.tok != tok && p.tok == token.SEMICOLON && p.lit == "\n" { p.error(p.pos, "missing ',' before newline in "+context) @@ -406,7 +403,6 @@ var exprEnd = map[token.Token]bool{ // token positions are invalid due to parse errors, the resulting end position // may be past the file's EOF position, which would lead to panics if used // later on. -// func (p *parser) safePos(pos token.Pos) (res token.Pos) { defer func() { if recover() != nil { @@ -1349,7 +1345,6 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr { // parseOperand may return an expression or a raw type (incl. array // types of the form [...]T. Callers must verify the result. -// func (p *parser) parseOperand() ast.Expr { if p.trace { defer un(trace(p, "Operand")) @@ -1640,7 +1635,6 @@ func unparen(x ast.Expr) ast.Expr { // checkExprOrType checks that x is an expression or a type // (and not a raw type such as [...]T). -// func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { switch t := unparen(x).(type) { case *ast.ParenExpr: diff --git a/src/go/parser/performance_test.go b/src/go/parser/performance_test.go index 6f8a97770b..1249f35d39 100644 --- a/src/go/parser/performance_test.go +++ b/src/go/parser/performance_test.go @@ -10,8 +10,8 @@ import ( "testing" ) -// TODO(rFindley): use a testdata file or file from another package here, to -// avoid a moving target. +// TODO(rfindley): use a testdata file or file from another package here, to +// avoid a moving target. var src = readFile("parser.go") func readFile(filename string) []byte { diff --git a/src/go/parser/resolver.go b/src/go/parser/resolver.go index d66a194c12..767a5e20ad 100644 --- a/src/go/parser/resolver.go +++ b/src/go/parser/resolver.go @@ -188,7 +188,6 @@ var unresolved = new(ast.Object) // the object it denotes. If no object is found and collectUnresolved is // set, x is marked as unresolved and collected in the list of unresolved // identifiers. -// func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) { if ident.Obj != nil { panic(r.sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name)) diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go index 9a09d58eb2..f4fbde8ae6 100644 --- a/src/go/printer/nodes.go +++ b/src/go/printer/nodes.go @@ -38,13 +38,12 @@ import ( // printed. // // TODO(gri): linebreak may add too many lines if the next statement at "line" -// is preceded by comments because the computation of n assumes -// the current position before the comment and the target position -// after the comment. Thus, after interspersing such comments, the -// space taken up by them is not considered to reduce the number of -// linebreaks. At the moment there is no easy way to know about -// future (not yet interspersed) comments in this function. -// +// is preceded by comments because the computation of n assumes +// the current position before the comment and the target position +// after the comment. Thus, after interspersing such comments, the +// space taken up by them is not considered to reduce the number of +// linebreaks. At the moment there is no easy way to know about +// future (not yet interspersed) comments in this function. func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbreaks int) { n := nlimit(line - p.pos.Line) if n < min { @@ -125,7 +124,7 @@ const filteredMsg = "contains filtered or unexported fields" // expressions. // // TODO(gri) Consider rewriting this to be independent of []ast.Expr -// so that we can use the algorithm for any kind of list +// so that we can use the algorithm for any kind of list // (e.g., pass list via a channel over which to range). func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) { if len(list) == 0 { @@ -745,7 +744,6 @@ func reduceDepth(depth int) int { // 3) If there are no level 4 operators or no level 5 operators, then the // cutoff is 6 (always use spaces) in Normal mode // and 4 (never use spaces) in Compact mode. -// func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) { prec := x.Op.Precedence() if prec < prec1 { @@ -1259,7 +1257,6 @@ func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, po // indentList reports whether an expression list would look better if it // were indented wholesale (starting with the very first element, rather // than starting at the first line break). -// func (p *printer) indentList(list []ast.Expr) bool { // Heuristic: indentList reports whether there are more than one multi- // line element in the list, or if there is any element that is not @@ -1503,7 +1500,6 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { // - V - V true column must be kept // - - - - false // - V V - false V is moved into T column -// func keepTypeColumn(specs []ast.Spec) []bool { m := make([]bool, len(specs)) @@ -1614,7 +1610,6 @@ func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit { // The parameter n is the number of specs in the group. If doIndent is set, // multi-line identifier lists in the spec are indented when the first // linebreak is encountered. -// func (p *printer) spec(spec ast.Spec, n int, doIndent bool) { switch s := spec.(type) { case *ast.ImportSpec: @@ -1710,7 +1705,6 @@ func (p *printer) genDecl(d *ast.GenDecl) { // The result is <= maxSize if the node fits on one line with at // most maxSize chars and the formatted output doesn't contain // any control chars. Otherwise, the result is > maxSize. -// func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { // nodeSize invokes the printer, which may invoke nodeSize // recursively. For deep composite literal nests, this can @@ -1784,7 +1778,6 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int { // the block is printed on the current line, without line breaks, spaced from the header // by sep. Otherwise the block's opening "{" is printed on the current line, followed by // lines for the block's statements and its closing "}". -// func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) { if b == nil { return diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go index e4679b0021..5014f59ab5 100644 --- a/src/go/printer/printer.go +++ b/src/go/printer/printer.go @@ -151,14 +151,12 @@ func (p *printer) nextComment() { // commentBefore reports whether the current comment group occurs // before the next position in the source code and printing it does // not introduce implicit semicolons. -// func (p *printer) commentBefore(next token.Position) bool { return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline) } // commentSizeBefore returns the estimated size of the // comments on the same line before the next position. -// func (p *printer) commentSizeBefore(next token.Position) int { // save/restore current p.commentInfo (p.nextComment() modifies it) defer func(info commentInfo) { @@ -179,7 +177,6 @@ func (p *printer) commentSizeBefore(next token.Position) int { // token in *linePtr. It is used to compute an accurate line number for a // formatted construct, independent of pending (not yet emitted) whitespace // or comments. -// func (p *printer) recordLine(linePtr *int) { p.linePtr = linePtr } @@ -188,7 +185,6 @@ func (p *printer) recordLine(linePtr *int) { // output line and the line argument, ignoring any pending (not yet // emitted) whitespace or comments. It is used to compute an accurate // size (in number of lines) for a formatted construct. -// func (p *printer) linesFrom(line int) int { return p.out.Line - line } @@ -282,7 +278,6 @@ func (p *printer) writeByte(ch byte, n int) { // needed (i.e., when we don't know that s contains no tabs or line breaks) // avoids processing extra escape characters and reduces run time of the // printer benchmark by up to 10%. -// func (p *printer) writeString(pos token.Position, s string, isLit bool) { if p.out.Column == 1 { if p.Config.Mode&SourcePos != 0 { @@ -352,7 +347,6 @@ func (p *printer) writeString(pos token.Position, s string, isLit bool) { // pos is the comment position, next the position of the item // after all pending comments, prev is the previous comment in // a group of comments (or nil), and tok is the next token. -// func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, tok token.Token) { if len(p.output) == 0 { // the comment is the first item to be printed - don't write any whitespace @@ -478,7 +472,6 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment // Returns true if s contains only white space // (only tabs and blanks can appear in the printer's context). -// func isBlank(s string) bool { for i := 0; i < len(s); i++ { if s[i] > ' ' { @@ -507,7 +500,6 @@ func trimRight(s string) string { // The prefix is computed using heuristics such that is likely that the comment // contents are nicely laid out after re-printing each line using the printer's // current indentation. -// func stripCommonPrefix(lines []string) { if len(lines) <= 1 { return // at most one line - nothing to do @@ -695,7 +687,6 @@ func (p *printer) writeComment(comment *ast.Comment) { // pending whitespace. The writeCommentSuffix result indicates if a // newline was written or if a formfeed was dropped from the whitespace // buffer. -// func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) { for i, ch := range p.wsbuf { switch ch { @@ -744,7 +735,6 @@ func (p *printer) containsLinebreak() bool { // that needs to be written before the next token). A heuristic is used to mix // the comments and whitespace. The intersperseComments result indicates if a // newline was written or if a formfeed was dropped from the whitespace buffer. -// func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) { var last *ast.Comment for p.commentBefore(next) { @@ -877,7 +867,6 @@ func mayCombine(prev token.Token, next byte) (b bool) { // taking into account the amount and structure of any pending white- // space for best comment placement. Then, any leftover whitespace is // printed, followed by the actual token. -// func (p *printer) print(args ...any) { for _, arg := range args { // information about the current arg @@ -1020,7 +1009,6 @@ func (p *printer) print(args ...any) { // before the position of the next token tok. The flush result indicates // if a newline was written or if a formfeed was dropped from the whitespace // buffer. -// func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) { if p.commentBefore(next) { // if there are comments before the next item, intersperse them @@ -1175,7 +1163,6 @@ unsupported: // and vtab characters into newlines and htabs (in case no tabwriter // is used). Text bracketed by tabwriter.Escape characters is passed // through unchanged. -// type trimmer struct { output io.Writer state int @@ -1363,7 +1350,6 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeS // A CommentedNode bundles an AST node and corresponding comments. // It may be provided as argument to any of the Fprint functions. -// type CommentedNode struct { Node any // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt Comments []*ast.CommentGroup @@ -1373,7 +1359,6 @@ type CommentedNode struct { // Position information is interpreted relative to the file set fset. // The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt, // or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. -// func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node any) error { return cfg.fprint(output, fset, node, make(map[ast.Node]int)) } @@ -1382,7 +1367,6 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node any) error // It calls Config.Fprint with default settings. // Note that gofmt uses tabs for indentation but spaces for alignment; // use format.Node (package go/format) for output that matches gofmt. -// func Fprint(output io.Writer, fset *token.FileSet, node any) error { return (&Config{Tabwidth: 8}).Fprint(output, fset, node) } diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go index 2071aa8aa6..ad2d86052a 100644 --- a/src/go/printer/printer_test.go +++ b/src/go/printer/printer_test.go @@ -212,7 +212,6 @@ func TestFiles(t *testing.T) { // TestLineComments, using a simple test case, checks that consecutive line // comments are properly terminated with a newline even if the AST position // information is incorrect. -// func TestLineComments(t *testing.T) { const src = `// comment 1 // comment 2 diff --git a/src/go/printer/testdata/parser.go b/src/go/printer/testdata/parser.go index 7e8379739c..bb06c8dd42 100644 --- a/src/go/printer/testdata/parser.go +++ b/src/go/printer/testdata/parser.go @@ -19,7 +19,6 @@ import ( // The mode parameter to the Parse* functions is a set of flags (or 0). // They control the amount of source code parsed and other optional // parser functionality. -// const ( PackageClauseOnly uint = 1 << iota // parsing stops after package clause ImportsOnly // parsing stops after import declarations @@ -271,7 +270,6 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) { // comments list, and return it together with the line at which // the last comment in the group ends. An empty line or non-comment // token terminates a comment group. -// func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) { var list []*ast.Comment endline = p.file.Line(p.pos) @@ -302,7 +300,6 @@ func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) // // Lead and line comments may be considered documentation that is // stored in the AST. -// func (p *parser) next() { p.leadComment = nil p.lineComment = nil @@ -947,7 +944,6 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr { // parseOperand may return an expression or a raw type (incl. array // types of the form [...]T. Callers must verify the result. // If lhs is set and the result is an identifier, it is not resolved. -// func (p *parser) parseOperand(lhs bool) ast.Expr { if p.trace { defer un(trace(p, "Operand")) @@ -1214,7 +1210,6 @@ func unparen(x ast.Expr) ast.Expr { // checkExprOrType checks that x is an expression or a type // (and not a raw type such as [...]T). -// func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { switch t := unparen(x).(type) { case *ast.ParenExpr: @@ -1352,7 +1347,7 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { // If lhs is set and the result is an identifier, it is not resolved. // TODO(gri): parseExpr may return a type or even a raw type ([..]int) - -// should reject when a type/raw type is obviously not allowed +// should reject when a type/raw type is obviously not allowed func (p *parser) parseExpr(lhs bool) ast.Expr { if p.trace { defer un(trace(p, "Expression")) diff --git a/src/go/scanner/errors.go b/src/go/scanner/errors.go index 3114f4b645..3e9c365cca 100644 --- a/src/go/scanner/errors.go +++ b/src/go/scanner/errors.go @@ -15,7 +15,6 @@ import ( // The position Pos, if valid, points to the beginning of // the offending token, and the error condition is described // by Msg. -// type Error struct { Pos token.Position Msg string @@ -33,7 +32,6 @@ func (e Error) Error() string { // ErrorList is a list of *Errors. // The zero value for an ErrorList is an empty ErrorList ready to use. -// type ErrorList []*Error // Add adds an Error with given position and error message to an ErrorList. @@ -69,7 +67,6 @@ func (p ErrorList) Less(i, j int) bool { // Sort sorts an ErrorList. *Error entries are sorted by position, // other errors are sorted by error message, and before any *Error // entry. -// func (p ErrorList) Sort() { sort.Sort(p) } @@ -112,7 +109,6 @@ func (p ErrorList) Err() error { // PrintError is a utility function that prints a list of errors to w, // one error per line, if the err parameter is an ErrorList. Otherwise // it prints the err string. -// func PrintError(w io.Writer, err error) { if list, ok := err.(ErrorList); ok { for _, e := range list { diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go index 23d8db9d1c..b53de7a427 100644 --- a/src/go/scanner/scanner.go +++ b/src/go/scanner/scanner.go @@ -22,13 +22,11 @@ import ( // encountered and a handler was installed, the handler is called with a // position and an error message. The position points to the beginning of // the offending token. -// type ErrorHandler func(pos token.Position, msg string) // A Scanner holds the scanner's internal state while processing // a given text. It can be allocated as part of another data // structure but must be initialized via Init before use. -// type Scanner struct { // immutable state file *token.File // source file handle @@ -101,7 +99,6 @@ func (s *Scanner) peek() byte { // A mode value is a set of flags (or 0). // They control scanner behavior. -// type Mode uint const ( @@ -123,7 +120,6 @@ const ( // // Note that Init may call err if there is an error in the first character // of the file. -// func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) { // Explicitly initialize all fields since a scanner may be reused. if file.Size() != len(src) { @@ -825,7 +821,6 @@ func (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Tok // Scan adds line information to the file added to the file // set with Init. Token positions are relative to that file // and thus relative to the file set. -// func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) { scanAgain: s.skipWhitespace() diff --git a/src/go/token/position.go b/src/go/token/position.go index ce4af03923..00f24535bf 100644 --- a/src/go/token/position.go +++ b/src/go/token/position.go @@ -16,7 +16,6 @@ import ( // Position describes an arbitrary source position // including the file, line, and column location. // A Position is valid if the line number is > 0. -// type Position struct { Filename string // filename, if any Offset int // offset, starting at 0 @@ -35,7 +34,6 @@ func (pos *Position) IsValid() bool { return pos.Line > 0 } // line valid position without file name and no column (column == 0) // file invalid position with file name // - invalid position without file name -// func (pos Position) String() string { s := pos.Filename if pos.IsValid() { @@ -75,14 +73,12 @@ func (pos Position) String() string { // equivalent to comparing the respective source file offsets. If p and q // are in different files, p < q is true if the file implied by p was added // to the respective file set before the file implied by q. -// type Pos int // The zero value for Pos is NoPos; there is no file and line information // associated with it, and NoPos.IsValid() is false. NoPos is always // smaller than any other Pos value. The corresponding Position value // for NoPos is the zero value for Position. -// const NoPos Pos = 0 // IsValid reports whether the position is valid. @@ -95,7 +91,6 @@ func (p Pos) IsValid() bool { // A File is a handle for a file belonging to a FileSet. // A File has a name, size, and line offset table. -// type File struct { set *FileSet name string // file name as provided to AddFile @@ -134,7 +129,6 @@ func (f *File) LineCount() int { // AddLine adds the line offset for a new line. // The line offset must be larger than the offset for the previous line // and smaller than the file size; otherwise the line offset is ignored. -// func (f *File) AddLine(offset int) { f.mutex.Lock() if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size { @@ -147,7 +141,6 @@ func (f *File) AddLine(offset int) { // the newline character at the end of the line with a space (to not change the // remaining offsets). To obtain the line number, consult e.g. Position.Line. // MergeLine will panic if given an invalid line number. -// func (f *File) MergeLine(line int) { if line < 1 { panic(fmt.Sprintf("invalid line number %d (should be >= 1)", line)) @@ -174,7 +167,6 @@ func (f *File) MergeLine(line int) { // and smaller than the file size; otherwise SetLines fails and returns // false. // Callers must not mutate the provided slice after SetLines returns. -// func (f *File) SetLines(lines []int) bool { // verify validity of lines table size := f.size @@ -239,7 +231,6 @@ type lineInfo struct { // AddLineInfo is like AddLineColumnInfo with a column = 1 argument. // It is here for backward-compatibility for code prior to Go 1.11. -// func (f *File) AddLineInfo(offset int, filename string, line int) { f.AddLineColumnInfo(offset, filename, line, 1) } @@ -252,7 +243,6 @@ func (f *File) AddLineInfo(offset int, filename string, line int) { // // AddLineColumnInfo is typically used to register alternative position // information for line directives such as //line filename:line:column. -// func (f *File) AddLineColumnInfo(offset int, filename string, line, column int) { f.mutex.Lock() if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size { @@ -264,7 +254,6 @@ func (f *File) AddLineColumnInfo(offset int, filename string, line, column int) // Pos returns the Pos value for the given file offset; // the offset must be <= f.Size(). // f.Pos(f.Offset(p)) == p. -// func (f *File) Pos(offset int) Pos { if offset > f.size { panic(fmt.Sprintf("invalid file offset %d (should be <= %d)", offset, f.size)) @@ -275,7 +264,6 @@ func (f *File) Pos(offset int) Pos { // Offset returns the offset for the given file position p; // p must be a valid Pos value in that file. // f.Offset(f.Pos(offset)) == offset. -// func (f *File) Offset(p Pos) int { if int(p) < f.base || int(p) > f.base+f.size { panic(fmt.Sprintf("invalid Pos value %d (should be in [%d, %d])", p, f.base, f.base+f.size)) @@ -285,7 +273,6 @@ func (f *File) Offset(p Pos) int { // Line returns the line number for the given file position p; // p must be a Pos value in that file or NoPos. -// func (f *File) Line(p Pos) int { return f.Position(p).Line } @@ -297,7 +284,6 @@ func searchLineInfos(a []lineInfo, x int) int { // unpack returns the filename and line and column number for a file offset. // If adjusted is set, unpack will return the filename and line information // possibly adjusted by //line comments; otherwise those comments are ignored. -// func (f *File) unpack(offset int, adjusted bool) (filename string, line, column int) { f.mutex.Lock() defer f.mutex.Unlock() @@ -342,7 +328,6 @@ func (f *File) position(p Pos, adjusted bool) (pos Position) { // If adjusted is set, the position may be adjusted by position-altering // //line comments; otherwise those comments are ignored. // p must be a Pos value in f or NoPos. -// func (f *File) PositionFor(p Pos, adjusted bool) (pos Position) { if p != NoPos { if int(p) < f.base || int(p) > f.base+f.size { @@ -355,7 +340,6 @@ func (f *File) PositionFor(p Pos, adjusted bool) (pos Position) { // Position returns the Position value for the given file position p. // Calling f.Position(p) is equivalent to calling f.PositionFor(p, true). -// func (f *File) Position(p Pos) (pos Position) { return f.PositionFor(p, true) } @@ -382,7 +366,6 @@ func (f *File) Position(p Pos) (pos Position) { // recently added file, plus one. Unless there is a need to extend an // interval later, using the FileSet.Base should be used as argument // for FileSet.AddFile. -// type FileSet struct { mutex sync.RWMutex // protects the file set base int // base offset for the next file @@ -399,7 +382,6 @@ func NewFileSet() *FileSet { // Base returns the minimum base offset that must be provided to // AddFile when adding the next file. -// func (s *FileSet) Base() int { s.mutex.RLock() b := s.base @@ -423,7 +405,6 @@ func (s *FileSet) Base() int { // with offs in the range [0, size] and thus p in the range [base, base+size]. // For convenience, File.Pos may be used to create file-specific position // values from a file offset. -// func (s *FileSet) AddFile(filename string, base, size int) *File { s.mutex.Lock() defer s.mutex.Unlock() @@ -451,7 +432,6 @@ func (s *FileSet) AddFile(filename string, base, size int) *File { // Iterate calls f for the files in the file set in the order they were added // until f returns false. -// func (s *FileSet) Iterate(f func(*File) bool) { for i := 0; ; i++ { var file *File @@ -496,7 +476,6 @@ func (s *FileSet) file(p Pos) *File { // File returns the file that contains the position p. // If no such file is found (for instance for p == NoPos), // the result is nil. -// func (s *FileSet) File(p Pos) (f *File) { if p != NoPos { f = s.file(p) @@ -508,7 +487,6 @@ func (s *FileSet) File(p Pos) (f *File) { // If adjusted is set, the position may be adjusted by position-altering // //line comments; otherwise those comments are ignored. // p must be a Pos value in s or NoPos. -// func (s *FileSet) PositionFor(p Pos, adjusted bool) (pos Position) { if p != NoPos { if f := s.file(p); f != nil { @@ -520,7 +498,6 @@ func (s *FileSet) PositionFor(p Pos, adjusted bool) (pos Position) { // Position converts a Pos p in the fileset into a Position value. // Calling s.Position(p) is equivalent to calling s.PositionFor(p, true). -// func (s *FileSet) Position(p Pos) (pos Position) { return s.PositionFor(p, true) } diff --git a/src/go/token/token.go b/src/go/token/token.go index 17047d8713..b691883261 100644 --- a/src/go/token/token.go +++ b/src/go/token/token.go @@ -239,7 +239,6 @@ var tokens = [...]string{ // token character sequence (e.g., for the token ADD, the string is // "+"). For all other tokens the string corresponds to the token // constant name (e.g. for the token IDENT, the string is "IDENT"). -// func (tok Token) String() string { s := "" if 0 <= tok && tok < Token(len(tokens)) { @@ -256,7 +255,6 @@ func (tok Token) String() string { // starting with precedence 1 up to unary operators. The highest // precedence serves as "catch-all" precedence for selector, // indexing, and other operator and delimiter tokens. -// const ( LowestPrec = 0 // non-operators UnaryPrec = 6 @@ -266,7 +264,6 @@ const ( // Precedence returns the operator precedence of the binary // operator op. If op is not a binary operator, the result // is LowestPrecedence. -// func (op Token) Precedence() int { switch op { case LOR: @@ -293,7 +290,6 @@ func init() { } // Lookup maps an identifier to its keyword token or IDENT (if not a keyword). -// func Lookup(ident string) Token { if tok, is_keyword := keywords[ident]; is_keyword { return tok @@ -305,30 +301,25 @@ func Lookup(ident string) Token { // IsLiteral returns true for tokens corresponding to identifiers // and basic type literals; it returns false otherwise. -// func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end } // IsOperator returns true for tokens corresponding to operators and // delimiters; it returns false otherwise. -// func (tok Token) IsOperator() bool { return (operator_beg < tok && tok < operator_end) || tok == TILDE } // IsKeyword returns true for tokens corresponding to keywords; // it returns false otherwise. -// func (tok Token) IsKeyword() bool { return keyword_beg < tok && tok < keyword_end } // IsExported reports whether name starts with an upper-case letter. -// func IsExported(name string) bool { ch, _ := utf8.DecodeRuneInString(name) return unicode.IsUpper(ch) } // IsKeyword reports whether name is a Go keyword, such as "func" or "return". -// func IsKeyword(name string) bool { // TODO: opt: use a perfect hash function instead of a global map. _, ok := keywords[name] @@ -338,7 +329,6 @@ func IsKeyword(name string) bool { // IsIdentifier reports whether name is a Go identifier, that is, a non-empty // string made up of letters, digits, and underscores, where the first character // is not a digit. Keywords are not identifiers. -// func IsIdentifier(name string) bool { if name == "" || IsKeyword(name) { return false diff --git a/src/go/types/api.go b/src/go/types/api.go index 2cbabb0a53..04342bfac5 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -280,7 +280,6 @@ type Info struct { // TypeOf returns the type of expression e, or nil if not found. // Precondition: the Types, Uses and Defs maps are populated. -// func (info *Info) TypeOf(e ast.Expr) Type { if t, ok := info.Types[e]; ok { return t.Type @@ -300,7 +299,6 @@ func (info *Info) TypeOf(e ast.Expr) Type { // it defines, not the type (*TypeName) it uses. // // Precondition: the Uses and Defs maps are populated. -// func (info *Info) ObjectOf(id *ast.Ident) Object { if obj := info.Defs[id]; obj != nil { return obj diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index d25e6116cf..0ad97c5922 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -330,7 +330,7 @@ func TestTypesInfo(t *testing.T) { // issue 47243 {`package issue47243_a; var x int32; var _ = x << 3`, `3`, `untyped int`}, - {`package issue47243_b; var x int32; var _ = x << 3.`, `3.`, `uint`}, // issue 47410: should be untyped float + {`package issue47243_b; var x int32; var _ = x << 3.`, `3.`, `untyped float`}, {`package issue47243_c; var x int32; var _ = 1 << x`, `1 << x`, `int`}, {`package issue47243_d; var x int32; var _ = 1 << x`, `1`, `int`}, {`package issue47243_e; var x int32; var _ = 1 << 2`, `1`, `untyped int`}, @@ -2305,6 +2305,104 @@ func TestInstanceIdentity(t *testing.T) { } } +// TestInstantiatedObjects verifies properties of instantiated objects. +func TestInstantiatedObjects(t *testing.T) { + const src = ` +package p + +type T[P any] struct { + field P +} + +func (recv *T[Q]) concreteMethod() {} + +type FT[P any] func(ftp P) (ftrp P) + +func F[P any](fp P) (frp P){ return } + +type I[P any] interface { + interfaceMethod(P) +} + +var ( + t T[int] + ft FT[int] + f = F[int] + i I[int] +) +` + info := &Info{ + Defs: make(map[*ast.Ident]Object), + } + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "p.go", src, 0) + if err != nil { + t.Fatal(err) + } + conf := Config{} + pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info) + if err != nil { + t.Fatal(err) + } + + lookup := func(name string) Type { return pkg.Scope().Lookup(name).Type() } + tests := []struct { + name string + obj Object + }{ + {"field", lookup("t").Underlying().(*Struct).Field(0)}, + {"concreteMethod", lookup("t").(*Named).Method(0)}, + {"recv", lookup("t").(*Named).Method(0).Type().(*Signature).Recv()}, + {"ftp", lookup("ft").Underlying().(*Signature).Params().At(0)}, + {"ftrp", lookup("ft").Underlying().(*Signature).Results().At(0)}, + {"fp", lookup("f").(*Signature).Params().At(0)}, + {"frp", lookup("f").(*Signature).Results().At(0)}, + {"interfaceMethod", lookup("i").Underlying().(*Interface).Method(0)}, + } + + // Collect all identifiers by name. + idents := make(map[string][]*ast.Ident) + ast.Inspect(f, func(n ast.Node) bool { + if id, ok := n.(*ast.Ident); ok { + idents[id.Name] = append(idents[id.Name], id) + } + return true + }) + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if got := len(idents[test.name]); got != 1 { + t.Fatalf("found %d identifiers named %s, want 1", got, test.name) + } + ident := idents[test.name][0] + def := info.Defs[ident] + if def == test.obj { + t.Fatalf("info.Defs[%s] contains the test object", test.name) + } + if def.Pkg() != test.obj.Pkg() { + t.Errorf("Pkg() = %v, want %v", def.Pkg(), test.obj.Pkg()) + } + if def.Name() != test.obj.Name() { + t.Errorf("Name() = %v, want %v", def.Name(), test.obj.Name()) + } + if def.Pos() != test.obj.Pos() { + t.Errorf("Pos() = %v, want %v", def.Pos(), test.obj.Pos()) + } + if def.Parent() != test.obj.Parent() { + t.Fatalf("Parent() = %v, want %v", def.Parent(), test.obj.Parent()) + } + if def.Exported() != test.obj.Exported() { + t.Fatalf("Exported() = %v, want %v", def.Exported(), test.obj.Exported()) + } + if def.Id() != test.obj.Id() { + t.Fatalf("Id() = %v, want %v", def.Id(), test.obj.Id()) + } + // String and Type are expected to differ. + }) + } +} + func TestImplements(t *testing.T) { const src = ` package p diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index c81e73c828..414c2c3ea0 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -16,7 +16,6 @@ import ( // reports whether the call is valid, with *x holding the result; // but x.expr is not set. If the call is invalid, the result is // false, and *x is undefined. -// func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) { // append is the only built-in that permits the use of ... for the last argument bin := predeclaredFuncs[id] diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 592ced41ec..88622d6b0c 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -55,7 +55,6 @@ var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(?s)(.*)`) // splitError splits an error's error message into a position string // and the actual error message. If there's no position information, // pos is the empty string, and msg is the entire error message. -// func splitError(err error) (pos, msg string) { msg = err.Error() if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 { @@ -92,7 +91,6 @@ func parseFiles(t *testing.T, filenames []string, srcs [][]byte, mode parser.Mod // Space around "rx" or rx is ignored. Use the form `ERROR HERE "rx"` // for error messages that are located immediately after rather than // at a token's position. -// var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`) // errMap collects the regular expressions of ERROR comments found @@ -237,6 +235,11 @@ func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, man t.Fatal(err) } + if manual && *goVersion != "" { + // goVersion overrides -lang for manual tests. + conf.GoVersion = *goVersion + } + // TODO(gri) remove this or use flag mechanism to set mode if still needed if strings.HasSuffix(filenames[0], ".go1") { // TODO(rfindley): re-enable this test by using GoVersion. diff --git a/src/go/types/context.go b/src/go/types/context.go index ff4bf89f3c..15756b062d 100644 --- a/src/go/types/context.go +++ b/src/go/types/context.go @@ -12,11 +12,32 @@ import ( "sync" ) -// An Context is an opaque type checking context. It may be used to share -// identical type instances across type-checked packages or calls to -// Instantiate. +// This file contains a definition of the type-checking context; an opaque type +// that may be supplied by users during instantiation. // -// It is safe for concurrent use. +// Contexts serve two purposes: +// - reduce the duplication of identical instances +// - short-circuit instantiation cycles +// +// For the latter purpose, we must always have a context during instantiation, +// whether or not it is supplied by the user. For both purposes, it must be the +// case that hashing a pointer-identical type produces consistent results +// (somewhat obviously). +// +// However, neither of these purposes require that our hash is perfect, and so +// this was not an explicit design goal of the context type. In fact, due to +// concurrent use it is convenient not to guarantee de-duplication. +// +// Nevertheless, in the future it could be helpful to allow users to leverage +// contexts to canonicalize instances, and it would probably be possible to +// achieve such a guarantee. + +// A Context is an opaque type checking context. It may be used to share +// identical type instances across type-checked packages or calls to +// Instantiate. Contexts are safe for concurrent use. +// +// The use of a shared context does not guarantee that identical instances are +// deduplicated in all cases. type Context struct { mu sync.Mutex typeMap map[string][]ctxtEntry // type hash -> instances entries diff --git a/src/go/types/eval.go b/src/go/types/eval.go index 5700cbf79c..084f746fe6 100644 --- a/src/go/types/eval.go +++ b/src/go/types/eval.go @@ -53,7 +53,6 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ Type // functions ignore the context in which an expression is used (e.g., an // assignment). Thus, top-level untyped constants will return an // untyped type rather then the respective context-specific type. -// func CheckExpr(fset *token.FileSet, pkg *Package, pos token.Pos, expr ast.Expr, info *Info) (err error) { // determine scope var scope *Scope diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go index b0745c16d9..6f5b548eb2 100644 --- a/src/go/types/eval_test.go +++ b/src/go/types/eval_test.go @@ -12,6 +12,7 @@ import ( "go/importer" "go/parser" "go/token" + "internal/goexperiment" "internal/testenv" "strings" "testing" @@ -208,7 +209,7 @@ func TestCheckExpr(t *testing.T) { // expr is an identifier or selector expression that is passed // to CheckExpr at the position of the comment, and object is // the string form of the object it denotes. - const src = ` + src := ` package p import "fmt" @@ -235,6 +236,13 @@ func f(a int, s string) S { return S{} }` + // The unified IR importer always sets interface method receiver + // parameters to point to the Interface type, rather than the Named. + // See #49906. + if goexperiment.Unified { + src = strings.ReplaceAll(src, "func (fmt.Stringer).", "func (interface).") + } + fset := token.NewFileSet() f, err := parser.ParseFile(fset, "p", src, parser.ParseComments) if err != nil { diff --git a/src/go/types/expr.go b/src/go/types/expr.go index a3c9041bdd..977153512f 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -934,28 +934,28 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) { return } } - } - - // Check that RHS is otherwise at least of integer type. - switch { - case allInteger(y.typ): - if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { - check.invalidOp(y, _InvalidShiftCount, "signed shift count %s requires go1.13 or later", y) + } else { + // Check that RHS is otherwise at least of integer type. + switch { + case allInteger(y.typ): + if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { + check.invalidOp(y, _InvalidShiftCount, "signed shift count %s requires go1.13 or later", y) + x.mode = invalid + return + } + case isUntyped(y.typ): + // This is incorrect, but preserves pre-existing behavior. + // See also bug #47410. + check.convertUntyped(y, Typ[Uint]) + if y.mode == invalid { + x.mode = invalid + return + } + default: + check.invalidOp(y, _InvalidShiftCount, "shift count %s must be integer", y) x.mode = invalid return } - case isUntyped(y.typ): - // This is incorrect, but preserves pre-existing behavior. - // See also bug #47410. - check.convertUntyped(y, Typ[Uint]) - if y.mode == invalid { - x.mode = invalid - return - } - default: - check.invalidOp(y, _InvalidShiftCount, "shift count %s must be integer", y) - x.mode = invalid - return } if x.mode == constant_ { @@ -1194,7 +1194,6 @@ const ( // If hint != nil, it is the type of a composite literal element. // If allowGeneric is set, the operand type may be an uninstantiated // parameterized type or function value. -// func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind { if trace { check.trace(e.Pos(), "-- expr %s", e) @@ -1242,7 +1241,6 @@ func (check *Checker) nonGeneric(x *operand) { // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. -// func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was issue 5770) @@ -1707,7 +1705,6 @@ func (check *Checker) typeAssertion(e ast.Expr, x *operand, T Type, typeSwitch b // expr typechecks expression e and initializes x with the expression value. // The result must be a single value. // If an error occurred, x.mode is set to invalid. -// func (check *Checker) expr(x *operand, e ast.Expr) { check.rawExpr(x, e, nil, false) check.exclude(x, 1<= 0 && v >= max { - check.invalidArg(&x, _InvalidIndex, "index %s is out of bounds", &x) + check.invalidArg(&x, _InvalidIndex, "index %s out of bounds [0:%d]", x.val.String(), max) return } @@ -411,7 +411,6 @@ func (check *Checker) isValidIndex(x *operand, code errorCode, what string, allo // against the literal's element type (typ), and the element indices against // the literal length if known (length >= 0). It returns the length of the // literal (maximum index value + 1). -// func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 { visited := make(map[int64]bool, len(elts)) var index, max int64 diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index d7045ff23c..4450847dfd 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -24,7 +24,8 @@ import ( // previous instances with the same identity. As a special case, generic // *Signature origin types are only considered identical if they are pointer // equivalent, so that instantiating distinct (but possibly identical) -// signatures will yield different instances. +// signatures will yield different instances. The use of a shared context does +// not guarantee that identical instances are deduplicated in all cases. // // If validate is set, Instantiate verifies that the number of type arguments // and parameters match, and that the type arguments satisfy their diff --git a/src/go/types/interface.go b/src/go/types/interface.go index 361ef7eddf..52ae123bb7 100644 --- a/src/go/types/interface.go +++ b/src/go/types/interface.go @@ -173,7 +173,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d // We have a method with name f.Names[0]. name := f.Names[0] if name.Name == "_" { - check.errorf(name, _BlankIfaceMethod, "invalid method name _") + check.errorf(name, _BlankIfaceMethod, "methods must have a unique non-blank name") continue // ignore } diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index 335fada7b7..e38f56c956 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -41,7 +41,6 @@ import ( // - If indirect is set, a method with a pointer receiver type was found // but there was no pointer on the path from the actual receiver type to // the method's formal receiver base type, nor was the receiver addressable. -// func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { if T == nil { panic("LookupFieldOrMethod on nil type") @@ -83,9 +82,9 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o } // TODO(gri) The named type consolidation and seen maps below must be -// indexed by unique keys for a given type. Verify that named -// types always have only one representation (even when imported -// indirectly via different packages.) +// indexed by unique keys for a given type. Verify that named +// types always have only one representation (even when imported +// indirectly via different packages.) // lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod. // If foldCase is true, the lookup for methods will include looking for any method @@ -281,7 +280,6 @@ func lookupType(m map[Type]int, typ Type) (int, bool) { // is not set), MissingMethod only checks that methods of T which are also // present in V have matching types (e.g., for a type assertion x.(T) where // x is of interface type V). -// func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { m, alt := (*Checker)(nil).missingMethod(V, T, static) // Only report a wrong type if the alternative method has the same name as m. diff --git a/src/go/types/named.go b/src/go/types/named.go index a0b94818f5..ee35080142 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -109,7 +109,7 @@ func (t *Named) Obj() *TypeName { func (t *Named) Origin() *Named { return t.orig } // TODO(gri) Come up with a better representation and API to distinguish -// between parameterized instantiated and non-instantiated types. +// between parameterized instantiated and non-instantiated types. // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) generic type even if it is instantiated. @@ -193,7 +193,7 @@ func (t *Named) instantiateMethod(i int) *Func { rtyp = t } - sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp) + sig.recv = substVar(origSig.recv, rtyp) return NewFunc(origm.pos, origm.pkg, origm.name, sig) } diff --git a/src/go/types/object.go b/src/go/types/object.go index fb377002aa..89dcd83c2d 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -14,7 +14,6 @@ import ( // An Object describes a named language entity such as a package, // constant, type, variable, function (incl. methods), or label. // All objects implement the Object interface. -// type Object interface { Parent() *Scope // scope in which this object is declared; nil for methods and struct fields Pos() token.Pos // position of object identifier in declaration @@ -297,7 +296,7 @@ func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var { // NewField returns a new variable representing a struct field. // For embedded fields, the name is the unqualified type name -/// under which the field is accessible. +// under which the field is accessible. func NewField(pos token.Pos, pkg *Package, name string, typ Type, embedded bool) *Var { return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, embedded: embedded, isField: true} } diff --git a/src/go/types/operand.go b/src/go/types/operand.go index 4d7f1e3b63..f9f109aa69 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -49,7 +49,6 @@ var operandModeString = [...]string{ // the operand, the operand's type, a value for constants, and an id // for built-in functions. // The zero value of operand is a ready to use invalid operand. -// type operand struct { mode operandMode expr ast.Expr @@ -60,7 +59,6 @@ type operand struct { // Pos returns the position of the expression corresponding to x. // If x is invalid the position is token.NoPos. -// func (x *operand) Pos() token.Pos { // x.expr may not be set if x is invalid if x.expr == nil { @@ -102,7 +100,6 @@ func (x *operand) Pos() token.Pos { // // cgofunc ( ) // cgofunc ( of type ) -// func operandString(x *operand, qf Qualifier) string { // special-case nil if x.mode == value && x.typ == Typ[UntypedNil] { diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 9edf41bf3c..ae21c6d927 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -192,8 +192,9 @@ func (check *Checker) importPackage(at positioner, path, dir string) *Package { // package should be complete or marked fake, but be cautious if imp.complete || imp.fake { check.impMap[key] = imp - // Once we've formatted an error message once, keep the pkgPathMap - // up-to-date on subsequent imports. + // Once we've formatted an error message, keep the pkgPathMap + // up-to-date on subsequent imports. It is used for package + // qualification in error messages. if check.pkgPathMap != nil { check.markImports(imp) } @@ -269,7 +270,7 @@ func (check *Checker) collectObjects() { if d.spec.Name != nil { name = d.spec.Name.Name if path == "C" { - // match cmd/compile (not prescribed by spec) + // match 1.17 cmd/compile (not prescribed by spec) check.errorf(d.spec.Name, _ImportCRenamed, `cannot rename import "C"`) return } @@ -296,8 +297,8 @@ func (check *Checker) collectObjects() { check.recordImplicit(d.spec, pkgName) } - if path == "C" { - // match cmd/compile (not prescribed by spec) + if imp.fake { + // match 1.17 cmd/compile (not prescribed by spec) pkgName.used = true } @@ -673,7 +674,7 @@ func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // unusedImports checks for unused imports. func (check *Checker) unusedImports() { - // if function bodies are not checked, packages' uses are likely missing - don't check + // If function bodies are not checked, packages' uses are likely missing - don't check. if check.conf.IgnoreFuncBodies { return } diff --git a/src/go/types/return.go b/src/go/types/return.go index 2d34a70b98..ee8c41a431 100644 --- a/src/go/types/return.go +++ b/src/go/types/return.go @@ -101,8 +101,8 @@ func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) boo } // TODO(gri) For nested breakable statements, the current implementation of hasBreak -// will traverse the same subtree repeatedly, once for each label. Replace -// with a single-pass label/break matching phase. +// will traverse the same subtree repeatedly, once for each label. Replace +// with a single-pass label/break matching phase. // hasBreak reports if s is or contains a break statement // referring to the label-ed statement or implicit-ly the diff --git a/src/go/types/selection.go b/src/go/types/selection.go index 6ec69d21db..4e06ab16b4 100644 --- a/src/go/types/selection.go +++ b/src/go/types/selection.go @@ -36,7 +36,6 @@ const ( // p.x FieldVal T x int {0} true // p.m MethodVal *T m func() {1, 0} true // T.m MethodExpr T m func(T) {1, 0} false -// type Selection struct { kind SelectionKind recv Type // type of x @@ -115,7 +114,6 @@ func (s *Selection) String() string { return SelectionString(s, nil) } // "field (T) f int" // "method (T) f(X) Y" // "method expr (T) f(X) Y" -// func SelectionString(s *Selection, qf Qualifier) string { var k string switch s.kind { diff --git a/src/go/types/signature.go b/src/go/types/signature.go index a340ac701e..9e7b63b451 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -206,62 +206,48 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast // Delay validation of receiver type as it may cause premature expansion // of types the receiver type is dependent on (see issues #51232, #51233). check.later(func() { - rtyp, _ := deref(recv.typ) - // spec: "The receiver type must be of the form T or *T where T is a type name." - // (ignore invalid types - error was reported before) - if rtyp != Typ[Invalid] { - var err string - switch T := rtyp.(type) { - case *Named: - T.resolve(check.bestContext(nil)) - // The receiver type may be an instantiated type referred to - // by an alias (which cannot have receiver parameters for now). - if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { - check.errorf(recv, _InvalidRecv, "cannot define methods on instantiated type %s", recv.typ) - break - } - // spec: "The type denoted by T is called the receiver base type; it must not - // be a pointer or interface type and it must be declared in the same package - // as the method." - if T.obj.pkg != check.pkg { - err = "type not defined in this package" - if compilerErrorMessages { - check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", recv.typ) - err = "" - } - } else { - // The underlying type of a receiver base type can be a type parameter; - // e.g. for methods with a generic receiver T[P] with type T[P any] P. - // TODO(gri) Such declarations are currently disallowed. - // Revisit the need for underIs. - underIs(T, func(u Type) bool { - switch u := u.(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - err = "unsafe.Pointer" - return false - } - case *Pointer, *Interface: - err = "pointer or interface type" - return false - } - return true - }) - } + rtyp, _ := deref(recv.typ) + if rtyp == Typ[Invalid] { + return // error was reported before + } + // spec: "The type denoted by T is called the receiver base type; it must not + // be a pointer or interface type and it must be declared in the same package + // as the method." + switch T := rtyp.(type) { + case *Named: + T.resolve(check.bestContext(nil)) + // The receiver type may be an instantiated type referred to + // by an alias (which cannot have receiver parameters for now). + if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { + check.errorf(recv, _InvalidRecv, "cannot define new methods on instantiated type %s", rtyp) + break + } + if T.obj.pkg != check.pkg { + check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", rtyp) + break + } + var cause string + switch u := T.under().(type) { case *Basic: - err = "basic or unnamed type" - if compilerErrorMessages { - check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", recv.typ) - err = "" + // unsafe.Pointer is treated like a regular pointer + if u.kind == UnsafePointer { + cause = "unsafe.Pointer" } - default: - check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ) + case *Pointer, *Interface: + cause = "pointer or interface type" + case *TypeParam: + // The underlying type of a receiver base type cannot be a + // type parameter: "type T[P any] P" is not a valid declaration. + unreachable() } - if err != "" { - check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err) + if cause != "" { + check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", rtyp, cause) } + case *Basic: + check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", rtyp) + default: + check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ) } }).describef(recv, "validate receiver %s", recv) } diff --git a/src/go/types/sizes.go b/src/go/types/sizes.go index dd4b78969f..fd18fc5796 100644 --- a/src/go/types/sizes.go +++ b/src/go/types/sizes.go @@ -39,7 +39,6 @@ type Sizes interface { // types are naturally aligned with a maximum alignment MaxAlign. // // *StdSizes implements Sizes. -// type StdSizes struct { WordSize int64 // word size in bytes - must be >= 4 (32bits) MaxAlign int64 // maximum alignment in bytes - must be >= 1 diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index d7f6a486ca..0fab70719e 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -317,7 +317,7 @@ L: } // TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead. -// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.) +// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.) // // func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[string]ast.Expr) (T Type) { // var dummy operand diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 4b4a0f4ad6..ef5593b71f 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -290,14 +290,18 @@ func (subst *subster) typOrNil(typ Type) Type { func (subst *subster) var_(v *Var) *Var { if v != nil { if typ := subst.typ(v.typ); typ != v.typ { - copy := *v - copy.typ = typ - return © + return substVar(v, typ) } } return v } +func substVar(v *Var, typ Type) *Var { + copy := *v + copy.typ = typ + return © +} + func (subst *subster) tuple(t *Tuple) *Tuple { if t != nil { if vars, copied := subst.varList(t.vars); copied { @@ -410,9 +414,8 @@ func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) { copied = true } newsig := *sig - sig = &newsig - sig.recv = NewVar(sig.recv.pos, sig.recv.pkg, "", new) - out[i] = NewFunc(method.pos, method.pkg, method.name, sig) + newsig.recv = substVar(sig.recv, new) + out[i] = NewFunc(method.pos, method.pkg, method.name, &newsig) } } return diff --git a/src/go/types/testdata/check/decls0.go b/src/go/types/testdata/check/decls0.go index 740c9b4fdf..d8fcef0824 100644 --- a/src/go/types/testdata/check/decls0.go +++ b/src/go/types/testdata/check/decls0.go @@ -196,8 +196,8 @@ func (S0) m4 () (x *S0 /* ERROR illegal cycle in method declaration */ .m4) { re // interfaces may not have any blank methods type BlankI interface { - _ /* ERROR "invalid method name" */ () - _ /* ERROR "invalid method name" */ (float32) int + _ /* ERROR "methods must have a unique non-blank name" */ () + _ /* ERROR "methods must have a unique non-blank name" */ (float32) int m() } diff --git a/src/go/types/testdata/check/decls2/decls2a.go b/src/go/types/testdata/check/decls2/decls2a.go index bdbecd9dbb..9dff17349c 100644 --- a/src/go/types/testdata/check/decls2/decls2a.go +++ b/src/go/types/testdata/check/decls2/decls2a.go @@ -93,10 +93,10 @@ func (a, b /* ERROR "exactly one receiver" */ T3) _() {} func (a, b, c /* ERROR "exactly one receiver" */ T3) _() {} // Methods associated with non-local or unnamed types. -func (int /* ERROR "invalid receiver" */ ) m() {} +func (int /* ERROR "cannot define new methods on non-local type int" */ ) m() {} func ([ /* ERROR "invalid receiver" */ ]int) m() {} -func (time /* ERROR "invalid receiver" */ .Time) m() {} -func (* /* ERROR "invalid receiver" */ time.Time) m() {} +func (time /* ERROR "cannot define new methods on non-local type time\.Time" */ .Time) m() {} +func (* /* ERROR "cannot define new methods on non-local type time\.Time" */ time.Time) m() {} func (x /* ERROR "invalid receiver" */ interface{}) m() {} // Unsafe.Pointer is treated like a pointer when used as receiver type. diff --git a/src/go/types/testdata/check/decls4.go b/src/go/types/testdata/check/decls4.go index 140bbfd31f..8a9a6ffba7 100644 --- a/src/go/types/testdata/check/decls4.go +++ b/src/go/types/testdata/check/decls4.go @@ -59,7 +59,7 @@ var ( ) // alias receiver types -func (Ai /* ERROR "invalid receiver" */) m1() {} +func (Ai /* ERROR "cannot define new methods on non-local type int" */) m1() {} func (T0) m1() {} func (A0) m1 /* ERROR already declared */ () {} func (A0) m2 () {} @@ -115,8 +115,8 @@ type ( B2 = int ) -func (B0 /* ERROR invalid receiver */ ) m() {} -func (B1 /* ERROR invalid receiver */ ) n() {} +func (B0 /* ERROR cannot define new methods on non-local type int */ ) m() {} +func (B1 /* ERROR cannot define new methods on non-local type int */ ) n() {} // cycles type ( diff --git a/src/go/types/testdata/check/typeparams.go b/src/go/types/testdata/check/typeparams.go index 29a3b16cd6..199828f55f 100644 --- a/src/go/types/testdata/check/typeparams.go +++ b/src/go/types/testdata/check/typeparams.go @@ -389,31 +389,6 @@ func _[T any] (x T) { m(S1[T]{x}) } -// type parameters in methods (generalization) - -// Type Parameter lists are not allowed on methods, and are not produced by -// go/parser. The test cases below are preserved for consistency with types2, -// which produces an error but stores type parameters. -// type R0 struct{} - -// func (R0) _[ /* ERROR methods cannot have type parameters */ T any](x T) {} -// func (R0 /* ERROR invalid receiver */ ) _[ /* ERROR methods cannot have type parameters */ R0 any]() {} // scope of type parameters starts at "func" - -// type R1[A, B any] struct{} - -// func (_ R1[A, B]) m0(A, B) -// func (_ R1[A, B]) m1[ /* ERROR methods cannot have type parameters */ T any](A, B, T) T { panic(0) } -// func (_ R1 /* ERROR not a generic type */ [R1, _]) _() -// func (_ R1[A, B]) _[ /* ERROR methods cannot have type parameters */ A /* ERROR redeclared */ any](B) {} - -// func _() { -// var r R1[int, string] -// r.m1[rune](42, "foo", 'a') -// r.m1[rune](42, "foo", 1.2 /* ERROR cannot use .* as rune .* \(truncated\) */) -// r.m1(42, "foo", 1.2) // using type inference -// var _ float64 = r.m1(42, "foo", 1.2) -// } - type I1[A any] interface { m1(A) } diff --git a/src/go/types/testdata/examples/constraints.go b/src/go/types/testdata/examples/constraints.go index 4d7f70313a..5b144893ce 100644 --- a/src/go/types/testdata/examples/constraints.go +++ b/src/go/types/testdata/examples/constraints.go @@ -24,7 +24,8 @@ type ( _ interface{int|any} _ interface{int|~string|union} _ interface{int|~string|interface{int}} - _ interface{union|union /* ERROR overlapping terms p.union and p.union */ } + _ interface{union|int} // interfaces (here: union) are ignored when checking for overlap + _ interface{union|union} // ditto // For now we do not permit interfaces with methods in unions. _ interface{~ /* ERROR invalid use of ~ */ any} @@ -44,9 +45,9 @@ type ( type ( _[T interface{ *T } ] struct{} // ok _[T interface{ int | *T } ] struct{} // ok - _[T interface{ T /* ERROR cannot embed a type parameter */ } ] struct{} - _[T interface{ ~T /* ERROR cannot embed a type parameter */ } ] struct{} - _[T interface{ int|T /* ERROR cannot embed a type parameter */ }] struct{} + _[T interface{ T /* ERROR term cannot be a type parameter */ } ] struct{} + _[T interface{ ~T /* ERROR type in term ~T cannot be a type parameter */ } ] struct{} + _[T interface{ int|T /* ERROR term cannot be a type parameter */ }] struct{} ) // Multiple embedded union elements are intersected. The order in which they diff --git a/src/go/types/testdata/fixedbugs/issue39948.go b/src/go/types/testdata/fixedbugs/issue39948.go index e38e57268d..c893cc049e 100644 --- a/src/go/types/testdata/fixedbugs/issue39948.go +++ b/src/go/types/testdata/fixedbugs/issue39948.go @@ -5,5 +5,5 @@ package p type T[P any] interface{ - P // ERROR cannot embed a type parameter + P // ERROR term cannot be a type parameter } diff --git a/src/go/types/testdata/fixedbugs/issue43109.go b/src/go/types/testdata/fixedbugs/issue43109.go new file mode 100644 index 0000000000..a4533c9bf7 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue43109.go @@ -0,0 +1,10 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Ensure there is no "imported but not used" error +// if a package wasn't imported in the first place. + +package p + +import . "/foo" // ERROR could not import \/foo diff --git a/src/go/types/testdata/fixedbugs/issue47127.go b/src/go/types/testdata/fixedbugs/issue47127.go index 108d600a38..bb4b487eb2 100644 --- a/src/go/types/testdata/fixedbugs/issue47127.go +++ b/src/go/types/testdata/fixedbugs/issue47127.go @@ -8,30 +8,30 @@ package p type ( _[P any] interface{ *P | []P | chan P | map[string]P } - _[P any] interface{ P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ ~P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | ~P /* ERROR "cannot embed a type parameter" */ } + _[P any] interface{ P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _[P any] interface{ int | P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } ) func _[P any]() { type ( _[P any] interface{ *P | []P | chan P | map[string]P } - _[P any] interface{ P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ ~P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | P /* ERROR "cannot embed a type parameter" */ } - _[P any] interface{ int | ~P /* ERROR "cannot embed a type parameter" */ } + _[P any] interface{ P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _[P any] interface{ int | P /* ERROR term cannot be a type parameter */ } + _[P any] interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } _ interface{ *P | []P | chan P | map[string]P } - _ interface{ P /* ERROR "cannot embed a type parameter" */ } - _ interface{ ~P /* ERROR "cannot embed a type parameter" */ } - _ interface{ int | P /* ERROR "cannot embed a type parameter" */ } - _ interface{ int | ~P /* ERROR "cannot embed a type parameter" */ } + _ interface{ P /* ERROR term cannot be a type parameter */ } + _ interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ } + _ interface{ int | P /* ERROR term cannot be a type parameter */ } + _ interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ } ) } func _[P any, Q interface{ *P | []P | chan P | map[string]P }]() {} -func _[P any, Q interface{ P /* ERROR "cannot embed a type parameter" */ }]() {} -func _[P any, Q interface{ ~P /* ERROR "cannot embed a type parameter" */ }]() {} -func _[P any, Q interface{ int | P /* ERROR "cannot embed a type parameter" */ }]() {} -func _[P any, Q interface{ int | ~P /* ERROR "cannot embed a type parameter" */ }]() {} +func _[P any, Q interface{ P /* ERROR term cannot be a type parameter */ }]() {} +func _[P any, Q interface{ ~P /* ERROR type in term ~P cannot be a type parameter */ }]() {} +func _[P any, Q interface{ int | P /* ERROR term cannot be a type parameter */ }]() {} +func _[P any, Q interface{ int | ~P /* ERROR type in term ~P cannot be a type parameter */ }]() {} diff --git a/src/go/types/testdata/fixedbugs/issue47968.go b/src/go/types/testdata/fixedbugs/issue47968.go index 711e50a55a..3dd303957c 100644 --- a/src/go/types/testdata/fixedbugs/issue47968.go +++ b/src/go/types/testdata/fixedbugs/issue47968.go @@ -14,8 +14,8 @@ func (A1[P]) m2() {} type A2 = T[int] -func (A2 /* ERROR cannot define methods on instantiated type T\[int\] */) m3() {} -func (_ /* ERROR cannot define methods on instantiated type T\[int\] */ A2) m4() {} +func (A2 /* ERROR cannot define new methods on instantiated type T\[int\] */) m3() {} +func (_ /* ERROR cannot define new methods on instantiated type T\[int\] */ A2) m4() {} func (T[int]) m5() {} // int is the type parameter name, not an instantiation func (T[* /* ERROR must be an identifier */ int]) m6() {} // syntax error diff --git a/src/go/types/testdata/fixedbugs/issue51607.go b/src/go/types/testdata/fixedbugs/issue51607.go new file mode 100644 index 0000000000..d8df143627 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue51607.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +// Interface types must be ignored during overlap test. + +type ( + T1 interface{int} + T2 interface{~int} + T3 interface{T1 | bool | string} + T4 interface{T2 | ~bool | ~string} +) + +type ( + // overlap errors for non-interface terms + // (like the interface terms, but explicitly inlined) + _ interface{int | int /* ERROR overlapping terms int and int */ } + _ interface{int | ~ /* ERROR overlapping terms ~int and int */ int} + _ interface{~int | int /* ERROR overlapping terms int and ~int */ } + _ interface{~int | ~ /* ERROR overlapping terms ~int and ~int */ int} + + _ interface{T1 | bool | string | T1 | bool /* ERROR overlapping terms bool and bool */ | string /* ERROR overlapping terms string and string */ } + _ interface{T1 | bool | string | T2 | ~ /* ERROR overlapping terms ~bool and bool */ bool | ~ /* ERROR overlapping terms ~string and string */ string} + + // no errors for interface terms + _ interface{T1 | T1} + _ interface{T1 | T2} + _ interface{T2 | T1} + _ interface{T2 | T2} + + _ interface{T3 | T3 | int} + _ interface{T3 | T4 | bool } + _ interface{T4 | T3 | string } + _ interface{T4 | T4 | float64 } +) + +func _[_ T1 | bool | string | T1 | bool /* ERROR overlapping terms */ ]() {} +func _[_ T1 | bool | string | T2 | ~ /* ERROR overlapping terms */ bool ]() {} +func _[_ T2 | ~bool | ~string | T1 | bool /* ERROR overlapping terms */ ]() {} +func _[_ T2 | ~bool | ~string | T2 | ~ /* ERROR overlapping terms */ bool ]() {} + +func _[_ T3 | T3 | int]() {} +func _[_ T3 | T4 | bool]() {} +func _[_ T4 | T3 | string]() {} +func _[_ T4 | T4 | float64]() {} + +// test cases from issue + +type _ interface { + interface {bool | int} | interface {bool | string} +} + +type _ interface { + interface {bool | int} ; interface {bool | string} +} + +type _ interface { + interface {bool; int} ; interface {bool; string} +} + +type _ interface { + interface {bool; int} | interface {bool; string} +} \ No newline at end of file diff --git a/src/go/types/testdata/fixedbugs/issue52031.go b/src/go/types/testdata/fixedbugs/issue52031.go new file mode 100644 index 0000000000..448a550b25 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue52031.go @@ -0,0 +1,33 @@ +// -lang=go1.12 + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type resultFlags uint + +// Example from #52031. +// +// The following shifts should not produce errors on Go < 1.13, as their +// untyped constant operands are representable by type uint. +const ( + _ resultFlags = (1 << iota) / 2 + + reportEqual + reportUnequal + reportByIgnore + reportByMethod + reportByFunc + reportByCycle +) + +// Invalid cases. +var x int = 1 +var _ = (8 << x /* ERROR "signed shift count .* requires go1.13 or later" */) + +const _ = (1 << 1.2 /* ERROR "truncated to uint" */) + +var y float64 +var _ = (1 << y /* ERROR "must be integer" */) diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 80210a2f34..0325d4a77f 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -26,7 +26,6 @@ import ( // // Using a nil Qualifier is equivalent to using (*Package).Path: the // object is qualified by the import path, e.g., "encoding/json.Marshal". -// type Qualifier func(*Package) string // RelativeTo returns a Qualifier that fully qualifies members of diff --git a/src/go/types/typeterm.go b/src/go/types/typeterm.go index 6b67821000..13b6ce6d0d 100644 --- a/src/go/types/typeterm.go +++ b/src/go/types/typeterm.go @@ -10,7 +10,6 @@ package types // 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) // T: &term{false, T} == {T} // set of type T // ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t -// type term struct { tilde bool // valid if typ != nil typ Type diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index bae9dc816c..b704372dcf 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -18,7 +18,6 @@ import ( // If an error occurred, x.mode is set to invalid. // For the meaning of def, see Checker.definedType, below. // If wantType is set, the identifier e is expected to denote a type. -// func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool) { x.mode = invalid x.expr = e @@ -177,7 +176,6 @@ func (check *Checker) validVarType(e ast.Expr, typ Type) { // If def != nil, e is the type specification for the defined type def, declared // in a type declaration, and def.underlying will be set to the type of e before // any components of e are type-checked. -// func (check *Checker) definedType(e ast.Expr, def *Named) Type { typ := check.typInternal(e, def) assert(isTyped(typ)) @@ -214,7 +212,6 @@ func goTypeName(typ Type) string { // typInternal drives type checking of types. // Must only be called by definedType or genericType. -// func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { if trace { check.trace(e0.Pos(), "-- type %s", e0) diff --git a/src/go/types/union.go b/src/go/types/union.go index 1a8825fcab..37a3489558 100644 --- a/src/go/types/union.go +++ b/src/go/types/union.go @@ -116,14 +116,12 @@ func parseUnion(check *Checker, uexpr ast.Expr) Type { switch { case tset.NumMethods() != 0: check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s contains methods)", t, t) - continue case t.typ == universeComparable.Type(): check.error(tlist[i], _InvalidUnion, "cannot use comparable in union") - continue case tset.comparable: check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s embeds comparable)", t, t) - continue } + continue // terms with interface types are not subject to the no-overlap rule } // Report overlapping (non-disjoint) terms such as @@ -151,7 +149,11 @@ func parseTilde(check *Checker, tx ast.Expr) *Term { // simply use its underlying type (like we do for other named, embedded interfaces), // and since the underlying type is an interface the embedding is well defined. if isTypeParam(typ) { - check.error(x, _MisplacedTypeParam, "cannot embed a type parameter") + if tilde { + check.errorf(x, _MisplacedTypeParam, "type in term %s cannot be a type parameter", tx) + } else { + check.error(x, _MisplacedTypeParam, "term cannot be a type parameter") + } typ = Typ[Invalid] } term := NewTerm(tilde, typ) @@ -163,10 +165,16 @@ func parseTilde(check *Checker, tx ast.Expr) *Term { // overlappingTerm reports the index of the term x in terms which is // overlapping (not disjoint) from y. The result is < 0 if there is no -// such term. +// such term. The type of term y must not be an interface, and terms +// with an interface type are ignored in the terms list. func overlappingTerm(terms []*Term, y *Term) int { + assert(!IsInterface(y.typ)) for i, x := range terms { - // disjoint requires non-nil, non-top arguments + if IsInterface(x.typ) { + continue + } + // disjoint requires non-nil, non-top arguments, + // and non-interface types as term types. if debug { if x == nil || x.typ == nil || y == nil || y.typ == nil { panic("empty or top union term") diff --git a/src/go/types/universe.go b/src/go/types/universe.go index f58128f480..8ac48e506e 100644 --- a/src/go/types/universe.go +++ b/src/go/types/universe.go @@ -246,7 +246,6 @@ func init() { // Objects with names containing blanks are internal and not entered into // a scope. Objects with exported names are inserted in the unsafe package // scope; other objects are inserted in the universe scope. -// func def(obj Object) { assert(obj.color() == black) name := obj.Name() diff --git a/src/go/types/validtype.go b/src/go/types/validtype.go index 2c686f2655..edb4c02ecd 100644 --- a/src/go/types/validtype.go +++ b/src/go/types/validtype.go @@ -140,8 +140,8 @@ func (env *tparamEnv) push(typ *Named) *tparamEnv { } // TODO(gri) Alternative implementation: -// We may not need to build a stack of environments to -// look up the type arguments for type parameters. The -// same information should be available via the path: -// We should be able to just walk the path backwards -// and find the type arguments in the instance objects. +// We may not need to build a stack of environments to +// look up the type arguments for type parameters. The +// same information should be available via the path: +// We should be able to just walk the path backwards +// and find the type arguments in the instance objects. diff --git a/src/html/template/exec_test.go b/src/html/template/exec_test.go index 6cf710efab..f042cf5125 100644 --- a/src/html/template/exec_test.go +++ b/src/html/template/exec_test.go @@ -1191,15 +1191,19 @@ var cmpTests = []cmpTest{ {"eq .Iface1 .Iface1", "true", true}, {"eq .Iface1 .Iface2", "false", true}, {"eq .Iface2 .Iface2", "true", true}, + {"eq .Map .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map nil", "true", true}, // Uncomparable types but nil is OK. + {"eq nil .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map .NonNilMap", "false", true}, // Uncomparable types but nil is OK. // Errors - {"eq `xy` 1", "", false}, // Different types. - {"eq 2 2.0", "", false}, // Different types. - {"lt true true", "", false}, // Unordered types. - {"lt 1+0i 1+0i", "", false}, // Unordered types. - {"eq .Ptr 1", "", false}, // Incompatible types. - {"eq .Ptr .NegOne", "", false}, // Incompatible types. - {"eq .Map .Map", "", false}, // Uncomparable types. - {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq `xy` 1", "", false}, // Different types. + {"eq 2 2.0", "", false}, // Different types. + {"lt true true", "", false}, // Unordered types. + {"lt 1+0i 1+0i", "", false}, // Unordered types. + {"eq .Ptr 1", "", false}, // Incompatible types. + {"eq .Ptr .NegOne", "", false}, // Incompatible types. + {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq .NonNilMap .NonNilMap", "", false}, // Uncomparable types. } func TestComparison(t *testing.T) { @@ -1208,16 +1212,18 @@ func TestComparison(t *testing.T) { Uthree, Ufour uint NegOne, Three int Ptr, NilPtr *int + NonNilMap map[int]int Map map[int]int V1, V2 V Iface1, Iface2 fmt.Stringer }{ - Uthree: 3, - Ufour: 4, - NegOne: -1, - Three: 3, - Ptr: new(int), - Iface1: b, + Uthree: 3, + Ufour: 4, + NegOne: -1, + Three: 3, + Ptr: new(int), + NonNilMap: make(map[int]int), + Iface1: b, } for _, test := range cmpTests { text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) diff --git a/src/html/template/template.go b/src/html/template/template.go index 7eba716f1b..a99f69231c 100644 --- a/src/html/template/template.go +++ b/src/html/template/template.go @@ -72,7 +72,6 @@ func (t *Template) Templates() []*Template { // The operation returns the zero value for the map type's element. // "missingkey=error" // Execution stops immediately with an error. -// func (t *Template) Option(opt ...string) *Template { t.text.Option(opt...) return t @@ -328,14 +327,7 @@ func (t *Template) Name() string { return t.text.Name() } -// FuncMap is the type of the map defining the mapping from names to -// functions. Each function must have either a single return value, or two -// return values of which the second has type error. In that case, if the -// second (error) argument evaluates to non-nil during execution, execution -// terminates and Execute returns that error. FuncMap has the same base type -// as FuncMap in "text/template", copied here so clients need not import -// "text/template". -type FuncMap map[string]any +type FuncMap = template.FuncMap // Funcs adds the elements of the argument map to the template's function map. // It must be called before the template is parsed. diff --git a/src/index/suffixarray/suffixarray.go b/src/index/suffixarray/suffixarray.go index 9c169e7aca..cdc6e81a80 100644 --- a/src/index/suffixarray/suffixarray.go +++ b/src/index/suffixarray/suffixarray.go @@ -230,7 +230,6 @@ func (x *Index) Write(w io.Writer) error { // Bytes returns the data over which the index was created. // It must not be modified. -// func (x *Index) Bytes() []byte { return x.data } @@ -255,7 +254,6 @@ func (x *Index) lookupAll(s []byte) ints { // The result is nil if s is empty, s is not found, or n == 0. // Lookup time is O(log(N)*len(s) + len(result)) where N is the // size of the indexed data. -// func (x *Index) Lookup(s []byte, n int) (result []int) { if len(s) > 0 && n != 0 { matches := x.lookupAll(s) @@ -286,7 +284,6 @@ func (x *Index) Lookup(s []byte, n int) (result []int) { // in successive order. Otherwise, at most n matches are returned and // they may not be successive. The result is nil if there are no matches, // or if n == 0. -// func (x *Index) FindAllIndex(r *regexp.Regexp, n int) (result [][]int) { // a non-empty literal prefix is used to determine possible // match start indices with Lookup diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index 6b770558fd..87b1686c20 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -70,7 +70,6 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { baseline := goexperiment.Flags{ RegabiWrappers: regabiSupported, RegabiArgs: regabiSupported, - PacerRedesign: true, } // Start with the statically enabled set of experiments. diff --git a/src/internal/fmtsort/sort.go b/src/internal/fmtsort/sort.go index 34c1f477f0..09e987d0e6 100644 --- a/src/internal/fmtsort/sort.go +++ b/src/internal/fmtsort/sort.go @@ -48,7 +48,6 @@ func (o *SortedMap) Swap(i, j int) { // Otherwise identical arrays compare by length. // - interface values compare first by reflect.Type describing the concrete type // and then by concrete value as described in the previous rules. -// func Sort(mapValue reflect.Value) *SortedMap { if mapValue.Type().Kind() != reflect.Map { return nil diff --git a/src/internal/goexperiment/exp_boringcrypto_off.go b/src/internal/goexperiment/exp_boringcrypto_off.go new file mode 100644 index 0000000000..020c75bd53 --- /dev/null +++ b/src/internal/goexperiment/exp_boringcrypto_off.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.boringcrypto +// +build !goexperiment.boringcrypto + +package goexperiment + +const BoringCrypto = false +const BoringCryptoInt = 0 diff --git a/src/internal/goexperiment/exp_boringcrypto_on.go b/src/internal/goexperiment/exp_boringcrypto_on.go new file mode 100644 index 0000000000..1454329a46 --- /dev/null +++ b/src/internal/goexperiment/exp_boringcrypto_on.go @@ -0,0 +1,9 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.boringcrypto +// +build goexperiment.boringcrypto + +package goexperiment + +const BoringCrypto = true +const BoringCryptoInt = 1 diff --git a/src/internal/goexperiment/exp_pacerredesign_off.go b/src/internal/goexperiment/exp_pacerredesign_off.go deleted file mode 100644 index 62e1831437..0000000000 --- a/src/internal/goexperiment/exp_pacerredesign_off.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build !goexperiment.pacerredesign -// +build !goexperiment.pacerredesign - -package goexperiment - -const PacerRedesign = false -const PacerRedesignInt = 0 diff --git a/src/internal/goexperiment/exp_pacerredesign_on.go b/src/internal/goexperiment/exp_pacerredesign_on.go deleted file mode 100644 index b22b031009..0000000000 --- a/src/internal/goexperiment/exp_pacerredesign_on.go +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build goexperiment.pacerredesign -// +build goexperiment.pacerredesign - -package goexperiment - -const PacerRedesign = true -const PacerRedesignInt = 1 diff --git a/src/internal/goexperiment/flags.go b/src/internal/goexperiment/flags.go index 9150493575..20d9c2da5d 100644 --- a/src/internal/goexperiment/flags.go +++ b/src/internal/goexperiment/flags.go @@ -58,6 +58,7 @@ type Flags struct { FieldTrack bool PreemptibleLoops bool StaticLockRanking bool + BoringCrypto bool // Unified enables the compiler's unified IR construction // experiment. @@ -79,12 +80,6 @@ type Flags struct { // reflection calls use registers). RegabiArgs bool - // PacerRedesign enables the new GC pacer in the runtime. - // - // Details regarding the new pacer may be found at - // https://golang.org/design/44167-gc-pacer-redesign - PacerRedesign bool - // HeapMinimum512KiB reduces the minimum heap size to 512 KiB. // // This was originally reduced as part of PacerRedesign, but diff --git a/src/internal/poll/errno_unix.go b/src/internal/poll/errno_unix.go index c177519732..8eed93a31c 100644 --- a/src/internal/poll/errno_unix.go +++ b/src/internal/poll/errno_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package poll diff --git a/src/internal/poll/export_posix_test.go b/src/internal/poll/export_posix_test.go index 3fcafac002..3415ab3839 100644 --- a/src/internal/poll/export_posix_test.go +++ b/src/internal/poll/export_posix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows // Export guts for testing on posix. // Since testing imports os and os imports internal/poll, diff --git a/src/internal/poll/fd_poll_runtime.go b/src/internal/poll/fd_poll_runtime.go index 4a4dddfd27..2e9cd5c9d7 100644 --- a/src/internal/poll/fd_poll_runtime.go +++ b/src/internal/poll/fd_poll_runtime.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || windows || solaris +//go:build unix || windows package poll diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go index dc1e29c6b7..778fe1e5c1 100644 --- a/src/internal/poll/fd_posix.go +++ b/src/internal/poll/fd_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package poll diff --git a/src/internal/poll/fd_posix_test.go b/src/internal/poll/fd_posix_test.go index 0b7ef7a577..b97e46595a 100644 --- a/src/internal/poll/fd_posix_test.go +++ b/src/internal/poll/fd_posix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package poll_test diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go index 85971a16cd..2786064d9f 100644 --- a/src/internal/poll/fd_unix.go +++ b/src/internal/poll/fd_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package poll diff --git a/src/internal/poll/hook_unix.go b/src/internal/poll/hook_unix.go index c9aa4b4ca2..1a5035675d 100644 --- a/src/internal/poll/hook_unix.go +++ b/src/internal/poll/hook_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package poll diff --git a/src/internal/poll/sockopt.go b/src/internal/poll/sockopt.go index 2d354700c5..a7c9d115b4 100644 --- a/src/internal/poll/sockopt.go +++ b/src/internal/poll/sockopt.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package poll diff --git a/src/internal/poll/sockopt_unix.go b/src/internal/poll/sockopt_unix.go index 54be1cc4b6..9cba44da9d 100644 --- a/src/internal/poll/sockopt_unix.go +++ b/src/internal/poll/sockopt_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package poll diff --git a/src/internal/poll/sockoptip.go b/src/internal/poll/sockoptip.go index 7fc9aeefb3..41955e1fda 100644 --- a/src/internal/poll/sockoptip.go +++ b/src/internal/poll/sockoptip.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package poll diff --git a/src/internal/syscall/unix/net.go b/src/internal/syscall/unix/net.go index 85632e1c03..5618d40ae0 100644 --- a/src/internal/syscall/unix/net.go +++ b/src/internal/syscall/unix/net.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package unix diff --git a/src/internal/testenv/testenv_unix.go b/src/internal/testenv/testenv_unix.go index 3dc5daf45e..a97e88da2f 100644 --- a/src/internal/testenv/testenv_unix.go +++ b/src/internal/testenv/testenv_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package testenv diff --git a/src/io/fs/walk.go b/src/io/fs/walk.go index 52a51bbd37..37800794a2 100644 --- a/src/io/fs/walk.go +++ b/src/io/fs/walk.go @@ -58,7 +58,6 @@ var SkipDir = errors.New("skip this directory") // to bypass the directory read entirely. // - If a directory read fails, the function is called a second time // for that directory to report the error. -// type WalkDirFunc func(path string, d DirEntry, err error) error // walkDir recursively descends path, calling walkDirFn. diff --git a/src/make.bash b/src/make.bash index d8c1da6766..e517a1bda9 100755 --- a/src/make.bash +++ b/src/make.bash @@ -41,12 +41,20 @@ # Default is "gcc". Also supported: "clang". # # CC_FOR_TARGET: Command line to run to compile C code for GOARCH. -# This is used by cgo. Default is CC. +# This is used by cgo. Default is CC. +# +# CC_FOR_${GOOS}_${GOARCH}: Command line to run to compile C code for specified ${GOOS} and ${GOARCH}. +# (for example, CC_FOR_linux_arm) +# If this is not set, the build will use CC_FOR_TARGET if appropriate, or CC. # # CXX_FOR_TARGET: Command line to run to compile C++ code for GOARCH. # This is used by cgo. Default is CXX, or, if that is not set, # "g++" or "clang++". # +# CXX_FOR_${GOOS}_${GOARCH}: Command line to run to compile C++ code for specified ${GOOS} and ${GOARCH}. +# (for example, CXX_FOR_linux_arm) +# If this is not set, the build will use CXX_FOR_TARGET if appropriate, or CXX. +# # FC: Command line to run to compile Fortran code for GOARCH. # This is used by cgo. Default is "gfortran". # @@ -196,7 +204,7 @@ if [ "$GOROOT_BOOTSTRAP" = "$GOROOT" ]; then exit 1 fi rm -f cmd/dist/dist -GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" GO111MODULE=off GOEXPERIMENT= "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist +GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" GO111MODULE=off GOEXPERIMENT="" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist # -e doesn't propagate out of eval, so check success by hand. eval $(./cmd/dist/dist env -p || echo FAIL=true) diff --git a/src/make.bat b/src/make.bat index 56da417dd1..c2f87ace75 100644 --- a/src/make.bat +++ b/src/make.bat @@ -100,6 +100,7 @@ set GOROOT=%GOROOT_BOOTSTRAP% set GOOS= set GOARCH= set GOBIN= +set GOEXPERIMENT= set GO111MODULE=off "%GOROOT_BOOTSTRAP%\bin\go.exe" build -o cmd\dist\dist.exe .\cmd\dist endlocal diff --git a/src/make.rc b/src/make.rc index 066c3ab323..273d151190 100755 --- a/src/make.rc +++ b/src/make.rc @@ -88,7 +88,7 @@ if(~ $GOROOT_BOOTSTRAP $GOROOT){ echo 'Building Go cmd/dist using '^$GOROOT_BOOTSTRAP if(~ $#vflag 1) echo cmd/dist -GOROOT=$GOROOT_BOOTSTRAP GOOS='' GOARCH='' GO111MODULE=off $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist +GOROOT=$GOROOT_BOOTSTRAP GOOS='' GOARCH='' GOEXPERIMENT='' GO111MODULE=off $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist eval `{./cmd/dist/dist env -9} if(~ $#vflag 1) diff --git a/src/math/big/arith_test.go b/src/math/big/arith_test.go index 7b3427f834..789b96bf35 100644 --- a/src/math/big/arith_test.go +++ b/src/math/big/arith_test.go @@ -510,7 +510,7 @@ func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) { } // TODO(gri) mulAddVWW and divWVW are symmetric operations but -// their signature is not symmetric. Try to unify. +// their signature is not symmetric. Try to unify. type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word) type argWVW struct { diff --git a/src/math/big/float.go b/src/math/big/float.go index a8c91a6e54..70c7b794a4 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -236,7 +236,6 @@ func (x *Float) Acc() Accuracy { // -1 if x < 0 // 0 if x is ±0 // +1 if x > 0 -// func (x *Float) Sign() int { if debugFloat { x.validate() @@ -1672,7 +1671,6 @@ func (z *Float) Quo(x, y *Float) *Float { // -1 if x < y // 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf) // +1 if x > y -// func (x *Float) Cmp(y *Float) int { if debugFloat { x.validate() @@ -1707,7 +1705,6 @@ func (x *Float) Cmp(y *Float) int { // 0 if x == 0 (signed or unsigned) // +1 if 0 < x < +Inf // +2 if x == +Inf -// func (x *Float) ord() int { var m int switch x.form { diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go index 57b7df3936..93f7195219 100644 --- a/src/math/big/floatconv.go +++ b/src/math/big/floatconv.go @@ -153,7 +153,6 @@ func (z *Float) scan(r io.ByteScanner, base int) (f *Float, b int, err error) { // for p, q := uint64(0), uint64(1); p < q; p, q = q, q*5 { // fmt.Println(q) // } -// var pow5tab = [...]uint64{ 1, 5, @@ -257,7 +256,6 @@ func (z *Float) pow5(n uint64) *Float { // // The returned *Float f is nil and the value of z is valid but not // defined if an error is reported. -// func (z *Float) Parse(s string, base int) (f *Float, b int, err error) { // scan doesn't handle ±Inf if len(s) == 3 && (s == "Inf" || s == "inf") { diff --git a/src/math/big/int.go b/src/math/big/int.go index 7647346486..700d00d031 100644 --- a/src/math/big/int.go +++ b/src/math/big/int.go @@ -34,7 +34,6 @@ var intOne = &Int{false, natOne} // -1 if x < 0 // 0 if x == 0 // +1 if x > 0 -// func (x *Int) Sign() int { if len(x.abs) == 0 { return 0 @@ -234,7 +233,6 @@ func (z *Int) Rem(x, y *Int) *Int { // // (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) // See DivMod for Euclidean division and modulus (unlike Go). -// func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs) z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign @@ -292,7 +290,6 @@ func (z *Int) Mod(x, y *Int) *Int { // Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. // ACM press.) // See QuoRem for T-division and modulus (like Go). -// func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { y0 := y // save y if z == y || alias(z.abs, y.abs) { @@ -316,7 +313,6 @@ func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { // -1 if x < y // 0 if x == y // +1 if x > y -// func (x *Int) Cmp(y *Int) (r int) { // x cmp y == x cmp y // x cmp (-y) == x @@ -343,7 +339,6 @@ func (x *Int) Cmp(y *Int) (r int) { // -1 if |x| < |y| // 0 if |x| == |y| // +1 if |x| > |y| -// func (x *Int) CmpAbs(y *Int) int { return x.abs.cmp(y.abs) } @@ -420,7 +415,6 @@ func (x *Int) IsUint64() bool { // Incorrect placement of underscores is reported as an error if there // are no other errors. If base != 0, underscores are not recognized // and act like any other character that is not a valid digit. -// func (z *Int) SetString(s string, base int) (*Int, bool) { return z.setFromScanner(strings.NewReader(s), base) } diff --git a/src/math/big/intconv.go b/src/math/big/intconv.go index 0567284105..2fe10ff0a2 100644 --- a/src/math/big/intconv.go +++ b/src/math/big/intconv.go @@ -63,7 +63,6 @@ var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter // specification of minimum digits precision, output field // width, space or zero padding, and '-' for left or right // justification. -// func (x *Int) Format(s fmt.State, ch rune) { // determine base var base int @@ -178,7 +177,6 @@ func (x *Int) Format(s fmt.State, ch rune) { // ``0b'' or ``0B'' selects base 2; a ``0'', ``0o'', or ``0O'' prefix selects // base 8, and a ``0x'' or ``0X'' prefix selects base 16. Otherwise the selected // base is 10. -// func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) { // determine sign neg, err := scanSign(r) diff --git a/src/math/big/nat.go b/src/math/big/nat.go index 512b2c229f..ee0c63eb28 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -31,7 +31,6 @@ import ( // During arithmetic operations, denormalized values may occur but are // always normalized before returning the final result. The normalized // representation of 0 is the empty or nil slice (length = 0). -// type nat []Word var ( @@ -363,10 +362,11 @@ func karatsuba(z, x, y nat) { } // alias reports whether x and y share the same base array. +// // Note: alias assumes that the capacity of underlying arrays -// is never changed for nat values; i.e. that there are -// no 3-operand slice expressions in this code (or worse, -// reflect-based operations to the same effect). +// is never changed for nat values; i.e. that there are +// no 3-operand slice expressions in this code (or worse, +// reflect-based operations to the same effect). func alias(x, y nat) bool { return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] } diff --git a/src/math/big/natconv.go b/src/math/big/natconv.go index 42d1cccf6f..99488ac833 100644 --- a/src/math/big/natconv.go +++ b/src/math/big/natconv.go @@ -105,7 +105,6 @@ var ( // parsed. A digit count <= 0 indicates the presence of a period (if fracOk // is set, only), and -count is the number of fractional digits found. // In this case, the actual value of the scanned number is res * b**count. -// func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) { // reject invalid bases baseOk := base == 0 || @@ -366,7 +365,6 @@ func (x nat) itoa(neg bool, base int) []byte { // range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and // ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for // specific hardware. -// func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []divisor) { // split larger blocks recursively if table != nil { diff --git a/src/math/big/rat.go b/src/math/big/rat.go index 731a979ff7..e77da67d1b 100644 --- a/src/math/big/rat.go +++ b/src/math/big/rat.go @@ -392,7 +392,6 @@ func (z *Rat) Inv(x *Rat) *Rat { // -1 if x < 0 // 0 if x == 0 // +1 if x > 0 -// func (x *Rat) Sign() int { return x.a.Sign() } @@ -482,7 +481,6 @@ func (z *Int) scaleDenom(x *Int, f nat) { // -1 if x < y // 0 if x == y // +1 if x > y -// func (x *Rat) Cmp(y *Rat) int { var a, b Int a.scaleDenom(&x.a, y.b.abs) diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go index 90053a9c81..dadd4d7b8e 100644 --- a/src/math/big/ratconv.go +++ b/src/math/big/ratconv.go @@ -113,7 +113,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { // special-case 0 (see also issue #16176) if len(z.a.abs) == 0 { - return z, true + return z.norm(), true } // len(z.a.abs) > 0 diff --git a/src/math/big/ratconv_test.go b/src/math/big/ratconv_test.go index e55e655718..45a35608f4 100644 --- a/src/math/big/ratconv_test.go +++ b/src/math/big/ratconv_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "math" + "reflect" "strconv" "strings" "testing" @@ -205,6 +206,14 @@ func TestRatSetString(t *testing.T) { } } +func TestRatSetStringZero(t *testing.T) { + got, _ := new(Rat).SetString("0") + want := new(Rat).SetInt64(0) + if !reflect.DeepEqual(got, want) { + t.Errorf("got %#+v, want %#+v", got, want) + } +} + func TestRatScan(t *testing.T) { var buf bytes.Buffer for i, test := range setStringTests { diff --git a/src/math/rand/exp.go b/src/math/rand/exp.go index 5a8d946c0c..9a07ba1be0 100644 --- a/src/math/rand/exp.go +++ b/src/math/rand/exp.go @@ -27,7 +27,6 @@ const ( // callers can adjust the output using: // // sample = ExpFloat64() / desiredRateParameter -// func (r *Rand) ExpFloat64() float64 { for { j := r.Uint32() diff --git a/src/math/rand/normal.go b/src/math/rand/normal.go index 2c5a7aa99b..48ecdd5adb 100644 --- a/src/math/rand/normal.go +++ b/src/math/rand/normal.go @@ -34,7 +34,6 @@ func absInt32(i int32) uint32 { // adjust the output using: // // sample = NormFloat64() * desiredStdDev + desiredMean -// func (r *Rand) NormFloat64() float64 { for { j := int32(r.Uint32()) // Possibly negative diff --git a/src/math/rand/rand.go b/src/math/rand/rand.go index 13f20ca5ef..dfbd1fa4e7 100644 --- a/src/math/rand/rand.go +++ b/src/math/rand/rand.go @@ -366,7 +366,6 @@ func Read(p []byte) (n int, err error) { return globalRand.Read(p) } // adjust the output using: // // sample = NormFloat64() * desiredStdDev + desiredMean -// func NormFloat64() float64 { return globalRand.NormFloat64() } // ExpFloat64 returns an exponentially distributed float64 in the range @@ -376,7 +375,6 @@ func NormFloat64() float64 { return globalRand.NormFloat64() } // callers can adjust the output using: // // sample = ExpFloat64() / desiredRateParameter -// func ExpFloat64() float64 { return globalRand.ExpFloat64() } type lockedSource struct { diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go index 81bf722d4e..c7bcb4d121 100644 --- a/src/mime/multipart/multipart.go +++ b/src/mime/multipart/multipart.go @@ -149,11 +149,11 @@ func newPart(mr *Reader, rawPart bool) (*Part, error) { return bp, nil } -func (bp *Part) populateHeaders() error { - r := textproto.NewReader(bp.mr.bufReader) +func (p *Part) populateHeaders() error { + r := textproto.NewReader(p.mr.bufReader) header, err := r.ReadMIMEHeader() if err == nil { - bp.Header = header + p.Header = header } return err } @@ -264,7 +264,8 @@ func scanUntilBoundary(buf, dashBoundary, nlDashBoundary []byte, total int64, re // and the caller has verified already that bytes.HasPrefix(buf, prefix) is true. // // matchAfterPrefix returns +1 if the buffer does match the boundary, -// meaning the prefix is followed by a dash, space, tab, cr, nl, or end of input. +// meaning the prefix is followed by a double dash, space, tab, cr, nl, +// or end of input. // It returns -1 if the buffer definitely does NOT match the boundary, // meaning the prefix is followed by some other character. // For example, "--foobar" does not match "--foo". @@ -278,9 +279,25 @@ func matchAfterPrefix(buf, prefix []byte, readErr error) int { return 0 } c := buf[len(prefix)] - if c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '-' { + + if c == ' ' || c == '\t' || c == '\r' || c == '\n' { return +1 } + + // Try to detect boundaryDash + if c == '-' { + if len(buf) == len(prefix)+1 { + if readErr != nil { + // Prefix + "-" does not match + return -1 + } + return 0 + } + if buf[len(prefix)+1] == '-' { + return +1 + } + } + return -1 } @@ -386,36 +403,36 @@ func (r *Reader) nextPart(rawPart bool) (*Part, error) { // isFinalBoundary reports whether line is the final boundary line // indicating that all parts are over. // It matches `^--boundary--[ \t]*(\r\n)?$` -func (mr *Reader) isFinalBoundary(line []byte) bool { - if !bytes.HasPrefix(line, mr.dashBoundaryDash) { +func (r *Reader) isFinalBoundary(line []byte) bool { + if !bytes.HasPrefix(line, r.dashBoundaryDash) { return false } - rest := line[len(mr.dashBoundaryDash):] + rest := line[len(r.dashBoundaryDash):] rest = skipLWSPChar(rest) - return len(rest) == 0 || bytes.Equal(rest, mr.nl) + return len(rest) == 0 || bytes.Equal(rest, r.nl) } -func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { +func (r *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { // https://tools.ietf.org/html/rfc2046#section-5.1 // The boundary delimiter line is then defined as a line // consisting entirely of two hyphen characters ("-", // decimal value 45) followed by the boundary parameter // value from the Content-Type header field, optional linear // whitespace, and a terminating CRLF. - if !bytes.HasPrefix(line, mr.dashBoundary) { + if !bytes.HasPrefix(line, r.dashBoundary) { return false } - rest := line[len(mr.dashBoundary):] + rest := line[len(r.dashBoundary):] rest = skipLWSPChar(rest) // On the first part, see our lines are ending in \n instead of \r\n // and switch into that mode if so. This is a violation of the spec, // but occurs in practice. - if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' { - mr.nl = mr.nl[1:] - mr.nlDashBoundary = mr.nlDashBoundary[1:] + if r.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' { + r.nl = r.nl[1:] + r.nlDashBoundary = r.nlDashBoundary[1:] } - return bytes.Equal(rest, mr.nl) + return bytes.Equal(rest, r.nl) } // skipLWSPChar returns b with leading spaces and tabs removed. diff --git a/src/mime/multipart/multipart_test.go b/src/mime/multipart/multipart_test.go index 741d2304ed..e043e36ef7 100644 --- a/src/mime/multipart/multipart_test.go +++ b/src/mime/multipart/multipart_test.go @@ -291,24 +291,34 @@ func TestLineLimit(t *testing.T) { } func TestMultipartTruncated(t *testing.T) { - testBody := ` + for _, body := range []string{ + ` This is a multi-part message. This line is ignored. --MyBoundary foo-bar: baz Oh no, premature EOF! -` - body := strings.ReplaceAll(testBody, "\n", "\r\n") - bodyReader := strings.NewReader(body) - r := NewReader(bodyReader, "MyBoundary") +`, + ` +This is a multi-part message. This line is ignored. +--MyBoundary +foo-bar: baz - part, err := r.NextPart() - if err != nil { - t.Fatalf("didn't get a part") - } - _, err = io.Copy(io.Discard, part) - if err != io.ErrUnexpectedEOF { - t.Fatalf("expected error io.ErrUnexpectedEOF; got %v", err) +Oh no, premature EOF! +--MyBoundary-`, + } { + body = strings.ReplaceAll(body, "\n", "\r\n") + bodyReader := strings.NewReader(body) + r := NewReader(bodyReader, "MyBoundary") + + part, err := r.NextPart() + if err != nil { + t.Fatalf("didn't get a part") + } + _, err = io.Copy(io.Discard, part) + if err != io.ErrUnexpectedEOF { + t.Fatalf("expected error io.ErrUnexpectedEOF; got %v", err) + } } } @@ -751,6 +761,7 @@ html things }, }, }, + // Issue 12662: Check that we don't consume the leading \r if the peekBuffer // ends in '\r\n--separator-' { @@ -767,6 +778,7 @@ Content-Type: application/octet-stream }, }, }, + // Issue 12662: Same test as above with \r\n at the end { name: "peek buffer boundary condition", @@ -782,6 +794,7 @@ Content-Type: application/octet-stream }, }, }, + // Issue 12662v2: We want to make sure that for short buffers that end with // '\r\n--separator-' we always consume at least one (valid) symbol from the // peekBuffer @@ -799,6 +812,7 @@ Content-Type: application/octet-stream }, }, }, + // Context: https://github.com/camlistore/camlistore/issues/642 // If the file contents in the form happens to have a size such as: // size = peekBufferSize - (len("\n--") + len(boundary) + len("\r") + 1), (modulo peekBufferSize) @@ -832,6 +846,52 @@ val }, }, + // Issue 46042; a nested multipart uses the outer separator followed by + // a dash. + { + name: "nested separator prefix is outer separator followed by a dash", + sep: "foo", + in: strings.Replace(`--foo +Content-Type: multipart/alternative; boundary="foo-bar" + +--foo-bar + +Body +--foo-bar + +Body2 +--foo-bar-- +--foo--`, "\n", "\r\n", -1), + want: []headerBody{ + {textproto.MIMEHeader{"Content-Type": {`multipart/alternative; boundary="foo-bar"`}}, + strings.Replace(`--foo-bar + +Body +--foo-bar + +Body2 +--foo-bar--`, "\n", "\r\n", -1), + }, + }, + }, + + // A nested boundary cannot be the outer separator followed by double dash. + { + name: "nested separator prefix is outer separator followed by double dash", + sep: "foo", + in: strings.Replace(`--foo +Content-Type: multipart/alternative; boundary="foo--" + +--foo-- + +Body + +--foo--`, "\n", "\r\n", -1), + want: []headerBody{ + {textproto.MIMEHeader{"Content-Type": {`multipart/alternative; boundary="foo--"`}}, ""}, + }, + }, + roundTripParseTest(), } diff --git a/src/mime/type_unix.go b/src/mime/type_unix.go index 3abc1fa10e..52579c56b9 100644 --- a/src/mime/type_unix.go +++ b/src/mime/type_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package mime diff --git a/src/mime/type_unix_test.go b/src/mime/type_unix_test.go index 4d109aa71a..6bb408566c 100644 --- a/src/mime/type_unix_test.go +++ b/src/mime/type_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package mime diff --git a/src/net/addrselect.go b/src/net/addrselect.go index 29e4ed85ab..8accdb89e1 100644 --- a/src/net/addrselect.go +++ b/src/net/addrselect.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // Minimal RFC 6724 address selection. diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go index 6fc2c1930e..b156b198ee 100644 --- a/src/net/cgo_unix.go +++ b/src/net/cgo_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build cgo && !netgo && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) +//go:build cgo && !netgo && unix package net @@ -250,12 +250,12 @@ func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, // These are roughly enough for the following: // -// Source Encoding Maximum length of single name entry -// Unicast DNS ASCII or <=253 + a NUL terminator -// Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator -// Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator -// the same as unicast DNS ASCII <=253 + a NUL terminator -// Local database various depends on implementation +// Source Encoding Maximum length of single name entry +// Unicast DNS ASCII or <=253 + a NUL terminator +// Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator +// Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator +// the same as unicast DNS ASCII <=253 + a NUL terminator +// Local database various depends on implementation const ( nameinfoLen = 64 maxNameinfoLen = 4096 diff --git a/src/net/cgo_unix_test.go b/src/net/cgo_unix_test.go index b4da25b84c..af9f9dc3f2 100644 --- a/src/net/cgo_unix_test.go +++ b/src/net/cgo_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build cgo && !netgo && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) +//go:build cgo && !netgo && unix package net diff --git a/src/net/conf.go b/src/net/conf.go index 415caedacc..716a37ff80 100644 --- a/src/net/conf.go +++ b/src/net/conf.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/dial.go b/src/net/dial.go index 486ced0f2a..9159e6b384 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -421,12 +421,7 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn primaries = addrs } - var c Conn - if len(fallbacks) > 0 { - c, err = sd.dialParallel(ctx, primaries, fallbacks) - } else { - c, err = sd.dialSerial(ctx, primaries) - } + c, err := sd.dialParallel(ctx, primaries, fallbacks) if err != nil { return nil, err } diff --git a/src/net/dial_unix_test.go b/src/net/dial_unix_test.go index 4170367c4b..d0df0b71ea 100644 --- a/src/net/dial_unix_test.go +++ b/src/net/dial_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index b989d12c58..15dbc25830 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // DNS client: see RFC 1035. // Has to be linked into package net for Dial. diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index e5f01dba2a..415c53e1e7 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go index 5ad254cd7c..7552bc51e6 100644 --- a/src/net/dnsconfig_unix.go +++ b/src/net/dnsconfig_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // Read system DNS config from /etc/resolv.conf diff --git a/src/net/dnsconfig_unix_test.go b/src/net/dnsconfig_unix_test.go index 4d35221497..513f624b00 100644 --- a/src/net/dnsconfig_unix_test.go +++ b/src/net/dnsconfig_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/error_posix.go b/src/net/error_posix.go index 10e3caa67b..8fc7d0bb73 100644 --- a/src/net/error_posix.go +++ b/src/net/error_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/error_test.go b/src/net/error_test.go index 4a191673e2..4467dc11b2 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -795,3 +795,12 @@ func parseLookupPortError(nestedErr error) error { } return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr) } + +func TestContextError(t *testing.T) { + if !errors.Is(errCanceled, context.Canceled) { + t.Error("errCanceled is not context.Canceled") + } + if !errors.Is(errTimeout, context.DeadlineExceeded) { + t.Error("errTimeout is not context.DeadlineExceeded") + } +} diff --git a/src/net/error_unix.go b/src/net/error_unix.go index 0e64b40ea1..1f9b6eb78c 100644 --- a/src/net/error_unix.go +++ b/src/net/error_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || js || linux || netbsd || openbsd || solaris +//go:build unix || js package net diff --git a/src/net/fd_posix.go b/src/net/fd_posix.go index 1845c173bb..ffb9bcf8b9 100644 --- a/src/net/fd_posix.go +++ b/src/net/fd_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package net diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index aaa7a1c185..a400c6075e 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/file_unix.go b/src/net/file_unix.go index 68d7eb9ca0..0df67db501 100644 --- a/src/net/file_unix.go +++ b/src/net/file_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/hook_unix.go b/src/net/hook_unix.go index 7c36b0d6e3..fa82c7e52b 100644 --- a/src/net/hook_unix.go +++ b/src/net/hook_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package net diff --git a/src/net/http/client.go b/src/net/http/client.go index 22db96b267..bc0ed1fc50 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -54,7 +54,6 @@ import ( // with the expectation that the Jar will insert those mutated cookies // with the updated values (assuming the origin matches). // If Jar is nil, the initial cookies are forwarded without change. -// type Client struct { // Transport specifies the mechanism by which individual // HTTP requests are made. @@ -519,17 +518,6 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect shouldRedirect = true includeBody = true - // Treat 307 and 308 specially, since they're new in - // Go 1.8, and they also require re-sending the request body. - if resp.Header.Get("Location") == "" { - // 308s have been observed in the wild being served - // without Location headers. Since Go 1.7 and earlier - // didn't follow these codes, just stop here instead - // of returning an error. - // See Issue 17773. - shouldRedirect = false - break - } if ireq.GetBody == nil && ireq.outgoingLength() != 0 { // We had a request body, and 307/308 require // re-sending it, but GetBody is not defined. So just @@ -641,8 +629,10 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { if len(reqs) > 0 { loc := resp.Header.Get("Location") if loc == "" { - resp.closeBody() - return nil, uerr(fmt.Errorf("%d response missing Location header", resp.StatusCode)) + // While most 3xx responses include a Location, it is not + // required and 3xx responses without a Location have been + // observed in the wild. See issues #17773 and #49281. + return resp, nil } u, err := req.URL.Parse(loc) if err != nil { @@ -951,9 +941,9 @@ func (c *Client) CloseIdleConnections() { } // cancelTimerBody is an io.ReadCloser that wraps rc with two features: -// 1) On Read error or close, the stop func is called. -// 2) On Read failure, if reqDidTimeout is true, the error is wrapped and -// marked as net.Error that hit its timeout. +// 1) On Read error or close, the stop func is called. +// 2) On Read failure, if reqDidTimeout is true, the error is wrapped and +// marked as net.Error that hit its timeout. type cancelTimerBody struct { stop func() // stops the time.Timer waiting to cancel the request rc io.ReadCloser diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index e91d526824..5e5bf8f2bb 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -531,27 +531,31 @@ func TestClientRedirectUseResponse(t *testing.T) { } } -// Issue 17773: don't follow a 308 (or 307) if the response doesn't +// Issues 17773 and 49281: don't follow a 3xx if the response doesn't // have a Location header. -func TestClientRedirect308NoLocation(t *testing.T) { - setParallel(t) - defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - w.Header().Set("Foo", "Bar") - w.WriteHeader(308) - })) - defer ts.Close() - c := ts.Client() - res, err := c.Get(ts.URL) - if err != nil { - t.Fatal(err) - } - res.Body.Close() - if res.StatusCode != 308 { - t.Errorf("status = %d; want %d", res.StatusCode, 308) - } - if got := res.Header.Get("Foo"); got != "Bar" { - t.Errorf("Foo header = %q; want Bar", got) +func TestClientRedirectNoLocation(t *testing.T) { + for _, code := range []int{301, 308} { + t.Run(fmt.Sprint(code), func(t *testing.T) { + setParallel(t) + defer afterTest(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Foo", "Bar") + w.WriteHeader(code) + })) + defer ts.Close() + c := ts.Client() + res, err := c.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + if res.StatusCode != code { + t.Errorf("status = %d; want %d", res.StatusCode, code) + } + if got := res.Header.Get("Foo"); got != "Bar" { + t.Errorf("Foo header = %q; want Bar", got) + } + }) } } diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go index cb37f2351f..6e1035330b 100644 --- a/src/net/http/cookie.go +++ b/src/net/http/cookie.go @@ -387,11 +387,11 @@ func sanitizeCookieName(n string) string { // sanitizeCookieValue produces a suitable cookie-value from v. // https://tools.ietf.org/html/rfc6265#section-4.1.1 -// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) -// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E -// ; US-ASCII characters excluding CTLs, -// ; whitespace DQUOTE, comma, semicolon, -// ; and backslash +// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) +// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E +// ; US-ASCII characters excluding CTLs, +// ; whitespace DQUOTE, comma, semicolon, +// ; and backslash // We loosen this as spaces and commas are common in cookie values // but we produce a quoted cookie-value if and only if v contains // commas or spaces. diff --git a/src/net/http/fs.go b/src/net/http/fs.go index 6caee9ed93..d8f924296b 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -836,7 +836,6 @@ func FS(fsys fs.FS) FileSystem { // To use an fs.FS implementation, use http.FS to convert it: // // http.Handle("/", http.FileServer(http.FS(fsys))) -// func FileServer(root FileSystem) Handler { return &fileHandler{root} } diff --git a/src/net/http/server.go b/src/net/http/server.go index ffb742ba4a..b91069f9a1 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1551,14 +1551,14 @@ func (w *response) bodyAllowed() bool { // // The Writers are wired together like: // -// 1. *response (the ResponseWriter) -> -// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes -> -// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type) -// and which writes the chunk headers, if needed -> -// 4. conn.bufw, a *bufio.Writer of default (4kB) bytes, writing to -> -// 5. checkConnErrorWriter{c}, which notes any non-nil error on Write -// and populates c.werr with it if so, but otherwise writes to -> -// 6. the rwc, the net.Conn. +// 1. *response (the ResponseWriter) -> +// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes -> +// 3. chunkWriter.Writer (whose writeHeader finalizes Content-Length/Type) +// and which writes the chunk headers, if needed -> +// 4. conn.bufw, a *bufio.Writer of default (4kB) bytes, writing to -> +// 5. checkConnErrorWriter{c}, which notes any non-nil error on Write +// and populates c.werr with it if so, but otherwise writes to -> +// 6. the rwc, the net.Conn. // // TODO(bradfitz): short-circuit some of the buffering when the // initial header contains both a Content-Type and Content-Length. diff --git a/src/net/http/transport.go b/src/net/http/transport.go index e41b20a15b..f2d538b04a 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -1795,7 +1795,6 @@ var _ io.ReaderFrom = (*persistConnWriter)(nil) // socks5://proxy.com|https|foo.com socks5 to proxy, then https to foo.com // https://proxy.com|https|foo.com https to proxy, then CONNECT to foo.com // https://proxy.com|http https to proxy, http to anywhere after that -// type connectMethod struct { _ incomparable proxyURL *url.URL // nil for no proxy, else full proxy URL diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index e5d60afb1b..440d6b969b 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -48,7 +48,7 @@ import ( ) // TODO: test 5 pipelined requests with responses: 1) OK, 2) OK, Connection: Close -// and then verify that the final 2 responses get errors back. +// and then verify that the final 2 responses get errors back. // hostPortHandler writes back the client's "host:port". var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) { diff --git a/src/net/internal/socktest/switch_unix.go b/src/net/internal/socktest/switch_unix.go index 77d68f6112..f2e95d68c1 100644 --- a/src/net/internal/socktest/switch_unix.go +++ b/src/net/internal/socktest/switch_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package socktest diff --git a/src/net/internal/socktest/sys_unix.go b/src/net/internal/socktest/sys_unix.go index 0424164ed3..e1040d3087 100644 --- a/src/net/internal/socktest/sys_unix.go +++ b/src/net/internal/socktest/sys_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package socktest diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go index 74f977e1ef..64112b08dd 100644 --- a/src/net/iprawsock_posix.go +++ b/src/net/iprawsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go index e433e8a91c..2c72447848 100644 --- a/src/net/ipsock_posix.go +++ b/src/net/ipsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go index 255a19dfdb..ad4164d865 100644 --- a/src/net/lookup_unix.go +++ b/src/net/lookup_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/main_unix_test.go b/src/net/main_unix_test.go index 2ed0615ad8..e7a5b4fe9a 100644 --- a/src/net/main_unix_test.go +++ b/src/net/main_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/net.go b/src/net/net.go index d91e743a01..ec718d5e43 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -413,15 +413,20 @@ var ( errMissingAddress = errors.New("missing address") // For both read and write operations. - errCanceled = errors.New("operation was canceled") + errCanceled = canceledError{} ErrWriteToConnected = errors.New("use of WriteTo with pre-connected connection") ) +// canceledError lets us return the same error string we have always +// returned, while still being Is context.Canceled. +type canceledError struct{} + +func (canceledError) Error() string { return "operation was canceled" } + +func (canceledError) Is(err error) bool { return err == context.Canceled } + // mapErr maps from the context errors to the historical internal net // error values. -// -// TODO(bradfitz): get rid of this after adjusting tests and making -// context.DeadlineExceeded implement net.Error? func mapErr(err error) error { switch err { case context.Canceled: @@ -580,10 +585,12 @@ func (e InvalidAddrError) Temporary() bool { return false } // errTimeout exists to return the historical "i/o timeout" string // for context.DeadlineExceeded. See mapErr. // It is also used when Dialer.Deadline is exceeded. +// error.Is(errTimeout, context.DeadlineExceeded) returns true. // // TODO(iant): We could consider changing this to os.ErrDeadlineExceeded -// in the future, but note that that would conflict with the TODO -// at mapErr that suggests changing it to context.DeadlineExceeded. +// in the future, if we make +// errors.Is(os.ErrDeadlineExceeded, context.DeadlineExceeded) +// return true. var errTimeout error = &timeoutError{} type timeoutError struct{} @@ -592,6 +599,10 @@ func (e *timeoutError) Error() string { return "i/o timeout" } func (e *timeoutError) Timeout() bool { return true } func (e *timeoutError) Temporary() bool { return true } +func (e *timeoutError) Is(err error) bool { + return err == context.DeadlineExceeded +} + // DNSConfigError represents an error reading the machine's DNS configuration. // (No longer used; kept for compatibility.) type DNSConfigError struct { diff --git a/src/net/net_test.go b/src/net/net_test.go index 76a9c8b151..fa5ad632bb 100644 --- a/src/net/net_test.go +++ b/src/net/net_test.go @@ -549,9 +549,7 @@ func TestNotTemporaryRead(t *testing.T) { if runtime.GOOS == "plan9" { return } - // TODO: during an open development cycle, try making this a failure - // and see whether it causes the test to become flaky anywhere else. - return + t.Fatal("Read unexpectedly returned io.EOF after socket was abruptly closed") } if ne, ok := err.(Error); !ok { t.Errorf("Read error does not implement net.Error: %v", err) diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go index 2a563a078c..947dda56f2 100644 --- a/src/net/net_windows_test.go +++ b/src/net/net_windows_test.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "fmt" + "internal/testenv" "io" "os" "os/exec" @@ -205,6 +206,13 @@ func runCmd(args ...string) ([]byte, error) { } func checkNetsh(t *testing.T) { + if testenv.Builder() == "windows-arm64-10" { + // netsh was observed to sometimes hang on this builder. + // We have not observed failures on windows-arm64-11, so for the + // moment we are leaving the test enabled elsewhere on the theory + // that it may have been a platform bug fixed in Windows 11. + testenv.SkipFlaky(t, 52082) + } out, err := runCmd("netsh", "help") if err != nil { t.Fatal(err) diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go index 1cf75fb5a1..8fad25db8d 100644 --- a/src/net/netip/netip.go +++ b/src/net/netip/netip.go @@ -456,7 +456,7 @@ func (ip Addr) lessOrEq(ip2 Addr) bool { return ip.Compare(ip2) <= 0 } // Is4 reports whether ip is an IPv4 address. // -// It returns false for IP4-mapped IPv6 addresses. See IP.Unmap. +// It returns false for IPv4-mapped IPv6 addresses. See IP.Unmap. func (ip Addr) Is4() bool { return ip.z == z4 } @@ -675,7 +675,7 @@ const ( ) // As16 returns the IP address in its 16-byte representation. -// IPv4 addresses are returned in their IPv4-mapped IPv6 form. +// IPv4 addresses are returned as IPv4-mapped IPv6 addresses. // IPv6 addresses with zones are returned without their zone (use the // Zone method to get it). // The ip zero value returns all zeroes. @@ -758,7 +758,7 @@ func (ip Addr) Prev() Addr { // - IPv6 with zone ("fe80:db8::1%eth0") // // Note that unlike package net's IP.String method, -// IP4-mapped IPv6 addresses format with a "::ffff:" +// IPv4-mapped IPv6 addresses format with a "::ffff:" // prefix before the dotted quad. func (ip Addr) String() string { switch ip.z { @@ -1290,6 +1290,8 @@ func (p Prefix) IsSingleIP() bool { return p.bits != 0 && int(p.bits) == p.ip.Bi // ParsePrefix parses s as an IP address prefix. // The string can be in the form "192.168.1.0/24" or "2001:db8::/32", // the CIDR notation defined in RFC 4632 and RFC 4291. +// IPv6 zones are not permitted in prefixes, and an error will be returned if a +// zone is present. // // Note that masked address bits are not zeroed. Use Masked for that. func ParsePrefix(s string) (Prefix, error) { @@ -1301,6 +1303,11 @@ func ParsePrefix(s string) (Prefix, error) { if err != nil { return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): " + err.Error()) } + // IPv6 zones are not allowed: https://go.dev/issue/51899 + if ip.Is6() && ip.z != z6noz { + return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): IPv6 zones cannot be present in a prefix") + } + bitsStr := s[i+1:] bits, err := strconv.Atoi(bitsStr) if err != nil { @@ -1373,7 +1380,7 @@ func (p Prefix) Contains(ip Addr) bool { // // If p and o are of different address families or either have a zero // IP, it reports false. Like the Contains method, a prefix with an -// IPv4-mapped IPv6 IP is still treated as an IPv6 mask. +// IPv4-mapped IPv6 address is still treated as an IPv6 mask. func (p Prefix) Overlaps(o Prefix) bool { if !p.IsValid() || !o.IsValid() { return false diff --git a/src/net/netip/netip_pkg_test.go b/src/net/netip/netip_pkg_test.go index f5cd9ee86d..677f523e6d 100644 --- a/src/net/netip/netip_pkg_test.go +++ b/src/net/netip/netip_pkg_test.go @@ -160,9 +160,9 @@ func TestPrefixContains(t *testing.T) { {mustPrefix("::1/127"), mustIP("::2"), false}, {mustPrefix("::1/128"), mustIP("::1"), true}, {mustPrefix("::1/127"), mustIP("::2"), false}, - // zones support - {mustPrefix("::1%a/128"), mustIP("::1"), true}, // prefix zones are stripped... - {mustPrefix("::1%a/128"), mustIP("::1%a"), false}, // but ip zones are not + // Zones ignored: https://go.dev/issue/51899 + {Prefix{mustIP("1.2.3.4").WithZone("a"), 32}, mustIP("1.2.3.4"), true}, + {Prefix{mustIP("::1").WithZone("a"), 128}, mustIP("::1"), true}, // invalid IP {mustPrefix("::1/0"), Addr{}, false}, {mustPrefix("1.2.3.4/0"), Addr{}, false}, diff --git a/src/net/netip/netip_test.go b/src/net/netip/netip_test.go index a72390fd5b..35f7cd69e1 100644 --- a/src/net/netip/netip_test.go +++ b/src/net/netip/netip_test.go @@ -421,8 +421,8 @@ func TestPrefixMarshalTextString(t *testing.T) { {mustPrefix("1.2.3.4/24"), "1.2.3.4/24"}, {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"}, {mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"}, - {mustPrefix("::ffff:c000:0280%eth0/37"), "::ffff:192.0.2.128/37"}, // Zone should be stripped {mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"}, + {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"}, // Zone should be stripped } for i, tt := range tests { if got := tt.in.String(); got != tt.want { @@ -448,7 +448,7 @@ func TestPrefixMarshalUnmarshalBinary(t *testing.T) { {mustPrefix("1.2.3.4/24"), 4 + 1}, {mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1}, {mustPrefix("::ffff:c000:0280/96"), 16 + 1}, - {mustPrefix("::ffff:c000:0280%eth0/37"), 16 + 1}, // Zone should be stripped + {PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1}, // Zone should be stripped } tests = append(tests, testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize}, @@ -901,25 +901,25 @@ func TestPrefixMasking(t *testing.T) { { ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)), bits: 32, - p: mustPrefix(fmt.Sprintf("2001:db8::%s/32", zone)), + p: mustPrefix("2001:db8::/32"), ok: true, }, { ip: mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)), bits: 96, - p: mustPrefix(fmt.Sprintf("fe80::dead:beef:0:0%s/96", zone)), + p: mustPrefix("fe80::dead:beef:0:0/96"), ok: true, }, { ip: mustIP(fmt.Sprintf("aaaa::%s", zone)), bits: 4, - p: mustPrefix(fmt.Sprintf("a000::%s/4", zone)), + p: mustPrefix("a000::/4"), ok: true, }, { ip: mustIP(fmt.Sprintf("::%s", zone)), bits: 63, - p: mustPrefix(fmt.Sprintf("::%s/63", zone)), + p: mustPrefix("::/63"), ok: true, }, } @@ -1047,26 +1047,6 @@ func TestPrefixMarshalUnmarshal(t *testing.T) { } } -func TestPrefixMarshalUnmarshalZone(t *testing.T) { - orig := `"fe80::1cc0:3e8c:119f:c2e1%ens18/128"` - unzoned := `"fe80::1cc0:3e8c:119f:c2e1/128"` - - var p Prefix - if err := json.Unmarshal([]byte(orig), &p); err != nil { - t.Fatalf("failed to unmarshal: %v", err) - } - - pb, err := json.Marshal(p) - if err != nil { - t.Fatalf("failed to marshal: %v", err) - } - - back := string(pb) - if back != unzoned { - t.Errorf("Marshal = %q; want %q", back, unzoned) - } -} - func TestPrefixUnmarshalTextNonZero(t *testing.T) { ip := mustPrefix("fe80::/64") if err := ip.UnmarshalText([]byte("xxx")); err == nil { @@ -1222,14 +1202,6 @@ func TestPrefix(t *testing.T) { contains: mustIPs("2001:db8::1"), notContains: mustIPs("fe80::1"), }, - { - prefix: "::%0/00/80", - ip: mustIP("::"), - bits: 80, - str: "::/80", - contains: mustIPs("::"), - notContains: mustIPs("ff::%0/00", "ff::%1/23", "::%0/00", "::%1/23"), - }, } for _, test := range tests { t.Run(test.prefix, func(t *testing.T) { @@ -1348,6 +1320,15 @@ func TestParsePrefixError(t *testing.T) { prefix: "2001::/129", errstr: "out of range", }, + // Zones are not allowed: https://go.dev/issue/51899 + { + prefix: "1.1.1.0%a/24", + errstr: "unexpected character", + }, + { + prefix: "2001:db8::%a/32", + errstr: "zones cannot be present", + }, } for _, test := range tests { t.Run(test.prefix, func(t *testing.T) { @@ -1776,7 +1757,7 @@ func TestPrefixOverlaps(t *testing.T) { {pfx("1::1/128"), pfx("2::2/128"), false}, {pfx("0100::0/8"), pfx("::1/128"), false}, - // IPv4-mapped IPv6 should not overlap with IPv4. + // IPv4-mapped IPv6 addresses should not overlap with IPv4. {PrefixFrom(AddrFrom16(mustIP("1.2.0.0").As16()), 16), pfx("1.2.3.0/24"), false}, // Invalid prefixes diff --git a/src/net/nss.go b/src/net/nss.go index ee5568883f..5df71dc268 100644 --- a/src/net/nss.go +++ b/src/net/nss.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/port_unix.go b/src/net/port_unix.go index 102722b2ca..b05b588f17 100644 --- a/src/net/port_unix.go +++ b/src/net/port_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) // Read system port mappings from /etc/services diff --git a/src/net/rawconn_unix_test.go b/src/net/rawconn_unix_test.go index e6e1ad771a..f11119ed9e 100644 --- a/src/net/rawconn_unix_test.go +++ b/src/net/rawconn_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/net/smtp/smtp_test.go b/src/net/smtp/smtp_test.go index 0f758f4a33..f23fd79234 100644 --- a/src/net/smtp/smtp_test.go +++ b/src/net/smtp/smtp_test.go @@ -1104,8 +1104,9 @@ func sendMail(hostPort string) error { } // localhostCert is a PEM-encoded TLS cert generated from src/crypto/tls: -// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com \ -// --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h +// +// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com \ +// --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var localhostCert = []byte(` -----BEGIN CERTIFICATE----- MIICFDCCAX2gAwIBAgIRAK0xjnaPuNDSreeXb+z+0u4wDQYJKoZIhvcNAQELBQAw diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go index 98a48229c7..4431c3a6b3 100644 --- a/src/net/sock_posix.go +++ b/src/net/sock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package net diff --git a/src/net/sockaddr_posix.go b/src/net/sockaddr_posix.go index c8e91936ad..76c3233b29 100644 --- a/src/net/sockaddr_posix.go +++ b/src/net/sockaddr_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/sockopt_posix.go b/src/net/sockopt_posix.go index 645080f988..32e8fcd505 100644 --- a/src/net/sockopt_posix.go +++ b/src/net/sockopt_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package net diff --git a/src/net/sockoptip_posix.go b/src/net/sockoptip_posix.go index 22031df22c..572ea455c0 100644 --- a/src/net/sockoptip_posix.go +++ b/src/net/sockoptip_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package net diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go index ed6b18b551..bc3d324e6b 100644 --- a/src/net/tcpsock_posix.go +++ b/src/net/tcpsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/tcpsockopt_posix.go b/src/net/tcpsockopt_posix.go index 73754b1a0f..d708f04875 100644 --- a/src/net/tcpsockopt_posix.go +++ b/src/net/tcpsockopt_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows +//go:build unix || windows package net diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go index 157c59b17a..ac47f00700 100644 --- a/src/net/textproto/reader.go +++ b/src/net/textproto/reader.go @@ -88,7 +88,6 @@ func (r *Reader) readLineSlice() ([]byte, error) { // and the second will return "Line 2". // // Empty lines are never continued. -// func (r *Reader) ReadContinuedLine() (string, error) { line, err := r.readContinuedLineSlice(noValidation) return string(line), err @@ -230,7 +229,6 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa // If the response is multi-line, ReadCodeLine returns an error. // // An expectCode <= 0 disables the check of the status code. -// func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) { code, continued, message, err := r.readCodeLine(expectCode) if err == nil && continued { @@ -265,7 +263,6 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err // the status is not in the range [310,319]. // // An expectCode <= 0 disables the check of the status code. -// func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) { code, continued, message, err := r.readCodeLine(expectCode) multi := continued @@ -481,7 +478,6 @@ var colon = []byte(":") // "My-Key": {"Value 1", "Value 2"}, // "Long-Key": {"Even Longer Value"}, // } -// func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { // Avoid lots of small slice allocations later by allocating one // large one ahead of time which we'll cut up into smaller diff --git a/src/net/textproto/textproto.go b/src/net/textproto/textproto.go index cc1a847e4e..3487a7dfaf 100644 --- a/src/net/textproto/textproto.go +++ b/src/net/textproto/textproto.go @@ -110,7 +110,6 @@ func Dial(network, addr string) (*Conn, error) { // return nil, err // } // return c.ReadCodeLine(250) -// func (c *Conn) Cmd(format string, args ...any) (id uint, err error) { id = c.Next() c.StartRequest(id) diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go index 6544397673..5b021d24ae 100644 --- a/src/net/udpsock_posix.go +++ b/src/net/udpsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go index 1b69df53bf..b244dbdbbd 100644 --- a/src/net/unixsock_posix.go +++ b/src/net/unixsock_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package net diff --git a/src/net/unixsock_readmsg_test.go b/src/net/unixsock_readmsg_test.go index c3bfbf9af2..414f626644 100644 --- a/src/net/unixsock_readmsg_test.go +++ b/src/net/unixsock_readmsg_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package net diff --git a/src/os/env_unix_test.go b/src/os/env_unix_test.go index 75225d8547..4609fc3b94 100644 --- a/src/os/env_unix_test.go +++ b/src/os/env_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package os_test diff --git a/src/os/error_posix.go b/src/os/error_posix.go index 234f4eb692..5ca2e60e5b 100644 --- a/src/os/error_posix.go +++ b/src/os/error_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package os diff --git a/src/os/error_unix_test.go b/src/os/error_unix_test.go index 81bccebedb..1c694fe5f1 100644 --- a/src/os/error_unix_test.go +++ b/src/os/error_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package os_test diff --git a/src/os/exec/exec_posix_test.go b/src/os/exec/exec_posix_test.go index fd7fb42d36..ce83a9e4b3 100644 --- a/src/os/exec/exec_posix_test.go +++ b/src/os/exec/exec_posix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package exec_test diff --git a/src/os/exec/internal/fdtest/exists_unix.go b/src/os/exec/internal/fdtest/exists_unix.go index 49f264cebd..265cb69822 100644 --- a/src/os/exec/internal/fdtest/exists_unix.go +++ b/src/os/exec/internal/fdtest/exists_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // Package fdtest provides test helpers for working with file descriptors across exec. package fdtest diff --git a/src/os/exec/lp_unix.go b/src/os/exec/lp_unix.go index 38b9fc7c27..5db6c5e109 100644 --- a/src/os/exec/lp_unix.go +++ b/src/os/exec/lp_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package exec diff --git a/src/os/exec/lp_unix_test.go b/src/os/exec/lp_unix_test.go index 52e401e580..ebeb5bb3ec 100644 --- a/src/os/exec/lp_unix_test.go +++ b/src/os/exec/lp_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package exec diff --git a/src/os/exec_posix.go b/src/os/exec_posix.go index d619984693..e1e7d53a27 100644 --- a/src/os/exec_posix.go +++ b/src/os/exec_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package os diff --git a/src/os/exec_unix.go b/src/os/exec_unix.go index 250c5c6402..90a4a61222 100644 --- a/src/os/exec_unix.go +++ b/src/os/exec_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/exec_unix_test.go b/src/os/exec_unix_test.go index fa332bf1ed..332ffe9041 100644 --- a/src/os/exec_unix_test.go +++ b/src/os/exec_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package os_test diff --git a/src/os/export_unix_test.go b/src/os/export_unix_test.go index d4eb7a4fb1..49c8dae036 100644 --- a/src/os/export_unix_test.go +++ b/src/os/export_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/file_posix.go b/src/os/file_posix.go index f34571d68d..c6d18ffeb6 100644 --- a/src/os/file_posix.go +++ b/src/os/file_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package os diff --git a/src/os/file_unix.go b/src/os/file_unix.go index a38db18954..666143b0de 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 378e2b1c7d..75b4707eaf 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -401,7 +401,7 @@ func openSymlink(path string) (syscall.Handle, error) { // normaliseLinkPath converts absolute paths returned by // DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, ...) // into paths acceptable by all Windows APIs. -// For example, it coverts +// For example, it converts // \??\C:\foo\bar into C:\foo\bar // \??\UNC\foo\bar into \\foo\bar // \??\Volume{abc}\ into C:\ diff --git a/src/os/os_test.go b/src/os/os_test.go index 63427deb6e..d071b47058 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -223,6 +223,29 @@ func TestStatError(t *testing.T) { } } +func TestStatSymlinkLoop(t *testing.T) { + testenv.MustHaveSymlink(t) + + defer chtmpdir(t)() + + err := os.Symlink("x", "y") + if err != nil { + t.Fatal(err) + } + defer os.Remove("y") + + err = os.Symlink("y", "x") + if err != nil { + t.Fatal(err) + } + defer os.Remove("x") + + _, err = os.Stat("x") + if _, ok := err.(*fs.PathError); !ok { + t.Errorf("expected *PathError, got %T: %v\n", err, err) + } +} + func TestFstat(t *testing.T) { path := sfdir + "/" + sfname file, err1 := Open(path) diff --git a/src/os/os_unix_test.go b/src/os/os_unix_test.go index 3ec3dee24b..c3c703f860 100644 --- a/src/os/os_unix_test.go +++ b/src/os/os_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package os_test diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index 195c30f50e..41a066dcbc 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -643,29 +643,6 @@ func TestDeleteReadOnly(t *testing.T) { } } -func TestStatSymlinkLoop(t *testing.T) { - testenv.MustHaveSymlink(t) - - defer chtmpdir(t)() - - err := os.Symlink("x", "y") - if err != nil { - t.Fatal(err) - } - defer os.Remove("y") - - err = os.Symlink("y", "x") - if err != nil { - t.Fatal(err) - } - defer os.Remove("x") - - _, err = os.Stat("x") - if _, ok := err.(*fs.PathError); !ok { - t.Errorf("expected *PathError, got %T: %v\n", err, err) - } -} - func TestReadStdin(t *testing.T) { old := poll.ReadConsole defer func() { diff --git a/src/os/path_unix.go b/src/os/path_unix.go index d1ffe2c187..3c6310a4df 100644 --- a/src/os/path_unix.go +++ b/src/os/path_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/removeall_at.go b/src/os/removeall_at.go index da804c436f..8b46152a9e 100644 --- a/src/os/removeall_at.go +++ b/src/os/removeall_at.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package os diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go index a0332e8c35..2b8a7727f4 100644 --- a/src/os/removeall_noat.go +++ b/src/os/removeall_noat.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris +//go:build !unix package os diff --git a/src/os/signal/example_unix_test.go b/src/os/signal/example_unix_test.go index b279ee9491..b7047ac45c 100644 --- a/src/os/signal/example_unix_test.go +++ b/src/os/signal/example_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package signal_test diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go index e6fb24c6a8..3182e83b4e 100644 --- a/src/os/signal/signal_test.go +++ b/src/os/signal/signal_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package signal diff --git a/src/os/signal/signal_unix.go b/src/os/signal/signal_unix.go index e2e5c4ae97..772175a922 100644 --- a/src/os/signal/signal_unix.go +++ b/src/os/signal/signal_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package signal diff --git a/src/os/stat_unix.go b/src/os/stat_unix.go index eb15db5453..437afc02b4 100644 --- a/src/os/stat_unix.go +++ b/src/os/stat_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package os diff --git a/src/os/sys_unix.go b/src/os/sys_unix.go index 5ff39780e5..79005c2cbd 100644 --- a/src/os/sys_unix.go +++ b/src/os/sys_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package os diff --git a/src/os/user/cgo_lookup_unix.go b/src/os/user/cgo_lookup_unix.go index 523269086e..4f8577bbc9 100644 --- a/src/os/user/cgo_lookup_unix.go +++ b/src/os/user/cgo_lookup_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && cgo && !osusergo +//go:build unix && !android && cgo && !osusergo package user diff --git a/src/os/user/lookup_unix.go b/src/os/user/lookup_unix.go index 058dab1fb5..ed06e73fbc 100644 --- a/src/os/user/lookup_unix.go +++ b/src/os/user/lookup_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo) +//go:build ((unix && !android) || (js && wasm)) && (!cgo || osusergo) package user diff --git a/src/os/user/lookup_unix_test.go b/src/os/user/lookup_unix_test.go index 77917677fc..399a03fc3c 100644 --- a/src/os/user/lookup_unix_test.go +++ b/src/os/user/lookup_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && !cgo +//go:build unix && !android && !cgo package user diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go index c77a26952a..847a78133d 100644 --- a/src/path/filepath/match.go +++ b/src/path/filepath/match.go @@ -40,7 +40,6 @@ var ErrBadPattern = errors.New("syntax error in pattern") // // On Windows, escaping is disabled. Instead, '\\' is treated as // path separator. -// func Match(pattern, name string) (matched bool, err error) { Pattern: for len(pattern) > 0 { diff --git a/src/path/filepath/path_unix.go b/src/path/filepath/path_unix.go index dcf1d187e7..93fdfdd8a0 100644 --- a/src/path/filepath/path_unix.go +++ b/src/path/filepath/path_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package filepath diff --git a/src/path/match.go b/src/path/match.go index 918624c60e..673bbc7ff6 100644 --- a/src/path/match.go +++ b/src/path/match.go @@ -34,7 +34,6 @@ var ErrBadPattern = errors.New("syntax error in pattern") // Match requires pattern to match all of name, not just a substring. // The only possible returned error is ErrBadPattern, when pattern // is malformed. -// func Match(pattern, name string) (matched bool, err error) { Pattern: for len(pattern) > 0 { diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 06026232ee..5a35d98b51 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -6291,7 +6291,6 @@ func TestAllocsInterfaceSmall(t *testing.T) { // [false false false false] // ... // [true true true true] -// type exhaustive struct { r *rand.Rand pos int diff --git a/src/reflect/asm_riscv64.s b/src/reflect/asm_riscv64.s index e707112277..1200b4d08e 100644 --- a/src/reflect/asm_riscv64.s +++ b/src/reflect/asm_riscv64.s @@ -5,34 +5,72 @@ #include "textflag.h" #include "funcdata.h" +// The frames of each of the two functions below contain two locals, at offsets +// that are known to the runtime. +// +// The first local is a bool called retValid with a whole pointer-word reserved +// for it on the stack. The purpose of this word is so that the runtime knows +// whether the stack-allocated return space contains valid values for stack +// scanning. +// +// The second local is an abi.RegArgs value whose offset is also known to the +// runtime, so that a stack map for it can be constructed, since it contains +// pointers visible to the GC. +#define LOCAL_RETVALID 40 +#define LOCAL_REGARGS 48 + +// The frame size of the functions below is +// 32 (args of callReflect/callMethod) + (8 bool with padding) + 392 (abi.RegArgs) = 432. + // makeFuncStub is the code half of the function returned by MakeFunc. // See the comment on the declaration of makeFuncStub in makefunc.go // for more details. // No arg size here, runtime pulls arg map out of the func value. -TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$432 NO_LOCAL_POINTERS + ADD $LOCAL_REGARGS, SP, X25 // spillArgs using X25 + CALL runtime·spillArgs(SB) + MOV CTXT, 32(SP) // save CTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS + MOV CTXT, 8(SP) + MOV X25, 16(SP) + CALL ·moveMakeFuncArgPtrs(SB) + MOV 32(SP), CTXT // restore CTXT + MOV CTXT, 8(SP) MOV $argframe+0(FP), T0 MOV T0, 16(SP) - ADD $40, SP, T1 + MOV ZERO, LOCAL_RETVALID(SP) + ADD $LOCAL_RETVALID, SP, T1 MOV T1, 24(SP) - MOV ZERO, 32(SP) - MOVB ZERO, 40(SP) + ADD $LOCAL_REGARGS, SP, T1 + MOV T1, 32(SP) CALL ·callReflect(SB) + ADD $LOCAL_REGARGS, SP, X25 // unspillArgs using X25 + CALL runtime·unspillArgs(SB) RET // methodValueCall is the code half of the function returned by makeMethodValue. // See the comment on the declaration of methodValueCall in makefunc.go // for more details. // No arg size here; runtime pulls arg map out of the func value. -TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40 +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$432 NO_LOCAL_POINTERS + ADD $LOCAL_REGARGS, SP, X25 // spillArgs using X25 + CALL runtime·spillArgs(SB) + MOV CTXT, 32(SP) // save CTXT + MOV CTXT, 8(SP) + MOV X25, 16(SP) + CALL ·moveMakeFuncArgPtrs(SB) + MOV 32(SP), CTXT // restore CTXT MOV CTXT, 8(SP) MOV $argframe+0(FP), T0 MOV T0, 16(SP) - ADD $40, SP, T1 + MOV ZERO, LOCAL_RETVALID(SP) + ADD $LOCAL_RETVALID, SP, T1 MOV T1, 24(SP) - MOV ZERO, 32(SP) - MOVB ZERO, 40(SP) + ADD $LOCAL_REGARGS, SP, T1 + MOV T1, 32(SP) // frame size to 32+SP as callreflect args CALL ·callMethod(SB) + ADD $LOCAL_REGARGS, SP, X25 // unspillArgs using X25 + CALL runtime·unspillArgs(SB) RET diff --git a/src/reflect/makefunc.go b/src/reflect/makefunc.go index d0b0935cb8..3d9279ceaa 100644 --- a/src/reflect/makefunc.go +++ b/src/reflect/makefunc.go @@ -43,7 +43,6 @@ type makeFuncImpl struct { // // The Examples section of the documentation includes an illustration // of how to use MakeFunc to build a swap function for different types. -// func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { if typ.Kind() != Func { panic("reflect: call of MakeFunc with non-Func type") @@ -55,14 +54,14 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { code := abi.FuncPCABI0(makeFuncStub) // makeFuncImpl contains a stack map for use by the runtime - _, _, abi := funcLayout(ftyp, nil) + _, _, abid := funcLayout(ftyp, nil) impl := &makeFuncImpl{ makeFuncCtxt: makeFuncCtxt{ fn: code, - stack: abi.stackPtrs, - argLen: abi.stackCallArgsSize, - regPtrs: abi.inRegPtrs, + stack: abid.stackPtrs, + argLen: abid.stackCallArgsSize, + regPtrs: abid.inRegPtrs, }, ftyp: ftyp, fn: fn, @@ -110,13 +109,13 @@ func makeMethodValue(op string, v Value) Value { code := methodValueCallCodePtr() // methodValue contains a stack map for use by the runtime - _, _, abi := funcLayout(ftyp, nil) + _, _, abid := funcLayout(ftyp, nil) fv := &methodValue{ makeFuncCtxt: makeFuncCtxt{ fn: code, - stack: abi.stackPtrs, - argLen: abi.stackCallArgsSize, - regPtrs: abi.inRegPtrs, + stack: abid.stackPtrs, + argLen: abid.stackCallArgsSize, + regPtrs: abid.inRegPtrs, }, method: int(v.flag) >> flagMethodShift, rcvr: rcvr, diff --git a/src/reflect/type.go b/src/reflect/type.go index 83047062bd..53c17f9e55 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -72,7 +72,9 @@ type Type interface { // NumMethod returns the number of methods accessible using Method. // - // Note that NumMethod counts unexported methods only for interface types. + // For a non-interface type, it returns the number of exported methods. + // + // For an interface type, it returns the number of exported and unexported methods. NumMethod() int // Name returns the type's name within its package for a defined type. @@ -1267,7 +1269,7 @@ func (t *structType) Field(i int) (f StructField) { } // TODO(gri): Should there be an error/bool indicator if the index -// is wrong for FieldByIndex? +// is wrong for FieldByIndex? // FieldByIndex returns the nested field corresponding to index. func (t *structType) FieldByIndex(index []int) (f StructField) { @@ -3051,7 +3053,7 @@ type layoutKey struct { type layoutType struct { t *rtype framePool *sync.Pool - abi abiDesc + abid abiDesc } var layoutCache sync.Map // map[layoutKey]layoutType @@ -3063,7 +3065,7 @@ var layoutCache sync.Map // map[layoutKey]layoutType // The returned type exists only for GC, so we only fill out GC relevant info. // Currently, that's just size and the GC program. We also fill in // the name for possible debugging use. -func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Pool, abi abiDesc) { +func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Pool, abid abiDesc) { if t.Kind() != Func { panic("reflect: funcLayout of non-func type " + t.String()) } @@ -3073,11 +3075,11 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo k := layoutKey{t, rcvr} if lti, ok := layoutCache.Load(k); ok { lt := lti.(layoutType) - return lt.t, lt.framePool, lt.abi + return lt.t, lt.framePool, lt.abid } // Compute the ABI layout. - abi = newAbiDesc(t, rcvr) + abid = newAbiDesc(t, rcvr) // build dummy rtype holding gc program x := &rtype{ @@ -3086,11 +3088,11 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo // reflectcall's frame, not in the allocated frame. // TODO(mknyszek): Remove this comment when register // spill space in the frame is no longer required. - size: align(abi.retOffset+abi.ret.stackBytes, goarch.PtrSize), - ptrdata: uintptr(abi.stackPtrs.n) * goarch.PtrSize, + size: align(abid.retOffset+abid.ret.stackBytes, goarch.PtrSize), + ptrdata: uintptr(abid.stackPtrs.n) * goarch.PtrSize, } - if abi.stackPtrs.n > 0 { - x.gcdata = &abi.stackPtrs.data[0] + if abid.stackPtrs.n > 0 { + x.gcdata = &abid.stackPtrs.data[0] } var s string @@ -3108,10 +3110,10 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo lti, _ := layoutCache.LoadOrStore(k, layoutType{ t: x, framePool: framePool, - abi: abi, + abid: abid, }) lt := lti.(layoutType) - return lt.t, lt.framePool, lt.abi + return lt.t, lt.framePool, lt.abid } // ifaceIndir reports whether t is stored indirectly in an interface value. diff --git a/src/reflect/value.go b/src/reflect/value.go index 89f0253570..f1454b8ae2 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -453,7 +453,7 @@ func (v Value) call(op string, in []Value) []Value { var regArgs abi.RegArgs // Compute frame type. - frametype, framePool, abi := funcLayout(t, rcvrtype) + frametype, framePool, abid := funcLayout(t, rcvrtype) // Allocate a chunk of memory for frame if needed. var stackArgs unsafe.Pointer @@ -470,7 +470,7 @@ func (v Value) call(op string, in []Value) []Value { if debugReflectCall { println("reflect.call", t.String()) - abi.dump() + abid.dump() } // Copy inputs into args. @@ -481,7 +481,7 @@ func (v Value) call(op string, in []Value) []Value { // Guaranteed to only be one word in size, // so it will only take up exactly 1 abiStep (either // in a register or on the stack). - switch st := abi.call.steps[0]; st.kind { + switch st := abid.call.steps[0]; st.kind { case abiStepStack: storeRcvr(rcvr, stackArgs) case abiStepIntReg, abiStepPointer: @@ -507,7 +507,7 @@ func (v Value) call(op string, in []Value) []Value { // was possible to use space in the argument frame. v = v.assignTo("reflect.Value.Call", targ, nil) stepsLoop: - for _, st := range abi.call.stepsForValue(i + inStart) { + for _, st := range abid.call.stepsForValue(i + inStart) { switch st.kind { case abiStepStack: // Copy values to the "stack." @@ -552,10 +552,10 @@ func (v Value) call(op string, in []Value) []Value { // TODO(mknyszek): Remove this when we no longer have // caller reserved spill space. frameSize = align(frameSize, goarch.PtrSize) - frameSize += abi.spill + frameSize += abid.spill // Mark pointers in registers for the return path. - regArgs.ReturnIsPtr = abi.outRegPtrs + regArgs.ReturnIsPtr = abid.outRegPtrs if debugReflectCall { regArgs.Dump() @@ -567,7 +567,7 @@ func (v Value) call(op string, in []Value) []Value { } // Call. - call(frametype, fn, stackArgs, uint32(frametype.size), uint32(abi.retOffset), uint32(frameSize), ®Args) + call(frametype, fn, stackArgs, uint32(frametype.size), uint32(abid.retOffset), uint32(frameSize), ®Args) // For testing; see TestCallMethodJump. if callGC { @@ -585,7 +585,7 @@ func (v Value) call(op string, in []Value) []Value { // Zero the now unused input area of args, // because the Values returned by this function contain pointers to the args object, // and will thus keep the args object alive indefinitely. - typedmemclrpartial(frametype, stackArgs, 0, abi.retOffset) + typedmemclrpartial(frametype, stackArgs, 0, abid.retOffset) } // Wrap Values around return values in args. @@ -598,7 +598,7 @@ func (v Value) call(op string, in []Value) []Value { ret[i] = Zero(tv) continue } - steps := abi.ret.stepsForValue(i) + steps := abid.ret.stepsForValue(i) if st := steps[0]; st.kind == abiStepStack { // This value is on the stack. If part of a value is stack // allocated, the entire value is according to the ABI. So @@ -690,7 +690,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs ftyp := ctxt.ftyp f := ctxt.fn - _, _, abi := funcLayout(ftyp, nil) + _, _, abid := funcLayout(ftyp, nil) // Copy arguments into Values. ptr := frame @@ -701,7 +701,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs continue } v := Value{typ, nil, flag(typ.Kind())} - steps := abi.call.stepsForValue(i) + steps := abid.call.stepsForValue(i) if st := steps[0]; st.kind == abiStepStack { if ifaceIndir(typ) { // value cannot be inlined in interface data. @@ -791,7 +791,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs // target location used as scratch space. See issue 39541. v = v.assignTo("reflect.MakeFunc", typ, nil) stepsLoop: - for _, st := range abi.ret.stepsForValue(i) { + for _, st := range abid.ret.stepsForValue(i) { switch st.kind { case abiStepStack: // Copy values to the "stack." @@ -1830,7 +1830,6 @@ func (iter *MapIter) Reset(v Value) { // v := iter.Value() // ... // } -// func (v Value) MapRange() *MapIter { v.mustBe(Map) return &MapIter{m: v} @@ -1869,7 +1868,11 @@ func (v Value) Method(i int) Value { return Value{v.typ, v.ptr, fl} } -// NumMethod returns the number of exported methods in the value's method set. +// NumMethod returns the number of methods in the value's method set. +// +// For a non-interface type, it returns the number of exported methods. +// +// For an interface type, it returns the number of exported and unexported methods. func (v Value) NumMethod() int { if v.typ == nil { panic(&ValueError{"reflect.Value.NumMethod", Invalid}) @@ -2785,7 +2788,6 @@ const ( // Normally Chan's underlying value must be a channel and Send must be a zero Value. // If Chan is a zero Value, then the case is ignored, but Send must still be a zero Value. // When a receive operation is selected, the received Value is returned by Select. -// type SelectCase struct { Dir SelectDir // direction of case Chan Value // channel to use (for send or receive) diff --git a/src/regexp/exec_test.go b/src/regexp/exec_test.go index 5f8442668c..a6e833050b 100644 --- a/src/regexp/exec_test.go +++ b/src/regexp/exec_test.go @@ -62,7 +62,6 @@ import ( // // At time of writing, re2-exhaustive.txt is 59 MB but compresses to 385 kB, // so we store re2-exhaustive.txt.bz2 in the repository and decompress it on the fly. -// func TestRE2Search(t *testing.T) { testRE2(t, "testdata/re2-search.txt") } diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go index 0f6587ab27..ebf8e11915 100644 --- a/src/regexp/syntax/parse.go +++ b/src/regexp/syntax/parse.go @@ -43,6 +43,7 @@ const ( ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator" ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression" ErrUnexpectedParen ErrorCode = "unexpected )" + ErrInvalidDepth ErrorCode = "invalid nesting depth" ) func (e ErrorCode) String() string { @@ -133,7 +134,7 @@ func (p *parser) checkHeight(re *Regexp) { } } if p.calcHeight(re, true) > maxHeight { - panic(ErrInternalError) + panic(ErrInvalidDepth) } } @@ -449,7 +450,6 @@ func (p *parser) collapse(subs []*Regexp, op Op) *Regexp { // A(B(C|D)|EF)|BC(X|Y) // which simplifies by character class introduction to // A(B[CD]|EF)|BC[XY] -// func (p *parser) factor(sub []*Regexp) []*Regexp { if len(sub) < 2 { return sub @@ -757,8 +757,8 @@ func parse(s string, flags Flags) (_ *Regexp, err error) { panic(r) case nil: // ok - case ErrInternalError: - err = &Error{Code: ErrInternalError, Expr: s} + case ErrInvalidDepth: + err = &Error{Code: ErrInvalidDepth, Expr: s} } }() diff --git a/src/runtime/asm_amd64.h b/src/runtime/asm_amd64.h new file mode 100644 index 0000000000..49e0ee2323 --- /dev/null +++ b/src/runtime/asm_amd64.h @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Define features that are guaranteed to be supported by setting the AMD64 variable. +// If a feature is supported, there's no need to check it at runtime every time. + +#ifdef GOAMD64_v3 +#define hasAVX2 +#endif + +#ifdef GOAMD64_v4 +#define hasAVX2 +#endif diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index c08ae610fb..86d6a96d88 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -136,9 +136,20 @@ GLOBL bad_cpu_msg<>(SB), RODATA, $84 #define NEED_EXT_FEATURES_CX V4_EXT_FEATURES_CX #define NEED_EXT_FEATURES_BX V4_EXT_FEATURES_BX -// Downgrading v4 OS checks on Darwin for now, see CL 285572. +// Darwin requires a different approach to check AVX512 support, see CL 285572. #ifdef GOOS_darwin #define NEED_OS_SUPPORT_AX V3_OS_SUPPORT_AX +// These values are from: +// https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h +#define commpage64_base_address 0x00007fffffe00000 +#define commpage64_cpu_capabilities64 (commpage64_base_address+0x010) +#define commpage64_version (commpage64_base_address+0x01E) +#define hasAVX512F 0x0000004000000000 +#define hasAVX512CD 0x0000008000000000 +#define hasAVX512DQ 0x0000010000000000 +#define hasAVX512BW 0x0000020000000000 +#define hasAVX512VL 0x0000100000000000 +#define NEED_DARWIN_SUPPORT (hasAVX512F | hasAVX512DQ | hasAVX512CD | hasAVX512BW | hasAVX512VL) #else #define NEED_OS_SUPPORT_AX V4_OS_SUPPORT_AX #endif @@ -311,6 +322,18 @@ ok: JNE bad_cpu #endif +#ifdef NEED_DARWIN_SUPPORT + MOVQ $commpage64_version, BX + CMPW (BX), $13 // cpu_capabilities64 undefined in versions < 13 + JL bad_cpu + MOVQ $commpage64_cpu_capabilities64, BX + MOVQ (BX), BX + MOVQ $NEED_DARWIN_SUPPORT, CX + ANDQ CX, BX + CMPQ BX, CX + JNE bad_cpu +#endif + CALL runtime·check(SB) MOVL 24(SP), AX // copy argc diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s index 62deb070aa..9ef7346e00 100644 --- a/src/runtime/asm_arm64.s +++ b/src/runtime/asm_arm64.s @@ -311,74 +311,42 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 // spillArgs stores return values from registers to a *internal/abi.RegArgs in R20. TEXT ·spillArgs(SB),NOSPLIT,$0-0 - MOVD R0, (0*8)(R20) - MOVD R1, (1*8)(R20) - MOVD R2, (2*8)(R20) - MOVD R3, (3*8)(R20) - MOVD R4, (4*8)(R20) - MOVD R5, (5*8)(R20) - MOVD R6, (6*8)(R20) - MOVD R7, (7*8)(R20) - MOVD R8, (8*8)(R20) - MOVD R9, (9*8)(R20) - MOVD R10, (10*8)(R20) - MOVD R11, (11*8)(R20) - MOVD R12, (12*8)(R20) - MOVD R13, (13*8)(R20) - MOVD R14, (14*8)(R20) - MOVD R15, (15*8)(R20) - FMOVD F0, (16*8)(R20) - FMOVD F1, (17*8)(R20) - FMOVD F2, (18*8)(R20) - FMOVD F3, (19*8)(R20) - FMOVD F4, (20*8)(R20) - FMOVD F5, (21*8)(R20) - FMOVD F6, (22*8)(R20) - FMOVD F7, (23*8)(R20) - FMOVD F8, (24*8)(R20) - FMOVD F9, (25*8)(R20) - FMOVD F10, (26*8)(R20) - FMOVD F11, (27*8)(R20) - FMOVD F12, (28*8)(R20) - FMOVD F13, (29*8)(R20) - FMOVD F14, (30*8)(R20) - FMOVD F15, (31*8)(R20) + STP (R0, R1), (0*8)(R20) + STP (R2, R3), (2*8)(R20) + STP (R4, R5), (4*8)(R20) + STP (R6, R7), (6*8)(R20) + STP (R8, R9), (8*8)(R20) + STP (R10, R11), (10*8)(R20) + STP (R12, R13), (12*8)(R20) + STP (R14, R15), (14*8)(R20) + FSTPD (F0, F1), (16*8)(R20) + FSTPD (F2, F3), (18*8)(R20) + FSTPD (F4, F5), (20*8)(R20) + FSTPD (F6, F7), (22*8)(R20) + FSTPD (F8, F9), (24*8)(R20) + FSTPD (F10, F11), (26*8)(R20) + FSTPD (F12, F13), (28*8)(R20) + FSTPD (F14, F15), (30*8)(R20) RET // unspillArgs loads args into registers from a *internal/abi.RegArgs in R20. TEXT ·unspillArgs(SB),NOSPLIT,$0-0 - MOVD (0*8)(R20), R0 - MOVD (1*8)(R20), R1 - MOVD (2*8)(R20), R2 - MOVD (3*8)(R20), R3 - MOVD (4*8)(R20), R4 - MOVD (5*8)(R20), R5 - MOVD (6*8)(R20), R6 - MOVD (7*8)(R20), R7 - MOVD (8*8)(R20), R8 - MOVD (9*8)(R20), R9 - MOVD (10*8)(R20), R10 - MOVD (11*8)(R20), R11 - MOVD (12*8)(R20), R12 - MOVD (13*8)(R20), R13 - MOVD (14*8)(R20), R14 - MOVD (15*8)(R20), R15 - FMOVD (16*8)(R20), F0 - FMOVD (17*8)(R20), F1 - FMOVD (18*8)(R20), F2 - FMOVD (19*8)(R20), F3 - FMOVD (20*8)(R20), F4 - FMOVD (21*8)(R20), F5 - FMOVD (22*8)(R20), F6 - FMOVD (23*8)(R20), F7 - FMOVD (24*8)(R20), F8 - FMOVD (25*8)(R20), F9 - FMOVD (26*8)(R20), F10 - FMOVD (27*8)(R20), F11 - FMOVD (28*8)(R20), F12 - FMOVD (29*8)(R20), F13 - FMOVD (30*8)(R20), F14 - FMOVD (31*8)(R20), F15 + LDP (0*8)(R20), (R0, R1) + LDP (2*8)(R20), (R2, R3) + LDP (4*8)(R20), (R4, R5) + LDP (6*8)(R20), (R6, R7) + LDP (8*8)(R20), (R8, R9) + LDP (10*8)(R20), (R10, R11) + LDP (12*8)(R20), (R12, R13) + LDP (14*8)(R20), (R14, R15) + FLDPD (16*8)(R20), (F0, F1) + FLDPD (18*8)(R20), (F2, F3) + FLDPD (20*8)(R20), (F4, F5) + FLDPD (22*8)(R20), (F6, F7) + FLDPD (24*8)(R20), (F8, F9) + FLDPD (26*8)(R20), (F10, F11) + FLDPD (28*8)(R20), (F12, F13) + FLDPD (30*8)(R20), (F14, F15) RET // reflectcall: call a function with the given argument list @@ -480,10 +448,8 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ // arguments in registers. TEXT callRet<>(SB), NOSPLIT, $48-0 NO_LOCAL_POINTERS - MOVD R7, 8(RSP) - MOVD R3, 16(RSP) - MOVD R5, 24(RSP) - MOVD R4, 32(RSP) + STP (R7, R3), 8(RSP) + STP (R5, R4), 24(RSP) MOVD R20, 40(RSP) BL runtime·reflectcallmove(SB) RET @@ -1215,8 +1181,7 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 // calls to it directly and it does not use the stack-based Go ABI. TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$200 // Save the registers clobbered by the fast path. - MOVD R0, 184(RSP) - MOVD R1, 192(RSP) + STP (R0, R1), 184(RSP) MOVD g_m(g), R0 MOVD m_p(R0), R0 MOVD (p_wbBuf+wbBuf_next)(R0), R1 @@ -1232,8 +1197,7 @@ TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$200 // Is the buffer full? (flags set in CMP above) BEQ flush ret: - MOVD 184(RSP), R0 - MOVD 192(RSP), R1 + LDP 184(RSP), (R0, R1) // Do the write. MOVD R3, (R2) RET @@ -1241,32 +1205,20 @@ ret: flush: // Save all general purpose registers since these could be // clobbered by wbBufFlush and were not saved by the caller. - MOVD R2, 8(RSP) // Also first argument to wbBufFlush - MOVD R3, 16(RSP) // Also second argument to wbBufFlush - // R0 already saved - // R1 already saved - MOVD R4, 24(RSP) - MOVD R5, 32(RSP) - MOVD R6, 40(RSP) - MOVD R7, 48(RSP) - MOVD R8, 56(RSP) - MOVD R9, 64(RSP) - MOVD R10, 72(RSP) - MOVD R11, 80(RSP) - MOVD R12, 88(RSP) - MOVD R13, 96(RSP) - MOVD R14, 104(RSP) - MOVD R15, 112(RSP) + // R0 and R1 already saved + STP (R2, R3), 1*8(RSP) // Also first and second arguments to wbBufFlush + STP (R4, R5), 3*8(RSP) + STP (R6, R7), 5*8(RSP) + STP (R8, R9), 7*8(RSP) + STP (R10, R11), 9*8(RSP) + STP (R12, R13), 11*8(RSP) + STP (R14, R15), 13*8(RSP) // R16, R17 may be clobbered by linker trampoline // R18 is unused. - MOVD R19, 120(RSP) - MOVD R20, 128(RSP) - MOVD R21, 136(RSP) - MOVD R22, 144(RSP) - MOVD R23, 152(RSP) - MOVD R24, 160(RSP) - MOVD R25, 168(RSP) - MOVD R26, 176(RSP) + STP (R19, R20), 15*8(RSP) + STP (R21, R22), 17*8(RSP) + STP (R23, R24), 19*8(RSP) + STP (R25, R26), 21*8(RSP) // R27 is temp register. // R28 is g. // R29 is frame pointer (unused). @@ -1275,29 +1227,17 @@ flush: // This takes arguments R2 and R3. CALL runtime·wbBufFlush(SB) - - MOVD 8(RSP), R2 - MOVD 16(RSP), R3 - MOVD 24(RSP), R4 - MOVD 32(RSP), R5 - MOVD 40(RSP), R6 - MOVD 48(RSP), R7 - MOVD 56(RSP), R8 - MOVD 64(RSP), R9 - MOVD 72(RSP), R10 - MOVD 80(RSP), R11 - MOVD 88(RSP), R12 - MOVD 96(RSP), R13 - MOVD 104(RSP), R14 - MOVD 112(RSP), R15 - MOVD 120(RSP), R19 - MOVD 128(RSP), R20 - MOVD 136(RSP), R21 - MOVD 144(RSP), R22 - MOVD 152(RSP), R23 - MOVD 160(RSP), R24 - MOVD 168(RSP), R25 - MOVD 176(RSP), R26 + LDP 1*8(RSP), (R2, R3) + LDP 3*8(RSP), (R4, R5) + LDP 5*8(RSP), (R6, R7) + LDP 7*8(RSP), (R8, R9) + LDP 9*8(RSP), (R10, R11) + LDP 11*8(RSP), (R12, R13) + LDP 13*8(RSP), (R14, R15) + LDP 15*8(RSP), (R19, R20) + LDP 17*8(RSP), (R21, R22) + LDP 19*8(RSP), (R23, R24) + LDP 21*8(RSP), (R25, R26) JMP ret // Note: these functions use a special calling convention to save generated code space. diff --git a/src/runtime/asm_riscv64.s b/src/runtime/asm_riscv64.s index 2a4837b399..9052649c9e 100644 --- a/src/runtime/asm_riscv64.s +++ b/src/runtime/asm_riscv64.s @@ -260,25 +260,31 @@ TEXT runtime·procyield(SB),NOSPLIT,$0-0 // to keep running g. // func mcall(fn func(*g)) -TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +#ifdef GOEXPERIMENT_regabiargs + MOV X10, CTXT +#else + MOV fn+0(FP), CTXT +#endif + // Save caller state in g->sched MOV X2, (g_sched+gobuf_sp)(g) MOV RA, (g_sched+gobuf_pc)(g) MOV ZERO, (g_sched+gobuf_lr)(g) // Switch to m->g0 & its stack, call fn. - MOV g, T0 + MOV g, X10 MOV g_m(g), T1 MOV m_g0(T1), g CALL runtime·save_g(SB) - BNE g, T0, 2(PC) + BNE g, X10, 2(PC) JMP runtime·badmcall(SB) - MOV fn+0(FP), CTXT // context MOV 0(CTXT), T1 // code pointer MOV (g_sched+gobuf_sp)(g), X2 // sp = m->g0->sched.sp + // we don't need special macro for regabi since arg0(X10) = g ADD $-16, X2 - MOV T0, 8(X2) - MOV ZERO, 0(X2) + MOV X10, 8(X2) // setup g + MOV ZERO, 0(X2) // clear return address JALR RA, T1 JMP runtime·badmcall2(SB) @@ -417,12 +423,17 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ MOVB A4, (A3); \ ADD $1, A3; \ JMP -5(PC); \ + /* set up argument registers */ \ + MOV regArgs+40(FP), X25; \ + CALL ·unspillArgs(SB); \ /* call function */ \ MOV f+8(FP), CTXT; \ - MOV (CTXT), A4; \ + MOV (CTXT), X25; \ PCDATA $PCDATA_StackMapIndex, $0; \ - JALR RA, A4; \ + JALR RA, X25; \ /* copy return values back */ \ + MOV regArgs+40(FP), X25; \ + CALL ·spillArgs(SB); \ MOV stackArgsType+0(FP), A5; \ MOV stackArgs+16(FP), A1; \ MOVWU stackArgsSize+24(FP), A2; \ @@ -439,11 +450,12 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ // to reflectcallmove. It does not follow the Go ABI; it expects its // arguments in registers. TEXT callRet<>(SB), NOSPLIT, $40-0 + NO_LOCAL_POINTERS MOV A5, 8(X2) MOV A1, 16(X2) MOV A3, 24(X2) MOV A2, 32(X2) - MOV ZERO, 40(X2) + MOV X25, 40(X2) CALL runtime·reflectcallmove(SB) RET @@ -625,6 +637,86 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 MOV T0, ret+0(FP) RET +#ifdef GOEXPERIMENT_regabiargs +// spillArgs stores return values from registers to a *internal/abi.RegArgs in X25. +TEXT ·spillArgs(SB),NOSPLIT,$0-0 + MOV X10, (0*8)(X25) + MOV X11, (1*8)(X25) + MOV X12, (2*8)(X25) + MOV X13, (3*8)(X25) + MOV X14, (4*8)(X25) + MOV X15, (5*8)(X25) + MOV X16, (6*8)(X25) + MOV X17, (7*8)(X25) + MOV X8, (8*8)(X25) + MOV X9, (9*8)(X25) + MOV X18, (10*8)(X25) + MOV X19, (11*8)(X25) + MOV X20, (12*8)(X25) + MOV X21, (13*8)(X25) + MOV X22, (14*8)(X25) + MOV X23, (15*8)(X25) + MOVD F10, (16*8)(X25) + MOVD F11, (17*8)(X25) + MOVD F12, (18*8)(X25) + MOVD F13, (19*8)(X25) + MOVD F14, (20*8)(X25) + MOVD F15, (21*8)(X25) + MOVD F16, (22*8)(X25) + MOVD F17, (23*8)(X25) + MOVD F8, (24*8)(X25) + MOVD F9, (25*8)(X25) + MOVD F18, (26*8)(X25) + MOVD F19, (27*8)(X25) + MOVD F20, (28*8)(X25) + MOVD F21, (29*8)(X25) + MOVD F22, (30*8)(X25) + MOVD F23, (31*8)(X25) + RET + +// unspillArgs loads args into registers from a *internal/abi.RegArgs in X25. +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 + MOV (0*8)(X25), X10 + MOV (1*8)(X25), X11 + MOV (2*8)(X25), X12 + MOV (3*8)(X25), X13 + MOV (4*8)(X25), X14 + MOV (5*8)(X25), X15 + MOV (6*8)(X25), X16 + MOV (7*8)(X25), X17 + MOV (8*8)(X25), X8 + MOV (9*8)(X25), X9 + MOV (10*8)(X25), X18 + MOV (11*8)(X25), X19 + MOV (12*8)(X25), X20 + MOV (13*8)(X25), X21 + MOV (14*8)(X25), X22 + MOV (15*8)(X25), X23 + MOVD (16*8)(X25), F10 + MOVD (17*8)(X25), F11 + MOVD (18*8)(X25), F12 + MOVD (19*8)(X25), F13 + MOVD (20*8)(X25), F14 + MOVD (21*8)(X25), F15 + MOVD (22*8)(X25), F16 + MOVD (23*8)(X25), F17 + MOVD (24*8)(X25), F8 + MOVD (25*8)(X25), F9 + MOVD (26*8)(X25), F18 + MOVD (27*8)(X25), F19 + MOVD (28*8)(X25), F20 + MOVD (29*8)(X25), F21 + MOVD (30*8)(X25), F22 + MOVD (31*8)(X25), F23 + RET +#else +TEXT ·spillArgs(SB),NOSPLIT,$0-0 + RET + +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 + RET +#endif + // gcWriteBarrier performs a heap pointer write and informs the GC. // // gcWriteBarrier does NOT follow the Go ABI. It takes two arguments: @@ -634,7 +726,7 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 // The act of CALLing gcWriteBarrier will clobber RA (LR). // It does not clobber any other general-purpose registers, // but may clobber others (e.g., floating point registers). -TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$208 +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$208 // Save the registers clobbered by the fast path. MOV A0, 24*8(X2) MOV A1, 25*8(X2) @@ -727,78 +819,164 @@ flush: JMP ret // Note: these functions use a special calling convention to save generated code space. -// Arguments are passed in registers, but the space for those arguments are allocated -// in the caller's stack frame. These stubs write the args into that stack space and -// then tail call to the corresponding runtime handler. +// Arguments are passed in registers (ssa/gen/RISCV64Ops.go), but the space for those +// arguments are allocated in the caller's stack frame. +// These stubs write the args into that stack space and then tail call to the +// corresponding runtime handler. // The tail call makes these stubs disappear in backtraces. -TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicIndex(SB) +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicIndexU(SB) +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAlen(SB) +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAlenU(SB) +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAcap(SB) +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceAcapU(SB) +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceB(SB) +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSliceBU(SB) +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3Alen(SB) +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3AlenU(SB) +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3Acap(SB) +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3AcapU(SB) +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3B(SB) +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T1, X10 + MOV T2, X11 +#else MOV T1, x+0(FP) MOV T2, y+8(FP) - JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3BU(SB) +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3C(SB) +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T0, X10 + MOV T1, X11 +#else MOV T0, x+0(FP) MOV T1, y+8(FP) - JMP runtime·goPanicSlice3CU(SB) -TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 +#endif + JMP runtime·goPanicSlice3CU(SB) +TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOV T2, X10 + MOV T3, X11 +#else MOV T2, x+0(FP) MOV T3, y+8(FP) - JMP runtime·goPanicSliceConvert(SB) +#endif + JMP runtime·goPanicSliceConvert(SB) -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 diff --git a/src/runtime/cgo/abi_arm64.h b/src/runtime/cgo/abi_arm64.h new file mode 100644 index 0000000000..e2b5e6d0be --- /dev/null +++ b/src/runtime/cgo/abi_arm64.h @@ -0,0 +1,43 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These macros save and restore the callee-saved registers +// from the stack, but they don't adjust stack pointer, so +// the user should prepare stack space in advance. +// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP). +// +// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP). +// +// R29 is not saved because Go will save and restore it. + +#define SAVE_R19_TO_R28(offset) \ + STP (R19, R20), ((offset)+0*8)(RSP) \ + STP (R21, R22), ((offset)+2*8)(RSP) \ + STP (R23, R24), ((offset)+4*8)(RSP) \ + STP (R25, R26), ((offset)+6*8)(RSP) \ + STP (R27, g), ((offset)+8*8)(RSP) + +#define RESTORE_R19_TO_R28(offset) \ + LDP ((offset)+0*8)(RSP), (R19, R20) \ + LDP ((offset)+2*8)(RSP), (R21, R22) \ + LDP ((offset)+4*8)(RSP), (R23, R24) \ + LDP ((offset)+6*8)(RSP), (R25, R26) \ + LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */ + +#define SAVE_F8_TO_F15(offset) \ + FSTPD (F8, F9), ((offset)+0*8)(RSP) \ + FSTPD (F10, F11), ((offset)+2*8)(RSP) \ + FSTPD (F12, F13), ((offset)+4*8)(RSP) \ + FSTPD (F14, F15), ((offset)+6*8)(RSP) + +#define RESTORE_F8_TO_F15(offset) \ + FLDPD ((offset)+0*8)(RSP), (F8, F9) \ + FLDPD ((offset)+2*8)(RSP), (F10, F11) \ + FLDPD ((offset)+4*8)(RSP), (F12, F13) \ + FLDPD ((offset)+6*8)(RSP), (F14, F15) + diff --git a/src/runtime/cgo/asm_arm64.s b/src/runtime/cgo/asm_arm64.s index 1cb25cf89e..e808dedcfc 100644 --- a/src/runtime/cgo/asm_arm64.s +++ b/src/runtime/cgo/asm_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "abi_arm64.h" // Called by C code generated by cmd/cgo. // func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) @@ -14,57 +15,23 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 * push 3 args for fn (R0, R1, R3), skipping R2. * Also note that at procedure entry in gc world, 8(RSP) will be the * first arg. - * TODO(minux): use LDP/STP here if it matters. */ SUB $(8*24), RSP - MOVD R0, (8*1)(RSP) - MOVD R1, (8*2)(RSP) + STP (R0, R1), (8*1)(RSP) MOVD R3, (8*3)(RSP) - MOVD R19, (8*4)(RSP) - MOVD R20, (8*5)(RSP) - MOVD R21, (8*6)(RSP) - MOVD R22, (8*7)(RSP) - MOVD R23, (8*8)(RSP) - MOVD R24, (8*9)(RSP) - MOVD R25, (8*10)(RSP) - MOVD R26, (8*11)(RSP) - MOVD R27, (8*12)(RSP) - MOVD g, (8*13)(RSP) - MOVD R29, (8*14)(RSP) - MOVD R30, (8*15)(RSP) - FMOVD F8, (8*16)(RSP) - FMOVD F9, (8*17)(RSP) - FMOVD F10, (8*18)(RSP) - FMOVD F11, (8*19)(RSP) - FMOVD F12, (8*20)(RSP) - FMOVD F13, (8*21)(RSP) - FMOVD F14, (8*22)(RSP) - FMOVD F15, (8*23)(RSP) + + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) + STP (R29, R30), (8*22)(RSP) + // Initialize Go ABI environment BL runtime·load_g(SB) - BL runtime·cgocallback(SB) - MOVD (8*4)(RSP), R19 - MOVD (8*5)(RSP), R20 - MOVD (8*6)(RSP), R21 - MOVD (8*7)(RSP), R22 - MOVD (8*8)(RSP), R23 - MOVD (8*9)(RSP), R24 - MOVD (8*10)(RSP), R25 - MOVD (8*11)(RSP), R26 - MOVD (8*12)(RSP), R27 - MOVD (8*13)(RSP), g - MOVD (8*14)(RSP), R29 - MOVD (8*15)(RSP), R30 - FMOVD (8*16)(RSP), F8 - FMOVD (8*17)(RSP), F9 - FMOVD (8*18)(RSP), F10 - FMOVD (8*19)(RSP), F11 - FMOVD (8*20)(RSP), F12 - FMOVD (8*21)(RSP), F13 - FMOVD (8*22)(RSP), F14 - FMOVD (8*23)(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) + LDP (8*22)(RSP), (R29, R30) + ADD $(8*24), RSP RET diff --git a/src/runtime/cgo/setenv.go b/src/runtime/cgo/setenv.go index 0f4780a945..2247cb2b59 100644 --- a/src/runtime/cgo/setenv.go +++ b/src/runtime/cgo/setenv.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package cgo diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 3cdb5dce11..a16782ae94 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -594,10 +594,10 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) // recv processes a receive operation on a full channel c. // There are 2 parts: -// 1) The value sent by the sender sg is put into the channel -// and the sender is woken up to go on its merry way. -// 2) The value received by the receiver (the current G) is -// written to ep. +// 1) The value sent by the sender sg is put into the channel +// and the sender is woken up to go on its merry way. +// 2) The value received by the receiver (the current G) is +// written to ep. // For synchronous channels, both values are the same. // For asynchronous channels, the receiver gets its data from // the channel buffer and the sender's data is put in the @@ -683,7 +683,6 @@ func chanparkcommit(gp *g, chanLock unsafe.Pointer) bool { // } else { // ... bar // } -// func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) { return chansend(c, elem, false, getcallerpc()) } @@ -704,7 +703,6 @@ func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) { // } else { // ... bar // } -// func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) { return chanrecv(c, elem, false) } diff --git a/src/runtime/compiler.go b/src/runtime/compiler.go index 1ebc62dea1..f430a27719 100644 --- a/src/runtime/compiler.go +++ b/src/runtime/compiler.go @@ -9,5 +9,4 @@ package runtime // // gc Also known as cmd/compile. // gccgo The gccgo front end, part of the GCC compiler suite. -// const Compiler = "gc" diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go index a218205af4..29d9c476f9 100644 --- a/src/runtime/crash_unix_test.go +++ b/src/runtime/crash_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package runtime_test diff --git a/src/runtime/duff_riscv64.s b/src/runtime/duff_riscv64.s index 9d7f0031a3..ec447677ad 100644 --- a/src/runtime/duff_riscv64.s +++ b/src/runtime/duff_riscv64.s @@ -4,7 +4,7 @@ #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 +TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 MOV ZERO, (X25) ADD $8, X25 MOV ZERO, (X25) @@ -263,7 +263,7 @@ TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 ADD $8, X25 RET -TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 +TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 MOV (X24), X31 ADD $8, X24 MOV X31, (X25) diff --git a/src/runtime/env_posix.go b/src/runtime/env_posix.go index 44086c1d63..7d01ab4dd7 100644 --- a/src/runtime/env_posix.go +++ b/src/runtime/env_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows || plan9 +//go:build unix || (js && wasm) || windows || plan9 package runtime diff --git a/src/runtime/export_mmap_test.go b/src/runtime/export_mmap_test.go index f9c3229316..f73fcbde9b 100644 --- a/src/runtime/export_mmap_test.go +++ b/src/runtime/export_mmap_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // Export guts for testing. diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 0ac15ce82c..0156981524 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -1308,9 +1308,9 @@ func (c *GCController) Revise(d GCControllerReviseDelta) { func (c *GCController) EndCycle(bytesMarked uint64, assistTime, elapsed int64, gomaxprocs int) { c.assistTime = assistTime - triggerRatio := c.endCycle(elapsed, gomaxprocs, false) + c.endCycle(elapsed, gomaxprocs, false) c.resetLive(bytesMarked) - c.commit(triggerRatio) + c.commit() } var escapeSink any diff --git a/src/runtime/export_unix_test.go b/src/runtime/export_unix_test.go index 4a587cb780..a548cf7b7a 100644 --- a/src/runtime/export_unix_test.go +++ b/src/runtime/export_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package runtime diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 871637a09e..c7f2b7a443 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -697,11 +697,6 @@ func writeheapdump_m(fd uintptr, m *MemStats) { casgstatus(_g_.m.curg, _Grunning, _Gwaiting) _g_.waitreason = waitReasonDumpingHeap - // Update stats so we can dump them. - // As a side effect, flushes all the mcaches so the mspan.freelist - // lists contain all the free objects. - updatememstats() - // Set dump file. dumpfd = fd diff --git a/src/runtime/internal/atomic/types.go b/src/runtime/internal/atomic/types.go index 1a240d7c91..d9cffbf88f 100644 --- a/src/runtime/internal/atomic/types.go +++ b/src/runtime/internal/atomic/types.go @@ -124,6 +124,28 @@ func (u *Uint8) Or(value uint8) { Or8(&u.value, value) } +// Bool is an atomically accessed bool value. +// +// A Bool must not be copied. +type Bool struct { + // Inherits noCopy from Uint8. + u Uint8 +} + +// Load accesses and returns the value atomically. +func (b *Bool) Load() bool { + return b.u.Load() != 0 +} + +// Store updates the value atomically. +func (b *Bool) Store(value bool) { + s := uint8(0) + if value { + s = 1 + } + b.u.Store(s) +} + // Uint32 is an atomically accessed uint32 value. // // A Uint32 must not be copied. @@ -326,6 +348,7 @@ func (u *Uintptr) Add(delta uintptr) uintptr { // // A Float64 must not be copied. type Float64 struct { + // Inherits noCopy from Uint64. u Uint64 } diff --git a/src/runtime/lock_sema.go b/src/runtime/lock_sema.go index db36df1f37..6961c2ea9b 100644 --- a/src/runtime/lock_sema.go +++ b/src/runtime/lock_sema.go @@ -23,7 +23,6 @@ import ( // // func semawakeup(mp *m) // Wake up mp, which is or will soon be sleeping on its semaphore. -// const ( locked uintptr = 1 diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index 6ed6ceade2..a00878a11c 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -354,75 +354,6 @@ var ( physHugePageShift uint ) -// OS memory management abstraction layer -// -// Regions of the address space managed by the runtime may be in one of four -// states at any given time: -// 1) None - Unreserved and unmapped, the default state of any region. -// 2) Reserved - Owned by the runtime, but accessing it would cause a fault. -// Does not count against the process' memory footprint. -// 3) Prepared - Reserved, intended not to be backed by physical memory (though -// an OS may implement this lazily). Can transition efficiently to -// Ready. Accessing memory in such a region is undefined (may -// fault, may give back unexpected zeroes, etc.). -// 4) Ready - may be accessed safely. -// -// This set of states is more than is strictly necessary to support all the -// currently supported platforms. One could get by with just None, Reserved, and -// Ready. However, the Prepared state gives us flexibility for performance -// purposes. For example, on POSIX-y operating systems, Reserved is usually a -// private anonymous mmap'd region with PROT_NONE set, and to transition -// to Ready would require setting PROT_READ|PROT_WRITE. However the -// underspecification of Prepared lets us use just MADV_FREE to transition from -// Ready to Prepared. Thus with the Prepared state we can set the permission -// bits just once early on, we can efficiently tell the OS that it's free to -// take pages away from us when we don't strictly need them. -// -// For each OS there is a common set of helpers defined that transition -// memory regions between these states. The helpers are as follows: -// -// sysAlloc transitions an OS-chosen region of memory from None to Ready. -// More specifically, it obtains a large chunk of zeroed memory from the -// operating system, typically on the order of a hundred kilobytes -// or a megabyte. This memory is always immediately available for use. -// -// sysFree transitions a memory region from any state to None. Therefore, it -// returns memory unconditionally. It is used if an out-of-memory error has been -// detected midway through an allocation or to carve out an aligned section of -// the address space. It is okay if sysFree is a no-op only if sysReserve always -// returns a memory region aligned to the heap allocator's alignment -// restrictions. -// -// sysReserve transitions a memory region from None to Reserved. It reserves -// address space in such a way that it would cause a fatal fault upon access -// (either via permissions or not committing the memory). Such a reservation is -// thus never backed by physical memory. -// If the pointer passed to it is non-nil, the caller wants the -// reservation there, but sysReserve can still choose another -// location if that one is unavailable. -// NOTE: sysReserve returns OS-aligned memory, but the heap allocator -// may use larger alignment, so the caller must be careful to realign the -// memory obtained by sysReserve. -// -// sysMap transitions a memory region from Reserved to Prepared. It ensures the -// memory region can be efficiently transitioned to Ready. -// -// sysUsed transitions a memory region from Prepared to Ready. It notifies the -// operating system that the memory region is needed and ensures that the region -// may be safely accessed. This is typically a no-op on systems that don't have -// an explicit commit step and hard over-commit limits, but is critical on -// Windows, for example. -// -// sysUnused transitions a memory region from Ready to Prepared. It notifies the -// operating system that the physical pages backing this memory region are no -// longer needed and can be reused for other purposes. The contents of a -// sysUnused memory region are considered forfeit and the region must not be -// accessed again until sysUsed is called. -// -// sysFault transitions a memory region from Ready or Prepared to Reserved. It -// marks a region such that it will always fault if accessed. Used only for -// debugging the runtime. - func mallocinit() { if class_to_size[_TinySizeClass] != _TinySize { throw("bad TinySizeClass") @@ -434,11 +365,6 @@ func mallocinit() { throw("heapArenaBitmapBytes not a power of 2") } - // Copy class sizes out for statistics table. - for i := range class_to_size { - memstats.by_size[i].size = uint32(class_to_size[i]) - } - // Check physPageSize. if physPageSize == 0 { // The OS init code failed to fetch the physical page size. diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index 465c21f83f..a0d145ec76 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -147,7 +147,7 @@ import ( // remove the deletion barrier, we'll have to work out a new way to // handle the profile logging. -// typedmemmove copies a value of type t to dst from src. +// typedmemmove copies a value of type typ to dst from src. // Must be nosplit, see #16026. // // TODO: Perfect for go:nosplitrec since we can't have a safe point diff --git a/src/runtime/mem.go b/src/runtime/mem.go new file mode 100644 index 0000000000..67af9c057f --- /dev/null +++ b/src/runtime/mem.go @@ -0,0 +1,119 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import "unsafe" + +// OS memory management abstraction layer +// +// Regions of the address space managed by the runtime may be in one of four +// states at any given time: +// 1) None - Unreserved and unmapped, the default state of any region. +// 2) Reserved - Owned by the runtime, but accessing it would cause a fault. +// Does not count against the process' memory footprint. +// 3) Prepared - Reserved, intended not to be backed by physical memory (though +// an OS may implement this lazily). Can transition efficiently to +// Ready. Accessing memory in such a region is undefined (may +// fault, may give back unexpected zeroes, etc.). +// 4) Ready - may be accessed safely. +// +// This set of states is more than is strictly necessary to support all the +// currently supported platforms. One could get by with just None, Reserved, and +// Ready. However, the Prepared state gives us flexibility for performance +// purposes. For example, on POSIX-y operating systems, Reserved is usually a +// private anonymous mmap'd region with PROT_NONE set, and to transition +// to Ready would require setting PROT_READ|PROT_WRITE. However the +// underspecification of Prepared lets us use just MADV_FREE to transition from +// Ready to Prepared. Thus with the Prepared state we can set the permission +// bits just once early on, we can efficiently tell the OS that it's free to +// take pages away from us when we don't strictly need them. +// +// This file defines a cross-OS interface for a common set of helpers +// that transition memory regions between these states. The helpers call into +// OS-specific implementations that handle errors, while the interface boundary +// implements cross-OS functionality, like updating runtime accounting. + +// sysAlloc transitions an OS-chosen region of memory from None to Ready. +// More specifically, it obtains a large chunk of zeroed memory from the +// operating system, typically on the order of a hundred kilobytes +// or a megabyte. This memory is always immediately available for use. +// +// Don't split the stack as this function may be invoked without a valid G, +// which prevents us from allocating more stack. +//go:nosplit +func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { + sysStat.add(int64(n)) + return sysAllocOS(n) +} + +// sysUnused transitions a memory region from Ready to Prepared. It notifies the +// operating system that the physical pages backing this memory region are no +// longer needed and can be reused for other purposes. The contents of a +// sysUnused memory region are considered forfeit and the region must not be +// accessed again until sysUsed is called. +func sysUnused(v unsafe.Pointer, n uintptr) { + sysUnusedOS(v, n) +} + +// sysUsed transitions a memory region from Prepared to Ready. It notifies the +// operating system that the memory region is needed and ensures that the region +// may be safely accessed. This is typically a no-op on systems that don't have +// an explicit commit step and hard over-commit limits, but is critical on +// Windows, for example. +func sysUsed(v unsafe.Pointer, n uintptr) { + sysUsedOS(v, n) +} + +// sysHugePage does not transition memory regions, but instead provides a +// hint to the OS that it would be more efficient to back this memory region +// with pages of a larger size transparently. +func sysHugePage(v unsafe.Pointer, n uintptr) { + sysHugePageOS(v, n) +} + +// sysFree transitions a memory region from any state to None. Therefore, it +// returns memory unconditionally. It is used if an out-of-memory error has been +// detected midway through an allocation or to carve out an aligned section of +// the address space. It is okay if sysFree is a no-op only if sysReserve always +// returns a memory region aligned to the heap allocator's alignment +// restrictions. +// +// Don't split the stack as this function may be invoked without a valid G, +// which prevents us from allocating more stack. +//go:nosplit +func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { + sysStat.add(-int64(n)) + sysFreeOS(v, n) +} + +// sysFault transitions a memory region from Ready or Prepared to Reserved. It +// marks a region such that it will always fault if accessed. Used only for +// debugging the runtime. +func sysFault(v unsafe.Pointer, n uintptr) { + sysFaultOS(v, n) +} + +// sysReserve transitions a memory region from None to Reserved. It reserves +// address space in such a way that it would cause a fatal fault upon access +// (either via permissions or not committing the memory). Such a reservation is +// thus never backed by physical memory. +// +// If the pointer passed to it is non-nil, the caller wants the +// reservation there, but sysReserve can still choose another +// location if that one is unavailable. +// +// NOTE: sysReserve returns OS-aligned memory, but the heap allocator +// may use larger alignment, so the caller must be careful to realign the +// memory obtained by sysReserve. +func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { + return sysReserveOS(v, n) +} + +// sysMap transitions a memory region from Reserved to Prepared. It ensures the +// memory region can be efficiently transitioned to Ready. +func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { + sysStat.add(int64(n)) + sysMapOS(v, n) +} diff --git a/src/runtime/mem_aix.go b/src/runtime/mem_aix.go index 489d7928e1..d6a181ad4d 100644 --- a/src/runtime/mem_aix.go +++ b/src/runtime/mem_aix.go @@ -11,7 +11,7 @@ import ( // Don't split the stack as this method may be invoked without a valid G, which // prevents us from allocating more stack. //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { if err == _EACCES { @@ -24,34 +24,31 @@ func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { } return nil } - sysStat.add(int64(n)) return p } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { madvise(v, n, _MADV_DONTNEED) } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) - } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil @@ -59,9 +56,7 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { return p } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { // AIX does not allow mapping a range that is already mapped. // So, call mprotect to change permissions. // Note that sysMap is always called with a non-nil pointer diff --git a/src/runtime/mem_bsd.go b/src/runtime/mem_bsd.go index 49337eafbf..e83145e86b 100644 --- a/src/runtime/mem_bsd.go +++ b/src/runtime/mem_bsd.go @@ -13,41 +13,39 @@ import ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { v, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil } - sysStat.add(int64(n)) return v } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { madvise(v, n, _MADV_FREE) } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } // Indicates not to reserve swap space for the mapping. const _sunosMAP_NORESERVE = 0x40 -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { flags := int32(_MAP_ANON | _MAP_PRIVATE) if GOOS == "solaris" || GOOS == "illumos" { // Be explicit that we don't want to reserve swap space @@ -65,9 +63,7 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { const _sunosEAGAIN = 11 const _ENOMEM = 12 -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) if err == _ENOMEM || ((GOOS == "solaris" || GOOS == "illumos") && err == _sunosEAGAIN) { throw("runtime: out of memory") diff --git a/src/runtime/mem_darwin.go b/src/runtime/mem_darwin.go index 9f836c0818..d63b5559aa 100644 --- a/src/runtime/mem_darwin.go +++ b/src/runtime/mem_darwin.go @@ -11,44 +11,42 @@ import ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { v, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil } - sysStat.add(int64(n)) return v } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { // MADV_FREE_REUSABLE is like MADV_FREE except it also propagates // accounting information about the process to task_info. madvise(v, n, _MADV_FREE_REUSABLE) } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { // MADV_FREE_REUSE is necessary to keep the kernel's accounting // accurate. If called on any memory region that hasn't been // MADV_FREE_REUSABLE'd, it's a no-op. madvise(v, n, _MADV_FREE_REUSE) } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil @@ -58,9 +56,7 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { const _ENOMEM = 12 -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) if err == _ENOMEM { throw("runtime: out of memory") diff --git a/src/runtime/mem_js.go b/src/runtime/mem_js.go index 4ca486ac4b..c66b91eedd 100644 --- a/src/runtime/mem_js.go +++ b/src/runtime/mem_js.go @@ -13,34 +13,33 @@ import ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { - p := sysReserve(nil, n) - sysMap(p, n, sysStat) +func sysAllocOS(n uintptr) unsafe.Pointer { + p := sysReserveOS(nil, n) + sysMapOS(p, n) return p } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { } var reserveEnd uintptr -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { // TODO(neelance): maybe unify with mem_plan9.go, depending on how https://github.com/WebAssembly/design/blob/master/FutureFeatures.md#finer-grained-control-over-memory turns out if v != nil { @@ -80,6 +79,5 @@ func growMemory(pages int32) int32 // This allows the front-end to replace the old DataView object with a new one. func resetMemoryDataView() -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) +func sysMapOS(v unsafe.Pointer, n uintptr) { } diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go index f8333014c2..980f7bb53d 100644 --- a/src/runtime/mem_linux.go +++ b/src/runtime/mem_linux.go @@ -17,7 +17,7 @@ const ( // Don't split the stack as this method may be invoked without a valid G, which // prevents us from allocating more stack. //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { if err == _EACCES { @@ -30,13 +30,12 @@ func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { } return nil } - sysStat.add(int64(n)) return p } var adviseUnused = uint32(_MADV_FREE) -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { // By default, Linux's "transparent huge page" support will // merge pages into a huge page if there's even a single // present regular page, undoing the effects of madvise(adviseUnused) @@ -123,7 +122,7 @@ func sysUnused(v unsafe.Pointer, n uintptr) { } } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { if debug.harddecommit > 0 { p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) if err == _ENOMEM { @@ -145,10 +144,10 @@ func sysUsed(v unsafe.Pointer, n uintptr) { // the end points as well, but it's probably not worth // the cost because when neighboring allocations are // freed sysUnused will just set NOHUGEPAGE again. - sysHugePage(v, n) + sysHugePageOS(v, n) } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { if physHugePageSize != 0 { // Round v up to a huge page boundary. beg := alignUp(uintptr(v), physHugePageSize) @@ -164,16 +163,15 @@ func sysHugePage(v unsafe.Pointer, n uintptr) { // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { munmap(v, n) } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE|_MAP_FIXED, -1, 0) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_PRIVATE, -1, 0) if err != 0 { return nil @@ -181,9 +179,7 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { return p } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) - +func sysMapOS(v unsafe.Pointer, n uintptr) { p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0) if err == _ENOMEM { throw("runtime: out of memory") diff --git a/src/runtime/mem_plan9.go b/src/runtime/mem_plan9.go index 53d8e6dffa..0e8bf74746 100644 --- a/src/runtime/mem_plan9.go +++ b/src/runtime/mem_plan9.go @@ -140,19 +140,15 @@ func sbrk(n uintptr) unsafe.Pointer { return unsafe.Pointer(bl) } -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { +func sysAllocOS(n uintptr) unsafe.Pointer { lock(&memlock) p := memAlloc(n) memCheck() unlock(&memlock) - if p != nil { - sysStat.add(int64(n)) - } return p } -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { lock(&memlock) if uintptr(v)+n == bloc { // Address range being freed is at the end of memory, @@ -167,25 +163,22 @@ func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { unlock(&memlock) } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - // sysReserve has already allocated all heap memory, - // but has not adjusted stats. - sysStat.add(int64(n)) +func sysMapOS(v unsafe.Pointer, n uintptr) { } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { lock(&memlock) var p unsafe.Pointer if uintptr(v) == bloc { diff --git a/src/runtime/mem_windows.go b/src/runtime/mem_windows.go index 3a805b9767..c8f039f50b 100644 --- a/src/runtime/mem_windows.go +++ b/src/runtime/mem_windows.go @@ -24,12 +24,11 @@ const ( // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer { - sysStat.add(int64(n)) +func sysAllocOS(n uintptr) unsafe.Pointer { return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_COMMIT|_MEM_RESERVE, _PAGE_READWRITE)) } -func sysUnused(v unsafe.Pointer, n uintptr) { +func sysUnusedOS(v unsafe.Pointer, n uintptr) { r := stdcall3(_VirtualFree, uintptr(v), n, _MEM_DECOMMIT) if r != 0 { return @@ -59,7 +58,7 @@ func sysUnused(v unsafe.Pointer, n uintptr) { } } -func sysUsed(v unsafe.Pointer, n uintptr) { +func sysUsedOS(v unsafe.Pointer, n uintptr) { p := stdcall4(_VirtualAlloc, uintptr(v), n, _MEM_COMMIT, _PAGE_READWRITE) if p == uintptr(v) { return @@ -91,14 +90,13 @@ func sysUsed(v unsafe.Pointer, n uintptr) { } } -func sysHugePage(v unsafe.Pointer, n uintptr) { +func sysHugePageOS(v unsafe.Pointer, n uintptr) { } // Don't split the stack as this function may be invoked without a valid G, // which prevents us from allocating more stack. //go:nosplit -func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(-int64(n)) +func sysFreeOS(v unsafe.Pointer, n uintptr) { r := stdcall3(_VirtualFree, uintptr(v), 0, _MEM_RELEASE) if r == 0 { print("runtime: VirtualFree of ", n, " bytes failed with errno=", getlasterror(), "\n") @@ -106,12 +104,12 @@ func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { } } -func sysFault(v unsafe.Pointer, n uintptr) { +func sysFaultOS(v unsafe.Pointer, n uintptr) { // SysUnused makes the memory inaccessible and prevents its reuse - sysUnused(v, n) + sysUnusedOS(v, n) } -func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { +func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer { // v is just a hint. // First try at v. // This will fail if any of [v, v+n) is already reserved. @@ -124,6 +122,5 @@ func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer { return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_RESERVE, _PAGE_READWRITE)) } -func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { - sysStat.add(int64(n)) +func sysMapOS(v unsafe.Pointer, n uintptr) { } diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s index 700bbd7b9b..26a6205e61 100644 --- a/src/runtime/memclr_amd64.s +++ b/src/runtime/memclr_amd64.s @@ -6,6 +6,7 @@ #include "go_asm.h" #include "textflag.h" +#include "asm_amd64.h" // See memclrNoHeapPointers Go doc for important implementation constraints. @@ -39,6 +40,8 @@ tail: JBE _65through128 CMPQ BX, $256 JBE _129through256 + +#ifndef hasAVX2 CMPB internal∕cpu·X86+const_offsetX86HasAVX2(SB), $1 JE loop_preheader_avx2 // TODO: for really big clears, use MOVNTDQ, even without AVX2. @@ -65,6 +68,7 @@ loop: CMPQ BX, $256 JAE loop JMP tail +#endif loop_preheader_avx2: VPXOR Y0, Y0, Y0 diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 44b96154e7..ba679e0af5 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -898,15 +898,15 @@ top: // endCycle depends on all gcWork cache stats being flushed. // The termination algorithm above ensured that up to // allocations since the ragged barrier. - nextTriggerRatio := gcController.endCycle(now, int(gomaxprocs), work.userForced) + gcController.endCycle(now, int(gomaxprocs), work.userForced) // Perform mark termination. This will restart the world. - gcMarkTermination(nextTriggerRatio) + gcMarkTermination() } // World must be stopped and mark assists and background workers must be // disabled. -func gcMarkTermination(nextTriggerRatio float64) { +func gcMarkTermination() { // Start marktermination (write barrier remains enabled for now). setGCPhase(_GCmarktermination) @@ -976,7 +976,7 @@ func gcMarkTermination(nextTriggerRatio float64) { memstats.last_heap_inuse = memstats.heap_inuse // Update GC trigger and pacing for the next cycle. - gcController.commit(nextTriggerRatio) + gcController.commit() gcPaceSweeper(gcController.trigger) gcPaceScavenger(gcController.heapGoal, gcController.lastHeapGoal) diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 0bf044e314..3e1a0b560a 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -8,7 +8,6 @@ package runtime import ( "internal/goarch" - "internal/goexperiment" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -247,12 +246,10 @@ func markroot(gcw *gcWork, i uint32, flushBgCredit bool) int64 { } }) } - if goexperiment.PacerRedesign { - if workCounter != nil && workDone != 0 { - workCounter.Add(workDone) - if flushBgCredit { - gcFlushBgCredit(workDone) - } + if workCounter != nil && workDone != 0 { + workCounter.Add(workDone) + if flushBgCredit { + gcFlushBgCredit(workDone) } } return workDone @@ -701,7 +698,6 @@ func gcFlushBgCredit(scanWork int64) { // scanstack scans gp's stack, greying all pointers found on the stack. // -// For goexperiment.PacerRedesign: // Returns the amount of scan work performed, but doesn't update // gcController.stackScanWork or flush any credit. Any background credit produced // by this function should be flushed by its caller. scanstack itself can't @@ -1157,10 +1153,7 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 { if work.markrootNext < work.markrootJobs { job := atomic.Xadd(&work.markrootNext, +1) - 1 if job < work.markrootJobs { - work := markroot(gcw, job, false) - if goexperiment.PacerRedesign { - workFlushed += work - } + workFlushed += markroot(gcw, job, false) continue } } @@ -1558,19 +1551,6 @@ func gcmarknewobject(span *mspan, obj, size, scanSize uintptr) { gcw := &getg().m.p.ptr().gcw gcw.bytesMarked += uint64(size) - if !goexperiment.PacerRedesign { - // The old pacer counts newly allocated memory toward - // heapScanWork because heapScan is continuously updated - // throughout the GC cycle with newly allocated memory. However, - // these objects are never actually scanned, so we need - // to account for them in heapScanWork here, "faking" their work. - // Otherwise the pacer will think it's always behind, potentially - // by a large margin. - // - // The new pacer doesn't care about this because it ceases to updated - // heapScan once a GC cycle starts, effectively snapshotting it. - gcw.heapScanWork += int64(scanSize) - } } // gcMarkTinyAllocs greys all active tiny alloc blocks. diff --git a/src/runtime/mgcpacer.go b/src/runtime/mgcpacer.go index d54dbc26c2..940bc526b4 100644 --- a/src/runtime/mgcpacer.go +++ b/src/runtime/mgcpacer.go @@ -14,8 +14,11 @@ import ( const ( // gcGoalUtilization is the goal CPU utilization for // marking as a fraction of GOMAXPROCS. - gcGoalUtilization = goexperiment.PacerRedesignInt*gcBackgroundUtilization + - (1-goexperiment.PacerRedesignInt)*(gcBackgroundUtilization+0.05) + // + // Increasing the goal utilization will shorten GC cycles as the GC + // has more resources behind it, lessening costs from the write barrier, + // but comes at the cost of increasing mutator latency. + gcGoalUtilization = gcBackgroundUtilization // gcBackgroundUtilization is the fixed CPU utilization for background // marking. It must be <= gcGoalUtilization. The difference between @@ -23,16 +26,14 @@ const ( // mark assists. The scheduler will aim to use within 50% of this // goal. // - // Setting this to < gcGoalUtilization avoids saturating the trigger - // feedback controller when there are no assists, which allows it to - // better control CPU and heap growth. However, the larger the gap, - // the more mutator assists are expected to happen, which impact - // mutator latency. - // - // If goexperiment.PacerRedesign, the trigger feedback controller - // is replaced with an estimate of the mark/cons ratio that doesn't - // have the same saturation issues, so this is set equal to - // gcGoalUtilization. + // As a general rule, there's little reason to set gcBackgroundUtilization + // < gcGoalUtilization. One reason might be in mostly idle applications, + // where goroutines are unlikely to assist at all, so the actual + // utilization will be lower than the goal. But this is moot point + // because the idle mark workers already soak up idle CPU resources. + // These two values are still kept separate however because they are + // distinct conceptually, and in previous iterations of the pacer the + // distinction was more important. gcBackgroundUtilization = 0.25 // gcCreditSlack is the amount of scan work credit that can @@ -72,15 +73,14 @@ func init() { // when to trigger concurrent garbage collection and how much marking // work to do in mutator assists and background marking. // -// It uses a feedback control algorithm to adjust the gcController.trigger -// trigger based on the heap growth and GC CPU utilization each cycle. -// This algorithm optimizes for heap growth to match GOGC and for CPU -// utilization between assist and background marking to be 25% of +// It calculates the ratio between the allocation rate (in terms of CPU +// time) and the GC scan throughput to determine the heap size at which to +// trigger a GC cycle such that no GC assists are required to finish on time. +// This algorithm thus optimizes GC CPU utilization to the dedicated background +// mark utilization of 25% of GOMAXPROCS by minimizing GC assists. // GOMAXPROCS. The high-level design of this algorithm is documented -// at https://golang.org/s/go15gcpacing. -// -// All fields of gcController are used only during a single mark -// cycle. +// at https://github.com/golang/proposal/blob/master/design/44167-gc-pacer-redesign.md. +// See https://golang.org/s/go15gcpacing for additional historical context. var gcController gcControllerState type gcControllerState struct { @@ -104,27 +104,14 @@ type gcControllerState struct { // debugging. heapMinimum uint64 - // triggerRatio is the heap growth ratio that triggers marking. - // - // E.g., if this is 0.6, then GC should start when the live - // heap has reached 1.6 times the heap size marked by the - // previous cycle. This should be ≤ GOGC/100 so the trigger - // heap size is less than the goal heap size. This is set - // during mark termination for the next cycle's trigger. - // - // Protected by mheap_.lock or a STW. - // - // Used if !goexperiment.PacerRedesign. - triggerRatio float64 - // trigger is the heap size that triggers marking. // // When heapLive ≥ trigger, the mark phase will start. // This is also the heap size by which proportional sweeping // must be complete. // - // This is computed from triggerRatio during mark termination - // for the next cycle's trigger. + // This is computed from consMark during mark termination for + // the next cycle's trigger. // // Protected by mheap_.lock or a STW. trigger uint64 @@ -141,8 +128,6 @@ type gcControllerState struct { // cycle, divided by the CPU time spent on each activity. // // Updated at the end of each GC cycle, in endCycle. - // - // For goexperiment.PacerRedesign. consMark float64 // consMarkController holds the state for the mark-cons ratio @@ -150,8 +135,6 @@ type gcControllerState struct { // // Its purpose is to smooth out noisiness in the computation of // consMark; see consMark for details. - // - // For goexperiment.PacerRedesign. consMarkController piController _ uint32 // Padding for atomics on 32-bit platforms. @@ -198,14 +181,9 @@ type gcControllerState struct { // is the live heap (as counted by heapLive), but omitting // no-scan objects and no-scan tails of objects. // - // For !goexperiment.PacerRedesign: Whenever this is updated, - // call this gcControllerState's revise() method. It is read - // and written atomically or with the world stopped. - // - // For goexperiment.PacerRedesign: This value is fixed at the - // start of a GC cycle, so during a GC cycle it is safe to - // read without atomics, and it represents the maximum scannable - // heap. + // This value is fixed at the start of a GC cycle, so during a + // GC cycle it is safe to read without atomics, and it represents + // the maximum scannable heap. heapScan uint64 // lastHeapScan is the number of bytes of heap that were scanned @@ -259,9 +237,6 @@ type gcControllerState struct { // // Note that stackScanWork includes all allocated space, not just the // size of the stack itself, mirroring stackSize. - // - // For !goexperiment.PacerRedesign, stackScanWork and globalsScanWork - // are always zero. heapScanWork atomic.Int64 stackScanWork atomic.Int64 globalsScanWork atomic.Int64 @@ -339,35 +314,25 @@ type gcControllerState struct { func (c *gcControllerState) init(gcPercent int32) { c.heapMinimum = defaultHeapMinimum - if goexperiment.PacerRedesign { - c.consMarkController = piController{ - // Tuned first via the Ziegler-Nichols process in simulation, - // then the integral time was manually tuned against real-world - // applications to deal with noisiness in the measured cons/mark - // ratio. - kp: 0.9, - ti: 4.0, + c.consMarkController = piController{ + // Tuned first via the Ziegler-Nichols process in simulation, + // then the integral time was manually tuned against real-world + // applications to deal with noisiness in the measured cons/mark + // ratio. + kp: 0.9, + ti: 4.0, - // Set a high reset time in GC cycles. - // This is inversely proportional to the rate at which we - // accumulate error from clipping. By making this very high - // we make the accumulation slow. In general, clipping is - // OK in our situation, hence the choice. - // - // Tune this if we get unintended effects from clipping for - // a long time. - tt: 1000, - min: -1000, - max: 1000, - } - } else { - // Set a reasonable initial GC trigger. - c.triggerRatio = 7 / 8.0 - - // Fake a heapMarked value so it looks like a trigger at - // heapMinimum is the appropriate growth from heapMarked. - // This will go into computing the initial GC goal. - c.heapMarked = uint64(float64(c.heapMinimum) / (1 + c.triggerRatio)) + // Set a high reset time in GC cycles. + // This is inversely proportional to the rate at which we + // accumulate error from clipping. By making this very high + // we make the accumulation slow. In general, clipping is + // OK in our situation, hence the choice. + // + // Tune this if we get unintended effects from clipping for + // a long time. + tt: 1000, + min: -1000, + max: 1000, } // This will also compute and set the GC trigger and goal. @@ -396,14 +361,8 @@ func (c *gcControllerState) startCycle(markStartTime int64, procs int) { // GOGC. Assist is proportional to this distance, so enforce a // minimum distance, even if it means going over the GOGC goal // by a tiny bit. - if goexperiment.PacerRedesign { - if c.heapGoal < c.heapLive+64<<10 { - c.heapGoal = c.heapLive + 64<<10 - } - } else { - if c.heapGoal < c.heapLive+1<<20 { - c.heapGoal = c.heapLive + 1<<20 - } + if c.heapGoal < c.heapLive+64<<10 { + c.heapGoal = c.heapLive + 64<<10 } // Compute the background mark utilization goal. In general, @@ -492,74 +451,50 @@ func (c *gcControllerState) revise() { // heapGoal assuming the heap is in steady-state. heapGoal := int64(atomic.Load64(&c.heapGoal)) - var scanWorkExpected int64 - if goexperiment.PacerRedesign { - // The expected scan work is computed as the amount of bytes scanned last - // GC cycle, plus our estimate of stacks and globals work for this cycle. - scanWorkExpected = int64(c.lastHeapScan + c.stackScan + c.globalsScan) + // The expected scan work is computed as the amount of bytes scanned last + // GC cycle, plus our estimate of stacks and globals work for this cycle. + scanWorkExpected := int64(c.lastHeapScan + c.stackScan + c.globalsScan) - // maxScanWork is a worst-case estimate of the amount of scan work that - // needs to be performed in this GC cycle. Specifically, it represents - // the case where *all* scannable memory turns out to be live. - maxScanWork := int64(scan + c.stackScan + c.globalsScan) - if work > scanWorkExpected { - // We've already done more scan work than expected. Because our expectation - // is based on a steady-state scannable heap size, we assume this means our - // heap is growing. Compute a new heap goal that takes our existing runway - // computed for scanWorkExpected and extrapolates it to maxScanWork, the worst-case - // scan work. This keeps our assist ratio stable if the heap continues to grow. - // - // The effect of this mechanism is that assists stay flat in the face of heap - // growths. It's OK to use more memory this cycle to scan all the live heap, - // because the next GC cycle is inevitably going to use *at least* that much - // memory anyway. - extHeapGoal := int64(float64(heapGoal-int64(c.trigger))/float64(scanWorkExpected)*float64(maxScanWork)) + int64(c.trigger) - scanWorkExpected = maxScanWork - - // hardGoal is a hard limit on the amount that we're willing to push back the - // heap goal, and that's twice the heap goal (i.e. if GOGC=100 and the heap and/or - // stacks and/or globals grow to twice their size, this limits the current GC cycle's - // growth to 4x the original live heap's size). - // - // This maintains the invariant that we use no more memory than the next GC cycle - // will anyway. - hardGoal := int64((1.0 + float64(gcPercent)/100.0) * float64(heapGoal)) - if extHeapGoal > hardGoal { - extHeapGoal = hardGoal - } - heapGoal = extHeapGoal - } - if int64(live) > heapGoal { - // We're already past our heap goal, even the extrapolated one. - // Leave ourselves some extra runway, so in the worst case we - // finish by that point. - const maxOvershoot = 1.1 - heapGoal = int64(float64(heapGoal) * maxOvershoot) - - // Compute the upper bound on the scan work remaining. - scanWorkExpected = maxScanWork - } - } else { - // Compute the expected scan work remaining. + // maxScanWork is a worst-case estimate of the amount of scan work that + // needs to be performed in this GC cycle. Specifically, it represents + // the case where *all* scannable memory turns out to be live. + maxScanWork := int64(scan + c.stackScan + c.globalsScan) + if work > scanWorkExpected { + // We've already done more scan work than expected. Because our expectation + // is based on a steady-state scannable heap size, we assume this means our + // heap is growing. Compute a new heap goal that takes our existing runway + // computed for scanWorkExpected and extrapolates it to maxScanWork, the worst-case + // scan work. This keeps our assist ratio stable if the heap continues to grow. // - // This is estimated based on the expected - // steady-state scannable heap. For example, with - // GOGC=100, only half of the scannable heap is - // expected to be live, so that's what we target. - // - // (This is a float calculation to avoid overflowing on - // 100*heapScan.) - scanWorkExpected = int64(float64(scan) * 100 / float64(100+gcPercent)) - if int64(live) > heapGoal || work > scanWorkExpected { - // We're past the soft goal, or we've already done more scan - // work than we expected. Pace GC so that in the worst case it - // will complete by the hard goal. - const maxOvershoot = 1.1 - heapGoal = int64(float64(heapGoal) * maxOvershoot) + // The effect of this mechanism is that assists stay flat in the face of heap + // growths. It's OK to use more memory this cycle to scan all the live heap, + // because the next GC cycle is inevitably going to use *at least* that much + // memory anyway. + extHeapGoal := int64(float64(heapGoal-int64(c.trigger))/float64(scanWorkExpected)*float64(maxScanWork)) + int64(c.trigger) + scanWorkExpected = maxScanWork - // Compute the upper bound on the scan work remaining. - scanWorkExpected = int64(scan) + // hardGoal is a hard limit on the amount that we're willing to push back the + // heap goal, and that's twice the heap goal (i.e. if GOGC=100 and the heap and/or + // stacks and/or globals grow to twice their size, this limits the current GC cycle's + // growth to 4x the original live heap's size). + // + // This maintains the invariant that we use no more memory than the next GC cycle + // will anyway. + hardGoal := int64((1.0 + float64(gcPercent)/100.0) * float64(heapGoal)) + if extHeapGoal > hardGoal { + extHeapGoal = hardGoal } + heapGoal = extHeapGoal + } + if int64(live) > heapGoal { + // We're already past our heap goal, even the extrapolated one. + // Leave ourselves some extra runway, so in the worst case we + // finish by that point. + const maxOvershoot = 1.1 + heapGoal = int64(float64(heapGoal) * maxOvershoot) + + // Compute the upper bound on the scan work remaining. + scanWorkExpected = maxScanWork } // Compute the remaining scan work estimate. @@ -604,12 +539,10 @@ func (c *gcControllerState) revise() { c.assistBytesPerWork.Store(assistBytesPerWork) } -// endCycle computes the trigger ratio (!goexperiment.PacerRedesign) -// or the consMark estimate (goexperiment.PacerRedesign) for the next cycle. -// Returns the trigger ratio if application, or 0 (goexperiment.PacerRedesign). +// endCycle computes the consMark estimate for the next cycle. // userForced indicates whether the current GC cycle was forced // by the application. -func (c *gcControllerState) endCycle(now int64, procs int, userForced bool) float64 { +func (c *gcControllerState) endCycle(now int64, procs int, userForced bool) { // Record last heap goal for the scavenger. // We'll be updating the heap goal soon. gcController.lastHeapGoal = gcController.heapGoal @@ -624,155 +557,91 @@ func (c *gcControllerState) endCycle(now int64, procs int, userForced bool) floa utilization += float64(c.assistTime) / float64(assistDuration*int64(procs)) } - if goexperiment.PacerRedesign { - if c.heapLive <= c.trigger { - // Shouldn't happen, but let's be very safe about this in case the - // GC is somehow extremely short. - // - // In this case though, the only reasonable value for c.heapLive-c.trigger - // would be 0, which isn't really all that useful, i.e. the GC was so short - // that it didn't matter. - // - // Ignore this case and don't update anything. - return 0 - } - idleUtilization := 0.0 - if assistDuration > 0 { - idleUtilization = float64(c.idleMarkTime) / float64(assistDuration*int64(procs)) - } - // Determine the cons/mark ratio. + if c.heapLive <= c.trigger { + // Shouldn't happen, but let's be very safe about this in case the + // GC is somehow extremely short. // - // The units we want for the numerator and denominator are both B / cpu-ns. - // We get this by taking the bytes allocated or scanned, and divide by the amount of - // CPU time it took for those operations. For allocations, that CPU time is + // In this case though, the only reasonable value for c.heapLive-c.trigger + // would be 0, which isn't really all that useful, i.e. the GC was so short + // that it didn't matter. // - // assistDuration * procs * (1 - utilization) - // - // Where utilization includes just background GC workers and assists. It does *not* - // include idle GC work time, because in theory the mutator is free to take that at - // any point. - // - // For scanning, that CPU time is - // - // assistDuration * procs * (utilization + idleUtilization) - // - // In this case, we *include* idle utilization, because that is additional CPU time that the - // the GC had available to it. - // - // In effect, idle GC time is sort of double-counted here, but it's very weird compared - // to other kinds of GC work, because of how fluid it is. Namely, because the mutator is - // *always* free to take it. - // - // So this calculation is really: - // (heapLive-trigger) / (assistDuration * procs * (1-utilization)) / - // (scanWork) / (assistDuration * procs * (utilization+idleUtilization) - // - // Note that because we only care about the ratio, assistDuration and procs cancel out. - scanWork := c.heapScanWork.Load() + c.stackScanWork.Load() + c.globalsScanWork.Load() - currentConsMark := (float64(c.heapLive-c.trigger) * (utilization + idleUtilization)) / - (float64(scanWork) * (1 - utilization)) - - // Update cons/mark controller. The time period for this is 1 GC cycle. - // - // This use of a PI controller might seem strange. So, here's an explanation: - // - // currentConsMark represents the consMark we *should've* had to be perfectly - // on-target for this cycle. Given that we assume the next GC will be like this - // one in the steady-state, it stands to reason that we should just pick that - // as our next consMark. In practice, however, currentConsMark is too noisy: - // we're going to be wildly off-target in each GC cycle if we do that. - // - // What we do instead is make a long-term assumption: there is some steady-state - // consMark value, but it's obscured by noise. By constantly shooting for this - // noisy-but-perfect consMark value, the controller will bounce around a bit, - // but its average behavior, in aggregate, should be less noisy and closer to - // the true long-term consMark value, provided its tuned to be slightly overdamped. - var ok bool - oldConsMark := c.consMark - c.consMark, ok = c.consMarkController.next(c.consMark, currentConsMark, 1.0) - if !ok { - // The error spiraled out of control. This is incredibly unlikely seeing - // as this controller is essentially just a smoothing function, but it might - // mean that something went very wrong with how currentConsMark was calculated. - // Just reset consMark and keep going. - c.consMark = 0 - } - - if debug.gcpacertrace > 0 { - printlock() - goal := gcGoalUtilization * 100 - print("pacer: ", int(utilization*100), "% CPU (", int(goal), " exp.) for ") - print(c.heapScanWork.Load(), "+", c.stackScanWork.Load(), "+", c.globalsScanWork.Load(), " B work (", c.lastHeapScan+c.stackScan+c.globalsScan, " B exp.) ") - print("in ", c.trigger, " B -> ", c.heapLive, " B (∆goal ", int64(c.heapLive)-int64(c.heapGoal), ", cons/mark ", oldConsMark, ")") - if !ok { - print("[controller reset]") - } - println() - printunlock() - } - return 0 + // Ignore this case and don't update anything. + return } - - // !goexperiment.PacerRedesign below. - - if userForced { - // Forced GC means this cycle didn't start at the - // trigger, so where it finished isn't good - // information about how to adjust the trigger. - // Just leave it where it is. - return c.triggerRatio + idleUtilization := 0.0 + if assistDuration > 0 { + idleUtilization = float64(c.idleMarkTime) / float64(assistDuration*int64(procs)) } + // Determine the cons/mark ratio. + // + // The units we want for the numerator and denominator are both B / cpu-ns. + // We get this by taking the bytes allocated or scanned, and divide by the amount of + // CPU time it took for those operations. For allocations, that CPU time is + // + // assistDuration * procs * (1 - utilization) + // + // Where utilization includes just background GC workers and assists. It does *not* + // include idle GC work time, because in theory the mutator is free to take that at + // any point. + // + // For scanning, that CPU time is + // + // assistDuration * procs * (utilization + idleUtilization) + // + // In this case, we *include* idle utilization, because that is additional CPU time that the + // the GC had available to it. + // + // In effect, idle GC time is sort of double-counted here, but it's very weird compared + // to other kinds of GC work, because of how fluid it is. Namely, because the mutator is + // *always* free to take it. + // + // So this calculation is really: + // (heapLive-trigger) / (assistDuration * procs * (1-utilization)) / + // (scanWork) / (assistDuration * procs * (utilization+idleUtilization) + // + // Note that because we only care about the ratio, assistDuration and procs cancel out. + scanWork := c.heapScanWork.Load() + c.stackScanWork.Load() + c.globalsScanWork.Load() + currentConsMark := (float64(c.heapLive-c.trigger) * (utilization + idleUtilization)) / + (float64(scanWork) * (1 - utilization)) - // Proportional response gain for the trigger controller. Must - // be in [0, 1]. Lower values smooth out transient effects but - // take longer to respond to phase changes. Higher values - // react to phase changes quickly, but are more affected by - // transient changes. Values near 1 may be unstable. - const triggerGain = 0.5 - - // Compute next cycle trigger ratio. First, this computes the - // "error" for this cycle; that is, how far off the trigger - // was from what it should have been, accounting for both heap - // growth and GC CPU utilization. We compute the actual heap - // growth during this cycle and scale that by how far off from - // the goal CPU utilization we were (to estimate the heap - // growth if we had the desired CPU utilization). The - // difference between this estimate and the GOGC-based goal - // heap growth is the error. - goalGrowthRatio := c.effectiveGrowthRatio() - actualGrowthRatio := float64(c.heapLive)/float64(c.heapMarked) - 1 - triggerError := goalGrowthRatio - c.triggerRatio - utilization/gcGoalUtilization*(actualGrowthRatio-c.triggerRatio) - - // Finally, we adjust the trigger for next time by this error, - // damped by the proportional gain. - triggerRatio := c.triggerRatio + triggerGain*triggerError + // Update cons/mark controller. The time period for this is 1 GC cycle. + // + // This use of a PI controller might seem strange. So, here's an explanation: + // + // currentConsMark represents the consMark we *should've* had to be perfectly + // on-target for this cycle. Given that we assume the next GC will be like this + // one in the steady-state, it stands to reason that we should just pick that + // as our next consMark. In practice, however, currentConsMark is too noisy: + // we're going to be wildly off-target in each GC cycle if we do that. + // + // What we do instead is make a long-term assumption: there is some steady-state + // consMark value, but it's obscured by noise. By constantly shooting for this + // noisy-but-perfect consMark value, the controller will bounce around a bit, + // but its average behavior, in aggregate, should be less noisy and closer to + // the true long-term consMark value, provided its tuned to be slightly overdamped. + var ok bool + oldConsMark := c.consMark + c.consMark, ok = c.consMarkController.next(c.consMark, currentConsMark, 1.0) + if !ok { + // The error spiraled out of control. This is incredibly unlikely seeing + // as this controller is essentially just a smoothing function, but it might + // mean that something went very wrong with how currentConsMark was calculated. + // Just reset consMark and keep going. + c.consMark = 0 + } if debug.gcpacertrace > 0 { - // Print controller state in terms of the design - // document. - H_m_prev := c.heapMarked - h_t := c.triggerRatio - H_T := c.trigger - h_a := actualGrowthRatio - H_a := c.heapLive - h_g := goalGrowthRatio - H_g := int64(float64(H_m_prev) * (1 + h_g)) - u_a := utilization - u_g := gcGoalUtilization - W_a := c.heapScanWork.Load() - print("pacer: H_m_prev=", H_m_prev, - " h_t=", h_t, " H_T=", H_T, - " h_a=", h_a, " H_a=", H_a, - " h_g=", h_g, " H_g=", H_g, - " u_a=", u_a, " u_g=", u_g, - " W_a=", W_a, - " goalΔ=", goalGrowthRatio-h_t, - " actualΔ=", h_a-h_t, - " u_a/u_g=", u_a/u_g, - "\n") + printlock() + goal := gcGoalUtilization * 100 + print("pacer: ", int(utilization*100), "% CPU (", int(goal), " exp.) for ") + print(c.heapScanWork.Load(), "+", c.stackScanWork.Load(), "+", c.globalsScanWork.Load(), " B work (", c.lastHeapScan+c.stackScan+c.globalsScan, " B exp.) ") + print("in ", c.trigger, " B -> ", c.heapLive, " B (∆goal ", int64(c.heapLive)-int64(c.heapGoal), ", cons/mark ", oldConsMark, ")") + if !ok { + print("[controller reset]") + } + println() + printunlock() } - - return triggerRatio } // enlistWorker encourages another dedicated mark worker to start on @@ -938,15 +807,14 @@ func (c *gcControllerState) update(dHeapLive, dHeapScan int64) { traceHeapAlloc() } } - // Only update heapScan in the new pacer redesign if we're not - // currently in a GC. - if !goexperiment.PacerRedesign || gcBlackenEnabled == 0 { + if gcBlackenEnabled == 0 { + // Update heapScan when we're not in a current GC. It is fixed + // at the beginning of a cycle. if dHeapScan != 0 { atomic.Xadd64(&gcController.heapScan, dHeapScan) } - } - if gcBlackenEnabled != 0 { - // gcController.heapLive and heapScan changed. + } else { + // gcController.heapLive changed. c.revise() } } @@ -970,8 +838,6 @@ func (c *gcControllerState) addGlobals(amount int64) { // commit recomputes all pacing parameters from scratch, namely // absolute trigger, the heap goal, mark pacing, and sweep pacing. // -// If goexperiment.PacerRedesign is true, triggerRatio is ignored. -// // This can be called any time. If GC is the in the middle of a // concurrent phase, it will adjust the pacing of that phase. // @@ -979,16 +845,11 @@ func (c *gcControllerState) addGlobals(amount int64) { // gcController.heapLive. These must be up to date. // // mheap_.lock must be held or the world must be stopped. -func (c *gcControllerState) commit(triggerRatio float64) { +func (c *gcControllerState) commit() { if !c.test { assertWorldStoppedOrLockHeld(&mheap_.lock) } - if !goexperiment.PacerRedesign { - c.oldCommit(triggerRatio) - return - } - // Compute the next GC goal, which is when the allocated heap // has grown by GOGC/100 over where it started the last cycle, // plus additional runway for non-heap sources of GC work. @@ -1096,113 +957,6 @@ func (c *gcControllerState) commit(triggerRatio float64) { } } -// oldCommit sets the trigger ratio and updates everything -// derived from it: the absolute trigger, the heap goal, mark pacing, -// and sweep pacing. -// -// This can be called any time. If GC is the in the middle of a -// concurrent phase, it will adjust the pacing of that phase. -// -// This depends on gcPercent, gcController.heapMarked, and -// gcController.heapLive. These must be up to date. -// -// For !goexperiment.PacerRedesign. -func (c *gcControllerState) oldCommit(triggerRatio float64) { - gcPercent := c.gcPercent.Load() - - // Compute the next GC goal, which is when the allocated heap - // has grown by GOGC/100 over the heap marked by the last - // cycle. - goal := ^uint64(0) - if gcPercent >= 0 { - goal = c.heapMarked + c.heapMarked*uint64(gcPercent)/100 - } - - // Set the trigger ratio, capped to reasonable bounds. - if gcPercent >= 0 { - scalingFactor := float64(gcPercent) / 100 - // Ensure there's always a little margin so that the - // mutator assist ratio isn't infinity. - maxTriggerRatio := 0.95 * scalingFactor - if triggerRatio > maxTriggerRatio { - triggerRatio = maxTriggerRatio - } - - // If we let triggerRatio go too low, then if the application - // is allocating very rapidly we might end up in a situation - // where we're allocating black during a nearly always-on GC. - // The result of this is a growing heap and ultimately an - // increase in RSS. By capping us at a point >0, we're essentially - // saying that we're OK using more CPU during the GC to prevent - // this growth in RSS. - // - // The current constant was chosen empirically: given a sufficiently - // fast/scalable allocator with 48 Ps that could drive the trigger ratio - // to <0.05, this constant causes applications to retain the same peak - // RSS compared to not having this allocator. - minTriggerRatio := 0.6 * scalingFactor - if triggerRatio < minTriggerRatio { - triggerRatio = minTriggerRatio - } - } else if triggerRatio < 0 { - // gcPercent < 0, so just make sure we're not getting a negative - // triggerRatio. This case isn't expected to happen in practice, - // and doesn't really matter because if gcPercent < 0 then we won't - // ever consume triggerRatio further on in this function, but let's - // just be defensive here; the triggerRatio being negative is almost - // certainly undesirable. - triggerRatio = 0 - } - c.triggerRatio = triggerRatio - - // Compute the absolute GC trigger from the trigger ratio. - // - // We trigger the next GC cycle when the allocated heap has - // grown by the trigger ratio over the marked heap size. - trigger := ^uint64(0) - if gcPercent >= 0 { - trigger = uint64(float64(c.heapMarked) * (1 + triggerRatio)) - // Don't trigger below the minimum heap size. - minTrigger := c.heapMinimum - if !isSweepDone() { - // Concurrent sweep happens in the heap growth - // from gcController.heapLive to trigger, so ensure - // that concurrent sweep has some heap growth - // in which to perform sweeping before we - // start the next GC cycle. - sweepMin := atomic.Load64(&c.heapLive) + sweepMinHeapDistance - if sweepMin > minTrigger { - minTrigger = sweepMin - } - } - if trigger < minTrigger { - trigger = minTrigger - } - if int64(trigger) < 0 { - print("runtime: heapGoal=", c.heapGoal, " heapMarked=", c.heapMarked, " gcController.heapLive=", c.heapLive, " initialHeapLive=", work.initialHeapLive, "triggerRatio=", triggerRatio, " minTrigger=", minTrigger, "\n") - throw("trigger underflow") - } - if trigger > goal { - // The trigger ratio is always less than GOGC/100, but - // other bounds on the trigger may have raised it. - // Push up the goal, too. - goal = trigger - } - } - - // Commit to the trigger and goal. - c.trigger = trigger - atomic.Store64(&c.heapGoal, goal) - if trace.enabled { - traceHeapGoal() - } - - // Update mark pacing. - if gcphase != _GCoff { - c.revise() - } -} - // effectiveGrowthRatio returns the current effective heap growth // ratio (GOGC/100) based on heapMarked from the previous GC and // heapGoal for the current GC. @@ -1243,7 +997,7 @@ func (c *gcControllerState) setGCPercent(in int32) int32 { c.heapMinimum = defaultHeapMinimum * uint64(in) / 100 c.gcPercent.Store(in) // Update pacing in response to gcPercent change. - c.commit(c.triggerRatio) + c.commit() return out } diff --git a/src/runtime/mgcpacer_test.go b/src/runtime/mgcpacer_test.go index 10a8ca2520..b49e3a8d24 100644 --- a/src/runtime/mgcpacer_test.go +++ b/src/runtime/mgcpacer_test.go @@ -6,7 +6,6 @@ package runtime_test import ( "fmt" - "internal/goexperiment" "math" "math/rand" . "runtime" @@ -35,11 +34,9 @@ func TestGcPacer(t *testing.T) { checker: func(t *testing.T, c []gcCycleResult) { n := len(c) if n >= 25 { - if goexperiment.PacerRedesign { - // For the pacer redesign, assert something even stronger: at this alloc/scan rate, - // it should be extremely close to the goal utilization. - assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) - } + // For the pacer redesign, assert something even stronger: at this alloc/scan rate, + // it should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) @@ -64,12 +61,10 @@ func TestGcPacer(t *testing.T) { // really handle this well, so don't check the goal ratio for it. n := len(c) if n >= 25 { - if goexperiment.PacerRedesign { - // For the pacer redesign, assert something even stronger: at this alloc/scan rate, - // it should be extremely close to the goal utilization. - assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) - assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) - } + // For the pacer redesign, assert something even stronger: at this alloc/scan rate, + // it should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) @@ -93,12 +88,10 @@ func TestGcPacer(t *testing.T) { // really handle this well, so don't check the goal ratio for it. n := len(c) if n >= 25 { - if goexperiment.PacerRedesign { - // For the pacer redesign, assert something even stronger: at this alloc/scan rate, - // it should be extremely close to the goal utilization. - assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) - assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) - } + // For the pacer redesign, assert something even stronger: at this alloc/scan rate, + // it should be extremely close to the goal utilization. + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, GCGoalUtilization, 0.005) + assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) // Make sure the pacer settles into a non-degenerate state in at least 25 GC cycles. assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.005) @@ -187,7 +180,7 @@ func TestGcPacer(t *testing.T) { length: 50, checker: func(t *testing.T, c []gcCycleResult) { n := len(c) - if goexperiment.PacerRedesign && n > 12 { + if n > 12 { if n == 26 { // In the 26th cycle there's a heap growth. Overshoot is expected to maintain // a stable utilization, but we should *never* overshoot more than GOGC of @@ -232,12 +225,7 @@ func TestGcPacer(t *testing.T) { // 1. Utilization isn't varying _too_ much, and // 2. The pacer is mostly keeping up with the goal. assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) - if goexperiment.PacerRedesign { - assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.3) - } else { - // The old pacer is messier here, and needs a lot more tolerance. - assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.4) - } + assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.3) } }, }, @@ -260,12 +248,7 @@ func TestGcPacer(t *testing.T) { // 1. Utilization isn't varying _too_ much, and // 2. The pacer is mostly keeping up with the goal. assertInRange(t, "goal ratio", c[n-1].goalRatio(), 0.95, 1.05) - if goexperiment.PacerRedesign { - assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.3) - } else { - // The old pacer is messier here, and needs a lot more tolerance. - assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.4) - } + assertInRange(t, "GC utilization", c[n-1].gcUtilization, 0.25, 0.3) } }, }, @@ -293,12 +276,7 @@ func TestGcPacer(t *testing.T) { // Unlike the other tests, GC utilization here will vary more and tend higher. // Just make sure it's not going too crazy. assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[n-2].gcUtilization, 0.05) - if goexperiment.PacerRedesign { - assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[11].gcUtilization, 0.05) - } else { - // The old pacer is messier here, and needs a little more tolerance. - assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[11].gcUtilization, 0.07) - } + assertInEpsilon(t, "GC utilization", c[n-1].gcUtilization, c[11].gcUtilization, 0.05) } }, }, diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index ecbd0a3a49..d2a63d0938 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -319,16 +319,16 @@ type arenaHint struct { // mSpanManual, or mSpanFree. Transitions between these states are // constrained as follows: // -// * A span may transition from free to in-use or manual during any GC -// phase. +// * A span may transition from free to in-use or manual during any GC +// phase. // -// * During sweeping (gcphase == _GCoff), a span may transition from -// in-use to free (as a result of sweeping) or manual to free (as a -// result of stacks being freed). +// * During sweeping (gcphase == _GCoff), a span may transition from +// in-use to free (as a result of sweeping) or manual to free (as a +// result of stacks being freed). // -// * During GC (gcphase != _GCoff), a span *must not* transition from -// manual or in-use to free. Because concurrent GC may read a pointer -// and then look up its span, the span state must be monotonic. +// * During GC (gcphase != _GCoff), a span *must not* transition from +// manual or in-use to free. Because concurrent GC may read a pointer +// and then look up its span, the span state must be monotonic. // // Setting mspan.state to mSpanInUse or mSpanManual must be done // atomically and only after all other span fields are valid. @@ -1706,7 +1706,7 @@ func spanHasNoSpecials(s *mspan) { // offset & next, which this routine will fill in. // Returns true if the special was successfully added, false otherwise. // (The add will fail only if a record with the same p and s->kind -// already exists.) +// already exists.) func addspecial(p unsafe.Pointer, s *special) bool { span := spanOfHeap(uintptr(p)) if span == nil { diff --git a/src/runtime/mkduff.go b/src/runtime/mkduff.go index e1c01fffce..f1e4ed75d0 100644 --- a/src/runtime/mkduff.go +++ b/src/runtime/mkduff.go @@ -237,7 +237,7 @@ func zeroRISCV64(w io.Writer) { // ZERO: always zero // X25: ptr to memory to be zeroed // X25 is updated as a side effect. - fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") + fmt.Fprintln(w, "TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0") for i := 0; i < 128; i++ { fmt.Fprintln(w, "\tMOV\tZERO, (X25)") fmt.Fprintln(w, "\tADD\t$8, X25") @@ -249,7 +249,7 @@ func copyRISCV64(w io.Writer) { // X24: ptr to source memory // X25: ptr to destination memory // X24 and X25 are updated as a side effect - fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") + fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0") for i := 0; i < 128; i++ { fmt.Fprintln(w, "\tMOV\t(X24), X31") fmt.Fprintln(w, "\tADD\t$8, X24") diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index c2303e5b8e..e5c3471ca3 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -20,43 +20,31 @@ import ( // Many of these fields are updated on the fly, while others are only // updated when updatememstats is called. type mstats struct { - // General statistics. - alloc uint64 // bytes allocated and not yet freed - total_alloc uint64 // bytes allocated (even if freed) - sys uint64 // bytes obtained from system (should be sum of xxx_sys below, no locking, approximate) - nlookup uint64 // number of pointer lookups (unused) - nmalloc uint64 // number of mallocs - nfree uint64 // number of frees - // Statistics about malloc heap. - // Updated atomically, or with the world stopped. + + heapStats consistentHeapStats + + // These stats are effectively duplicates of fields from heapStats + // but are updated atomically or with the world stopped and don't + // provide the same consistency guarantees. They are used internally + // by the runtime. // // Like MemStats, heap_sys and heap_inuse do not count memory // in manually-managed spans. heap_sys sysMemStat // virtual address space obtained from system for GC'd heap heap_inuse uint64 // bytes in mSpanInUse spans - heap_released uint64 // bytes released to the os - - // heap_objects is not used by the runtime directly and instead - // computed on the fly by updatememstats. - heap_objects uint64 // total number of allocated objects + heap_released uint64 // bytes released to the OS // Statistics about stacks. - stacks_inuse uint64 // bytes in manually-managed stack spans; computed by updatememstats - stacks_sys sysMemStat // only counts newosproc0 stack in mstats; differs from MemStats.StackSys + stacks_sys sysMemStat // only counts newosproc0 stack in mstats; differs from MemStats.StackSys // Statistics about allocation of low-level fixed-size structures. - // Protected by FixAlloc locks. - mspan_inuse uint64 // mspan structures mspan_sys sysMemStat - mcache_inuse uint64 // mcache structures mcache_sys sysMemStat buckhash_sys sysMemStat // profiling bucket hash table // Statistics about GC overhead. - gcWorkBufInUse uint64 // computed by updatememstats - gcProgPtrScalarBitsInUse uint64 // computed by updatememstats - gcMiscSys sysMemStat // updated atomically or during STW + gcMiscSys sysMemStat // updated atomically or during STW // Miscellaneous statistics. other_sys sysMemStat // updated atomically or during STW @@ -71,28 +59,13 @@ type mstats struct { numgc uint32 numforcedgc uint32 // number of user-forced GCs gc_cpu_fraction float64 // fraction of CPU time used by GC - enablegc bool - debuggc bool - - // Statistics about allocation size classes. - - by_size [_NumSizeClasses]struct { - size uint32 - nmalloc uint64 - nfree uint64 - } - - // Add an uint32 for even number of size classes to align below fields - // to 64 bits for atomic operations on 32 bit platforms. - _ [1 - _NumSizeClasses%2]uint32 last_gc_nanotime uint64 // last gc (monotonic time) last_heap_inuse uint64 // heap_inuse at mark termination of the previous GC - // heapStats is a set of statistics - heapStats consistentHeapStats + enablegc bool - // _ uint32 // ensure gcPauseDist is aligned + _ uint32 // ensure gcPauseDist is aligned. // gcPauseDist represents the distribution of all GC-related // application pauses in the runtime. @@ -409,15 +382,113 @@ func ReadMemStats(m *MemStats) { startTheWorld() } +// readmemstats_m populates stats for internal runtime values. +// +// The world must be stopped. func readmemstats_m(stats *MemStats) { - updatememstats() + assertWorldStopped() - stats.Alloc = memstats.alloc - stats.TotalAlloc = memstats.total_alloc - stats.Sys = memstats.sys - stats.Mallocs = memstats.nmalloc - stats.Frees = memstats.nfree - stats.HeapAlloc = memstats.alloc + // Flush mcaches to mcentral before doing anything else. + // + // Flushing to the mcentral may in general cause stats to + // change as mcentral data structures are manipulated. + systemstack(flushallmcaches) + + // Calculate memory allocator stats. + // During program execution we only count number of frees and amount of freed memory. + // Current number of alive objects in the heap and amount of alive heap memory + // are calculated by scanning all spans. + // Total number of mallocs is calculated as number of frees plus number of alive objects. + // Similarly, total amount of allocated memory is calculated as amount of freed memory + // plus amount of alive heap memory. + + // Collect consistent stats, which are the source-of-truth in some cases. + var consStats heapStatsDelta + memstats.heapStats.unsafeRead(&consStats) + + // Collect large allocation stats. + totalAlloc := uint64(consStats.largeAlloc) + nMalloc := uint64(consStats.largeAllocCount) + totalFree := uint64(consStats.largeFree) + nFree := uint64(consStats.largeFreeCount) + + // Collect per-sizeclass stats. + var bySize [_NumSizeClasses]struct { + Size uint32 + Mallocs uint64 + Frees uint64 + } + for i := range bySize { + bySize[i].Size = uint32(class_to_size[i]) + + // Malloc stats. + a := uint64(consStats.smallAllocCount[i]) + totalAlloc += a * uint64(class_to_size[i]) + nMalloc += a + bySize[i].Mallocs = a + + // Free stats. + f := uint64(consStats.smallFreeCount[i]) + totalFree += f * uint64(class_to_size[i]) + nFree += f + bySize[i].Frees = f + } + + // Account for tiny allocations. + // For historical reasons, MemStats includes tiny allocations + // in both the total free and total alloc count. This double-counts + // memory in some sense because their tiny allocation block is also + // counted. Tracking the lifetime of individual tiny allocations is + // currently not done because it would be too expensive. + nFree += uint64(consStats.tinyAllocCount) + nMalloc += uint64(consStats.tinyAllocCount) + + // Calculate derived stats. + + stackInUse := uint64(consStats.inStacks) + gcWorkBufInUse := uint64(consStats.inWorkBufs) + gcProgPtrScalarBitsInUse := uint64(consStats.inPtrScalarBits) + + // The world is stopped, so the consistent stats (after aggregation) + // should be identical to some combination of memstats. In particular: + // + // * heap_inuse == inHeap + // * heap_released == released + // * heap_sys - heap_released == committed - inStacks - inWorkBufs - inPtrScalarBits + // + // Check if that's actually true. + // + // TODO(mknyszek): Maybe don't throw here. It would be bad if a + // bug in otherwise benign accounting caused the whole application + // to crash. + if memstats.heap_inuse != uint64(consStats.inHeap) { + print("runtime: heap_inuse=", memstats.heap_inuse, "\n") + print("runtime: consistent value=", consStats.inHeap, "\n") + throw("heap_inuse and consistent stats are not equal") + } + if memstats.heap_released != uint64(consStats.released) { + print("runtime: heap_released=", memstats.heap_released, "\n") + print("runtime: consistent value=", consStats.released, "\n") + throw("heap_released and consistent stats are not equal") + } + globalRetained := memstats.heap_sys.load() - memstats.heap_released + consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) + if globalRetained != consRetained { + print("runtime: global value=", globalRetained, "\n") + print("runtime: consistent value=", consRetained, "\n") + throw("measures of the retained heap are not equal") + } + + // We've calculated all the values we need. Now, populate stats. + + stats.Alloc = totalAlloc - totalFree + stats.TotalAlloc = totalAlloc + stats.Sys = memstats.heap_sys.load() + memstats.stacks_sys.load() + memstats.mspan_sys.load() + + memstats.mcache_sys.load() + memstats.buckhash_sys.load() + memstats.gcMiscSys.load() + + memstats.other_sys.load() + stackInUse + gcWorkBufInUse + gcProgPtrScalarBitsInUse + stats.Mallocs = nMalloc + stats.Frees = nFree + stats.HeapAlloc = totalAlloc - totalFree stats.HeapSys = memstats.heap_sys.load() // By definition, HeapIdle is memory that was mapped // for the heap but is not currently used to hold heap @@ -438,20 +509,20 @@ func readmemstats_m(stats *MemStats) { stats.HeapIdle = memstats.heap_sys.load() - memstats.heap_inuse stats.HeapInuse = memstats.heap_inuse stats.HeapReleased = memstats.heap_released - stats.HeapObjects = memstats.heap_objects - stats.StackInuse = memstats.stacks_inuse + stats.HeapObjects = nMalloc - nFree + stats.StackInuse = stackInUse // memstats.stacks_sys is only memory mapped directly for OS stacks. // Add in heap-allocated stack memory for user consumption. - stats.StackSys = memstats.stacks_inuse + memstats.stacks_sys.load() - stats.MSpanInuse = memstats.mspan_inuse + stats.StackSys = stackInUse + memstats.stacks_sys.load() + stats.MSpanInuse = uint64(mheap_.spanalloc.inuse) stats.MSpanSys = memstats.mspan_sys.load() - stats.MCacheInuse = memstats.mcache_inuse + stats.MCacheInuse = uint64(mheap_.cachealloc.inuse) stats.MCacheSys = memstats.mcache_sys.load() stats.BuckHashSys = memstats.buckhash_sys.load() // MemStats defines GCSys as an aggregate of all memory related // to the memory management system, but we track this memory // at a more granular level in the runtime. - stats.GCSys = memstats.gcMiscSys.load() + memstats.gcWorkBufInUse + memstats.gcProgPtrScalarBitsInUse + stats.GCSys = memstats.gcMiscSys.load() + gcWorkBufInUse + gcProgPtrScalarBitsInUse stats.OtherSys = memstats.other_sys.load() stats.NextGC = gcController.heapGoal stats.LastGC = memstats.last_gc_unix @@ -463,23 +534,11 @@ func readmemstats_m(stats *MemStats) { stats.GCCPUFraction = memstats.gc_cpu_fraction stats.EnableGC = true - // Handle BySize. Copy N values, where N is - // the minimum of the lengths of the two arrays. - // Unfortunately copy() won't work here because - // the arrays have different structs. - // - // TODO(mknyszek): Consider renaming the fields - // of by_size's elements to align so we can use - // the copy built-in. - bySizeLen := len(stats.BySize) - if l := len(memstats.by_size); l < bySizeLen { - bySizeLen = l - } - for i := 0; i < bySizeLen; i++ { - stats.BySize[i].Size = memstats.by_size[i].size - stats.BySize[i].Mallocs = memstats.by_size[i].nmalloc - stats.BySize[i].Frees = memstats.by_size[i].nfree - } + // stats.BySize and bySize might not match in length. + // That's OK, stats.BySize cannot change due to backwards + // compatibility issues. copy will copy the minimum amount + // of values between the two of them. + copy(stats.BySize[:], bySize[:]) } //go:linkname readGCStats runtime/debug.readGCStats @@ -525,113 +584,6 @@ func readGCStats_m(pauses *[]uint64) { *pauses = p[:n+n+3] } -// Updates the memstats structure. -// -// The world must be stopped. -// -//go:nowritebarrier -func updatememstats() { - assertWorldStopped() - - // Flush mcaches to mcentral before doing anything else. - // - // Flushing to the mcentral may in general cause stats to - // change as mcentral data structures are manipulated. - systemstack(flushallmcaches) - - memstats.mcache_inuse = uint64(mheap_.cachealloc.inuse) - memstats.mspan_inuse = uint64(mheap_.spanalloc.inuse) - memstats.sys = memstats.heap_sys.load() + memstats.stacks_sys.load() + memstats.mspan_sys.load() + - memstats.mcache_sys.load() + memstats.buckhash_sys.load() + memstats.gcMiscSys.load() + - memstats.other_sys.load() - - // Calculate memory allocator stats. - // During program execution we only count number of frees and amount of freed memory. - // Current number of alive objects in the heap and amount of alive heap memory - // are calculated by scanning all spans. - // Total number of mallocs is calculated as number of frees plus number of alive objects. - // Similarly, total amount of allocated memory is calculated as amount of freed memory - // plus amount of alive heap memory. - memstats.alloc = 0 - memstats.total_alloc = 0 - memstats.nmalloc = 0 - memstats.nfree = 0 - for i := 0; i < len(memstats.by_size); i++ { - memstats.by_size[i].nmalloc = 0 - memstats.by_size[i].nfree = 0 - } - // Collect consistent stats, which are the source-of-truth in the some cases. - var consStats heapStatsDelta - memstats.heapStats.unsafeRead(&consStats) - - // Collect large allocation stats. - totalAlloc := uint64(consStats.largeAlloc) - memstats.nmalloc += uint64(consStats.largeAllocCount) - totalFree := uint64(consStats.largeFree) - memstats.nfree += uint64(consStats.largeFreeCount) - - // Collect per-sizeclass stats. - for i := 0; i < _NumSizeClasses; i++ { - // Malloc stats. - a := uint64(consStats.smallAllocCount[i]) - totalAlloc += a * uint64(class_to_size[i]) - memstats.nmalloc += a - memstats.by_size[i].nmalloc = a - - // Free stats. - f := uint64(consStats.smallFreeCount[i]) - totalFree += f * uint64(class_to_size[i]) - memstats.nfree += f - memstats.by_size[i].nfree = f - } - - // Account for tiny allocations. - memstats.nfree += uint64(consStats.tinyAllocCount) - memstats.nmalloc += uint64(consStats.tinyAllocCount) - - // Calculate derived stats. - memstats.total_alloc = totalAlloc - memstats.alloc = totalAlloc - totalFree - memstats.heap_objects = memstats.nmalloc - memstats.nfree - - memstats.stacks_inuse = uint64(consStats.inStacks) - memstats.gcWorkBufInUse = uint64(consStats.inWorkBufs) - memstats.gcProgPtrScalarBitsInUse = uint64(consStats.inPtrScalarBits) - - // We also count stacks_inuse, gcWorkBufInUse, and gcProgPtrScalarBitsInUse as sys memory. - memstats.sys += memstats.stacks_inuse + memstats.gcWorkBufInUse + memstats.gcProgPtrScalarBitsInUse - - // The world is stopped, so the consistent stats (after aggregation) - // should be identical to some combination of memstats. In particular: - // - // * heap_inuse == inHeap - // * heap_released == released - // * heap_sys - heap_released == committed - inStacks - inWorkBufs - inPtrScalarBits - // - // Check if that's actually true. - // - // TODO(mknyszek): Maybe don't throw here. It would be bad if a - // bug in otherwise benign accounting caused the whole application - // to crash. - if memstats.heap_inuse != uint64(consStats.inHeap) { - print("runtime: heap_inuse=", memstats.heap_inuse, "\n") - print("runtime: consistent value=", consStats.inHeap, "\n") - throw("heap_inuse and consistent stats are not equal") - } - if memstats.heap_released != uint64(consStats.released) { - print("runtime: heap_released=", memstats.heap_released, "\n") - print("runtime: consistent value=", consStats.released, "\n") - throw("heap_released and consistent stats are not equal") - } - globalRetained := memstats.heap_sys.load() - memstats.heap_released - consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) - if globalRetained != consRetained { - print("runtime: global value=", globalRetained, "\n") - print("runtime: consistent value=", consRetained, "\n") - throw("measures of the retained heap are not equal") - } -} - // flushmcache flushes the mcache of allp[i]. // // The world must be stopped. diff --git a/src/runtime/nbpipe_test.go b/src/runtime/nbpipe_test.go index b6869e7974..0b0f64d076 100644 --- a/src/runtime/nbpipe_test.go +++ b/src/runtime/nbpipe_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package runtime_test diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go index bb3dd35317..864148b715 100644 --- a/src/runtime/netpoll.go +++ b/src/runtime/netpoll.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || windows +//go:build unix || (js && wasm) || windows package runtime @@ -47,16 +47,16 @@ const ( // pollDesc contains 2 binary semaphores, rg and wg, to park reader and writer // goroutines respectively. The semaphore can be in the following states: -// pdReady - io readiness notification is pending; -// a goroutine consumes the notification by changing the state to nil. -// pdWait - a goroutine prepares to park on the semaphore, but not yet parked; -// the goroutine commits to park by changing the state to G pointer, -// or, alternatively, concurrent io notification changes the state to pdReady, -// or, alternatively, concurrent timeout/close changes the state to nil. -// G pointer - the goroutine is blocked on the semaphore; -// io notification or timeout/close changes the state to pdReady or nil respectively -// and unparks the goroutine. -// nil - none of the above. +// pdReady - io readiness notification is pending; +// a goroutine consumes the notification by changing the state to nil. +// pdWait - a goroutine prepares to park on the semaphore, but not yet parked; +// the goroutine commits to park by changing the state to G pointer, +// or, alternatively, concurrent io notification changes the state to pdReady, +// or, alternatively, concurrent timeout/close changes the state to nil. +// G pointer - the goroutine is blocked on the semaphore; +// io notification or timeout/close changes the state to pdReady or nil respectively +// and unparks the goroutine. +// nil - none of the above. const ( pdReady uintptr = 1 pdWait uintptr = 2 diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index d75efce7a8..e3cd6b9d2a 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -130,7 +130,6 @@ import ( // The CPU profile is not available as a Profile. It has a special API, // the StartCPUProfile and StopCPUProfile functions, because it streams // output to a writer during profiling. -// type Profile struct { name string mu sync.Mutex @@ -276,7 +275,6 @@ func (p *Profile) Count() int { // // Passing skip=0 begins the stack trace at the call to Add inside rpc.NewClient. // Passing skip=1 begins the stack trace at the call to NewClient inside mypkg.Run. -// func (p *Profile) Add(value any, skip int) { if p.name == "" { panic("pprof: use of uninitialized Profile") diff --git a/src/runtime/proc.go b/src/runtime/proc.go index df16e0f9b6..f9f82f3867 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -5575,11 +5575,11 @@ func (p pMask) clear(id int32) { // // Thus, we get the following effects on timer-stealing in findrunnable: // -// * Idle Ps with no timers when they go idle are never checked in findrunnable -// (for work- or timer-stealing; this is the ideal case). -// * Running Ps must always be checked. -// * Idle Ps whose timers are stolen must continue to be checked until they run -// again, even after timer expiration. +// * Idle Ps with no timers when they go idle are never checked in findrunnable +// (for work- or timer-stealing; this is the ideal case). +// * Running Ps must always be checked. +// * Idle Ps whose timers are stolen must continue to be checked until they run +// again, even after timer expiration. // // When the P starts running again, the mask should be set, as a timer may be // added at any time. diff --git a/src/runtime/profbuf.go b/src/runtime/profbuf.go index f40881aed5..3d907d5612 100644 --- a/src/runtime/profbuf.go +++ b/src/runtime/profbuf.go @@ -84,7 +84,6 @@ import ( // if uint32(overflow) > 0 { // emit entry for uint32(overflow), time // } -// type profBuf struct { // accessed atomically r, w profAtomic diff --git a/src/runtime/race/README b/src/runtime/race/README index fdbf1d55e6..7ec2f80d3b 100644 --- a/src/runtime/race/README +++ b/src/runtime/race/README @@ -4,12 +4,12 @@ the LLVM project (https://github.com/llvm/llvm-project/tree/main/compiler-rt). To update the .syso files use golang.org/x/build/cmd/racebuild. -race_darwin_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 with https://reviews.llvm.org/D114825 applied and Go 7ccbcc90560468937f02609a43cb39a6e13ff797. -race_freebsd_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. +race_darwin_amd64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. +race_freebsd_amd64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. race_linux_amd64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. -race_linux_ppc64le.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_netbsd_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. +race_linux_ppc64le.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. +race_netbsd_amd64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. race_windows_amd64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_linux_arm64.syso built with LLVM 89f7ccea6f6488c443655880229c54db1f180153 and Go f62d3202bf9dbb3a00ad2a2c63ff4fa4188c5d3b. -race_darwin_arm64.syso built with LLVM 00da38ce2d36c07f12c287dc515d37bb7bc410e9 with https://reviews.llvm.org/D114825 applied and Go 7ccbcc90560468937f02609a43cb39a6e13ff797. +race_linux_arm64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. +race_darwin_arm64.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. race_openbsd_amd64.syso built with LLVM fcf6ae2f070eba73074b6ec8d8281e54d29dbeeb and Go 8f2db14cd35bbd674cb2988a508306de6655e425. diff --git a/src/runtime/race/race_darwin_amd64.syso b/src/runtime/race/race_darwin_amd64.syso index 6fbe140026..dde17add91 100644 Binary files a/src/runtime/race/race_darwin_amd64.syso and b/src/runtime/race/race_darwin_amd64.syso differ diff --git a/src/runtime/race/race_darwin_arm64.syso b/src/runtime/race/race_darwin_arm64.syso index 207099eb1d..4a23df2725 100644 Binary files a/src/runtime/race/race_darwin_arm64.syso and b/src/runtime/race/race_darwin_arm64.syso differ diff --git a/src/runtime/race/race_freebsd_amd64.syso b/src/runtime/race/race_freebsd_amd64.syso index 2a5b46f4ce..8be9ff7a64 100644 Binary files a/src/runtime/race/race_freebsd_amd64.syso and b/src/runtime/race/race_freebsd_amd64.syso differ diff --git a/src/runtime/race/race_linux_arm64.syso b/src/runtime/race/race_linux_arm64.syso index 9dae738700..c8b3f48ca7 100644 Binary files a/src/runtime/race/race_linux_arm64.syso and b/src/runtime/race/race_linux_arm64.syso differ diff --git a/src/runtime/race/race_linux_ppc64le.syso b/src/runtime/race/race_linux_ppc64le.syso index b562656d56..1939f29ac0 100644 Binary files a/src/runtime/race/race_linux_ppc64le.syso and b/src/runtime/race/race_linux_ppc64le.syso differ diff --git a/src/runtime/race/race_netbsd_amd64.syso b/src/runtime/race/race_netbsd_amd64.syso index 11af16f046..e6cc4bf2d8 100644 Binary files a/src/runtime/race/race_netbsd_amd64.syso and b/src/runtime/race/race_netbsd_amd64.syso differ diff --git a/src/runtime/race_arm64.s b/src/runtime/race_arm64.s index 8c0dd25f0b..edbb3b12c7 100644 --- a/src/runtime/race_arm64.s +++ b/src/runtime/race_arm64.s @@ -8,6 +8,7 @@ #include "funcdata.h" #include "textflag.h" #include "tls_arm64.h" +#include "cgo/abi_arm64.h" // The following thunks allow calling the gcc-compiled race runtime directly // from Go code without going all the way through cgo. @@ -450,13 +451,12 @@ TEXT runtime·racecallbackthunk(SB), NOSPLIT|NOFRAME, $0 rest: // Save callee-saved registers (Go code won't respect that). // 8(RSP) and 16(RSP) are for args passed through racecallback - SUB $112, RSP + SUB $176, RSP MOVD LR, 0(RSP) - STP (R19, R20), 24(RSP) - STP (R21, R22), 40(RSP) - STP (R23, R24), 56(RSP) - STP (R25, R26), 72(RSP) - STP (R27, g), 88(RSP) + + SAVE_R19_TO_R28(8*3) + SAVE_F8_TO_F15(8*13) + MOVD R29, (8*21)(RSP) // Set g = g0. // load_g will clobber R0, Save R0 MOVD R0, R13 @@ -479,12 +479,10 @@ rest: ret: // Restore callee-saved registers. MOVD 0(RSP), LR - LDP 24(RSP), (R19, R20) - LDP 40(RSP), (R21, R22) - LDP 56(RSP), (R23, R24) - LDP 72(RSP), (R25, R26) - LDP 88(RSP), (R27, g) - ADD $112, RSP + MOVD (8*21)(RSP), R29 + RESTORE_F8_TO_F15(8*13) + RESTORE_R19_TO_R28(8*3) + ADD $176, RSP JMP (LR) noswitch: diff --git a/src/runtime/race_ppc64le.s b/src/runtime/race_ppc64le.s index 0d8aaa01c1..a1bf3665ad 100644 --- a/src/runtime/race_ppc64le.s +++ b/src/runtime/race_ppc64le.s @@ -442,6 +442,9 @@ TEXT racecall<>(SB), NOSPLIT, $0-0 BEQ call // already on g0 MOVD (g_sched+gobuf_sp)(R10), R1 // switch R1 call: + // prepare frame for C ABI + SUB $32, R1 // create frame for callee saving LR, CR, R2 etc. + RLDCR $0, R1, $~15, R1 // align SP to 16 bytes MOVD R8, CTR // R8 = caller addr MOVD R8, R12 // expected by PPC64 ABI BL (CTR) diff --git a/src/runtime/rt0_darwin_arm64.s b/src/runtime/rt0_darwin_arm64.s index 0040361215..697104ac64 100644 --- a/src/runtime/rt0_darwin_arm64.s +++ b/src/runtime/rt0_darwin_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" TEXT _rt0_arm64_darwin(SB),NOSPLIT|NOFRAME,$0 MOVD $runtime·rt0_go(SB), R2 @@ -18,26 +19,10 @@ exit: // // Note that all currently shipping darwin/arm64 platforms require // cgo and do not support c-shared. -TEXT _rt0_arm64_darwin_lib(SB),NOSPLIT,$168 +TEXT _rt0_arm64_darwin_lib(SB),NOSPLIT,$152 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - MOVD g, 96(RSP) - FMOVD F8, 104(RSP) - FMOVD F9, 112(RSP) - FMOVD F10, 120(RSP) - FMOVD F11, 128(RSP) - FMOVD F12, 136(RSP) - FMOVD F13, 144(RSP) - FMOVD F14, 152(RSP) - FMOVD F15, 160(RSP) + SAVE_R19_TO_R28(8) + SAVE_F8_TO_F15(88) MOVD R0, _rt0_arm64_darwin_lib_argc<>(SB) MOVD R1, _rt0_arm64_darwin_lib_argv<>(SB) @@ -57,24 +42,8 @@ TEXT _rt0_arm64_darwin_lib(SB),NOSPLIT,$168 ADD $16, RSP // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - MOVD 96(RSP), g - FMOVD 104(RSP), F8 - FMOVD 112(RSP), F9 - FMOVD 120(RSP), F10 - FMOVD 128(RSP), F11 - FMOVD 136(RSP), F12 - FMOVD 144(RSP), F13 - FMOVD 152(RSP), F14 - FMOVD 160(RSP), F15 + RESTORE_R19_TO_R28(8) + RESTORE_F8_TO_F15(88) RET diff --git a/src/runtime/rt0_freebsd_arm64.s b/src/runtime/rt0_freebsd_arm64.s index a938d98262..e517ae059d 100644 --- a/src/runtime/rt0_freebsd_arm64.s +++ b/src/runtime/rt0_freebsd_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" // On FreeBSD argc/argv are passed in R0, not RSP TEXT _rt0_arm64_freebsd(SB),NOSPLIT|NOFRAME,$0 @@ -14,24 +15,8 @@ TEXT _rt0_arm64_freebsd(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_freebsd_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -63,24 +48,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_freebsd_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/rt0_linux_arm64.s b/src/runtime/rt0_linux_arm64.s index f48a8d6190..0eb8fc2f48 100644 --- a/src/runtime/rt0_linux_arm64.s +++ b/src/runtime/rt0_linux_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" TEXT _rt0_arm64_linux(SB),NOSPLIT|NOFRAME,$0 MOVD 0(RSP), R0 // argc @@ -13,24 +14,8 @@ TEXT _rt0_arm64_linux(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_linux_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -62,24 +47,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_linux_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/rt0_netbsd_arm64.s b/src/runtime/rt0_netbsd_arm64.s index 2f3b5a5a87..691a8e4be7 100644 --- a/src/runtime/rt0_netbsd_arm64.s +++ b/src/runtime/rt0_netbsd_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" TEXT _rt0_arm64_netbsd(SB),NOSPLIT|NOFRAME,$0 MOVD 0(RSP), R0 // argc @@ -13,24 +14,8 @@ TEXT _rt0_arm64_netbsd(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_netbsd_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -62,24 +47,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_netbsd_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/rt0_openbsd_arm64.s b/src/runtime/rt0_openbsd_arm64.s index 722fab6129..49d49b34ac 100644 --- a/src/runtime/rt0_openbsd_arm64.s +++ b/src/runtime/rt0_openbsd_arm64.s @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "textflag.h" +#include "cgo/abi_arm64.h" // See comment in runtime/sys_openbsd_arm64.s re this construction. #define INVOKE_SYSCALL \ @@ -19,24 +20,8 @@ TEXT _rt0_arm64_openbsd(SB),NOSPLIT|NOFRAME,$0 // library is loaded. TEXT _rt0_arm64_openbsd_lib(SB),NOSPLIT,$184 // Preserve callee-save registers. - MOVD R19, 24(RSP) - MOVD R20, 32(RSP) - MOVD R21, 40(RSP) - MOVD R22, 48(RSP) - MOVD R23, 56(RSP) - MOVD R24, 64(RSP) - MOVD R25, 72(RSP) - MOVD R26, 80(RSP) - MOVD R27, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) - MOVD g, 160(RSP) + SAVE_R19_TO_R28(24) + SAVE_F8_TO_F15(104) // Initialize g as null in case of using g later e.g. sigaction in cgo_sigaction.go MOVD ZR, g @@ -68,24 +53,8 @@ nocgo: restore: // Restore callee-save registers. - MOVD 24(RSP), R19 - MOVD 32(RSP), R20 - MOVD 40(RSP), R21 - MOVD 48(RSP), R22 - MOVD 56(RSP), R23 - MOVD 64(RSP), R24 - MOVD 72(RSP), R25 - MOVD 80(RSP), R26 - MOVD 88(RSP), R27 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 - MOVD 160(RSP), g + RESTORE_R19_TO_R28(24) + RESTORE_F8_TO_F15(104) RET TEXT _rt0_arm64_openbsd_lib_go(SB),NOSPLIT,$0 diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index bb76116ee9..063b9a7d45 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -500,6 +500,10 @@ func TestGdbAutotmpTypes(t *testing.T) { args := []string{"-nx", "-batch", "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), "-ex", "set startup-with-shell off", + // Some gdb may set scheduling-locking as "step" by default. This prevents background tasks + // (e.g GC) from completing which may result in a hang when executing the step command. + // See #49852. + "-ex", "set scheduler-locking off", "-ex", "break main.main", "-ex", "run", "-ex", "step", diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 1fb9e195e5..dc18bf927e 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -291,10 +291,10 @@ func (pp *puintptr) set(p *p) { *pp = puintptr(unsafe.Pointer(p)) } // Because we do free Ms, there are some additional constrains on // muintptrs: // -// 1. Never hold an muintptr locally across a safe point. +// 1. Never hold an muintptr locally across a safe point. // -// 2. Any muintptr in the heap must be owned by the M itself so it can -// ensure it is not in use when the last true *m is released. +// 2. Any muintptr in the heap must be owned by the M itself so it can +// ensure it is not in use when the last true *m is released. type muintptr uintptr //go:nosplit diff --git a/src/runtime/runtime_mmap_test.go b/src/runtime/runtime_mmap_test.go index 9323c09355..456f913954 100644 --- a/src/runtime/runtime_mmap_test.go +++ b/src/runtime/runtime_mmap_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package runtime_test diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go index 2dd4cc51a3..0e11c57683 100644 --- a/src/runtime/signal_unix.go +++ b/src/runtime/signal_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package runtime diff --git a/src/runtime/stack.go b/src/runtime/stack.go index edc37d4878..54a02173c3 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -1332,7 +1332,8 @@ func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args } // stack objects. - if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le") && unsafe.Sizeof(abi.RegArgs{}) > 0 && frame.argmap != nil { + if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") && + unsafe.Sizeof(abi.RegArgs{}) > 0 && frame.argmap != nil { // argmap is set when the function is reflect.makeFuncStub or reflect.methodValueCall. // We don't actually use argmap in this case, but we need to fake the stack object // record for these frames which contain an internal/abi.RegArgs at a hard-coded offset. diff --git a/src/runtime/string.go b/src/runtime/string.go index 980a9866e6..eec29075b9 100644 --- a/src/runtime/string.go +++ b/src/runtime/string.go @@ -147,10 +147,10 @@ func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) { // and otherwise intrinsified by the compiler. // // Some internal compiler optimizations use this function. -// - Used for m[T1{... Tn{..., string(k), ...} ...}] and m[string(k)] -// where k is []byte, T1 to Tn is a nesting of struct and array literals. -// - Used for "<"+string(b)+">" concatenation where b is []byte. -// - Used for string(b)=="foo" comparison where b is []byte. +// - Used for m[T1{... Tn{..., string(k), ...} ...}] and m[string(k)] +// where k is []byte, T1 to Tn is a nesting of struct and array literals. +// - Used for "<"+string(b)+">" concatenation where b is []byte. +// - Used for string(b)=="foo" comparison where b is []byte. func slicebytetostringtmp(ptr *byte, n int) (str string) { if raceenabled && n > 0 { racereadrangepc(unsafe.Pointer(ptr), diff --git a/src/runtime/stubs_riscv64.go b/src/runtime/stubs_riscv64.go new file mode 100644 index 0000000000..f677117871 --- /dev/null +++ b/src/runtime/stubs_riscv64.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +// Called from assembly only; declared for go vet. +func load_g() +func save_g() + +// Used by reflectcall and the reflect package. +// +// Spills/loads arguments in registers to/from an internal/abi.RegArgs +// respectively. Does not follow the Go ABI. +func spillArgs() +func unspillArgs() diff --git a/src/runtime/symtab_test.go b/src/runtime/symtab_test.go index 99ff0d4420..a83afc3385 100644 --- a/src/runtime/symtab_test.go +++ b/src/runtime/symtab_test.go @@ -205,15 +205,15 @@ func tracebackFunc(t *testing.T) uintptr { // Go obviously doesn't easily expose the problematic PCs to running programs, // so this test is a bit fragile. Some details: // -// * tracebackFunc is our target function. We want to get a PC in the -// alignment region following this function. This function also has other -// functions inlined into it to ensure it has an InlTree (this was the source -// of the bug in issue 44971). +// * tracebackFunc is our target function. We want to get a PC in the +// alignment region following this function. This function also has other +// functions inlined into it to ensure it has an InlTree (this was the source +// of the bug in issue 44971). // -// * We acquire a PC in tracebackFunc, walking forwards until FuncForPC says -// we're in a new function. The last PC of the function according to FuncForPC -// should be in the alignment region (assuming the function isn't already -// perfectly aligned). +// * We acquire a PC in tracebackFunc, walking forwards until FuncForPC says +// we're in a new function. The last PC of the function according to FuncForPC +// should be in the alignment region (assuming the function isn't already +// perfectly aligned). // // This is a regression test for issue 44971. func TestFunctionAlignmentTraceback(t *testing.T) { diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s index e57ac53e10..dc7c9bffa8 100644 --- a/src/runtime/sys_darwin_arm64.s +++ b/src/runtime/sys_darwin_arm64.s @@ -9,6 +9,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME 0 @@ -175,28 +176,11 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 BL (R11) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // Save arguments. MOVW R0, (8*1)(RSP) // sig @@ -249,25 +233,8 @@ nog: #endif // Restore callee-save registers. - MOVD (8*4)(RSP), R19 - MOVD (8*5)(RSP), R20 - MOVD (8*6)(RSP), R21 - MOVD (8*7)(RSP), R22 - MOVD (8*8)(RSP), R23 - MOVD (8*9)(RSP), R24 - MOVD (8*10)(RSP), R25 - MOVD (8*11)(RSP), R26 - MOVD (8*12)(RSP), R27 - MOVD (8*13)(RSP), g - MOVD (8*14)(RSP), R29 - FMOVD (8*15)(RSP), F8 - FMOVD (8*16)(RSP), F9 - FMOVD (8*17)(RSP), F10 - FMOVD (8*18)(RSP), F11 - FMOVD (8*19)(RSP), F12 - FMOVD (8*20)(RSP), F13 - FMOVD (8*21)(RSP), F14 - FMOVD (8*22)(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET @@ -376,25 +343,8 @@ TEXT runtime·mstart_stub(SB),NOSPLIT,$160 // We are already on m's g0 stack. // Save callee-save registers. - MOVD R19, 8(RSP) - MOVD R20, 16(RSP) - MOVD R21, 24(RSP) - MOVD R22, 32(RSP) - MOVD R23, 40(RSP) - MOVD R24, 48(RSP) - MOVD R25, 56(RSP) - MOVD R26, 64(RSP) - MOVD R27, 72(RSP) - MOVD g, 80(RSP) - MOVD R29, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) + SAVE_R19_TO_R28(8) + SAVE_F8_TO_F15(88) MOVD m_g0(R0), g BL ·save_g(SB) @@ -402,25 +352,8 @@ TEXT runtime·mstart_stub(SB),NOSPLIT,$160 BL runtime·mstart(SB) // Restore callee-save registers. - MOVD 8(RSP), R19 - MOVD 16(RSP), R20 - MOVD 24(RSP), R21 - MOVD 32(RSP), R22 - MOVD 40(RSP), R23 - MOVD 48(RSP), R24 - MOVD 56(RSP), R25 - MOVD 64(RSP), R26 - MOVD 72(RSP), R27 - MOVD 80(RSP), g - MOVD 88(RSP), R29 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 + RESTORE_R19_TO_R28(8) + RESTORE_F8_TO_F15(88) // Go is all done with this OS thread. // Tell pthread everything is ok (we never join with this thread, so diff --git a/src/runtime/sys_freebsd_arm64.s b/src/runtime/sys_freebsd_arm64.s index 59adf4e5f3..7b05fb0900 100644 --- a/src/runtime/sys_freebsd_arm64.s +++ b/src/runtime/sys_freebsd_arm64.s @@ -10,6 +10,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 4 @@ -279,28 +280,11 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // func sigtramp() -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // this might be called in external code context, // where g is not set. @@ -317,25 +301,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192 BL (R0) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s index ca362ed552..36ac014f03 100644 --- a/src/runtime/sys_linux_arm64.s +++ b/src/runtime/sys_linux_arm64.s @@ -9,6 +9,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define AT_FDCWD -100 @@ -444,28 +445,11 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 RET // Called from c-abi, R0: sig, R1: info, R2: cxt -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // this might be called in external code context, // where g is not set. @@ -481,52 +465,16 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192 BL (R0) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET // Called from c-abi, R0: sig, R1: info, R2: cxt -TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$192 - // TODO(eric): In multiple places we need to save and restore the - // callee-saved registers, we can define a macro for this. +TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$176 // Save callee-save registers because it's a callback from c code. - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) MOVW R0, 8(RSP) // sig MOVD R1, 16(RSP) // info @@ -534,25 +482,8 @@ TEXT runtime·sigprofNonGoWrapper<>(SB),NOSPLIT,$192 CALL runtime·sigprofNonGo(SB) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET // Called from c-abi, R0: sig, R1: info, R2: cxt diff --git a/src/runtime/sys_netbsd_arm64.s b/src/runtime/sys_netbsd_arm64.s index f7cce57c2d..32e6740c52 100644 --- a/src/runtime/sys_netbsd_arm64.s +++ b/src/runtime/sys_netbsd_arm64.s @@ -9,6 +9,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 3 @@ -294,34 +295,17 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 BL (R11) RET -TEXT runtime·sigtramp(SB),NOSPLIT,$192 +TEXT runtime·sigtramp(SB),NOSPLIT,$176 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // Unclobber g for now (kernel uses it as ucontext ptr) // See https://github.com/golang/go/issues/30824#issuecomment-492772426 // This is only correct in the non-cgo case. // XXX should use lwp_getprivate as suggested. // 8*36 is ucontext.uc_mcontext.__gregs[_REG_X28] MOVD 8*36(g), g - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) // this might be called in external code context, // where g is not set. @@ -338,25 +322,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192 BL runtime·sigtrampgo(SB) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s index 3fa7e1ede2..7c1886e0a0 100644 --- a/src/runtime/sys_openbsd_arm64.s +++ b/src/runtime/sys_openbsd_arm64.s @@ -11,6 +11,7 @@ #include "go_asm.h" #include "go_tls.h" #include "textflag.h" +#include "cgo/abi_arm64.h" #define CLOCK_REALTIME $0 #define CLOCK_MONOTONIC $3 @@ -18,30 +19,13 @@ // mstart_stub is the first function executed on a new thread started by pthread_create. // It just does some low-level setup and then calls mstart. // Note: called with the C calling convention. -TEXT runtime·mstart_stub(SB),NOSPLIT,$160 +TEXT runtime·mstart_stub(SB),NOSPLIT,$144 // R0 points to the m. // We are already on m's g0 stack. // Save callee-save registers. - MOVD R19, 8(RSP) - MOVD R20, 16(RSP) - MOVD R21, 24(RSP) - MOVD R22, 32(RSP) - MOVD R23, 40(RSP) - MOVD R24, 48(RSP) - MOVD R25, 56(RSP) - MOVD R26, 64(RSP) - MOVD R27, 72(RSP) - MOVD g, 80(RSP) - MOVD R29, 88(RSP) - FMOVD F8, 96(RSP) - FMOVD F9, 104(RSP) - FMOVD F10, 112(RSP) - FMOVD F11, 120(RSP) - FMOVD F12, 128(RSP) - FMOVD F13, 136(RSP) - FMOVD F14, 144(RSP) - FMOVD F15, 152(RSP) + SAVE_R19_TO_R28(8) + SAVE_F8_TO_F15(88) MOVD m_g0(R0), g BL runtime·save_g(SB) @@ -49,25 +33,8 @@ TEXT runtime·mstart_stub(SB),NOSPLIT,$160 BL runtime·mstart(SB) // Restore callee-save registers. - MOVD 8(RSP), R19 - MOVD 16(RSP), R20 - MOVD 24(RSP), R21 - MOVD 32(RSP), R22 - MOVD 40(RSP), R23 - MOVD 48(RSP), R24 - MOVD 56(RSP), R25 - MOVD 64(RSP), R26 - MOVD 72(RSP), R27 - MOVD 80(RSP), g - MOVD 88(RSP), R29 - FMOVD 96(RSP), F8 - FMOVD 104(RSP), F9 - FMOVD 112(RSP), F10 - FMOVD 120(RSP), F11 - FMOVD 128(RSP), F12 - FMOVD 136(RSP), F13 - FMOVD 144(RSP), F14 - FMOVD 152(RSP), F15 + RESTORE_R19_TO_R28(8) + RESTORE_F8_TO_F15(88) // Go is all done with this OS thread. // Tell pthread everything is ok (we never join with this thread, so @@ -87,25 +54,8 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 TEXT runtime·sigtramp(SB),NOSPLIT,$192 // Save callee-save registers in the case of signal forwarding. // Please refer to https://golang.org/issue/31827 . - MOVD R19, 8*4(RSP) - MOVD R20, 8*5(RSP) - MOVD R21, 8*6(RSP) - MOVD R22, 8*7(RSP) - MOVD R23, 8*8(RSP) - MOVD R24, 8*9(RSP) - MOVD R25, 8*10(RSP) - MOVD R26, 8*11(RSP) - MOVD R27, 8*12(RSP) - MOVD g, 8*13(RSP) - MOVD R29, 8*14(RSP) - FMOVD F8, 8*15(RSP) - FMOVD F9, 8*16(RSP) - FMOVD F10, 8*17(RSP) - FMOVD F11, 8*18(RSP) - FMOVD F12, 8*19(RSP) - FMOVD F13, 8*20(RSP) - FMOVD F14, 8*21(RSP) - FMOVD F15, 8*22(RSP) + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) // If called from an external code context, g will not be set. // Save R0, since runtime·load_g will clobber it. @@ -117,25 +67,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$192 BL runtime·sigtrampgo(SB) // Restore callee-save registers. - MOVD 8*4(RSP), R19 - MOVD 8*5(RSP), R20 - MOVD 8*6(RSP), R21 - MOVD 8*7(RSP), R22 - MOVD 8*8(RSP), R23 - MOVD 8*9(RSP), R24 - MOVD 8*10(RSP), R25 - MOVD 8*11(RSP), R26 - MOVD 8*12(RSP), R27 - MOVD 8*13(RSP), g - MOVD 8*14(RSP), R29 - FMOVD 8*15(RSP), F8 - FMOVD 8*16(RSP), F9 - FMOVD 8*17(RSP), F10 - FMOVD 8*18(RSP), F11 - FMOVD 8*19(RSP), F12 - FMOVD 8*20(RSP), F13 - FMOVD 8*21(RSP), F14 - FMOVD 8*22(RSP), F15 + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) RET diff --git a/src/runtime/sys_windows_arm64.s b/src/runtime/sys_windows_arm64.s index 87f8f0d218..7b1514f552 100644 --- a/src/runtime/sys_windows_arm64.s +++ b/src/runtime/sys_windows_arm64.s @@ -7,6 +7,7 @@ #include "textflag.h" #include "funcdata.h" #include "time_windows.h" +#include "cgo/abi_arm64.h" // Offsets into Thread Environment Block (pointer in R18) #define TEB_error 0x68 @@ -128,30 +129,6 @@ TEXT runtime·getlasterror(SB),NOSPLIT|NOFRAME,$0 MOVD R0, ret+0(FP) RET -#define SAVE_R19_TO_R28(offset) \ - MOVD R19, savedR19+((offset)+0*8)(SP); \ - MOVD R20, savedR20+((offset)+1*8)(SP); \ - MOVD R21, savedR21+((offset)+2*8)(SP); \ - MOVD R22, savedR22+((offset)+3*8)(SP); \ - MOVD R23, savedR23+((offset)+4*8)(SP); \ - MOVD R24, savedR24+((offset)+5*8)(SP); \ - MOVD R25, savedR25+((offset)+6*8)(SP); \ - MOVD R26, savedR26+((offset)+7*8)(SP); \ - MOVD R27, savedR27+((offset)+8*8)(SP); \ - MOVD g, savedR28+((offset)+9*8)(SP); - -#define RESTORE_R19_TO_R28(offset) \ - MOVD savedR19+((offset)+0*8)(SP), R19; \ - MOVD savedR20+((offset)+1*8)(SP), R20; \ - MOVD savedR21+((offset)+2*8)(SP), R21; \ - MOVD savedR22+((offset)+3*8)(SP), R22; \ - MOVD savedR23+((offset)+4*8)(SP), R23; \ - MOVD savedR24+((offset)+5*8)(SP), R24; \ - MOVD savedR25+((offset)+6*8)(SP), R25; \ - MOVD savedR26+((offset)+7*8)(SP), R26; \ - MOVD savedR27+((offset)+8*8)(SP), R27; \ - MOVD savedR28+((offset)+9*8)(SP), g; /* R28 */ - // Called by Windows as a Vectored Exception Handler (VEH). // First argument is pointer to struct containing // exception record and context pointers. @@ -221,7 +198,8 @@ TEXT sigtramp_g0<>(SB),NOSPLIT,$128 NO_LOCAL_POINTERS // Push C callee-save registers R19-R28. LR, FP already saved. - SAVE_R19_TO_R28(-10*8) + // These registers will occupy the upper 10 words of the frame. + SAVE_R19_TO_R28(8*7) MOVD 0(R0), R5 // R5 = ExceptionPointers->ExceptionRecord MOVD 8(R0), R6 // R6 = ExceptionPointers->ContextRecord @@ -275,7 +253,7 @@ TEXT sigtramp_g0<>(SB),NOSPLIT,$128 MOVD R2, context_pc(R6) return: - RESTORE_R19_TO_R28(-10*8) // smashes g + RESTORE_R19_TO_R28(8*7) // smashes g RET // Trampoline to resume execution from exception handler. @@ -317,18 +295,14 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 // but we are not called from Go so that space is ours to use, // and we must to be contiguous with the stack arguments. MOVD $arg0-(7*8)(SP), R14 - MOVD R0, (0*8)(R14) - MOVD R1, (1*8)(R14) - MOVD R2, (2*8)(R14) - MOVD R3, (3*8)(R14) - MOVD R4, (4*8)(R14) - MOVD R5, (5*8)(R14) - MOVD R6, (6*8)(R14) - MOVD R7, (7*8)(R14) + STP (R0, R1), (0*8)(R14) + STP (R2, R3), (2*8)(R14) + STP (R4, R5), (4*8)(R14) + STP (R6, R7), (6*8)(R14) // Push C callee-save registers R19-R28. // LR, FP already saved. - SAVE_R19_TO_R28(-18*8) + SAVE_R19_TO_R28(8*9) // Create a struct callbackArgs on our stack. MOVD $cbargs-(18*8+callbackArgs__size)(SP), R13 @@ -342,8 +316,7 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 MOVD $·callbackWrap(SB), R0 // PC of function to call, cgocallback takes an ABIInternal entry-point MOVD R13, R1 // frame (&callbackArgs{...}) MOVD $0, R2 // context - MOVD R0, (1*8)(RSP) - MOVD R1, (2*8)(RSP) + STP (R0, R1), (1*8)(RSP) MOVD R2, (3*8)(RSP) BL runtime·cgocallback(SB) @@ -351,13 +324,13 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$208-0 MOVD $cbargs-(18*8+callbackArgs__size)(SP), R13 MOVD callbackArgs_result(R13), R0 - RESTORE_R19_TO_R28(-18*8) + RESTORE_R19_TO_R28(8*9) RET // uint32 tstart_stdcall(M *newm); TEXT runtime·tstart_stdcall(SB),NOSPLIT,$96-0 - SAVE_R19_TO_R28(-10*8) + SAVE_R19_TO_R28(8*3) MOVD m_g0(R0), g MOVD R0, g_m(g) @@ -374,7 +347,7 @@ TEXT runtime·tstart_stdcall(SB),NOSPLIT,$96-0 BL runtime·emptyfunc(SB) // fault if stack check is wrong BL runtime·mstart(SB) - RESTORE_R19_TO_R28(-10*8) + RESTORE_R19_TO_R28(8*3) // Exit the thread. MOVD $0, R0 diff --git a/src/runtime/testdata/testprogcgo/windows/win.go b/src/runtime/testdata/testprogcgo/windows/win.go index 12488aa658..9d9f86c9be 100644 --- a/src/runtime/testdata/testprogcgo/windows/win.go +++ b/src/runtime/testdata/testprogcgo/windows/win.go @@ -1,8 +1,6 @@ package windows /* -#cgo amd64 386 CFLAGS: -mnop-fun-dllimport - #include DWORD agetthread() { diff --git a/src/runtime/trace/annotation.go b/src/runtime/trace/annotation.go index d05b5e2261..bf3dbc3d79 100644 --- a/src/runtime/trace/annotation.go +++ b/src/runtime/trace/annotation.go @@ -149,7 +149,6 @@ func WithRegion(ctx context.Context, regionType string, fn func()) { // Recommended usage is // // defer trace.StartRegion(ctx, "myTracedRegion").End() -// func StartRegion(ctx context.Context, regionType string) *Region { if !IsEnabled() { return noopRegion diff --git a/src/runtime/type.go b/src/runtime/type.go index da47147897..a00394f3b3 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -17,7 +17,7 @@ import ( // cmd/compile/internal/reflectdata/reflect.go // cmd/link/internal/ld/decodesym.go // reflect/type.go -// internal/reflectlite/type.go +// internal/reflectlite/type.go type tflag uint8 const ( diff --git a/src/sort/search.go b/src/sort/search.go index fcff0f9491..601557a94b 100644 --- a/src/sort/search.go +++ b/src/sort/search.go @@ -55,7 +55,6 @@ package sort // }) // fmt.Printf("Your number is %d.\n", answer) // } -// func Search(n int, f func(int) bool) int { // Define f(-1) == false and f(n) == true. // Invariant: f(i-1) == false, f(j) == true. @@ -79,7 +78,6 @@ func Search(n int, f func(int) bool) int { // as specified by Search. The return value is the index to insert x if x is // not present (it could be len(a)). // The slice must be sorted in ascending order. -// func SearchInts(a []int, x int) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } @@ -88,7 +86,6 @@ func SearchInts(a []int, x int) int { // as specified by Search. The return value is the index to insert x if x is not // present (it could be len(a)). // The slice must be sorted in ascending order. -// func SearchFloat64s(a []float64, x float64) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } @@ -97,7 +94,6 @@ func SearchFloat64s(a []float64, x float64) int { // as specified by Search. The return value is the index to insert x if x is not // present (it could be len(a)). // The slice must be sorted in ascending order. -// func SearchStrings(a []string, x string) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } diff --git a/src/sort/search_test.go b/src/sort/search_test.go index ded68ebde0..f06897ee21 100644 --- a/src/sort/search_test.go +++ b/src/sort/search_test.go @@ -59,7 +59,6 @@ func TestSearch(t *testing.T) { // log2 computes the binary logarithm of x, rounded up to the next integer. // (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.) -// func log2(x int) int { n := 0 for p := 1; p < x; p += p { diff --git a/src/sort/sort.go b/src/sort/sort.go index 2c197afc03..aed0eaba30 100644 --- a/src/sort/sort.go +++ b/src/sort/sort.go @@ -111,7 +111,6 @@ func (x Float64Slice) Len() int { return len(x) } // This implementation of Less places NaN values before any others, by using: // // x[i] < x[j] || (math.IsNaN(x[i]) && !math.IsNaN(x[j])) -// func (x Float64Slice) Less(i, j int) bool { return x[i] < x[j] || (isNaN(x[i]) && !isNaN(x[j])) } func (x Float64Slice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } diff --git a/src/strconv/itoa.go b/src/strconv/itoa.go index 45e4192c82..b0c2666e7c 100644 --- a/src/strconv/itoa.go +++ b/src/strconv/itoa.go @@ -85,7 +85,6 @@ const digits = "0123456789abcdefghijklmnopqrstuvwxyz" // set, the string is appended to dst and the resulting byte slice is // returned as the first result value; otherwise the string is returned // as the second result value. -// func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) { if base < 2 || base > len(digits) { panic("strconv: illegal AppendInt/FormatInt base") diff --git a/src/strconv/quote.go b/src/strconv/quote.go index 9d20b75a58..6c022846c0 100644 --- a/src/strconv/quote.go +++ b/src/strconv/quote.go @@ -99,7 +99,7 @@ func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bo buf = append(buf, `\v`...) default: switch { - case r < ' ': + case r < ' ' || r == 0x7f: buf = append(buf, `\x`...) buf = append(buf, lowerhex[byte(r)>>4]) buf = append(buf, lowerhex[byte(r)&0xF]) diff --git a/src/strconv/quote_test.go b/src/strconv/quote_test.go index 81fc8f79e1..fc000de7b1 100644 --- a/src/strconv/quote_test.go +++ b/src/strconv/quote_test.go @@ -55,6 +55,7 @@ var quotetests = []quoteTest{ {"\x04", `"\x04"`, `"\x04"`, `"\x04"`}, // Some non-printable but graphic runes. Final column is double-quoted. {"!\u00a0!\u2000!\u3000!", `"!\u00a0!\u2000!\u3000!"`, `"!\u00a0!\u2000!\u3000!"`, "\"!\u00a0!\u2000!\u3000!\""}, + {"\x7f", `"\x7f"`, `"\x7f"`, `"\x7f"`}, } func TestQuote(t *testing.T) { diff --git a/src/strings/example_test.go b/src/strings/example_test.go index 94aa167f90..2a59512ceb 100644 --- a/src/strings/example_test.go +++ b/src/strings/example_test.go @@ -95,7 +95,12 @@ func ExampleCut() { func ExampleEqualFold() { fmt.Println(strings.EqualFold("Go", "go")) - // Output: true + fmt.Println(strings.EqualFold("AB", "ab")) // true because comparison uses simple case-folding + fmt.Println(strings.EqualFold("ß", "ss")) // false because comparison does not use full case-folding + // Output: + // true + // true + // false } func ExampleFields() { diff --git a/src/strings/strings.go b/src/strings/strings.go index 5793d9e26f..74e505338e 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -244,6 +244,9 @@ func genSplit(s, sep string, sepSave, n int) []string { n = Count(s, sep) + 1 } + if n > len(s)+1 { + n = len(s) + 1 + } a := make([]string, n) n-- i := 0 @@ -1038,8 +1041,10 @@ func ReplaceAll(s, old, new string) string { } // EqualFold reports whether s and t, interpreted as UTF-8 strings, -// are equal under Unicode case-folding, which is a more general +// are equal under simple Unicode case-folding, which is a more general // form of case-insensitivity. +// +// EqualFold(s, t) is equivalent to Tolower(s) == Tolower(t). func EqualFold(s, t string) bool { for s != "" && t != "" { // Extract first rune from each string. diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go index 0f30ca738e..9e7fb85ddf 100644 --- a/src/strings/strings_test.go +++ b/src/strings/strings_test.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "io" + "math" "math/rand" "reflect" "strconv" @@ -404,6 +405,7 @@ var splittests = []SplitTest{ {faces, "~", -1, []string{faces}}, {"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}}, {"1 2", " ", 3, []string{"1", "2"}}, + {"", "T", math.MaxInt / 4, []string{""}}, } func TestSplit(t *testing.T) { diff --git a/src/sync/cond.go b/src/sync/cond.go index b254c9360a..d86ebc8b50 100644 --- a/src/sync/cond.go +++ b/src/sync/cond.go @@ -48,7 +48,6 @@ func NewCond(l Locker) *Cond { // } // ... make use of condition ... // c.L.Unlock() -// func (c *Cond) Wait() { c.checker.check() t := runtime_notifyListAdd(&c.notify) diff --git a/src/sync/once.go b/src/sync/once.go index 8844314e7e..e5ba257d87 100644 --- a/src/sync/once.go +++ b/src/sync/once.go @@ -38,7 +38,6 @@ type Once struct { // // If f panics, Do considers it to have returned; future calls of Do return // without calling f. -// func (o *Once) Do(f func()) { // Note: Here is an incorrect implementation of Do: // diff --git a/src/syscall/dirent.go b/src/syscall/dirent.go index 237ea79ad6..b10608a662 100644 --- a/src/syscall/dirent.go +++ b/src/syscall/dirent.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package syscall diff --git a/src/syscall/dirent_test.go b/src/syscall/dirent_test.go index a2d1b61342..68e766e6b0 100644 --- a/src/syscall/dirent_test.go +++ b/src/syscall/dirent_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package syscall_test diff --git a/src/syscall/env_unix.go b/src/syscall/env_unix.go index 521967c79f..67e6c5fbe2 100644 --- a/src/syscall/env_unix.go +++ b/src/syscall/env_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || plan9 +//go:build unix || (js && wasm) || plan9 // Unix environment variables. diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go index 80440ca4d6..0ec9c4db0f 100644 --- a/src/syscall/exec_linux_test.go +++ b/src/syscall/exec_linux_test.go @@ -258,12 +258,14 @@ func TestGroupCleanup(t *testing.T) { t.Fatalf("Cmd failed with err %v, output: %s", err, out) } strOut := strings.TrimSpace(string(out)) + t.Logf("id: %s", strOut) + expected := "uid=0(root) gid=0(root)" // Just check prefix because some distros reportedly output a // context parameter; see https://golang.org/issue/16224. // Alpine does not output groups; see https://golang.org/issue/19938. if !strings.HasPrefix(strOut, expected) { - t.Errorf("id command output: %q, expected prefix: %q", strOut, expected) + t.Errorf("expected prefix: %q", expected) } } @@ -292,23 +294,14 @@ func TestGroupCleanupUserNamespace(t *testing.T) { t.Fatalf("Cmd failed with err %v, output: %s", err, out) } strOut := strings.TrimSpace(string(out)) + t.Logf("id: %s", strOut) - // Strings we've seen in the wild. - expected := []string{ - "uid=0(root) gid=0(root) groups=0(root)", - "uid=0(root) gid=0(root) groups=0(root),65534(nobody)", - "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)", - "uid=0(root) gid=0(root) groups=0(root),65534", - "uid=0(root) gid=0(root) groups=0(root),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody)", // Alpine; see https://golang.org/issue/19938 - "uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023", // CentOS with SELinux context, see https://golang.org/issue/34547 - "uid=0(root) gid=0(root) groups=0(root),65534(nobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023", // Fedora with SElinux context, see https://golang.org/issue/46752 + // As in TestGroupCleanup, just check prefix. + // The actual groups and contexts seem to vary from one distro to the next. + expected := "uid=0(root) gid=0(root) groups=0(root)" + if !strings.HasPrefix(strOut, expected) { + t.Errorf("expected prefix: %q", expected) } - for _, e := range expected { - if strOut == e { - return - } - } - t.Errorf("id command output: %q, expected one of %q", strOut, expected) } // TestUnshareHelperProcess isn't a real test. It's used as a helper process diff --git a/src/syscall/exec_unix.go b/src/syscall/exec_unix.go index 0e41959ffe..286be454d8 100644 --- a/src/syscall/exec_unix.go +++ b/src/syscall/exec_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // Fork, exec, wait, etc. diff --git a/src/syscall/exec_unix_test.go b/src/syscall/exec_unix_test.go index b7ae77552b..4253cda5cb 100644 --- a/src/syscall/exec_unix_test.go +++ b/src/syscall/exec_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package syscall_test diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go index 9d10d6a512..41e58d4355 100644 --- a/src/syscall/exec_windows.go +++ b/src/syscall/exec_windows.go @@ -19,11 +19,11 @@ var ForkLock sync.RWMutex // in https://msdn.microsoft.com/en-us/library/ms880421. // This function returns "" (2 double quotes) if s is empty. // Alternatively, these transformations are done: -// - every back slash (\) is doubled, but only if immediately -// followed by double quote ("); -// - every double quote (") is escaped by back slash (\); -// - finally, s is wrapped with double quotes (arg -> "arg"), -// but only if there is space or tab inside s. +// - every back slash (\) is doubled, but only if immediately +// followed by double quote ("); +// - every double quote (") is escaped by back slash (\); +// - finally, s is wrapped with double quotes (arg -> "arg"), +// but only if there is space or tab inside s. func EscapeArg(s string) string { if len(s) == 0 { return `""` diff --git a/src/syscall/sockcmsg_unix.go b/src/syscall/sockcmsg_unix.go index a3dcf818da..6ade73e87e 100644 --- a/src/syscall/sockcmsg_unix.go +++ b/src/syscall/sockcmsg_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix // Socket control messages diff --git a/src/syscall/syscall_unix.go b/src/syscall/syscall_unix.go index 61ae09de4e..56d21b4ec1 100644 --- a/src/syscall/syscall_unix.go +++ b/src/syscall/syscall_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package syscall diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go index 317c0c1f34..56e771e086 100644 --- a/src/syscall/syscall_unix_test.go +++ b/src/syscall/syscall_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package syscall_test diff --git a/src/syscall/timestruct.go b/src/syscall/timestruct.go index 7cf4be45b1..8a03171ee5 100644 --- a/src/syscall/timestruct.go +++ b/src/syscall/timestruct.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package syscall diff --git a/src/testing/fstest/testfs.go b/src/testing/fstest/testfs.go index 9a65fbbd0b..ddb6080882 100644 --- a/src/testing/fstest/testfs.go +++ b/src/testing/fstest/testfs.go @@ -33,7 +33,6 @@ import ( // if err := fstest.TestFS(myFS, "file/that/should/be/present"); err != nil { // t.Fatal(err) // } -// func TestFS(fsys fs.FS, expected ...string) error { if err := testFS(fsys, expected...); err != nil { return err diff --git a/src/text/scanner/scanner.go b/src/text/scanner/scanner.go index 735982afcb..44be0b6bd4 100644 --- a/src/text/scanner/scanner.go +++ b/src/text/scanner/scanner.go @@ -60,7 +60,6 @@ func (pos Position) String() string { // // Use GoTokens to configure the Scanner such that it accepts all Go // literal tokens including Go identifiers. Comments will be skipped. -// const ( ScanIdents = 1 << -Ident ScanInts = 1 << -Int diff --git a/src/text/tabwriter/tabwriter.go b/src/text/tabwriter/tabwriter.go index 76dec7b358..d4cfcf556a 100644 --- a/src/text/tabwriter/tabwriter.go +++ b/src/text/tabwriter/tabwriter.go @@ -23,7 +23,6 @@ import ( // The text itself is stored in a separate buffer; cell only describes the // segment's size in bytes, its width in runes, and whether it's an htab // ('\t') terminated cell. -// type cell struct { size int // cell size in bytes width int // cell width in runes @@ -87,7 +86,6 @@ type cell struct { // The Writer must buffer input internally, because proper spacing // of one line may depend on the cells in future lines. Clients must // call Flush when done calling Write. -// type Writer struct { // configuration output io.Writer @@ -207,7 +205,6 @@ const ( // (for correct-looking results, tabwidth must correspond // to the tab width in the viewer displaying the result) // flags formatting control -// func (b *Writer) Init(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { if minwidth < 0 || tabwidth < 0 || padding < 0 { panic("negative minwidth, tabwidth, or padding") @@ -350,7 +347,6 @@ func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int) { // is the buffer position corresponding to the beginning of line0. // Returns the buffer position corresponding to the beginning of // line1 and an error, if any. -// func (b *Writer) format(pos0 int, line0, line1 int) (pos int) { pos = pos0 column := len(b.widths) @@ -427,7 +423,6 @@ func (b *Writer) updateWidth() { // width one for formatting purposes. // // The value 0xff was chosen because it cannot appear in a valid UTF-8 sequence. -// const Escape = '\xff' // Start escaped mode. @@ -446,7 +441,6 @@ func (b *Writer) startEscape(ch byte) { // is assumed to be zero for formatting purposes; if it was an HTML entity, // its width is assumed to be one. In all other cases, the width is the // unicode width of the text. -// func (b *Writer) endEscape() { switch b.endChar { case Escape: @@ -464,7 +458,6 @@ func (b *Writer) endEscape() { // Terminate the current cell by adding it to the list of cells of the // current line. Returns the number of cells in that line. -// func (b *Writer) terminateCell(htab bool) int { b.cell.htab = htab line := &b.lines[len(b.lines)-1] @@ -526,7 +519,6 @@ var hbar = []byte("---\n") // Write writes buf to the writer b. // The only errors returned are ones encountered // while writing to the underlying output stream. -// func (b *Writer) Write(buf []byte) (n int, err error) { defer b.handlePanic(&err, "Write") @@ -603,7 +595,6 @@ func (b *Writer) Write(buf []byte) (n int, err error) { // NewWriter allocates and initializes a new tabwriter.Writer. // The parameters are the same as for the Init function. -// func NewWriter(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { return new(Writer).Init(output, minwidth, tabwidth, padding, padchar, flags) } diff --git a/src/text/template/doc.go b/src/text/template/doc.go index 10093881fb..58cc97371b 100644 --- a/src/text/template/doc.go +++ b/src/text/template/doc.go @@ -18,7 +18,6 @@ structure as execution proceeds. The input text for a template is UTF-8-encoded text in any format. "Actions"--data evaluations or control structures--are delimited by "{{" and "}}"; all text outside actions is copied to the output unchanged. -Except for raw strings, actions may not span newlines, although comments can. Once parsed, a template may be executed safely in parallel, although if parallel executions share a Writer the output may be interleaved. diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index 8c8143396d..56566b920f 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -1220,15 +1220,19 @@ var cmpTests = []cmpTest{ {"eq .NilIface .Iface1", "false", true}, {"eq .NilIface 0", "false", true}, {"eq 0 .NilIface", "false", true}, + {"eq .Map .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map nil", "true", true}, // Uncomparable types but nil is OK. + {"eq nil .Map", "true", true}, // Uncomparable types but nil is OK. + {"eq .Map .NonNilMap", "false", true}, // Uncomparable types but nil is OK. // Errors - {"eq `xy` 1", "", false}, // Different types. - {"eq 2 2.0", "", false}, // Different types. - {"lt true true", "", false}, // Unordered types. - {"lt 1+0i 1+0i", "", false}, // Unordered types. - {"eq .Ptr 1", "", false}, // Incompatible types. - {"eq .Ptr .NegOne", "", false}, // Incompatible types. - {"eq .Map .Map", "", false}, // Uncomparable types. - {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq `xy` 1", "", false}, // Different types. + {"eq 2 2.0", "", false}, // Different types. + {"lt true true", "", false}, // Unordered types. + {"lt 1+0i 1+0i", "", false}, // Unordered types. + {"eq .Ptr 1", "", false}, // Incompatible types. + {"eq .Ptr .NegOne", "", false}, // Incompatible types. + {"eq .Map .V1", "", false}, // Uncomparable types. + {"eq .NonNilMap .NonNilMap", "", false}, // Uncomparable types. } func TestComparison(t *testing.T) { @@ -1237,16 +1241,18 @@ func TestComparison(t *testing.T) { Uthree, Ufour uint NegOne, Three int Ptr, NilPtr *int + NonNilMap map[int]int Map map[int]int V1, V2 V Iface1, NilIface fmt.Stringer }{ - Uthree: 3, - Ufour: 4, - NegOne: -1, - Three: 3, - Ptr: new(int), - Iface1: b, + Uthree: 3, + Ufour: 4, + NegOne: -1, + Three: 3, + Ptr: new(int), + NonNilMap: make(map[int]int), + Iface1: b, } for _, test := range cmpTests { text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go index dca5ed28db..1f63b361f8 100644 --- a/src/text/template/funcs.go +++ b/src/text/template/funcs.go @@ -436,14 +436,33 @@ func basicKind(v reflect.Value) (kind, error) { return invalidKind, errBadComparisonType } +// isNil returns true if v is the zero reflect.Value, or nil of its type. +func isNil(v reflect.Value) bool { + if v == zero { + return true + } + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: + return v.IsNil() + } + return false +} + +// canCompare reports whether v1 and v2 are both the same kind, or one is nil. +// Called only when dealing with nillable types, or there's about to be an error. +func canCompare(v1, v2 reflect.Value) bool { + k1 := v1.Kind() + k2 := v2.Kind() + if k1 == k2 { + return true + } + // We know the type can be compared to nil. + return k1 == reflect.Invalid || k2 == reflect.Invalid +} + // eq evaluates the comparison a == b || a == c || ... func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { arg1 = indirectInterface(arg1) - if arg1 != zero { - if t1 := arg1.Type(); !t1.Comparable() { - return false, fmt.Errorf("uncomparable type %s: %v", t1, arg1) - } - } if len(arg2) == 0 { return false, errNoComparison } @@ -479,11 +498,14 @@ func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { case uintKind: truth = arg1.Uint() == arg.Uint() default: - if arg == zero || arg1 == zero { - truth = arg1 == arg + if !canCompare(arg1, arg) { + return false, fmt.Errorf("non-comparable types %s: %v, %s: %v", arg1, arg1.Type(), arg.Type(), arg) + } + if isNil(arg1) || isNil(arg) { + truth = isNil(arg) == isNil(arg1) } else { - if t2 := arg.Type(); !t2.Comparable() { - return false, fmt.Errorf("uncomparable type %s: %v", t2, arg) + if !arg.Type().Comparable() { + return false, fmt.Errorf("non-comparable type %s: %v", arg, arg.Type()) } truth = arg1.Interface() == arg.Interface() } diff --git a/src/text/template/option.go b/src/text/template/option.go index 1035afad72..8d7d436bd0 100644 --- a/src/text/template/option.go +++ b/src/text/template/option.go @@ -38,7 +38,6 @@ type option struct { // The operation returns the zero value for the map type's element. // "missingkey=error" // Execution stops immediately with an error. -// func (t *Template) Option(opt ...string) *Template { t.init() for _, s := range opt { diff --git a/src/time/format.go b/src/time/format.go index 33e6543289..95fe08b772 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -87,7 +87,6 @@ import "errors" // // Some valid layouts are invalid time values for time.Parse, due to formats // such as _ for space padding and Z for zone information. -// const ( Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order. ANSIC = "Mon Jan _2 15:04:05 2006" diff --git a/src/time/sys_unix.go b/src/time/sys_unix.go index a949a6af22..0f06aa6ccd 100644 --- a/src/time/sys_unix.go +++ b/src/time/sys_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris +//go:build unix || (js && wasm) package time diff --git a/src/time/time.go b/src/time/time.go index d77074c5c2..88301ec16b 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -123,7 +123,6 @@ import ( // to t == u, since t.Equal uses the most accurate comparison available and // correctly handles the case when only one of its arguments has a monotonic // clock reading. -// type Time struct { // wall and ext encode the wall time seconds, wall time nanoseconds, // and optional monotonic clock reading in nanoseconds. @@ -603,7 +602,6 @@ const ( // To convert an integer number of units to a Duration, multiply: // seconds := 10 // fmt.Print(time.Duration(seconds)*time.Second) // prints 10s -// const ( Nanosecond Duration = 1 Microsecond = 1000 * Nanosecond diff --git a/src/time/zoneinfo.go b/src/time/zoneinfo.go index b460b9e6c5..9bcb183d77 100644 --- a/src/time/zoneinfo.go +++ b/src/time/zoneinfo.go @@ -197,14 +197,14 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64, // The reference implementation in localtime.c from // https://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz // implements the following algorithm for these cases: -// 1) If the first zone is unused by the transitions, use it. -// 2) Otherwise, if there are transition times, and the first -// transition is to a zone in daylight time, find the first -// non-daylight-time zone before and closest to the first transition -// zone. -// 3) Otherwise, use the first zone that is not daylight time, if -// there is one. -// 4) Otherwise, use the first zone. +// 1) If the first zone is unused by the transitions, use it. +// 2) Otherwise, if there are transition times, and the first +// transition is to a zone in daylight time, find the first +// non-daylight-time zone before and closest to the first transition +// zone. +// 3) Otherwise, use the first zone that is not daylight time, if +// there is one. +// 4) Otherwise, use the first zone. func (l *Location) lookupFirstZone() int { // Case 1. if !l.firstZoneUsed() { diff --git a/src/time/zoneinfo_unix.go b/src/time/zoneinfo_unix.go index 6414be3879..67b8beb47b 100644 --- a/src/time/zoneinfo_unix.go +++ b/src/time/zoneinfo_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || (darwin && !ios) || dragonfly || freebsd || (linux && !android) || netbsd || openbsd || solaris +//go:build unix && !ios && !android // Parse "zoneinfo" time zone file. // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. diff --git a/src/time/zoneinfo_unix_test.go b/src/time/zoneinfo_unix_test.go index de95295cd3..92680c4f8f 100644 --- a/src/time/zoneinfo_unix_test.go +++ b/src/time/zoneinfo_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || (darwin && !ios) || dragonfly || freebsd || (linux && !android) || netbsd || openbsd || solaris +//go:build unix && !ios && !android package time_test diff --git a/src/unicode/letter.go b/src/unicode/letter.go index 268e457a87..f4c950a883 100644 --- a/src/unicode/letter.go +++ b/src/unicode/letter.go @@ -334,7 +334,6 @@ type foldPair struct { // SimpleFold('1') = '1' // // SimpleFold(-2) = -2 -// func SimpleFold(r rune) rune { if r < 0 || r > MaxRune { return r diff --git a/src/unsafe/unsafe.go b/src/unsafe/unsafe.go index a6a255658b..ae69dea4af 100644 --- a/src/unsafe/unsafe.go +++ b/src/unsafe/unsafe.go @@ -180,7 +180,6 @@ type IntegerType int // hdr.Data = uintptr(unsafe.Pointer(p)) // hdr.Len = n // s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost -// type Pointer *ArbitraryType // Sizeof takes an expression x of any type and returns the size in bytes @@ -188,6 +187,7 @@ type Pointer *ArbitraryType // The size does not include any memory possibly referenced by x. // For instance, if x is a slice, Sizeof returns the size of the slice // descriptor, not the size of the memory referenced by the slice. +// For a struct, the size includes any padding introduced by field alignment. // The return value of Sizeof is a Go constant. func Sizeof(x ArbitraryType) uintptr diff --git a/test/codegen/bmi.go b/test/codegen/bmi.go index 0c25e0b796..2908d1b796 100644 --- a/test/codegen/bmi.go +++ b/test/codegen/bmi.go @@ -45,3 +45,19 @@ func blsr32(x int32) int32 { // amd64/v3:"BLSRL" return x & (x - 1) } + +func shlrx64(x []uint64, i int, s uint64) uint64 { + // amd64/v3: `SHRXQ\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*` + s = x[i] >> i + // amd64/v3: `SHLXQ\t[A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*` + s = x[i+1] << s + return s +} + +func shlrx32(x []uint32, i int, s uint32) uint32 { + // amd64/v3: `SHRXL\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*` + s = x[i] >> i + // amd64/v3: `SHLXL\t[A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*` + s = x[i+1] << s + return s +} diff --git a/test/codegen/mathbits.go b/test/codegen/mathbits.go index 859490c363..58d57b3523 100644 --- a/test/codegen/mathbits.go +++ b/test/codegen/mathbits.go @@ -13,7 +13,8 @@ import "math/bits" // ----------------------- // func LeadingZeros(n uint) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3:"LZCNTQ", -"BSRQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -22,7 +23,8 @@ func LeadingZeros(n uint) int { } func LeadingZeros64(n uint64) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3:"LZCNTQ", -"BSRQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -31,7 +33,8 @@ func LeadingZeros64(n uint64) int { } func LeadingZeros32(n uint32) int { - // amd64:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v3: "LZCNTL",- "BSRL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZW" // mips:"CLZ" @@ -40,7 +43,8 @@ func LeadingZeros32(n uint32) int { } func LeadingZeros16(n uint16) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL",- "BSRL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -49,7 +53,8 @@ func LeadingZeros16(n uint16) int { } func LeadingZeros8(n uint8) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL",- "BSRL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -62,7 +67,8 @@ func LeadingZeros8(n uint8) int { // --------------- // func Len(n uint) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3: "LZCNTQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -71,7 +77,8 @@ func Len(n uint) int { } func Len64(n uint64) int { - // amd64:"BSRQ" + // amd64/v1,amd64/v2:"BSRQ" + // amd64/v3: "LZCNTQ" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -88,7 +95,8 @@ func SubFromLen64(n uint64) int { } func Len32(n uint32) int { - // amd64:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRQ","LEAQ",-"CMOVQEQ" + // amd64/v3: "LZCNTL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -99,7 +107,8 @@ func Len32(n uint32) int { } func Len16(n uint16) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" @@ -108,7 +117,8 @@ func Len16(n uint16) int { } func Len8(n uint8) int { - // amd64:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v1,amd64/v2:"BSRL","LEAL",-"CMOVQEQ" + // amd64/v3: "LZCNTL" // s390x:"FLOGR" // arm:"CLZ" arm64:"CLZ" // mips:"CLZ" diff --git a/test/codegen/memcombine.go b/test/codegen/memcombine.go index 97e1d4bdfb..ad42538dcd 100644 --- a/test/codegen/memcombine.go +++ b/test/codegen/memcombine.go @@ -105,20 +105,20 @@ func load_be32_idx(b []byte, idx int) { sink32 = binary.BigEndian.Uint32(b[idx:]) } -func load_be16(b []byte) { +func load_be16(b []byte) uint16 { // amd64:`ROLW\s\$8`,-`MOVB`,-`OR` // arm64:`REV16W`,`MOVHU\s\(R[0-9]+\),`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVHZ\s\(.*\),`,-`OR`,-`ORW`,-`SLD`,-`SLW` - sink16 = binary.BigEndian.Uint16(b) + return binary.BigEndian.Uint16(b) } -func load_be16_idx(b []byte, idx int) { +func load_be16_idx(b []byte, idx int) uint16 { // amd64:`ROLW\s\$8`,-`MOVB`,-`OR` // arm64:`REV16W`,`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVHZ\s\(.*\)\(.*\*1\),`,-`OR`,-`ORW`,-`SLD`,-`SLW` - sink16 = binary.BigEndian.Uint16(b[idx:]) + return binary.BigEndian.Uint16(b[idx:]) } func load_le_byte2_uint16(s []byte) uint16 { @@ -463,7 +463,8 @@ func store_be32_idx(b []byte, idx int) { } func store_be16(b []byte) { - // amd64:`ROLW\s\$8`,-`SHR.` + // amd64/v1,amd64/v2:`ROLW\s\$8`,-`SHR.` + // amd64/v3:`MOVBEW`,-`ROLW` // arm64:`MOVH`,`REV16W`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVH\s.*\(.*\)$`,-`SRW\s`,-`SRD\s` @@ -471,7 +472,8 @@ func store_be16(b []byte) { } func store_be16_idx(b []byte, idx int) { - // amd64:`ROLW\s\$8`,-`SHR.` + // amd64/v1,amd64/v2:`ROLW\s\$8`,-`SHR.` + // amd64/v3: `MOVBEW` // arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,`REV16W`,-`MOVB` // ppc64le:`MOVHBR` // s390x:`MOVH\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s` @@ -511,7 +513,8 @@ func store_le_byte_8(b []byte, val uint64) { func store_be_byte_2(b []byte, val uint16) { _ = b[2] // arm64:`REV16W`,`MOVH\sR[0-9]+,\s1\(R[0-9]+\)`,-`MOVB` - // amd64:`MOVW\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB` + // amd64/v1,amd64/v2:`MOVW\s[A-Z]+,\s1\([A-Z]+\)`,-`MOVB` + // amd64/v3: `MOVBEW` b[1], b[2] = byte(val>>8), byte(val) } diff --git a/test/fixedbugs/bug121.go b/test/fixedbugs/bug121.go index 22c7181752..471c27eb82 100644 --- a/test/fixedbugs/bug121.go +++ b/test/fixedbugs/bug121.go @@ -9,7 +9,7 @@ package main type T func() type I interface { - f, g (); // ERROR "name list not allowed" + f, g (); // ERROR "unexpected comma" } type J interface { diff --git a/test/fixedbugs/issue13319.go b/test/fixedbugs/issue13319.go index c9b4896a05..7e1df3e45e 100644 --- a/test/fixedbugs/issue13319.go +++ b/test/fixedbugs/issue13319.go @@ -9,10 +9,10 @@ package main func f(int, int) { switch x { case 1: - f(1, g() // ERROR "expecting \)|expecting comma or \)" + f(1, g() // ERROR "expecting \)|possibly missing comma or \)" case 2: f() case 3: - f(1, g() // ERROR "expecting \)|expecting comma or \)" + f(1, g() // ERROR "expecting \)|possibly missing comma or \)" } } diff --git a/test/fixedbugs/issue13365.go b/test/fixedbugs/issue13365.go index b22fa0fb4e..02c6e03698 100644 --- a/test/fixedbugs/issue13365.go +++ b/test/fixedbugs/issue13365.go @@ -16,7 +16,7 @@ func main() { _ = [...]int{-1: 0} // ERROR "index must be non\-negative integer constant|index expression is negative|must not be negative" _ = []int{100: 0} - _ = [10]int{100: 0} // ERROR "array index 100 out of bounds|out of range" + _ = [10]int{100: 0} // ERROR "index 100 out of bounds|out of range" _ = [...]int{100: 0} _ = []int{t} // ERROR "cannot use .* as (type )?int( in slice literal)?|incompatible type" diff --git a/test/fixedbugs/issue52020.go b/test/fixedbugs/issue52020.go new file mode 100644 index 0000000000..0d18b1f7ae --- /dev/null +++ b/test/fixedbugs/issue52020.go @@ -0,0 +1,11 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + var _ interface{} = struct{ _ [1]int8 }{} +} diff --git a/test/run.go b/test/run.go index 61b31780d5..468379b4a9 100644 --- a/test/run.go +++ b/test/run.go @@ -312,8 +312,8 @@ type test struct { err error // expectFail indicates whether the (overall) test recipe is - // expected to fail under the current test configuration (e.g., -G=3 - // or GOEXPERIMENT=unified). + // expected to fail under the current test configuration (e.g., + // GOEXPERIMENT=unified). expectFail bool } @@ -336,7 +336,7 @@ func (t *test) initExpectFail() { if unifiedEnabled { failureSets = append(failureSets, unifiedFailures) } else { - failureSets = append(failureSets, g3Failures) + failureSets = append(failureSets, go118Failures) } filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows @@ -1961,25 +1961,21 @@ func overlayDir(dstRoot, srcRoot string) error { }) } -// The following is temporary scaffolding to get types2 typechecker -// up and running against the existing test cases. The explicitly -// listed files don't pass yet, usually because the error messages -// are slightly different (this list is not complete). Any errorcheck -// tests that require output from analysis phases past initial type- -// checking are also excluded since these phases are not running yet. -// We can get rid of this code once types2 is fully plugged in. +// The following sets of files are excluded from testing depending on configuration. +// The types2Failures(32Bit) files pass with the 1.17 compiler but don't pass with +// the 1.18 compiler using the new types2 type checker, or pass with sub-optimal +// error(s). -// List of files that the compiler cannot errorcheck with the new typechecker (compiler -G option). -// Temporary scaffolding until we pass all the tests at which point this map can be removed. +// List of files that the compiler cannot errorcheck with the new typechecker (types2). var types2Failures = setOf( "notinheap.go", // types2 doesn't report errors about conversions that are invalid due to //go:notinheap "shift1.go", // types2 reports two new errors which are probably not right "fixedbugs/issue10700.go", // types2 should give hint about ptr to interface "fixedbugs/issue18331.go", // missing error about misuse of //go:noescape (irgen needs code from noder) "fixedbugs/issue18419.go", // types2 reports no field or method member, but should say unexported - "fixedbugs/issue20233.go", // types2 reports two instead of one error (pref: -G=0) - "fixedbugs/issue20245.go", // types2 reports two instead of one error (pref: -G=0) - "fixedbugs/issue28268.go", // types2 reports follow-on errors (pref: -G=0) + "fixedbugs/issue20233.go", // types2 reports two instead of one error (preference: 1.17 compiler) + "fixedbugs/issue20245.go", // types2 reports two instead of one error (preference: 1.17 compiler) + "fixedbugs/issue28268.go", // types2 reports follow-on errors (preference: 1.17 compiler) "fixedbugs/issue31053.go", // types2 reports "unknown field" instead of "cannot refer to unexported field" ) @@ -1989,15 +1985,16 @@ var types2Failures32Bit = setOf( "fixedbugs/issue23305.go", // large untyped int passed to println (32-bit) ) -var g3Failures = setOf( - "typeparam/nested.go", // -G=3 doesn't support function-local types with generics - "typeparam/issue51521.go", // -G=3 produces bad panic message and link error +var go118Failures = setOf( + "typeparam/nested.go", // 1.18 compiler doesn't support function-local types with generics + "typeparam/issue51521.go", // 1.18 compiler produces bad panic message and link error ) -// In all of these cases, -G=0 reports reasonable errors, but either -G=0 or types2 -// report extra errors, so we can't match correctly on both. We now set the patterns -// to match correctly on all the types2 errors. -var g0Failures = setOf( +// In all of these cases, the 1.17 compiler reports reasonable errors, but either the +// 1.17 or 1.18 compiler report extra errors, so we can't match correctly on both. We +// now set the patterns to match correctly on all the 1.18 errors. +// This list remains here just as a reference and for comparison - these files all pass. +var _ = setOf( "import1.go", // types2 reports extra errors "initializerr.go", // types2 reports extra error "typecheck.go", // types2 reports extra error at function call diff --git a/test/syntax/composite.go b/test/syntax/composite.go index f891931b6c..b4e03f3167 100644 --- a/test/syntax/composite.go +++ b/test/syntax/composite.go @@ -7,5 +7,5 @@ package main var a = []int{ - 3 // ERROR "need trailing comma before newline in composite literal|expecting comma or }" + 3 // ERROR "need trailing comma before newline in composite literal|possibly missing comma or }" } diff --git a/test/typeparam/issue52124.go b/test/typeparam/issue52124.go new file mode 100644 index 0000000000..56318d5d4c --- /dev/null +++ b/test/typeparam/issue52124.go @@ -0,0 +1,9 @@ +// compile + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type I interface{ any | int }