diff --git a/.github/ISSUE_TEMPLATE/00-bug.md b/.github/ISSUE_TEMPLATE/00-bug.md deleted file mode 100644 index f056dab7dd..0000000000 --- a/.github/ISSUE_TEMPLATE/00-bug.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: Bugs -about: The go command, standard library, or anything else -title: "affected/package: " ---- - - - -### What version of Go are you using (`go version`)? - -
-$ go version
-
-
- -### Does this issue reproduce with the latest release? - - - -### What operating system and processor architecture are you using (`go env`)? - -
go env Output
-$ go env
-
-
- -### What did you do? - - - - - -### What did you expect to see? - - - -### What did you see instead? - - diff --git a/.github/ISSUE_TEMPLATE/00-bug.yml b/.github/ISSUE_TEMPLATE/00-bug.yml new file mode 100644 index 0000000000..5b0fda4950 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/00-bug.yml @@ -0,0 +1,94 @@ +# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#creating-issue-forms +# https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +name: Bugs +description: The go command, standard library, or anything else +title: "import/path: issue title" + +body: + - type: markdown + attributes: + value: | + Thanks for helping us improve! 🙏 Please answer these questions and provide as much information as possible about your problem. + + - type: input + id: go-version + attributes: + label: Go version + description: | + What version of Go are you using (`go version`)? + + Note: we only [support](https://go.dev/doc/devel/release#policy) the two most recent major releases. + placeholder: ex. go version go1.20.7 darwin/arm64 + validations: + required: true + + - type: textarea + id: go-env + attributes: + label: "Output of `go env` in your module/workspace:" + placeholder: | + GO111MODULE="" + GOARCH="arm64" + GOBIN="/Users/gopher/go/bin" + GOCACHE="/Users/gopher/go/cache" + GOENV="/Users/gopher/Library/Application Support/go/env" + GOEXE="" + GOEXPERIMENT="" + GOFLAGS="" + GOHOSTARCH="arm64" + GOHOSTOS="darwin" + GOINSECURE="" + GOMODCACHE="/Users/gopher/go/pkg/mod" + GONOPROXY="" + GONOSUMDB="" + GOOS="darwin" + GOPATH="/Users/gopher/go" + GOPRIVATE="" + GOPROXY="https://proxy.golang.org,direct" + GOROOT="/usr/local/go" + GOSUMDB="sum.golang.org" + GOTMPDIR="" + GOTOOLDIR="/usr/local/go/pkg/tool/darwin_arm64" + GOVCS="" + GOVERSION="go1.20.7" + GCCGO="gccgo" + AR="ar" + CC="clang" + CXX="clang++" + CGO_ENABLED="1" + GOMOD="/dev/null" + GOWORK="" + CGO_CFLAGS="-O2 -g" + CGO_CPPFLAGS="" + CGO_CXXFLAGS="-O2 -g" + CGO_FFLAGS="-O2 -g" + CGO_LDFLAGS="-O2 -g" + PKG_CONFIG="pkg-config" + GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/44/nbbyll_10jd0z8rj_qxm43740000gn/T/go-build2331607515=/tmp/go-build -gno-record-gcc-switches -fno-common" + render: shell + validations: + required: true + + - type: textarea + id: what-did-you-do + attributes: + label: "What did you do?" + description: "If possible, provide a recipe for reproducing the error. A complete runnable program is good. A link on [go.dev/play](https://go.dev/play) is best." + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: "What did you see happen?" + description: Command invocations and their associated output, functions with their arguments and return results, full stacktraces for panics (upload a file if it is very long), etc. Prefer copying text output over using screenshots. + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: "What did you expect to see?" + description: Why is the current output incorrect, and any additional context we may need to understand the issue. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/01-pkgsite.md b/.github/ISSUE_TEMPLATE/01-pkgsite.md deleted file mode 100644 index 31f0fd16b1..0000000000 --- a/.github/ISSUE_TEMPLATE/01-pkgsite.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: Pkg.go.dev bugs or feature requests -about: Issues or feature requests for the documentation site -title: "x/pkgsite: " -labels: pkgsite ---- - - - -### What is the URL of the page with the issue? - - - -### What is your user agent? - - - - - -### Screenshot - - - - - -### What did you do? - - - - - -### What did you expect to see? - - - -### What did you see instead? - - diff --git a/.github/ISSUE_TEMPLATE/01-pkgsite.yml b/.github/ISSUE_TEMPLATE/01-pkgsite.yml new file mode 100644 index 0000000000..aaf39b2928 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-pkgsite.yml @@ -0,0 +1,47 @@ +name: Pkg.go.dev bugs or feature requests +description: Issues or feature requests for the documentation site +title: "x/pkgsite: issue title" +labels: ["pkgsite"] +body: + - type: markdown + attributes: + value: "Please answer these questions before submitting your issue. Thanks!" + - type: input + id: url + attributes: + label: "What is the URL of the page with the issue?" + validations: + required: true + - type: input + id: user-agent + attributes: + label: "What is your user agent?" + description: "You can find your user agent here: https://www.google.com/search?q=what+is+my+user+agent" + validations: + required: true + - type: textarea + id: screenshot + attributes: + label: "Screenshot" + description: "Please paste a screenshot of the page." + validations: + required: false + - type: textarea + id: what-did-you-do + attributes: + label: "What did you do?" + description: "If possible, provide a recipe for reproducing the error. Starting with a Private/Incognito tab/window may help rule out problematic browser extensions." + validations: + required: true + - type: textarea + id: actual-behavior + attributes: + label: "What did you see happen?" + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: "What did you expect to see?" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/02-pkgsite-removal.md b/.github/ISSUE_TEMPLATE/02-pkgsite-removal.md deleted file mode 100644 index 97fe317f5b..0000000000 --- a/.github/ISSUE_TEMPLATE/02-pkgsite-removal.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: Pkg.go.dev package removal request -about: Request a package be removed from the documentation site (pkg.go.dev) -title: "x/pkgsite: package removal request for [type path here]" -labels: pkgsite/package-removal ---- - - - -### What is the path of the package that you would like to have removed? - - - - - -### Are you the owner of this package? - - - - - -### What is the reason that you could not retract this package instead? - - - - diff --git a/.github/ISSUE_TEMPLATE/02-pkgsite-removal.yml b/.github/ISSUE_TEMPLATE/02-pkgsite-removal.yml new file mode 100644 index 0000000000..693f4999dc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-pkgsite-removal.yml @@ -0,0 +1,42 @@ +name: Pkg.go.dev package removal request +description: Request a package be removed from the documentation site (pkg.go.dev) +title: "x/pkgsite: package removal request for [type path here]" +labels: ["pkgsite/package-removal"] +body: + - type: markdown + attributes: + value: "Please answer these questions before submitting your issue. Thanks!" + - type: input + id: package-path + attributes: + label: "What is the path of the package that you would like to have removed?" + description: | + We can remove packages with a shared path prefix. + For example, a request for 'github.com/author' would remove all pkg.go.dev pages with that package path prefix. + validations: + required: true + - type: textarea + id: package-owner + attributes: + label: "Are you the owner of this package?" + description: | + Only the package owners can request to have their packages removed from pkg.go.dev. + If the package path doesn't include your github username, please provide some other form of proof of ownership. + validations: + required: true + - type: textarea + id: retraction-reason + attributes: + label: "What is the reason that you could not retract this package instead?" + description: | + Requesting we remove a module here only hides the generated documentation on pkg.go.dev. + It does not affect the behaviour of proxy.golang.org or the go command. + Instead we recommend using the retract directive which will be processed by all 3 of the above. + + If you have deleted your repo, please recreate it and publish a retraction. + + Retracting a module version involves adding a retract directive to your go.mod file and publishing a new version. + For example: https://github.com/jba/retract-demo/blob/main/go.mod#L5-L8. + See https://pkg.go.dev/about#removing-a-package for additional tips on retractions. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/03-gopls.md b/.github/ISSUE_TEMPLATE/03-gopls.md deleted file mode 100644 index a6b9d913c1..0000000000 --- a/.github/ISSUE_TEMPLATE/03-gopls.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -name: Gopls bugs or feature requests -about: Issues or feature requests for the Go language server (gopls) -title: "x/tools/gopls: " -labels: - - gopls - - Tools ---- - - - -### gopls version - - - - - -### go env - - - - -### What did you do? - - - - - -### What did you expect to see? - - - -### What did you see instead? - - - -### Editor and settings - - - - - -### Logs - - - - diff --git a/.github/ISSUE_TEMPLATE/03-gopls.yml b/.github/ISSUE_TEMPLATE/03-gopls.yml new file mode 100644 index 0000000000..5db1315f27 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03-gopls.yml @@ -0,0 +1,56 @@ +name: Gopls bugs or feature requests +description: Issues or feature requests for the Go language server (gopls) +title: "x/tools/gopls: issue title" +labels: ["gopls", "Tools"] +body: + - type: markdown + attributes: + value: "Please answer these questions before submitting your issue. Thanks!" + - type: input + id: gopls-version + attributes: + label: "gopls version" + description: "Output of `gopls -v version` on the command line" + validations: + required: true + - type: textarea + id: go-env + attributes: + label: "go env" + description: "Output of `go env` on the command line in your workspace directory" + render: shell + validations: + required: true + - type: textarea + id: what-did-you-do + attributes: + label: "What did you do?" + description: "If possible, provide a recipe for reproducing the error. A complete runnable program is good. A link on [go.dev/play](https://go.dev/play) is better. A failing unit test is the best." + validations: + required: true + - type: textarea + id: actual-behavior + attributes: + label: "What did you see happen?" + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: "What did you expect to see?" + validations: + required: true + - type: textarea + id: editor-and-settings + attributes: + label: "Editor and settings" + description: "Your editor and any settings you have configured (for example, your VSCode settings.json file)" + validations: + required: false + - type: textarea + id: logs + attributes: + label: "Logs" + description: "If possible please include gopls logs. Instructions for capturing them can be found here: https://github.com/golang/tools/blob/master/gopls/doc/troubleshooting.md#capture-logs" + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/04-vuln.md b/.github/ISSUE_TEMPLATE/04-vuln.md deleted file mode 100644 index 7e129d78db..0000000000 --- a/.github/ISSUE_TEMPLATE/04-vuln.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -name: Go vulnerability management - bugs and feature requests -about: Issues or feature requests about Go vulnerability management -title: "x/vuln: " -labels: "vulncheck or vulndb" ---- - - - -### What version of Go are you using (`go version`)? - -
-$ go version
-
-
- -### Does this issue reproduce at the latest version of golang.org/x/vuln? - - - -### What operating system and processor architecture are you using (`go env`)? - -
go env Output
-$ go env
-
-
- -### What did you do? - - - - - -### What did you expect to see? - - - -### What did you see instead? - - diff --git a/.github/ISSUE_TEMPLATE/04-vuln.yml b/.github/ISSUE_TEMPLATE/04-vuln.yml new file mode 100644 index 0000000000..dd40af99c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/04-vuln.yml @@ -0,0 +1,52 @@ +name: Go vulnerability management - bugs and feature requests +description: Issues or feature requests about Go vulnerability management +title: "x/vuln: issue title" +labels: ["vulncheck or vulndb"] +body: + - type: markdown + attributes: + value: "Please answer these questions before submitting your issue. Thanks! To add a new vulnerability to the Go vulnerability database (https://vuln.go.dev), see https://go.dev/s/vulndb-report-new. To report an issue about a report, see https://go.dev/s/vulndb-report-feedback." + - type: textarea + id: govulncheck-version + attributes: + label: govulncheck version + description: What version of govulncheck are you using (`govulncheck -version`)? + placeholder: | + Go: devel go1.22-0262ea1ff9 Thu Oct 26 18:46:50 2023 +0000 + Scanner: govulncheck@v1.0.2-0.20231108200754-fcf7dff7b242 + DB: https://vuln.go.dev + DB updated: 2023-11-21 15:39:17 +0000 UTC + validations: + required: true + - type: textarea + id: reproduce-latest-version + attributes: + label: "Does this issue reproduce at the latest version of golang.org/x/vuln?" + validations: + required: true + - type: textarea + id: go-env + attributes: + label: "Output of `go env` in your module/workspace:" + render: shell + validations: + required: true + - type: textarea + id: what-did-you-do + attributes: + label: "What did you do?" + description: "If possible, provide a recipe for reproducing the error. A complete runnable program is good. A link on [go.dev/play](https://go.dev/play) is best." + validations: + required: true + - type: textarea + id: actual-behavior + attributes: + label: "What did you see happen?" + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: "What did you expect to see?" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/10-proposal.md b/.github/ISSUE_TEMPLATE/10-proposal.md deleted file mode 100644 index ab30ddf417..0000000000 --- a/.github/ISSUE_TEMPLATE/10-proposal.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: Proposals -about: New external API or other notable changes -title: "proposal: affected/package: " -labels: Proposal ---- - - - - diff --git a/.github/ISSUE_TEMPLATE/10-proposal.yml b/.github/ISSUE_TEMPLATE/10-proposal.yml new file mode 100644 index 0000000000..d2a256c5ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/10-proposal.yml @@ -0,0 +1,15 @@ +name: Proposals +description: New external API or other notable changes +title: "proposal: import/path: proposal title" +labels: ["Proposal"] +body: + - type: markdown + attributes: + value: "Our proposal process is documented here: https://go.dev/s/proposal-process" + - type: textarea + id: proposal-details + attributes: + label: "Proposal Details" + description: "Please provide the details of your proposal here." + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/11-language-change.md b/.github/ISSUE_TEMPLATE/11-language-change.md deleted file mode 100644 index cc9b82b3b7..0000000000 --- a/.github/ISSUE_TEMPLATE/11-language-change.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -name: Language Change Proposals -about: Changes to the language -title: "proposal: Go 2: " -labels: - - Proposal - - v2 - - LanguageChange ---- - - - -### Author background - -- **Would you consider yourself a novice, intermediate, or experienced Go programmer?** -- **What other languages do you have experience with?** - -### Related proposals - -- **Has this idea, or one like it, been proposed before?** - - **If so, how does this proposal differ?** -- **Does this affect error handling?** - - **If so, how does this differ from previous error handling proposals?** -- **Is this about generics?** - - **If so, how does this relate to the accepted design and other generics proposals?** - -### Proposal - -- **What is the proposed change?** -- **Who does this proposal help, and why?** -- **Please describe as precisely as possible the change to the language.** -- **What would change in the language spec?** -- **Please also describe the change informally, as in a class teaching Go.** -- **Is this change backward compatible?** - - Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit. - Show example code before and after the change. - - **Before** - - **After** -- **Orthogonality: how does this change interact or overlap with existing features?** -- **Is the goal of this change a performance improvement?** - - **If so, what quantifiable improvement should we expect?** - - **How would we measure it?** - -### Costs - -- **Would this change make Go easier or harder to learn, and why?** -- **What is the cost of this proposal? (Every language change has a cost).** -- **How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?** -- **What is the compile time cost?** -- **What is the run time cost?** -- **Can you describe a possible implementation?** -- **Do you have a prototype? (This is not required.)** diff --git a/.github/ISSUE_TEMPLATE/11-language-change.yml b/.github/ISSUE_TEMPLATE/11-language-change.yml new file mode 100644 index 0000000000..37ba2d7e40 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/11-language-change.yml @@ -0,0 +1,165 @@ +name: Language Change Proposals +description: Changes to the language +labels: ["Proposal", "v2", "LanguageChange"] +title: "proposal: Go 2: proposal title" + + +body: + - type: markdown + attributes: + value: | + ## Our process for evaluating language changes can be found [here](https://go.googlesource.com/proposal/+/refs/heads/master#language-changes) + + - type: dropdown + id: author-go-experience + attributes: + label: "Go Programming Experience" + description: "Would you consider yourself a novice, intermediate, or experienced Go programmer?" + options: + - "Novice" + - "Intermediate" + - "Experienced" + default: 1 + + - type: input + id: author-other-languages-experience + attributes: + label: "Other Languages Experience" + description: "What other languages do you have experience with?" + placeholder: "Go, Python, JS, Rust" + validations: + required: false + + - type: checkboxes + id: related-idea + attributes: + label: "Related Idea" + options: + - label: "Has this idea, or one like it, been proposed before?" + - label: "Does this affect error handling?" + - label: "Is this about generics?" + - label: "Is this change backward compatible? Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit" + + - type: textarea + id: related-proposals + attributes: + label: Has this idea, or one like it, been proposed before? + description: If so, how does this proposal differ? + placeholder: | + Yes or No + + If yes, + 1. Mention the related proposals + 2. then describe how this proposal differs + validations: + required: true + + - type: textarea + id: error-handling-proposal + attributes: + label: Does this affect error handling? + description: If so, how does this differ from previous error handling proposals? + placeholder: | + Yes or No + + If yes, + 1.how does this differ from previous error handling proposals? + + validations: + required: true + + - type: textarea + id: generics-proposal + attributes: + label: Is this about generics? + description: If so, how does this relate to the accepted design and other generics proposals? + placeholder: | + Yes or No + + If yes, + 1. how does this relate to the accepted design and other generics proposals? + + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: "Proposal" + description: "What is the proposed change? Who does this proposal help, and why? Please describe as precisely as possible the change to the language." + validations: + required: true + + - type: textarea + id: language-spec-changes + attributes: + label: "Language Spec Changes" + description: "What would change in the language spec?" + validations: + required: false + + - type: textarea + id: informal-change + attributes: + label: "Informal Change" + description: "Please also describe the change informally, as in a class teaching Go." + validations: + required: false + + - type: textarea + id: go-backwards-compatiblity + attributes: + label: Is this change backward compatible? + description: Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit. + placeholder: | + Yes or No + + If yes, + 1. Show example code before and after the change. + + validations: + required: true + + - type: textarea + id: orthogonality + attributes: + label: "Orthogonality: How does this change interact or overlap with existing features?" + description: "Is the goal of this change a performance improvement? If so, what quantifiable improvement should we expect? How would we measure it?" + validations: + required: false + + - type: textarea + id: learning-curve + attributes: + label: "Would this change make Go easier or harder to learn, and why?" + + - type: textarea + id: cost-description + attributes: + label: "Cost Description" + description: "What is the cost of this proposal? (Every language change has a cost)" + + - type: input + id: go-toolchain + attributes: + label: Changes to Go ToolChain + description: "How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected? " + validations: + required: false + + - type: input + id: perf-costs + attributes: + label: Performance Costs + description: "What is the compile time cost? What is the run time cost? " + validations: + required: false + + - type: textarea + id: prototype + attributes: + label: "Prototype" + description: "Can you describe a possible implementation?" + validations: + required: false + diff --git a/.github/ISSUE_TEMPLATE/12-telemetry.yml b/.github/ISSUE_TEMPLATE/12-telemetry.yml index 7f1a29c634..4215abfa99 100644 --- a/.github/ISSUE_TEMPLATE/12-telemetry.yml +++ b/.github/ISSUE_TEMPLATE/12-telemetry.yml @@ -1,6 +1,6 @@ name: Go Telemetry Proposals description: New telemetry counter or update on an existing one -title: "x/telemetry/config: " +title: "x/telemetry/config: proposal title" labels: ["Telemetry-Proposal"] projects: ["golang/29"] body: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c07f1e4d1c..d6257daf2f 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: false +blank_issues_enabled: true contact_links: - name: Questions about: Please use one of the forums for questions or general discussions diff --git a/api/go1.22.txt b/api/go1.22.txt new file mode 100644 index 0000000000..86eb80deaf --- /dev/null +++ b/api/go1.22.txt @@ -0,0 +1,144 @@ +pkg archive/tar, method (*Writer) AddFS(fs.FS) error #58000 +pkg archive/tar, type FileInfoNames interface { Gname, IsDir, ModTime, Mode, Name, Size, Sys, Uname } #50102 +pkg archive/tar, type FileInfoNames interface, Gname(int) (string, error) #50102 +pkg archive/tar, type FileInfoNames interface, IsDir() bool #50102 +pkg archive/tar, type FileInfoNames interface, ModTime() time.Time #50102 +pkg archive/tar, type FileInfoNames interface, Mode() fs.FileMode #50102 +pkg archive/tar, type FileInfoNames interface, Name() string #50102 +pkg archive/tar, type FileInfoNames interface, Size() int64 #50102 +pkg archive/tar, type FileInfoNames interface, Sys() interface{} #50102 +pkg archive/tar, type FileInfoNames interface, Uname(int) (string, error) #50102 +pkg archive/zip, method (*Writer) AddFS(fs.FS) error #54898 +pkg cmp, func Or[$0 comparable](...$0) $0 #60204 +pkg crypto/x509, func OIDFromInts([]uint64) (OID, error) #60665 +pkg crypto/x509, method (*CertPool) AddCertWithConstraint(*Certificate, func([]*Certificate) error) #57178 +pkg crypto/x509, method (OID) Equal(OID) bool #60665 +pkg crypto/x509, method (OID) EqualASN1OID(asn1.ObjectIdentifier) bool #60665 +pkg crypto/x509, method (OID) String() string #60665 +pkg crypto/x509, type Certificate struct, Policies []OID #60665 +pkg crypto/x509, type OID struct #60665 +pkg database/sql, method (*Null[$0]) Scan(interface{}) error #60370 +pkg database/sql, method (Null[$0]) Value() (driver.Value, error) #60370 +pkg database/sql, type Null[$0 interface{}] struct #60370 +pkg database/sql, type Null[$0 interface{}] struct, V $0 #60370 +pkg database/sql, type Null[$0 interface{}] struct, Valid bool #60370 +pkg debug/elf, const R_LARCH_64_PCREL = 109 #63725 +pkg debug/elf, const R_LARCH_64_PCREL R_LARCH #63725 +pkg debug/elf, const R_LARCH_ADD6 = 105 #63725 +pkg debug/elf, const R_LARCH_ADD6 R_LARCH #63725 +pkg debug/elf, const R_LARCH_ADD_ULEB128 = 107 #63725 +pkg debug/elf, const R_LARCH_ADD_ULEB128 R_LARCH #63725 +pkg debug/elf, const R_LARCH_ALIGN = 102 #63725 +pkg debug/elf, const R_LARCH_ALIGN R_LARCH #63725 +pkg debug/elf, const R_LARCH_CFA = 104 #63725 +pkg debug/elf, const R_LARCH_CFA R_LARCH #63725 +pkg debug/elf, const R_LARCH_DELETE = 101 #63725 +pkg debug/elf, const R_LARCH_DELETE R_LARCH #63725 +pkg debug/elf, const R_LARCH_PCREL20_S2 = 103 #63725 +pkg debug/elf, const R_LARCH_PCREL20_S2 R_LARCH #63725 +pkg debug/elf, const R_LARCH_SUB6 = 106 #63725 +pkg debug/elf, const R_LARCH_SUB6 R_LARCH #63725 +pkg debug/elf, const R_LARCH_SUB_ULEB128 = 108 #63725 +pkg debug/elf, const R_LARCH_SUB_ULEB128 R_LARCH #63725 +pkg debug/elf, const R_MIPS_PC32 = 248 #61974 +pkg debug/elf, const R_MIPS_PC32 R_MIPS #61974 +pkg encoding/base32, method (*Encoding) AppendDecode([]uint8, []uint8) ([]uint8, error) #53693 +pkg encoding/base32, method (*Encoding) AppendEncode([]uint8, []uint8) []uint8 #53693 +pkg encoding/base64, method (*Encoding) AppendDecode([]uint8, []uint8) ([]uint8, error) #53693 +pkg encoding/base64, method (*Encoding) AppendEncode([]uint8, []uint8) []uint8 #53693 +pkg encoding/hex, func AppendDecode([]uint8, []uint8) ([]uint8, error) #53693 +pkg encoding/hex, func AppendEncode([]uint8, []uint8) []uint8 #53693 +pkg go/ast, func NewPackage //deprecated #52463 +pkg go/ast, func Unparen(Expr) Expr #60061 +pkg go/ast, type Importer //deprecated #52463 +pkg go/ast, type Object //deprecated #52463 +pkg go/ast, type Package //deprecated #52463 +pkg go/ast, type Scope //deprecated #52463 +pkg go/types, func NewAlias(*TypeName, Type) *Alias #63223 +pkg go/types, func Unalias(Type) Type #63223 +pkg go/types, method (*Alias) Obj() *TypeName #63223 +pkg go/types, method (*Alias) String() string #63223 +pkg go/types, method (*Alias) Underlying() Type #63223 +pkg go/types, method (*Info) PkgNameOf(*ast.ImportSpec) *PkgName #62037 +pkg go/types, method (Checker) PkgNameOf(*ast.ImportSpec) *PkgName #62037 +pkg go/types, type Alias struct #63223 +pkg go/types, type Info struct, FileVersions map[*ast.File]string #62605 +pkg go/version, func Compare(string, string) int #62039 +pkg go/version, func IsValid(string) bool #62039 +pkg go/version, func Lang(string) string #62039 +pkg html/template, const ErrJSTemplate //deprecated #61619 +pkg io, method (*SectionReader) Outer() (ReaderAt, int64, int64) #61870 +pkg log/slog, func SetLogLoggerLevel(Level) Level #62418 +pkg math/big, method (*Rat) FloatPrec() (int, bool) #50489 +pkg math/rand/v2, func ExpFloat64() float64 #61716 +pkg math/rand/v2, func Float32() float32 #61716 +pkg math/rand/v2, func Float64() float64 #61716 +pkg math/rand/v2, func Int() int #61716 +pkg math/rand/v2, func Int32() int32 #61716 +pkg math/rand/v2, func Int32N(int32) int32 #61716 +pkg math/rand/v2, func Int64() int64 #61716 +pkg math/rand/v2, func Int64N(int64) int64 #61716 +pkg math/rand/v2, func IntN(int) int #61716 +pkg math/rand/v2, func N[$0 intType]($0) $0 #61716 +pkg math/rand/v2, func New(Source) *Rand #61716 +pkg math/rand/v2, func NewChaCha8([32]uint8) *ChaCha8 #61716 +pkg math/rand/v2, func NewPCG(uint64, uint64) *PCG #61716 +pkg math/rand/v2, func NewZipf(*Rand, float64, float64, uint64) *Zipf #61716 +pkg math/rand/v2, func NormFloat64() float64 #61716 +pkg math/rand/v2, func Perm(int) []int #61716 +pkg math/rand/v2, func Shuffle(int, func(int, int)) #61716 +pkg math/rand/v2, func Uint32() uint32 #61716 +pkg math/rand/v2, func Uint32N(uint32) uint32 #61716 +pkg math/rand/v2, func Uint64() uint64 #61716 +pkg math/rand/v2, func Uint64N(uint64) uint64 #61716 +pkg math/rand/v2, func UintN(uint) uint #61716 +pkg math/rand/v2, method (*ChaCha8) MarshalBinary() ([]uint8, error) #61716 +pkg math/rand/v2, method (*ChaCha8) Seed([32]uint8) #61716 +pkg math/rand/v2, method (*ChaCha8) Uint64() uint64 #61716 +pkg math/rand/v2, method (*ChaCha8) UnmarshalBinary([]uint8) error #61716 +pkg math/rand/v2, method (*PCG) MarshalBinary() ([]uint8, error) #61716 +pkg math/rand/v2, method (*PCG) Seed(uint64, uint64) #61716 +pkg math/rand/v2, method (*PCG) Uint64() uint64 #61716 +pkg math/rand/v2, method (*PCG) UnmarshalBinary([]uint8) error #61716 +pkg math/rand/v2, method (*Rand) ExpFloat64() float64 #61716 +pkg math/rand/v2, method (*Rand) Float32() float32 #61716 +pkg math/rand/v2, method (*Rand) Float64() float64 #61716 +pkg math/rand/v2, method (*Rand) Int() int #61716 +pkg math/rand/v2, method (*Rand) Int32() int32 #61716 +pkg math/rand/v2, method (*Rand) Int32N(int32) int32 #61716 +pkg math/rand/v2, method (*Rand) Int64() int64 #61716 +pkg math/rand/v2, method (*Rand) Int64N(int64) int64 #61716 +pkg math/rand/v2, method (*Rand) IntN(int) int #61716 +pkg math/rand/v2, method (*Rand) NormFloat64() float64 #61716 +pkg math/rand/v2, method (*Rand) Perm(int) []int #61716 +pkg math/rand/v2, method (*Rand) Shuffle(int, func(int, int)) #61716 +pkg math/rand/v2, method (*Rand) Uint32() uint32 #61716 +pkg math/rand/v2, method (*Rand) Uint32N(uint32) uint32 #61716 +pkg math/rand/v2, method (*Rand) Uint64() uint64 #61716 +pkg math/rand/v2, method (*Rand) Uint64N(uint64) uint64 #61716 +pkg math/rand/v2, method (*Rand) UintN(uint) uint #61716 +pkg math/rand/v2, method (*Zipf) Uint64() uint64 #61716 +pkg math/rand/v2, type ChaCha8 struct #61716 +pkg math/rand/v2, type PCG struct #61716 +pkg math/rand/v2, type Rand struct #61716 +pkg math/rand/v2, type Source interface { Uint64 } #61716 +pkg math/rand/v2, type Source interface, Uint64() uint64 #61716 +pkg math/rand/v2, type Zipf struct #61716 +pkg net, method (*TCPConn) WriteTo(io.Writer) (int64, error) #58808 +pkg net/http, func FileServerFS(fs.FS) Handler #51971 +pkg net/http, func NewFileTransportFS(fs.FS) RoundTripper #51971 +pkg net/http, func ServeFileFS(ResponseWriter, *Request, fs.FS, string) #51971 +pkg net/http, method (*Request) PathValue(string) string #61410 +pkg net/http, method (*Request) SetPathValue(string, string) #61410 +pkg net/netip, method (AddrPort) Compare(AddrPort) int #61642 +pkg os, method (*File) WriteTo(io.Writer) (int64, error) #58808 +pkg reflect, func PtrTo //deprecated #59599 +pkg reflect, func TypeFor[$0 interface{}]() Type #60088 +pkg slices, func Concat[$0 interface{ ~[]$1 }, $1 interface{}](...$0) $0 #56353 +pkg syscall (linux-386), type SysProcAttr struct, PidFD *int #51246 +pkg syscall (linux-386-cgo), type SysProcAttr struct, PidFD *int #51246 +pkg syscall (linux-amd64), type SysProcAttr struct, PidFD *int #51246 +pkg syscall (linux-amd64-cgo), type SysProcAttr struct, PidFD *int #51246 +pkg syscall (linux-arm), type SysProcAttr struct, PidFD *int #51246 +pkg syscall (linux-arm-cgo), type SysProcAttr struct, PidFD *int #51246 +pkg testing/slogtest, func Run(*testing.T, func(*testing.T) slog.Handler, func(*testing.T) map[string]interface{}) #61758 diff --git a/api/next/50102.txt b/api/next/50102.txt deleted file mode 100644 index dcb7977e83..0000000000 --- a/api/next/50102.txt +++ /dev/null @@ -1,9 +0,0 @@ -pkg archive/tar, type FileInfoNames interface { Gname, IsDir, ModTime, Mode, Name, Size, Sys, Uname } #50102 -pkg archive/tar, type FileInfoNames interface, Gname(int) (string, error) #50102 -pkg archive/tar, type FileInfoNames interface, IsDir() bool #50102 -pkg archive/tar, type FileInfoNames interface, ModTime() time.Time #50102 -pkg archive/tar, type FileInfoNames interface, Mode() fs.FileMode #50102 -pkg archive/tar, type FileInfoNames interface, Name() string #50102 -pkg archive/tar, type FileInfoNames interface, Size() int64 #50102 -pkg archive/tar, type FileInfoNames interface, Sys() interface{} #50102 -pkg archive/tar, type FileInfoNames interface, Uname(int) (string, error) #50102 diff --git a/api/next/50489.txt b/api/next/50489.txt deleted file mode 100644 index 5fc8723c9e..0000000000 --- a/api/next/50489.txt +++ /dev/null @@ -1 +0,0 @@ -pkg math/big, method (*Rat) FloatPrec() (int, bool) #50489 diff --git a/api/next/51246.txt b/api/next/51246.txt deleted file mode 100644 index c8806c64a3..0000000000 --- a/api/next/51246.txt +++ /dev/null @@ -1,6 +0,0 @@ -pkg syscall (linux-386), type SysProcAttr struct, PidFD *int #51246 -pkg syscall (linux-386-cgo), type SysProcAttr struct, PidFD *int #51246 -pkg syscall (linux-amd64), type SysProcAttr struct, PidFD *int #51246 -pkg syscall (linux-amd64-cgo), type SysProcAttr struct, PidFD *int #51246 -pkg syscall (linux-arm), type SysProcAttr struct, PidFD *int #51246 -pkg syscall (linux-arm-cgo), type SysProcAttr struct, PidFD *int #51246 diff --git a/api/next/51971.txt b/api/next/51971.txt deleted file mode 100644 index f884c3c079..0000000000 --- a/api/next/51971.txt +++ /dev/null @@ -1,3 +0,0 @@ -pkg net/http, func ServeFileFS(ResponseWriter, *Request, fs.FS, string) #51971 -pkg net/http, func FileServerFS(fs.FS) Handler #51971 -pkg net/http, func NewFileTransportFS(fs.FS) RoundTripper #51971 diff --git a/api/next/53693.txt b/api/next/53693.txt deleted file mode 100644 index 5a6f09e6c8..0000000000 --- a/api/next/53693.txt +++ /dev/null @@ -1,6 +0,0 @@ -pkg encoding/base32, method (*Encoding) AppendDecode([]uint8, []uint8) ([]uint8, error) #53693 -pkg encoding/base32, method (*Encoding) AppendEncode([]uint8, []uint8) []uint8 #53693 -pkg encoding/base64, method (*Encoding) AppendDecode([]uint8, []uint8) ([]uint8, error) #53693 -pkg encoding/base64, method (*Encoding) AppendEncode([]uint8, []uint8) []uint8 #53693 -pkg encoding/hex, func AppendDecode([]uint8, []uint8) ([]uint8, error) #53693 -pkg encoding/hex, func AppendEncode([]uint8, []uint8) []uint8 #53693 diff --git a/api/next/54898.txt b/api/next/54898.txt deleted file mode 100644 index 44133bd377..0000000000 --- a/api/next/54898.txt +++ /dev/null @@ -1 +0,0 @@ -pkg archive/zip, method (*Writer) AddFS(fs.FS) error #54898 diff --git a/api/next/56353.txt b/api/next/56353.txt deleted file mode 100644 index c2504a7f63..0000000000 --- a/api/next/56353.txt +++ /dev/null @@ -1 +0,0 @@ -pkg slices, func Concat[$0 interface{ ~[]$1 }, $1 interface{}](...$0) $0 #56353 diff --git a/api/next/57178.txt b/api/next/57178.txt deleted file mode 100644 index 3ce4d408eb..0000000000 --- a/api/next/57178.txt +++ /dev/null @@ -1 +0,0 @@ -pkg crypto/x509, method (*CertPool) AddCertWithConstraint(*Certificate, func([]*Certificate) error) #57178 diff --git a/api/next/58000.txt b/api/next/58000.txt deleted file mode 100644 index 94db9637cb..0000000000 --- a/api/next/58000.txt +++ /dev/null @@ -1 +0,0 @@ -pkg archive/tar, method (*Writer) AddFS(fs.FS) error #58000 diff --git a/api/next/58808.txt b/api/next/58808.txt deleted file mode 100644 index f1105c3168..0000000000 --- a/api/next/58808.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg net, method (*TCPConn) WriteTo(io.Writer) (int64, error) #58808 -pkg os, method (*File) WriteTo(io.Writer) (int64, error) #58808 diff --git a/api/next/59599.txt b/api/next/59599.txt deleted file mode 100644 index 952291f323..0000000000 --- a/api/next/59599.txt +++ /dev/null @@ -1 +0,0 @@ -pkg reflect, func PtrTo //deprecated #59599 diff --git a/api/next/60061.txt b/api/next/60061.txt deleted file mode 100644 index 3e497addb7..0000000000 --- a/api/next/60061.txt +++ /dev/null @@ -1 +0,0 @@ -pkg go/ast, func Unparen(Expr) Expr #60061 diff --git a/api/next/60088.txt b/api/next/60088.txt deleted file mode 100644 index 6eacb139a7..0000000000 --- a/api/next/60088.txt +++ /dev/null @@ -1 +0,0 @@ -pkg reflect, func TypeFor[$0 interface{}]() Type #60088 diff --git a/api/next/60204.txt b/api/next/60204.txt deleted file mode 100644 index 62dddc620c..0000000000 --- a/api/next/60204.txt +++ /dev/null @@ -1 +0,0 @@ -pkg cmp, func Or[$0 comparable](...$0) $0 #60204 diff --git a/api/next/60370.txt b/api/next/60370.txt deleted file mode 100644 index 66ced0bfb7..0000000000 --- a/api/next/60370.txt +++ /dev/null @@ -1,5 +0,0 @@ -pkg database/sql, method (*Null[$0]) Scan(interface{}) error #60370 -pkg database/sql, method (Null[$0]) Value() (driver.Value, error) #60370 -pkg database/sql, type Null[$0 interface{}] struct #60370 -pkg database/sql, type Null[$0 interface{}] struct, Valid bool #60370 -pkg database/sql, type Null[$0 interface{}] struct, V $0 #60370 diff --git a/api/next/60665.txt b/api/next/60665.txt deleted file mode 100644 index 10e50e1832..0000000000 --- a/api/next/60665.txt +++ /dev/null @@ -1,6 +0,0 @@ -pkg crypto/x509, type Certificate struct, Policies []OID #60665 -pkg crypto/x509, type OID struct #60665 -pkg crypto/x509, method (OID) Equal(OID) bool #60665 -pkg crypto/x509, method (OID) EqualASN1OID(asn1.ObjectIdentifier) bool #60665 -pkg crypto/x509, method (OID) String() string #60665 -pkg crypto/x509, func OIDFromInts([]uint64) (OID, error) #60665 diff --git a/api/next/61410.txt b/api/next/61410.txt deleted file mode 100644 index 01c8a2c3e8..0000000000 --- a/api/next/61410.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg net/http, method (*Request) PathValue(string) string #61410 -pkg net/http, method (*Request) SetPathValue(string, string) #61410 diff --git a/api/next/61619.txt b/api/next/61619.txt deleted file mode 100644 index c63a3140e8..0000000000 --- a/api/next/61619.txt +++ /dev/null @@ -1 +0,0 @@ -pkg html/template, const ErrJSTemplate //deprecated #61619 diff --git a/api/next/61642.txt b/api/next/61642.txt deleted file mode 100644 index 4c8bf252df..0000000000 --- a/api/next/61642.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg net/netip, method (AddrPort) Compare(AddrPort) int #61642 -pkg net/netip, method (Prefix) Compare(Prefix) int #61642 diff --git a/api/next/61716.txt b/api/next/61716.txt deleted file mode 100644 index 05b9bb8429..0000000000 --- a/api/next/61716.txt +++ /dev/null @@ -1,48 +0,0 @@ -pkg math/rand/v2, func ExpFloat64() float64 #61716 -pkg math/rand/v2, func Float32() float32 #61716 -pkg math/rand/v2, func Float64() float64 #61716 -pkg math/rand/v2, func Int() int #61716 -pkg math/rand/v2, func Int32() int32 #61716 -pkg math/rand/v2, func Int32N(int32) int32 #61716 -pkg math/rand/v2, func Int64() int64 #61716 -pkg math/rand/v2, func Int64N(int64) int64 #61716 -pkg math/rand/v2, func IntN(int) int #61716 -pkg math/rand/v2, func N[$0 intType]($0) $0 #61716 -pkg math/rand/v2, func New(Source) *Rand #61716 -pkg math/rand/v2, func NewPCG(uint64, uint64) *PCG #61716 -pkg math/rand/v2, func NewZipf(*Rand, float64, float64, uint64) *Zipf #61716 -pkg math/rand/v2, func NormFloat64() float64 #61716 -pkg math/rand/v2, func Perm(int) []int #61716 -pkg math/rand/v2, func Shuffle(int, func(int, int)) #61716 -pkg math/rand/v2, func Uint32() uint32 #61716 -pkg math/rand/v2, func Uint32N(uint32) uint32 #61716 -pkg math/rand/v2, func Uint64() uint64 #61716 -pkg math/rand/v2, func Uint64N(uint64) uint64 #61716 -pkg math/rand/v2, func UintN(uint) uint #61716 -pkg math/rand/v2, method (*PCG) MarshalBinary() ([]uint8, error) #61716 -pkg math/rand/v2, method (*PCG) Seed(uint64, uint64) #61716 -pkg math/rand/v2, method (*PCG) Uint64() uint64 #61716 -pkg math/rand/v2, method (*PCG) UnmarshalBinary([]uint8) error #61716 -pkg math/rand/v2, method (*Rand) ExpFloat64() float64 #61716 -pkg math/rand/v2, method (*Rand) Float32() float32 #61716 -pkg math/rand/v2, method (*Rand) Float64() float64 #61716 -pkg math/rand/v2, method (*Rand) Int() int #61716 -pkg math/rand/v2, method (*Rand) Int32() int32 #61716 -pkg math/rand/v2, method (*Rand) Int32N(int32) int32 #61716 -pkg math/rand/v2, method (*Rand) Int64() int64 #61716 -pkg math/rand/v2, method (*Rand) Int64N(int64) int64 #61716 -pkg math/rand/v2, method (*Rand) IntN(int) int #61716 -pkg math/rand/v2, method (*Rand) NormFloat64() float64 #61716 -pkg math/rand/v2, method (*Rand) Perm(int) []int #61716 -pkg math/rand/v2, method (*Rand) Shuffle(int, func(int, int)) #61716 -pkg math/rand/v2, method (*Rand) Uint32() uint32 #61716 -pkg math/rand/v2, method (*Rand) Uint32N(uint32) uint32 #61716 -pkg math/rand/v2, method (*Rand) Uint64() uint64 #61716 -pkg math/rand/v2, method (*Rand) Uint64N(uint64) uint64 #61716 -pkg math/rand/v2, method (*Rand) UintN(uint) uint #61716 -pkg math/rand/v2, method (*Zipf) Uint64() uint64 #61716 -pkg math/rand/v2, type PCG struct #61716 -pkg math/rand/v2, type Rand struct #61716 -pkg math/rand/v2, type Source interface { Uint64 } #61716 -pkg math/rand/v2, type Source interface, Uint64() uint64 #61716 -pkg math/rand/v2, type Zipf struct #61716 diff --git a/api/next/61758.txt b/api/next/61758.txt deleted file mode 100644 index 35bd224965..0000000000 --- a/api/next/61758.txt +++ /dev/null @@ -1 +0,0 @@ -pkg testing/slogtest, func Run(*testing.T, func(*testing.T) slog.Handler, func(*testing.T) map[string]interface{}) #61758 diff --git a/api/next/61870.txt b/api/next/61870.txt deleted file mode 100644 index 27bb9f6425..0000000000 --- a/api/next/61870.txt +++ /dev/null @@ -1 +0,0 @@ -pkg io, method (*SectionReader) Outer() (ReaderAt, int64, int64) #61870 diff --git a/api/next/61974.txt b/api/next/61974.txt deleted file mode 100644 index d231a62848..0000000000 --- a/api/next/61974.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg debug/elf, const R_MIPS_PC32 = 248 #61974 -pkg debug/elf, const R_MIPS_PC32 R_MIPS #61974 diff --git a/api/next/62037.txt b/api/next/62037.txt deleted file mode 100644 index 78374214c8..0000000000 --- a/api/next/62037.txt +++ /dev/null @@ -1,2 +0,0 @@ -pkg go/types, method (*Info) PkgNameOf(*ast.ImportSpec) *PkgName #62037 -pkg go/types, method (Checker) PkgNameOf(*ast.ImportSpec) *PkgName #62037 diff --git a/api/next/62039.txt b/api/next/62039.txt deleted file mode 100644 index 8280e87751..0000000000 --- a/api/next/62039.txt +++ /dev/null @@ -1,3 +0,0 @@ -pkg go/version, func Compare(string, string) int #62039 -pkg go/version, func IsValid(string) bool #62039 -pkg go/version, func Lang(string) string #62039 diff --git a/api/next/62418.txt b/api/next/62418.txt deleted file mode 100644 index fd482f4ba8..0000000000 --- a/api/next/62418.txt +++ /dev/null @@ -1 +0,0 @@ -pkg log/slog, func SetLogLoggerLevel(Level) Level #62418 diff --git a/api/next/62605.txt b/api/next/62605.txt deleted file mode 100644 index 1b0e533d02..0000000000 --- a/api/next/62605.txt +++ /dev/null @@ -1 +0,0 @@ -pkg go/types, type Info struct, FileVersions map[*ast.File]string #62605 diff --git a/api/next/63223.txt b/api/next/63223.txt deleted file mode 100644 index 2dcafb872b..0000000000 --- a/api/next/63223.txt +++ /dev/null @@ -1,6 +0,0 @@ -pkg go/types, func NewAlias(*TypeName, Type) *Alias #63223 -pkg go/types, func Unalias(Type) Type #63223 -pkg go/types, method (*Alias) Obj() *TypeName #63223 -pkg go/types, method (*Alias) String() string #63223 -pkg go/types, method (*Alias) Underlying() Type #63223 -pkg go/types, type Alias struct #63223 diff --git a/api/next/63725.txt b/api/next/63725.txt deleted file mode 100644 index ff3e05348b..0000000000 --- a/api/next/63725.txt +++ /dev/null @@ -1,18 +0,0 @@ -pkg debug/elf, const R_LARCH_64_PCREL = 109 #63725 -pkg debug/elf, const R_LARCH_64_PCREL R_LARCH #63725 -pkg debug/elf, const R_LARCH_ADD6 = 105 #63725 -pkg debug/elf, const R_LARCH_ADD6 R_LARCH #63725 -pkg debug/elf, const R_LARCH_ADD_ULEB128 = 107 #63725 -pkg debug/elf, const R_LARCH_ADD_ULEB128 R_LARCH #63725 -pkg debug/elf, const R_LARCH_ALIGN = 102 #63725 -pkg debug/elf, const R_LARCH_ALIGN R_LARCH #63725 -pkg debug/elf, const R_LARCH_CFA = 104 #63725 -pkg debug/elf, const R_LARCH_CFA R_LARCH #63725 -pkg debug/elf, const R_LARCH_DELETE = 101 #63725 -pkg debug/elf, const R_LARCH_DELETE R_LARCH #63725 -pkg debug/elf, const R_LARCH_PCREL20_S2 = 103 #63725 -pkg debug/elf, const R_LARCH_PCREL20_S2 R_LARCH #63725 -pkg debug/elf, const R_LARCH_SUB6 = 106 #63725 -pkg debug/elf, const R_LARCH_SUB6 R_LARCH #63725 -pkg debug/elf, const R_LARCH_SUB_ULEB128 = 108 #63725 -pkg debug/elf, const R_LARCH_SUB_ULEB128 R_LARCH #63725 diff --git a/doc/asm.html b/doc/asm.html index f7787a4076..dd395ec833 100644 --- a/doc/asm.html +++ b/doc/asm.html @@ -464,6 +464,23 @@ Function is the outermost frame of the call stack. Traceback should stop at this +

Special instructions

+ +

+The PCALIGN pseudo-instruction is used to indicate that the next instruction should be aligned +to a specified boundary by padding with no-op instructions. +

+ +

+It is currently supported on arm64, amd64, ppc64, loong64 and riscv64. + +For example, the start of the MOVD instruction below is aligned to 32 bytes: +

+PCALIGN $32
+MOVD $2, R0
+
+

+

Interacting with Go types and constants

diff --git a/doc/go1.17_spec.html b/doc/go1.17_spec.html index 15e73c3867..c87d9aff3c 100644 --- a/doc/go1.17_spec.html +++ b/doc/go1.17_spec.html @@ -7,8 +7,11 @@

Introduction

-This is a reference manual for the Go programming language. For -more information and other documents, see golang.org. +This is the reference manual for the Go programming language as it was for +language version 1.17, in October 2021, before the introduction of generics. +It is provided for historical interest. +The current reference manual can be found here. +For more information and other documents, see go.dev.

diff --git a/doc/go1.22.html b/doc/go1.22.html index 287ee77bb5..44d783e1bd 100644 --- a/doc/go1.22.html +++ b/doc/go1.22.html @@ -26,41 +26,390 @@ Do not send CLs removing the interior tags from such phrases.

Changes to the language

- TODO: complete this section + +Go 1.22 makes two changes to "for" loops. +

+ + +

+

+ Go 1.22 includes a preview of a language change we are considering + for a future version of Go: range-over-function iterators. + Building with GOEXPERIMENT=rangefunc enables this feature.

Tools

Go command

+

- TODO: complete this section, or delete if not needed + Commands in workspaces can now + use a vendor directory containing the dependencies of the + workspace. The directory is created by + go work vendor, + and used by build commands when the -mod flag is set to + vendor, which is the default when a workspace vendor + directory is present. +

+

+ Note that the vendor directory's contents for a workspace are different + from those of a single module: if the directory at the root of a workspace also + contains one of the modules in the workspace, its vendor directory + can contain the dependencies of either the workspace or of the module, + but not both.

-

Cgo

+ +

+ go get is no longer supported outside of a module in the + legacy GOPATH mode (that is, with GO111MODULE=off). + Other build commands, such as go build and + go test, will continue to work indefinitely + for legacy GOPATH programs. +

- + +

+ go mod init no longer attempts to import + module requirements from configuration files for other vendoring tools + (such as Gopkg.lock). +

+ + +

+go test -cover now prints coverage summaries for covered +packages that do not have their own test files. Prior to Go 1.22 a +go test -cover run for such a package would report +

+ +

+ ? mymod/mypack [no test files] +

+ +

+ and now with Go 1.22, functions in the package are treated as uncovered: +

+ +

+ mymod/mypack coverage: 0.0% of statements +

+ +

Trace

+ + +

+ The trace tool's web UI has been gently refreshed as part of the + work to support the new tracer, resolving several issues and improving the + readability of various sub-pages. + The web UI now supports exploring traces in a thread-oriented view. + The trace viewer also now displays the full duration of all system calls. +
+ These improvements only apply for viewing traces produced by programs built with + Go 1.22 or newer. + A future release will bring some of these improvements to traces produced by older + version of Go. +

+ +

Vet

+ +

References to loop variables

+ +

+ The behavior of the vet tool has changed to match + the new semantics (see above) of loop variables in Go 1.22. + When analyzing a file that requires Go 1.22 or newer + (due to its go.mod file or a per-file build constraint), + vetcode> no longer reports references to + loop variables from within a function literal that + might outlive the iteration of the loop. + In Go 1.22, loop variables are created anew for each iteration, + so such references are no longer at risk of using a variable + after it has been updated by the loop. +

+ +

New warnings for missing values after append

+ +

+ The vet tool now reports calls to + append that pass + no values to be appended to the slice, such as slice = append(slice). + Such a statement has no effect, and experience has shown that is nearly always a mistake. +

+ +

New warnings for deferring time.Since

+ +

+ The vet tool now reports a non-deferred call to + time.Since(t) within a defer statement. + This is equivalent to calling time.Now().Sub(t) before the defer statement, + not when the deferred function is called. In nearly all cases, the correct code + requires deferring the time.Since call. For example: +

+ +
+t := time.Now()
+defer log.Println(time.Since(t)) // non-deferred call to time.Since
+tmp := time.Since(t); defer log.Println(tmp) // equivalent to the previous defer
+
+defer func() {
+  log.Println(time.Since(t)) // a correctly deferred call to time.Since
+}()
+
+ +

New warnings for mismatched key-value pairs in log/slog calls

+ +

+ The vet tool now reports invalid arguments in calls to functions and methods + in the structured logging package, log/slog, + that accept alternating key/value pairs. + It reports calls where an argument in a key position is neither a + string nor a slog.Attr, and where a final key is missing its value. +

Runtime

+

+ The runtime now keeps type-based garbage collection metadata nearer to each + heap object, improving the CPU performance (latency or throughput) of Go programs + by 1–3%. + This change also reduces the memory overhead of the majority Go programs by + approximately 1% by deduplicating redundant metadata. + Some programs may see a smaller improvement because this change adjusts the size + class boundaries of the memory allocator, so some objects may be moved up a size + class. +

- TODO: complete this section, or delete if not needed + A consequence of this change is that some objects' addresses that were previously + always aligned to a 16 byte (or higher) boundary will now only be aligned to an 8 + byte boundary. + Some programs that use assembly instructions that require memory addresses to be + more than 8-byte aligned and rely on the memory allocator's previous alignment behavior + may break, but we expect such programs to be rare. + Such programs may be built with GOEXPERIMENT=noallocheaders to revert + to the old metadata layout and restore the previous alignment behavior, but package + owners should update their assembly code to avoid the alignment assumption, as this + workaround will be removed in a future release. +

+ +

+ On the windows/amd64 port, programs linking or loading Go libraries built with + -buildmode=c-archive or -buildmode=c-shared can now use + the SetUnhandledExceptionFilter Win32 function to catch exceptions not handled + by the Go runtime. Note that this was already supported on the windows/386 port.

Compiler

-

- TODO: complete this section, or delete if not needed +

+ Profile-guided Optimization (PGO) builds + can now devirtualize a higher proportion of calls than previously possible. + Most programs from a representative set of Go programs now see between 2 and + 14% improvement from enabling PGO. +

+ +

+ The compiler now interleaves devirtualization and inlining, so interface + method calls are better optimized. +

+ +

+ Go 1.22 also includes a preview of an enhanced implementation of the compiler's inlining phase that uses heuristics to boost inlinability at call sites deemed "important" (for example, in loops) and discourage inlining at call sites deemed "unimportant" (for example, on panic paths). + Building with GOEXPERIMENT=newinliner enables the new call-site + heuristics; see issue #61502 for + more info and to provide feedback.

Linker

+

+ The linker's -s and -w flags are now behave more + consistently across all platforms. + The -w flag suppresses DWARF debug information generation. + The -s flag suppresses symbol table generation. + The -s flag also implies the -w flag, which can be + negated with -w=0. + That is, -s -w=0 will generate a binary with DWARF + debug information generation but without the symbol table. +

+ +

+ On ELF platforms, the -B linker flag now accepts a special form: + with -B gobuildid, the linker will generate a GNU + build ID (the ELF NT_GNU_BUILD_ID note) derived from the Go + build ID. +

+ +

+ On Windows, when building with -linkmode=internal, the linker now + preserves SEH information from C object files by copying the .pdata + and .xdata sections into the final binary. + This helps with debugging and profiling binaries using native tools, such as WinDbg. + Note that until now, C functions' SEH exception handlers were not being honored, + so this change may cause some programs to behave differently. + -linkmode=external is not affected by this change, as external linkers + already preserve SEH information. +

+ +

Bootstrap

+

- TODO: complete this section, or delete if not needed + As mentioned in the Go 1.20 release notes, Go 1.22 now requires + the final point release of Go 1.20 or later for bootstrap. + We expect that Go 1.24 will require the final point release of Go 1.22 or later for bootstrap.

Core library

+

New math/rand/v2 package

+ + + + + + + + + + + +

+ Go 1.22 includes the first “v2” package in the standard library, + math/rand/v2. + The changes compared to math/rand are + detailed in proposal #61716. The most important changes are: +

+ + + +

+We plan to include an API migration tool in a future release, likely Go 1.23. +

+ +

Enhanced routing patterns

+ +

+ HTTP routing in the standard library is now more expressive. + The patterns used by net/http.ServeMux have been enhanced to accept methods and wildcards. +

+ +

+ Registering a handler with a method, like "POST /items/create", restricts + invocations of the handler to requests with the given method. A pattern with a method takes precedence over a matching pattern without one. + As a special case, registering a handler with "GET" also registers it with "HEAD". +

+ +

+ Wildcards in patterns, like /items/{id}, match segments of the URL path. + The actual segment value may be accessed by calling the Request.PathValue method. + A wildcard ending in "...", like /files/{path...}, must occur at the end of a pattern and matches all the remaining segments. +

+ +

+ A pattern that ends in "/" matches all paths that have it as a prefix, as always. + To match the exact pattern including the trailing slash, end it with {$}, + as in /exact/match/{$}. +

+ +

+ If two patterns overlap in the requests that they match, then the more specific pattern takes precedence. + If neither is more specific, the patterns conflict. + This rule generalizes the original precedence rules and maintains the property that the order in which + patterns are registered does not matter. +

+ +

+ This change breaks backwards compatibility in small ways, some obvious—patterns with "{" and "}" behave differently— + and some less so—treatment of escaped paths has been improved. + The change is controlled by a GODEBUG field named httpmuxgo121. + Set httpmuxgo121=1 to restore the old behavior. +

+

Minor changes to the library

@@ -70,9 +419,95 @@ Do not send CLs removing the interior tags from such phrases. There are also various performance improvements, not enumerated here.

-

- TODO: complete this section -

+ + + + +
archive/tar
+
+

+ The new method Writer.AddFS adds all of the files from an fs.FS to the archive. +

+ +

+ If the argument to FileInfoHeader implements the new FileInfoNames interface, then the interface methods will be used to set the UID/GID of the file header. This allows applications to override the default UID/GID resolution. +

+
+
+ +
archive/zip
+
+

+ The new method Writer.AddFS adds all of the files from an fs.FS to the archive. +

+
+
+ +
bufio
+
+

+ When a SplitFunc returns ErrFinalToken with a nil token, Scanner will now stop immediately. + Previously, it would report a final empty token before stopping, which was usually not desired. + Callers that do want to report a final empty token can do so by returning []byte{} rather than nil. +

+
+
+ +
cmp
+
+

+ The new function Or returns the first in a sequence of values that is not the zero value. +

+
+
+ +
crypto/tls
+
+

+ ConnectionState.ExportKeyingMaterial will now + return an error unless TLS 1.3 is in use, or the extended_master_secret extension is supported by both the server and + client. crypto/tls has supported this extension since Go 1.20. This can be disabled with the + tlsunsafeekm=1 GODEBUG setting. +

+ +

+ By default, the minimum version offered by crypto/tls servers is now TLS 1.2 if not specified with + config.MinimumVersion, matching the behavior of crypto/tls + clients. This change can be reverted with the tls10server=1 GODEBUG setting. +

+ +

+ By default, cipher suites without ECDHE support are no longer offered by either clients or servers during pre-TLS 1.3 + handshakes. This change can be reverted with the tlsrsakex=1 GODEBUG setting. +

+
+
+ +
crypto/x509
+
+

+ The new CertPool.AddCertWithConstraint + method can be used to add customized constraints to root certificates to be applied during chain building. +

+ +

+ On Android, root certificates will now be loaded from /data/misc/keychain/certs-added as well as /system/etc/security/cacerts. +

+ +

+ A new type, OID, supports ASN.1 Object Identifiers with individual + components larger than 31 bits. A new field which uses this type, Policies, + is added to the Certificate struct, and is now populated during parsing. Any OIDs which cannot be represented + using a asn1.ObjectIdentifier will appear in Policies, + but not in the old PolicyIdentifiers field. + + When calling CreateCertificate, the Policies field is ignored, and + policies are taken from the PolicyIdentifiers field. Using the x509usepolicies=1 GODEBUG setting inverts this, + populating certificate policies from the Policies field, and ignoring the PolicyIdentifiers field. We may change the + default value of x509usepolicies in Go 1.23, making Policies the default field for marshaling. +

+
+
database/sql
@@ -81,8 +516,306 @@ Do not send CLs removing the interior tags from such phrases. provide a way to scan nullable columns for any column types.

+
+ +
debug/elf
+
+

+ Constant R_MIPS_PC32 is defined for use with MIPS64 systems. +

+
+
+

+ Additional R_LARCH_* constants are defined for use with LoongArch systems. +

+
+
+ +
encoding
+
+

+ The new methods AppendEncode and AppendDecode added to + each of the Encoding types in the packages + encoding/base32, + encoding/base64, and + encoding/hex + simplify encoding and decoding from and to byte slices by taking care of byte slice buffer management. +

+ +

+ The methods + base32.Encoding.WithPadding and + base64.Encoding.WithPadding + now panic if the padding argument is a negative value other than + NoPadding. +

+
+
+ +
encoding/json
+
+

+ Marshaling and encoding functionality now escapes + '\b' and '\f' characters as + \b and \f instead of + \u0008 and \u000c. +

+
+
+ +
go/ast
+
+

+ The following declarations related to + syntactic identifier resolution + are now deprecated: + Ident.Obj, + Object, + Scope, + File.Scope, + File.Unresolved, + Importer, + Package, + NewPackage. + + In general, identifiers cannot be accurately resolved without type information. + Consider, for example, the identifier K + in T{K: ""}: it could be the name of a local variable + if T is a map type, or the name of a field if T is a struct type. + + New programs should use the go/types + package to resolve identifiers; see + Object, + Info.Uses, and + Info.Defs for details. +

+ +

+ The new ast.Unparen + function removes any enclosing + parentheses from + an expression. +

+
+
+ +
go/types
+
+

+ The new Alias type represents type aliases. + Previously, type aliases were not represented explicitly, so a reference to a type alias was equivalent + to spelling out the aliased type, and the name of the alias was lost. + The new representation retains the intermediate Alias. + This enables improved error reporting (the name of a type alias can be reported), and allows for better handling + of cyclic type declarations involving type aliases. + In a future release, Alias types will also carry type parameter information. + The new function Unalias returns the actual type denoted by an + Alias type (or any other Type for that matter). +

+

+ Because Alias types may break existing type switches that do not know to check for them, + this functionality is controlled by a GODEBUG field named gotypesalias. + With gotypesalias=0, everything behaves as before, and Alias types are never created. + With gotypesalias=1, Alias types are created and clients must expect them. + The default is gotypesalias=0. + In a future release, the default will be changed to gotypesalias=1. + Clients of go/types are urged to adjust their code as soon as possible + to work with gotypesalias=1 to eliminate problems early. +

+ +

+ The Info struct now exports the + FileVersions map + which provides per-file Go version information. +

+ +

+ The new helper method PkgNameOf returns the local package name + for the given import declaration. +

+ +

+ The implementation of SizesFor has been adjusted to compute + the same type sizes as the compiler when the compiler argument for SizesFor is "gc". + The default Sizes implementation used by the type checker is now + types.SizesFor("gc", "amd64"). +

+ +

+ The start position (Pos) + of the lexical environment block (Scope) + that represents a function body has changed: + it used to start at the opening curly brace of the function body, + but now starts at the function's func token. +

+
+
go/version
+
+

+ The new go/version package implements functions + for validating and comparing Go version strings. +

+
+
+ +
html/template
+
+

+ Javascript template literals may now contain Go template actions, and parsing a template containing one will + no longer return ErrJSTemplate. Similarly the GODEBUG setting jstmpllitinterp no + longer has any effect. +

+
+
+ +
io
+
+

+ The new SectionReader.Outer method returns the ReaderAt, offset, and size passed to NewSectionReader. +

+
+
+ +
log/slog
+
+

+ The new SetLogLoggerLevel function + controls the level for the bridge between the `slog` and `log` packages. It sets the minimum level + for calls to the top-level `slog` logging functions, and it sets the level for calls to `log.Logger` + that go through `slog`. +

+
+
+ +
math/big
+
+

+ The new method Rat.FloatPrec computes the number of fractional decimal digits + required to represent a rational number accurately as a floating-point number, and whether accurate decimal representation + is possible in the first place. +

+
+
+ +
net
+
+

+ When io.Copy copies + from a TCPConn to a UnixConn, + it will now use Linux's splice(2) system call if possible, + using the new method TCPConn.WriteTo. +

+ +

+ The Go DNS Resolver, used when building with "-tags=netgo", + now searches for a matching name in the Windows hosts file, + located at %SystemRoot%\System32\drivers\etc\hosts, + before making a DNS query. +

+
+
+ +
net/http
+
+

+ The new functions + ServeFileFS, + FileServerFS, and + NewFileTransportFS + are versions of the existing + ServeFile, FileServer, and NewFileTransport, + operating on an fs.FS. +

+ +

+ The HTTP server and client now reject requests and responses containing + an invalid empty Content-Length header. + The previous behavior may be restored by setting + GODEBUG field httplaxcontentlength=1. +

+ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + +
os
+
+

+ On Windows, the Stat function now follows all reparse points + that link to another named entity in the system. + It was previously only following IO_REPARSE_TAG_SYMLINK and + IO_REPARSE_TAG_MOUNT_POINT reparse points. +

+ +

+ On Windows, passing O_SYNC to OpenFile now causes write operations to go directly to disk, equivalent to O_SYNC on Unix platforms. +

+ +

+ On Windows, the ReadDir, + File.ReadDir, + File.Readdir, + and File.Readdirnames functions + now read directory entries in batches to reduce the number of system calls, + improving performance up to 30%. +

+ +

+ When io.Copy copies + from a File to a net.UnixConn, + it will now use Linux's sendfile(2) system call if possible, + using the new method File.WriteTo. +

+
+
+ +
os/exec
+
+

+ On Windows, LookPath now + ignores empty entries in %PATH%, and returns + ErrNotFound (instead of ErrNotExist) if + no executable file extension is found to resolve an otherwise-unambiguous + name. +

+ +

+ On Windows, Command and + Cmd.Start no + longer call LookPath if the path to the executable is already + absolute and has an executable file extension. In addition, + Cmd.Start no longer writes the resolved extension back to + the Path field, + so it is now safe to call the String method concurrently + with a call to Start. +

+
+
+
reflect

@@ -94,12 +827,189 @@ Do not send CLs removing the interior tags from such phrases. These changes make IsZero consistent with comparing a value to zero using the language == operator.

+ +

+ The PtrTo function is deprecated, + in favor of PointerTo. +

+ +

+ The new function TypeFor + returns the Type that represents + the type argument T. + Previously, to get the reflect.Type value for a type, one had to use + reflect.TypeOf((*T)(nil)).Elem(). + This may now be written as reflect.TypeFor[T](). +

-
+ + +
runtime/metrics
+
+

+ Four new histogram metrics + /sched/pauses/stopping/gc:seconds, + /sched/pauses/stopping/other:seconds, + /sched/pauses/total/gc:seconds, and + /sched/pauses/total/other:seconds provide additional details + about stop-the-world pauses. + The "stopping" metrics report the time taken from deciding to stop the + world until all goroutines are stopped. + The "total" metrics report the time taken from deciding to stop the world + until it is started again. +

+ +

+ The /gc/pauses:seconds metric is deprecated, as it is + equivalent to the new /sched/pauses/total/gc:seconds metric. +

+ +

+ /sync/mutex/wait/total:seconds now includes contention on + runtime-internal locks in addition to + sync.Mutex and + sync.RWMutex. +

+
+
+ +
runtime/pprof
+
+

+ Mutex profiles now scale contention by the number of goroutines blocked on the mutex. + This provides a more accurate representation of the degree to which a mutex is a bottleneck in + a Go program. + For instance, if 100 goroutines are blocked on a mutex for 10 milliseconds, a mutex profile will + now record 1 second of delay instead of 10 milliseconds of delay. +

+ +

+ Mutex profiles also now include contention on runtime-internal locks in addition to + sync.Mutex and + sync.RWMutex. + Contention on runtime-internal locks is always reported at runtime._LostContendedRuntimeLock. + A future release will add complete stack traces in these cases. +

+ +

+ CPU profiles on Darwin platforms now contain the process's memory map, enabling the disassembly + view in the pprof tool. +

+
+
+ +
runtime/trace
+
+

+ The execution tracer has been completely overhauled in this release, resolving several long-standing + issues and paving the way for new use-cases for execution traces. +

+

+ Execution traces now use the operating system's clock on most platforms (Windows excluded) so + it is possible to correlate them with traces produced by lower-level components. + Execution traces no longer depend on the reliability of the platform's clock to produce a correct trace. + Execution traces are now partitioned regularly on-the-fly and as a result may be processed in a + streamable way. + Execution traces now contain complete durations for all system calls. + Execution traces now contain information about the operating system threads that goroutines executed on. + The latency impact of starting and stopping execution traces has been dramatically reduced. + Execution traces may now begin or end during the garbage collection mark phase. +

+

+ To allow Go developers to take advantage of these improvements, an experimental + trace reading package is available at golang.org/x/exp/trace. + Note that this package only works on traces produced by programs built with Go 1.22 at the moment. + Please try out the package and provide feedback on + the corresponding proposal issue. +

+

+ If you experience any issues with the new execution tracer implementation, you may switch back to the + old implementation by building your Go program with GOEXPERIMENT=noexectracer2. + If you do, please file an issue, otherwise this option will be removed in a future release. +

+
+
+ +
slices
+
+

+ The new function Concat concatenates multiple slices. +

+ +

+ Functions that shrink the size of a slice (Delete, DeleteFunc, Compact, CompactFunc, and Replace) now zero the elements between the new length and the old length. +

+ +

+ Insert now always panics if the argument i is out of range. Previously it did not panic in this situation if there were no elements to be inserted. +

+
+
+ +
syscall
+
+

+ The syscall package has been frozen since Go 1.4 and was marked as deprecated in Go 1.11, causing many editors to warn about any use of the package. + However, some non-deprecated functionality requires use of the syscall package, such as the os/exec.Cmd.SysProcAttr field. + To avoid unnecessary complaints on such code, the syscall package is no longer marked as deprecated. + The package remains frozen to most new functionality, and new code remains encouraged to use golang.org/x/sys/unix or golang.org/x/sys/windows where possible. +

+ +

+ On Linux, the new SysProcAttr.PidFD field allows obtaining a PID FD when starting a child process via StartProcess or os/exec. +

+ +

+ On Windows, passing O_SYNC to Open now causes write operations to go directly to disk, equivalent to O_SYNC on Unix platforms. +

+
+
+ +
testing/slogtest
+
+

+ The new Run function uses sub-tests to run test cases, + providing finer-grained control. +

+
+

Ports

-

- TODO: complete this section, or delete if not needed +

Darwin

+

+ On macOS on 64-bit x86 architecture (the darwin/amd64 port), + the Go toolchain now generates position-independent executables (PIE) by default. + Non-PIE binaries can be generated by specifying the -buildmode=exe + build flag. + On 64-bit ARM-based macOS (the darwin/arm64 port), + the Go toolchain already generates PIE by default. +

+

+ Go 1.22 is the last release that will run on macOS 10.15 Catalina. Go 1.23 will require macOS 11 Big Sur or later.

+

Arm

+

+ The GOARM environment variable now allows you to select whether to use software or hardware floating point. + Previously, valid GOARM values were 5, 6, or 7. Now those same values can + be optionally followed by ,softfloat or ,hardfloat to select the floating-point implementation. +

+

+ This new option defaults to softfloat for version 5 and hardfloat for versions + 6 and 7. +

+ +

Loong64

+

+ The loong64 port now supports passing function arguments and results using registers. +

+

+ The linux/loong64 port now supports the address sanitizer, memory sanitizer, new-style linker relocations, and the plugin build mode. +

+ +

OpenBSD

+

+ Go 1.22 adds an experimental port to OpenBSD on big-endian 64-bit PowerPC + (openbsd/ppc64). +

diff --git a/doc/go_spec.html b/doc/go_spec.html index 18f88d5ead..bd974b3c48 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -10,7 +10,7 @@ This is the reference manual for the Go programming language. The pre-Go1.18 version, without generics, can be found here. -For more information and other documents, see golang.org. +For more information and other documents, see go.dev.

@@ -70,6 +70,14 @@ enumerations or code snippets that are not further specified. The character +

+A link of the form [Go 1.xx] indicates that a described +language feature (or some aspect of it) was changed or added with language version 1.xx and +thus requires at minimum that language version to build. +For details, see the linked section +in the appendix. +

+

Source code representation

@@ -263,7 +271,8 @@ continue for import return var

The following character sequences represent operators -(including assignment operators) and punctuation: +(including assignment operators) and punctuation +[Go 1.18]:

 +    &     +=    &=     &&    ==    !=    (    )
@@ -281,7 +290,8 @@ An integer literal is a sequence of digits representing an
 integer constant.
 An optional prefix sets a non-decimal base: 0b or 0B
 for binary, 0, 0o, or 0O for octal,
-and 0x or 0X for hexadecimal.
+and 0x or 0X for hexadecimal
+[Go 1.13].
 A single 0 is considered a decimal zero.
 In hexadecimal literals, letters a through f
 and A through F represent values 10 through 15.
@@ -347,7 +357,8 @@ prefix, an integer part (hexadecimal digits), a radix point, a fractional part (
 and an exponent part (p or P followed by an optional sign and decimal digits).
 One of the integer part or the fractional part may be elided; the radix point may be elided as well,
 but the exponent part is required. (This syntax matches the one given in IEEE 754-2008 §5.12.3.)
-An exponent value exp scales the mantissa (integer and fractional part) by 2exp.
+An exponent value exp scales the mantissa (integer and fractional part) by 2exp
+[Go 1.13].
 

@@ -411,7 +422,8 @@ It consists of an integer or floating-point literal followed by the lowercase letter i. The value of an imaginary literal is the value of the respective -integer or floating-point literal multiplied by the imaginary unit i. +integer or floating-point literal multiplied by the imaginary unit i +[Go 1.13]

@@ -1340,6 +1352,7 @@ interface{}
 
 

For convenience, the predeclared type any is an alias for the empty interface. +[Go 1.18]

@@ -1375,13 +1388,15 @@ as the File interface. In a slightly more general form an interface T may use a (possibly qualified) interface type name E as an interface element. This is called -embedding interface E in T. +embedding interface E in T +[Go 1.14]. The type set of T is the intersection of the type sets defined by T's explicitly declared methods and the type sets of T’s embedded interfaces. In other words, the type set of T is the set of all types that implement all the explicitly declared methods of T and also all the methods of -E. +E +[Go 1.18].

@@ -1420,7 +1435,8 @@ type ReadCloser interface {
 

In their most general form, an interface element may also be an arbitrary type term T, or a term of the form ~T specifying the underlying type T, -or a union of terms t1|t2|…|tn. +or a union of terms t1|t2|…|tn +[Go 1.18]. Together with method specifications, these elements enable the precise definition of an interface's type set as follows:

@@ -2303,7 +2319,9 @@ as an operand, and in a

The following identifiers are implicitly declared in the -universe block: +universe block +[Go 1.18] +[Go 1.21]:

 Types:
@@ -2487,7 +2505,8 @@ TypeSpec = AliasDecl | TypeDef .
 

Alias declarations

-An alias declaration binds an identifier to the given type. +An alias declaration binds an identifier to the given type +[Go 1.9].

@@ -2636,7 +2655,8 @@ func (l *List[T]) Len() int  { … }
 A type parameter list declares the type parameters of a generic function or type declaration.
 The type parameter list looks like an ordinary function parameter list
 except that the type parameter names must all be present and the list is enclosed
-in square brackets rather than parentheses.
+in square brackets rather than parentheses
+[Go 1.18].
 

@@ -2719,7 +2739,8 @@ type T6[P int] struct{ f *T6[P] }     // ok: reference to T6 is not in type para
 

A type constraint is an interface that defines the set of permissible type arguments for the respective type parameter and controls the -operations supported by values of that type parameter. +operations supported by values of that type parameter +[Go 1.18].

@@ -2749,7 +2770,8 @@ other interfaces based on their type sets. But this should get us going for now.
 The predeclared
 interface type comparable
 denotes the set of all non-interface types that are
-strictly comparable.
+strictly comparable
+[Go 1.18].
 

@@ -2782,7 +2804,8 @@ if T is an element of the type set defined by C; i.e., if T implements C. As an exception, a strictly comparable type constraint may also be satisfied by a comparable -(not necessarily strictly comparable) type argument. +(not necessarily strictly comparable) type argument +[Go 1.20]. More precisely:

@@ -4306,7 +4329,7 @@ with the same underlying array.

A generic function or type is instantiated by substituting type arguments -for the type parameters. +for the type parameters [Go 1.18]. Instantiation proceeds in two steps:

@@ -4759,6 +4782,7 @@ to the type of the other operand.

The right operand in a shift expression must have integer type +[Go 1.13] or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, @@ -5426,7 +5450,8 @@ in any of these cases: x is a string and T is a slice of bytes or runes.

  • - x is a slice, T is an array or a pointer to an array, + x is a slice, T is an array [Go 1.20] + or a pointer to an array [Go 1.17], and the slice and array types have identical element types.
  • @@ -6516,7 +6541,6 @@ additionally it may specify an init and a post statement, such as an assignment, an increment or decrement statement. The init statement may be a short variable declaration, but the post statement must not. -Variables declared by the init statement are re-used in each iteration.

    @@ -6548,12 +6572,54 @@ for cond { S() }    is the same as    for ; cond ; { S() }
     for      { S() }    is the same as    for true     { S() }
     
    +

    +Each iteration has its own separate declared variable (or variables) +[Go 1.22]. +The variable used by the first iteration is declared by the init statement. +The variable used by each subsequent iteration is declared implicitly before +executing the post statement and initialized to the value of the previous +iteration's variable at that moment. +

    + +
    +var prints []func()
    +for i := 0; i < 5; i++ {
    +	prints = append(prints, func() { println(i) })
    +	i++
    +}
    +for _, p := range prints {
    +	p()
    +}
    +
    + +

    +prints +

    + +
    +1
    +3
    +5
    +
    + +

    +Prior to [Go 1.22], iterations share one set of variables +instead of having their own separate variables. +In that case, the example above prints +

    + +
    +6
    +6
    +6
    +
    +

    For statements with range clause

    A "for" statement with a "range" clause iterates through all entries of an array, slice, string or map, values received on -a channel, or integer values from zero to an upper limit. +a channel, or integer values from zero to an upper limit [Go 1.22]. For each entry it assigns iteration values to corresponding iteration variables if present and then executes the block.

    @@ -6652,9 +6718,10 @@ The iteration variables may be declared by the "range" clause using a form of short variable declaration (:=). In this case their types are set to the types of the respective iteration values -and their scope is the block of the "for" -statement; they are re-used in each iteration. -If the iteration variables are declared outside the "for" statement, +and their scope is the block of the "for" statement; +each iteration has its own separate variables [Go 1.22] +(see also "for" statements with a ForClause). +If the iteration variables are declared outside the “for” statement, after execution their values will be those of the last iteration.

    @@ -7249,7 +7316,8 @@ n3 := copy(b, "Hello, World!") // n3 == 5, b is []byte("Hello")

    The built-in function clear takes an argument of map, slice, or type parameter type, -and deletes or zeroes out all elements. +and deletes or zeroes out all elements +[Go 1.21].

    @@ -7516,7 +7584,8 @@ The precise behavior is implementation-dependent.
     The built-in functions min and max compute the
     smallest—or largest, respectively—value of a fixed number of
     arguments of ordered types.
    -There must be at least one argument.
    +There must be at least one argument
    +[Go 1.21].
     

    @@ -8232,8 +8301,8 @@ of if the general conversion rules take care of this.

    A Pointer is a pointer type but a Pointer value may not be dereferenced. -Any pointer or value of underlying type uintptr can be -converted to a type of underlying type Pointer and vice versa. +Any pointer or value of core type uintptr can be +converted to a type of core type Pointer and vice versa. The effect of converting between Pointer and uintptr is implementation-defined.

    @@ -8244,6 +8313,10 @@ bits = *(*uint64)(unsafe.Pointer(&f)) type ptr unsafe.Pointer bits = *(*uint64)(ptr(&f)) +func f[P ~*B, B any](p P) uintptr { + return uintptr(unsafe.Pointer(p)) +} + var p ptr = nil
    @@ -8292,7 +8365,8 @@ of constant size.

    The function Add adds len to ptr -and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)). +and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)) +[Go 1.17]. The len argument must be of integer type or an untyped constant. A constant len argument must be representable by a value of type int; if it is an untyped constant it is given type int. @@ -8312,7 +8386,8 @@ and whose length and capacity are len.

    except that, as a special case, if ptr is nil and len is zero, -Slice returns nil. +Slice returns nil +[Go 1.17].

    @@ -8321,14 +8396,16 @@ A constant len argument must be non-negative and run-time panic occurs. +a run-time panic occurs +[Go 1.17].

    The function SliceData returns a pointer to the underlying array of the slice argument. If the slice's capacity cap(slice) is not zero, that pointer is &slice[:1][0]. If slice is nil, the result is nil. -Otherwise it is a non-nil pointer to an unspecified memory address. +Otherwise it is a non-nil pointer to an unspecified memory address +[Go 1.20].

    @@ -8337,12 +8414,14 @@ The function String returns a string value whose under The same requirements apply to the ptr and len argument as in the function Slice. If len is zero, the result is the empty string "". Since Go strings are immutable, the bytes passed to String must not be modified afterwards. +[Go 1.20]

    The function StringData returns a pointer to the underlying bytes of the str argument. For an empty string the return value is unspecified, and may be nil. -Since Go strings are immutable, the bytes returned by StringData must not be modified. +Since Go strings are immutable, the bytes returned by StringData must not be modified +[Go 1.20].

    Size and alignment guarantees

    @@ -8383,6 +8462,145 @@ A struct or array type has size zero if it contains no fields (or elements, resp

    Appendix

    +

    Language versions

    + +

    +The Go 1 compatibility guarantee ensures that +programs written to the Go 1 specification will continue to compile and run +correctly, unchanged, over the lifetime of that specification. +More generally, as adjustments are made and features added to the language, +the compatibility guarantee ensures that a Go program that works with a +specific Go language version will continue to work with any subsequent version. +

    + +

    +For instance, the ability to use the prefix 0b for binary +integer literals was introduced with Go 1.13, indicated +by [Go 1.13] in the section on +integer literals. +Source code containing an integer literal such as 0b1011 +will be rejected if the implied or required language version used by +the compiler is older than Go 1.13. +

    + +

    +The following table describes the minimum language version required for +features introduced after Go 1. +

    + +

    Go 1.9

    + + +

    Go 1.13

    +
      +
    • +Integer literals may use the prefixes 0b, 0B, 0o, +and 0O for binary, and octal literals, respectively. +
    • +
    • +Hexadecimal floating-point literals may be written using the prefixes +0x and 0X. +
    • +
    • +The imaginary suffix i may be used with any (binary, decimal, hexadecimal) +integer or floating-point literal, not just decimal literals. +
    • +
    • +The digits of any number literal may be separated (grouped) +using underscores _. +
    • +
    • +The shift count in a shift operation may be a signed integer type. +
    • +
    + +

    Go 1.14

    +
      +
    • +Emdedding a method more than once through different embedded interfaces +is not an error. +
    • +
    + +

    Go 1.17

    +
      +
    • +A slice may be converted to an array pointer if the slice and array element +types match, and the array is not longer than the slice. +
    • +
    • +The built-in package unsafe includes the new functions +Add and Slice. +
    • +
    + +

    Go 1.18

    +

    +The 1.18 release adds polymorphic functions and types ("generics") to the language. +Specifically: +

    + + +

    Go 1.20

    +
      +
    • +A slice may be converted to an array if the slice and array element +types match and the array is not longer than the slice. +
    • +
    • +The built-in package unsafe includes the new functions +SliceData, String, and StringData. +
    • +
    • +Comparable types (such as ordinary interfaces) may satisfy +comparable constraints, even if the type arguments are not strictly comparable. +
    • +
    + +

    Go 1.21

    +
      +
    • +The set of predeclared functions includes the new functions +min, max, and clear. +
    • +
    • +Type inference uses the types of interface methods for inference. +It also infers type arguments for generic functions assigned to variables or +passed as arguments to other (possibly generic) functions. +
    • +
    + +

    Go 1.22

    +
      +
    • +In a "for" statement, each iteration has its own set of iteration +variables rather than sharing the same variables in each iteration. +
    • +
    • +A "for" statement with "range" clause may iterate over +integer values from zero to an upper limit. +
    • +
    +

    Type unification rules

    diff --git a/doc/godebug.md b/doc/godebug.md index 9235635bdd..a7619c9a3d 100644 --- a/doc/godebug.md +++ b/doc/godebug.md @@ -159,6 +159,41 @@ Go 1.22 changed the default TLS cipher suites used by clients and servers when not explicitly configured, removing the cipher suites which used RSA based key exchange. The default can be revert using the [`tlsrsakex` setting](/pkg/crypto/tls/#Config). +Go 1.22 disabled +[`ConnectionState.ExportKeyingMaterial`](/pkg/crypto/tls/#ConnectionState.ExportKeyingMaterial) +when the connection supports neither TLS 1.3 nor Extended Master Secret +(implemented in Go 1.21). It can be reenabled with the [`tlsunsafeekm` +setting](/pkg/crypto/tls/#ConnectionState.ExportKeyingMaterial). + +Go 1.22 changed how the runtime interacts with transparent huge pages on Linux. +In particular, a common default Linux kernel configuration can result in +significant memory overheads, and Go 1.22 no longer works around this default. +To work around this issue without adjusting kernel settings, transparent huge +pages can be disabled for Go memory with the +[`disablethp` setting](/pkg/runtime#hdr-Environment_Variable). +This behavior was backported to Go 1.21.1, but the setting is only available +starting with Go 1.21.6. +This setting may be removed in a future release, and users impacted by this issue +should adjust their Linux configuration according to the recommendations in the +[GC guide](/doc/gc-guide#Linux_transparent_huge_pages), or switch to a Linux +distribution that disables transparent huge pages altogether. + +Go 1.22 added contention on runtime-internal locks to the [`mutex` +profile](/pkg/runtime/pprof#Profile). Contention on these locks is always +reported at `runtime._LostContendedRuntimeLock`. Complete stack traces of +runtime locks can be enabled with the [`runtimecontentionstacks` +setting](/pkg/runtime#hdr-Environment_Variable). These stack traces have +non-standard semantics, see setting documentation for details. + +Go 1.22 added a new [`crypto/x509.Certificate`](/pkg/crypto/x509/#Certificate) +field, [`Policies`](/pkg/crypto/x509/#Certificate.Policies), which supports +certificate policy OIDs with components larger than 31 bits. By default this +field is only used during parsing, when it is populated with policy OIDs, but +not used during marshaling. It can be used to marshal these larger OIDs, instead +of the existing PolicyIdentifiers field, by using the +[`x509usepolicies` setting.](/pkg/crypto/x509/#CreateCertificate). + + ### Go 1.21 Go 1.21 made it a run-time error to call `panic` with a nil interface value, diff --git a/lib/time/update.bash b/lib/time/update.bash index 605afa76d3..e72850079f 100755 --- a/lib/time/update.bash +++ b/lib/time/update.bash @@ -24,8 +24,8 @@ # in the CL match the update.bash in the CL. # Versions to use. -CODE=2023c -DATA=2023c +CODE=2023d +DATA=2023d set -e diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip index 417ee2b194..7cf689f830 100644 Binary files a/lib/time/zoneinfo.zip and b/lib/time/zoneinfo.zip differ diff --git a/misc/wasm/go_wasip1_wasm_exec b/misc/wasm/go_wasip1_wasm_exec index dc110327af..cd16b96ea7 100755 --- a/misc/wasm/go_wasip1_wasm_exec +++ b/misc/wasm/go_wasip1_wasm_exec @@ -14,8 +14,15 @@ case "$GOWASIRUNTIME" in exec wazero run -mount /:/ -env-inherit -cachedir "${TMPDIR:-/tmp}"/wazero ${GOWASIRUNTIMEARGS:-} "$1" "${@:2}" ;; "wasmtime" | "") - # TODO(go.dev/issue/63718): Switch to the new CLI offered in the major version 14 of Wasmtime. - exec env WASMTIME_NEW_CLI=0 wasmtime run --dir=/ --env PWD="$PWD" --env PATH="$PATH" --max-wasm-stack 1048576 ${GOWASIRUNTIMEARGS:-} "$1" -- "${@:2}" + # Match the major version in "wasmtime-cli 14.0.0". For versions before 14 + # we need to use the old CLI. This requires Bash v3.0 and above. + # TODO(johanbrandhorst): Remove this condition once 1.22 is released. + # From 1.23 onwards we'll only support the new wasmtime CLI. + if [[ "$(wasmtime --version)" =~ wasmtime-cli[[:space:]]([0-9]+)\.[0-9]+\.[0-9]+ && "${BASH_REMATCH[1]}" -lt 14 ]]; then + exec wasmtime run --dir=/ --env PWD="$PWD" --env PATH="$PATH" --max-wasm-stack 1048576 ${GOWASIRUNTIMEARGS:-} "$1" -- "${@:2}" + else + exec wasmtime run --dir=/ --env PWD="$PWD" --env PATH="$PATH" -W max-wasm-stack=1048576 ${GOWASIRUNTIMEARGS:-} "$1" "${@:2}" + fi ;; *) echo "Unknown Go WASI runtime specified: $GOWASIRUNTIME" diff --git a/src/builtin/builtin.go b/src/builtin/builtin.go index da0ace1498..668c799ca7 100644 --- a/src/builtin/builtin.go +++ b/src/builtin/builtin.go @@ -284,9 +284,10 @@ func panic(v any) // by restoring normal execution and retrieves the error value passed to the // call of panic. If recover is called outside the deferred function it will // not stop a panicking sequence. In this case, or when the goroutine is not -// panicking, or if the argument supplied to panic was nil, recover returns -// nil. Thus the return value from recover reports whether the goroutine is -// panicking. +// panicking, recover returns nil. +// +// Prior to Go 1.21, recover would also return nil if panic is called with +// a nil argument. See [panic] for details. func recover() any // The print built-in function formats its arguments in an diff --git a/src/bytes/boundary_test.go b/src/bytes/boundary_test.go index f9855fcb05..67f377e089 100644 --- a/src/bytes/boundary_test.go +++ b/src/bytes/boundary_test.go @@ -98,3 +98,18 @@ func TestIndexNearPageBoundary(t *testing.T) { } q[len(q)-1] = 0 } + +func TestCountNearPageBoundary(t *testing.T) { + t.Parallel() + b := dangerousSlice(t) + for i := range b { + c := Count(b[i:], []byte{1}) + if c != 0 { + t.Fatalf("Count(b[%d:], {1})=%d, want 0\n", i, c) + } + c = Count(b[:i], []byte{0}) + if c != i { + t.Fatalf("Count(b[:%d], {0})=%d, want %d\n", i, c, i) + } + } +} diff --git a/src/cmd/api/api_test.go b/src/cmd/api/api_test.go index 910e046f12..ba358d364d 100644 --- a/src/cmd/api/api_test.go +++ b/src/cmd/api/api_test.go @@ -285,6 +285,25 @@ func TestIssue41358(t *testing.T) { } } +func TestIssue64958(t *testing.T) { + defer func() { + if x := recover(); x != nil { + t.Errorf("expected no panic; recovered %v", x) + } + }() + + testenv.MustHaveGoBuild(t) + + for _, context := range contexts { + w := NewWalker(context, "testdata/src/issue64958") + pkg, err := w.importFrom("p", "", 0) + if err != nil { + t.Errorf("expected no error importing; got %T", err) + } + w.export(pkg) + } +} + func TestCheck(t *testing.T) { if !*flagCheck { t.Skip("-check not specified") diff --git a/src/cmd/api/main_test.go b/src/cmd/api/main_test.go index 94e159e7d8..7985055b5c 100644 --- a/src/cmd/api/main_test.go +++ b/src/cmd/api/main_test.go @@ -957,17 +957,17 @@ func (w *Walker) emitType(obj *types.TypeName) { if w.isDeprecated(obj) { w.emitf("type %s //deprecated", name) } + typ := obj.Type() + if obj.IsAlias() { + w.emitf("type %s = %s", name, w.typeString(typ)) + return + } if tparams := obj.Type().(*types.Named).TypeParams(); tparams != nil { var buf bytes.Buffer buf.WriteString(name) w.writeTypeParams(&buf, tparams, true) name = buf.String() } - typ := obj.Type() - if obj.IsAlias() { - w.emitf("type %s = %s", name, w.typeString(typ)) - return - } switch typ := typ.Underlying().(type) { case *types.Struct: w.emitStructType(name, typ) diff --git a/src/cmd/api/testdata/src/issue64958/p/p.go b/src/cmd/api/testdata/src/issue64958/p/p.go new file mode 100644 index 0000000000..feba86797f --- /dev/null +++ b/src/cmd/api/testdata/src/issue64958/p/p.go @@ -0,0 +1,3 @@ +package p + +type BasicAlias = uint8 diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index a2de63685c..6e1aa1cd95 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -141,11 +141,17 @@ Diff: // Turn relative (PC) into absolute (PC) automatically, // so that most branch instructions don't need comments // giving the absolute form. - if len(f) > 0 && strings.HasSuffix(printed, "(PC)") { - last := f[len(f)-1] - n, err := strconv.Atoi(last[:len(last)-len("(PC)")]) + if len(f) > 0 && strings.Contains(printed, "(PC)") { + index := len(f) - 1 + suf := "(PC)" + for !strings.HasSuffix(f[index], suf) { + index-- + suf = "(PC)," + } + str := f[index] + n, err := strconv.Atoi(str[:len(str)-len(suf)]) if err == nil { - f[len(f)-1] = fmt.Sprintf("%d(PC)", seq+n) + f[index] = fmt.Sprintf("%d%s", seq+n, suf) } } @@ -372,10 +378,10 @@ func Test386EndToEnd(t *testing.T) { } func TestARMEndToEnd(t *testing.T) { - defer func(old int) { buildcfg.GOARM = old }(buildcfg.GOARM) + defer func(old int) { buildcfg.GOARM.Version = old }(buildcfg.GOARM.Version) for _, goarm := range []int{5, 6, 7} { t.Logf("GOARM=%d", goarm) - buildcfg.GOARM = goarm + buildcfg.GOARM.Version = goarm testEndToEnd(t, "arm", "arm") if goarm == 6 { testEndToEnd(t, "arm", "armv6") diff --git a/src/cmd/asm/internal/asm/testdata/arm.s b/src/cmd/asm/internal/asm/testdata/arm.s index 2ba22c71de..93edc8854e 100644 --- a/src/cmd/asm/internal/asm/testdata/arm.s +++ b/src/cmd/asm/internal/asm/testdata/arm.s @@ -870,10 +870,13 @@ jmp_label_3: BIC.S R0@>R1, R2 // 7021d2e1 // SRL + SRL $0, R5, R6 // 0560a0e1 + SRL $1, R5, R6 // a560a0e1 SRL $14, R5, R6 // 2567a0e1 SRL $15, R5, R6 // a567a0e1 SRL $30, R5, R6 // 256fa0e1 SRL $31, R5, R6 // a56fa0e1 + SRL $32, R5, R6 // 2560a0e1 SRL.S $14, R5, R6 // 2567b0e1 SRL.S $15, R5, R6 // a567b0e1 SRL.S $30, R5, R6 // 256fb0e1 @@ -892,10 +895,13 @@ jmp_label_3: SRL.S R5, R7 // 3775b0e1 // SRA + SRA $0, R5, R6 // 0560a0e1 + SRA $1, R5, R6 // c560a0e1 SRA $14, R5, R6 // 4567a0e1 SRA $15, R5, R6 // c567a0e1 SRA $30, R5, R6 // 456fa0e1 SRA $31, R5, R6 // c56fa0e1 + SRA $32, R5, R6 // 4560a0e1 SRA.S $14, R5, R6 // 4567b0e1 SRA.S $15, R5, R6 // c567b0e1 SRA.S $30, R5, R6 // 456fb0e1 @@ -914,6 +920,8 @@ jmp_label_3: SRA.S R5, R7 // 5775b0e1 // SLL + SLL $0, R5, R6 // 0560a0e1 + SLL $1, R5, R6 // 8560a0e1 SLL $14, R5, R6 // 0567a0e1 SLL $15, R5, R6 // 8567a0e1 SLL $30, R5, R6 // 056fa0e1 @@ -935,6 +943,20 @@ jmp_label_3: SLL R5, R7 // 1775a0e1 SLL.S R5, R7 // 1775b0e1 +// Ops with zero shifts should encode as left shifts + ADD R0<<0, R1, R2 // 002081e0 + ADD R0>>0, R1, R2 // 002081e0 + ADD R0->0, R1, R2 // 002081e0 + ADD R0@>0, R1, R2 // 002081e0 + MOVW R0<<0(R1), R2 // 002091e7 + MOVW R0>>0(R1), R2 // 002091e7 + MOVW R0->0(R1), R2 // 002091e7 + MOVW R0@>0(R1), R2 // 002091e7 + MOVW R0, R1<<0(R2) // 010082e7 + MOVW R0, R1>>0(R2) // 010082e7 + MOVW R0, R1->0(R2) // 010082e7 + MOVW R0, R1@>0(R2) // 010082e7 + // MULA / MULS MULAWT R1, R2, R3, R4 // c23124e1 MULAWB R1, R2, R3, R4 // 823124e1 diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s index 46ea6645af..ecad08b37a 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64.s +++ b/src/cmd/asm/internal/asm/testdata/arm64.s @@ -981,6 +981,14 @@ again: ADR next, R11 // ADR R11 // 2b000010 next: NOP + ADR -2(PC), R10 // 0a000010 + ADR 2(PC), R16 // 10000010 + ADR -26(PC), R1 // 01000010 + ADR 12(PC), R2 // 02000010 + ADRP -2(PC), R10 // 0a000090 + ADRP 2(PC), R16 // 10000090 + ADRP -26(PC), R1 // 01000090 + ADRP 12(PC), R2 // 02000090 // LDP/STP LDP (R0), (R0, R1) // 000440a9 @@ -1003,6 +1011,7 @@ next: LDP -8(R0), (R1, R2) // 01887fa9 LDP x(SB), (R1, R2) LDP x+8(SB), (R1, R2) + LDP 8(R1), (ZR, R2) // 3f8840a9 LDPW -5(R0), (R1, R2) // 1b1400d1610b4029 LDPW (R0), (R1, R2) // 01084029 LDPW 4(R0), (R1, R2) // 01884029 @@ -1020,6 +1029,7 @@ next: LDPW 1024(RSP), (R1, R2) // fb031091610b4029 LDPW x(SB), (R1, R2) LDPW x+8(SB), (R1, R2) + LDPW 8(R1), (ZR, R2) // 3f084129 LDPSW (R0), (R1, R2) // 01084069 LDPSW 4(R0), (R1, R2) // 01884069 LDPSW -4(R0), (R1, R2) // 01887f69 @@ -1036,6 +1046,7 @@ next: LDPSW 1024(RSP), (R1, R2) // fb031091610b4069 LDPSW x(SB), (R1, R2) LDPSW x+8(SB), (R1, R2) + LDPSW 8(R1), (ZR, R2) // 3f084169 STP (R3, R4), (R5) // a31000a9 STP (R3, R4), 8(R5) // a39000a9 STP.W (R3, R4), 8(R5) // a39080a9 diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s index e1eafa2b46..3ac8788424 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64error.s +++ b/src/cmd/asm/internal/asm/testdata/arm64error.s @@ -66,7 +66,6 @@ TEXT errors(SB),$0 LDP.W 8(R3), (R2, R3) // ERROR "constrained unpredictable behavior" LDP (R1), (R2, R2) // ERROR "constrained unpredictable behavior" LDP (R0), (F0, F1) // ERROR "invalid register pair" - LDP (R0), (R3, ZR) // ERROR "invalid register pair" LDXPW (RSP), (R2, R2) // ERROR "constrained unpredictable behavior" LDAXPW (R5), (R2, R2) // ERROR "constrained unpredictable behavior" MOVD.P 300(R2), R3 // ERROR "offset out of range [-256,255]" diff --git a/src/cmd/asm/internal/asm/testdata/s390x.s b/src/cmd/asm/internal/asm/testdata/s390x.s index 82aa445356..977190678f 100644 --- a/src/cmd/asm/internal/asm/testdata/s390x.s +++ b/src/cmd/asm/internal/asm/testdata/s390x.s @@ -419,9 +419,9 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16- KMC R2, R6 // b92f0026 KLMD R2, R8 // b93f0028 KIMD R0, R4 // b93e0004 - KDSA R0, R8 // b93a0008 - KMA R6, R2, R4 // b9296024 - KMCTR R6, R2, R4 // b92d6024 + KDSA R0, R8 // b93a0008 + KMA R2, R6, R4 // b9296024 + KMCTR R2, R6, R4 // b92d6024 // vector add and sub instructions VAB V3, V4, V4 // e743400000f3 diff --git a/src/cmd/cgo/internal/test/callback_windows.go b/src/cmd/cgo/internal/test/callback_windows.go index 95e97c9af9..77bdfa4dd3 100644 --- a/src/cmd/cgo/internal/test/callback_windows.go +++ b/src/cmd/cgo/internal/test/callback_windows.go @@ -29,7 +29,7 @@ USHORT backtrace(ULONG FramesToCapture, PVOID *BackTrace) { } ControlPc = context.Rip; - // Check if we left the user range. + // Check if we left the user range. if (ControlPc < 0x10000) { break; } @@ -65,32 +65,17 @@ func testCallbackCallersSEH(t *testing.T) { // TODO: support SEH on other architectures. t.Skip("skipping on non-amd64") } - const cgoexpPrefix = "_cgoexp_" + // Only frames in the test package are checked. want := []string{ - "runtime.asmcgocall_landingpad", - "runtime.asmcgocall", - "runtime.cgocall", "test._Cfunc_backtrace", "test.testCallbackCallersSEH.func1.1", "test.testCallbackCallersSEH.func1", "test.goCallback", - cgoexpPrefix + "goCallback", - "runtime.cgocallbackg1", - "runtime.cgocallbackg", - "runtime.cgocallbackg", - "runtime.cgocallback", - "crosscall2", - "runtime.asmcgocall_landingpad", - "runtime.asmcgocall", - "runtime.cgocall", "test._Cfunc_callback", "test.nestedCall.func1", "test.nestedCall", "test.testCallbackCallersSEH", "test.TestCallbackCallersSEH", - "testing.tRunner", - "testing.(*T).Run.gowrap1", - "runtime.goexit", } pc := make([]uintptr, 100) n := 0 @@ -105,26 +90,17 @@ func testCallbackCallersSEH(t *testing.T) { } fname := f.Name() switch fname { - case "goCallback", "callback": - // TODO(qmuntal): investigate why these functions don't appear + case "goCallback": + // TODO(qmuntal): investigate why this function doesn't appear // when using the external linker. continue } - // Skip cgo-generated functions, the runtime might not know about them, - // depending on the link mode. - if strings.HasPrefix(fname, "_cgo_") { - continue - } - // Remove the cgo-generated random prefix. - if strings.HasPrefix(fname, cgoexpPrefix) { - idx := strings.Index(fname[len(cgoexpPrefix):], "_") - if idx >= 0 { - fname = cgoexpPrefix + fname[len(cgoexpPrefix)+idx+1:] - } - } // In module mode, this package has a fully-qualified import path. // Remove it if present. fname = strings.TrimPrefix(fname, "cmd/cgo/internal/") + if !strings.HasPrefix(fname, "test.") { + continue + } got = append(got, fname) } if !reflect.DeepEqual(want, got) { diff --git a/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s b/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s index 1f492eafe9..3edba3dd82 100644 --- a/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s +++ b/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s b/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s index fa34f6bd37..0f10e3a326 100644 --- a/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s +++ b/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s @@ -1,4 +1,4 @@ -// Copyright 2020 The Go Authors. All rights reserved. +// 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. diff --git a/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go b/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go index b0c507477f..d3ab1902c1 100644 --- a/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go +++ b/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Go Authors. All rights reserve d. +// 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. diff --git a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go index f84c9f37ae..3f5b1d91c7 100644 --- a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go +++ b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go @@ -7,11 +7,14 @@ package sanitizers_test import ( + "internal/testenv" "strings" "testing" ) func TestLibFuzzer(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) goos, err := goEnv("GOOS") if err != nil { t.Fatal(err) diff --git a/src/cmd/cgo/internal/testsanitizers/msan_test.go b/src/cmd/cgo/internal/testsanitizers/msan_test.go index 1a22b5246c..83d66f6660 100644 --- a/src/cmd/cgo/internal/testsanitizers/msan_test.go +++ b/src/cmd/cgo/internal/testsanitizers/msan_test.go @@ -8,11 +8,14 @@ package sanitizers_test import ( "internal/platform" + "internal/testenv" "strings" "testing" ) func TestMSAN(t *testing.T) { + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) goos, err := goEnv("GOOS") if err != nil { t.Fatal(err) diff --git a/src/cmd/compile/abi-internal.md b/src/cmd/compile/abi-internal.md index 43dc39689b..eae230dc07 100644 --- a/src/cmd/compile/abi-internal.md +++ b/src/cmd/compile/abi-internal.md @@ -633,6 +633,56 @@ modifying or saving the FPCR. Functions are allowed to modify it between calls (as long as they restore it), but as of this writing Go code never does. +### loong64 architecture + +The loong64 architecture uses R4 – R19 for integer arguments and integer results. + +It uses F0 – F15 for floating-point arguments and results. + +Registers R20 - R21, R23 – R28, R30 - R31, F16 – F31 are permanent scratch registers. + +Register R2 is reserved and never used. + +Register R20, R21 is Used by runtime.duffcopy, runtime.duffzero. + +Special-purpose registers used within Go generated code and Go assembly code +are as follows: + +| Register | Call meaning | Return meaning | Body meaning | +| --- | --- | --- | --- | +| R0 | Zero value | Same | Same | +| R1 | Link register | Link register | Scratch | +| R3 | Stack pointer | Same | Same | +| R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero | +| R22 | Current goroutine | Same | Same | +| R29 | Closure context pointer | Same | Same | +| R30, R31 | used by the assembler | Same | Same | + +*Rationale*: These register meanings are compatible with Go’s stack-based +calling convention. + +#### Stack layout + +The stack pointer, R3, grows down and is aligned to 8 bytes. + +A function's stack frame, after the frame is created, is laid out as +follows: + + +------------------------------+ + | ... locals ... | + | ... outgoing arguments ... | + | return PC | ← R3 points to + +------------------------------+ ↓ lower addresses + +This stack layout is used by both register-based (ABIInternal) and +stack-based (ABI0) calling conventions. + +The "return PC" is loaded to the link register, R1, as part of the +loong64 `JAL` operation. + +#### Flags +All bits in CSR are system flags and are not modified by Go. + ### ppc64 architecture The ppc64 architecture uses R3 – R10 and R14 – R17 for integer arguments diff --git a/src/cmd/compile/default.pgo b/src/cmd/compile/default.pgo index 2ba79688d4..0f925ec69c 100644 Binary files a/src/cmd/compile/default.pgo and b/src/cmd/compile/default.pgo differ diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go index 23e52bacbf..43d811832e 100644 --- a/src/cmd/compile/internal/arm/galign.go +++ b/src/cmd/compile/internal/arm/galign.go @@ -15,7 +15,7 @@ func Init(arch *ssagen.ArchInfo) { arch.LinkArch = &arm.Linkarm arch.REGSP = arm.REGSP arch.MAXWIDTH = (1 << 32) - 1 - arch.SoftFloat = buildcfg.GOARM == 5 + arch.SoftFloat = buildcfg.GOARM.SoftFloat arch.ZeroRange = zerorange arch.Ginsnop = ginsnop diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go index 7fcbb4d024..638ed3ed4e 100644 --- a/src/cmd/compile/internal/arm/ssa.go +++ b/src/cmd/compile/internal/arm/ssa.go @@ -289,7 +289,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpARMANDconst, ssa.OpARMBICconst: // try to optimize ANDconst and BICconst to BFC, which saves bytes and ticks // BFC is only available on ARMv7, and its result and source are in the same register - if buildcfg.GOARM == 7 && v.Reg() == v.Args[0].Reg() { + if buildcfg.GOARM.Version == 7 && v.Reg() == v.Args[0].Reg() { var val uint32 if v.Op == ssa.OpARMANDconst { val = ^uint32(v.AuxInt) @@ -646,7 +646,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { default: } } - if buildcfg.GOARM >= 6 { + if buildcfg.GOARM.Version >= 6 { // generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7 genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0) return diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go index a85f0139fc..aadd950a0a 100644 --- a/src/cmd/compile/internal/base/debug.go +++ b/src/cmd/compile/internal/base/debug.go @@ -36,7 +36,6 @@ type DebugFlags struct { Gossahash string `help:"hash value for use in debugging the compiler"` InlFuncsWithClosures int `help:"allow functions with closures to be inlined" concurrent:"ok"` InlStaticInit int `help:"allow static initialization of inlined calls" concurrent:"ok"` - InterfaceCycles int `help:"allow anonymous interface cycles"` Libfuzzer int `help:"enable coverage instrumentation for libfuzzer"` LoopVar int `help:"shared (0, default), 1 (private loop variables), 2, private + log"` LoopVarHash string `help:"for debugging changes in loop behavior. Overrides experiment and loopvar flag."` diff --git a/src/cmd/compile/internal/base/hashdebug.go b/src/cmd/compile/internal/base/hashdebug.go index de7f01f09e..8342a5b9d9 100644 --- a/src/cmd/compile/internal/base/hashdebug.go +++ b/src/cmd/compile/internal/base/hashdebug.go @@ -204,7 +204,6 @@ func NewHashDebug(ev, s string, file io.Writer) *HashDebug { i++ } return hd - } // TODO: Delete when we switch to bisect-only. diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go index 7b3a869d8e..5d1b952627 100644 --- a/src/cmd/compile/internal/devirtualize/devirtualize.go +++ b/src/cmd/compile/internal/devirtualize/devirtualize.go @@ -18,39 +18,27 @@ import ( "cmd/compile/internal/types" ) -// Static devirtualizes calls within fn where possible when the concrete callee +// StaticCall devirtualizes the given call if possible when the concrete callee // is available statically. -func Static(fn *ir.Func) { - ir.CurFunc = fn +func StaticCall(call *ir.CallExpr) { + // For promoted methods (including value-receiver methods promoted + // to pointer-receivers), the interface method wrapper may contain + // expressions that can panic (e.g., ODEREF, ODOTPTR, + // ODOTINTER). Devirtualization involves inlining these expressions + // (and possible panics) to the call site. This normally isn't a + // problem, but for go/defer statements it can move the panic from + // when/where the call executes to the go/defer statement itself, + // which is a visible change in semantics (e.g., #52072). To prevent + // this, we skip devirtualizing calls within go/defer statements + // altogether. + if call.GoDefer { + return + } - // For promoted methods (including value-receiver methods promoted to pointer-receivers), - // the interface method wrapper may contain expressions that can panic (e.g., ODEREF, ODOTPTR, ODOTINTER). - // Devirtualization involves inlining these expressions (and possible panics) to the call site. - // This normally isn't a problem, but for go/defer statements it can move the panic from when/where - // the call executes to the go/defer statement itself, which is a visible change in semantics (e.g., #52072). - // To prevent this, we skip devirtualizing calls within go/defer statements altogether. - goDeferCall := make(map[*ir.CallExpr]bool) - ir.VisitList(fn.Body, func(n ir.Node) { - switch n := n.(type) { - case *ir.GoDeferStmt: - if call, ok := n.Call.(*ir.CallExpr); ok { - goDeferCall[call] = true - } - return - case *ir.CallExpr: - if !goDeferCall[n] { - staticCall(n) - } - } - }) -} - -// staticCall devirtualizes the given call if possible when the concrete callee -// is available statically. -func staticCall(call *ir.CallExpr) { if call.Op() != ir.OCALLINTER { return } + sel := call.Fun.(*ir.SelectorExpr) r := ir.StaticValue(sel.X) if r.Op() != ir.OCONVIFACE { @@ -70,7 +58,7 @@ func staticCall(call *ir.CallExpr) { return } - // If typ *has* a shape type, then it's an shaped, instantiated + // If typ *has* a shape type, then it's a shaped, instantiated // type like T[go.shape.int], and its methods (may) have an extra // dictionary parameter. We could devirtualize this call if we // could derive an appropriate dictionary argument. diff --git a/src/cmd/compile/internal/devirtualize/pgo.go b/src/cmd/compile/internal/devirtualize/pgo.go index 05b37d6be6..170bf74673 100644 --- a/src/cmd/compile/internal/devirtualize/pgo.go +++ b/src/cmd/compile/internal/devirtualize/pgo.go @@ -107,9 +107,6 @@ func ProfileGuided(fn *ir.Func, p *pgo.Profile) { name := ir.LinkFuncName(fn) - // Can't devirtualize go/defer calls. See comment in Static. - goDeferCall := make(map[*ir.CallExpr]bool) - var jsonW *json.Encoder if base.Debug.PGODebug >= 3 { jsonW = json.NewEncoder(os.Stdout) @@ -121,12 +118,6 @@ func ProfileGuided(fn *ir.Func, p *pgo.Profile) { return n } - if gds, ok := n.(*ir.GoDeferStmt); ok { - if call, ok := gds.Call.(*ir.CallExpr); ok { - goDeferCall[call] = true - } - } - ir.EditChildren(n, edit) call, ok := n.(*ir.CallExpr) @@ -156,7 +147,7 @@ func ProfileGuided(fn *ir.Func, p *pgo.Profile) { fmt.Printf("%v: PGO devirtualize considering call %v\n", ir.Line(call), call) } - if goDeferCall[call] { + if call.GoDefer { if base.Debug.PGODebug >= 2 { fmt.Printf("%v: can't PGO devirtualize go/defer call %v\n", ir.Line(call), call) } diff --git a/src/cmd/compile/internal/escape/call.go b/src/cmd/compile/internal/escape/call.go index bf40de0544..4a3753ada9 100644 --- a/src/cmd/compile/internal/escape/call.go +++ b/src/cmd/compile/internal/escape/call.go @@ -155,10 +155,17 @@ func (e *escape) call(ks []hole, call ir.Node) { e.discard(call.X) e.discard(call.Y) - case ir.ODELETE, ir.OMAX, ir.OMIN, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP: + case ir.ODELETE, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP: call := call.(*ir.CallExpr) - for i := range call.Args { - e.discard(call.Args[i]) + for _, arg := range call.Args { + e.discard(arg) + } + e.discard(call.RType) + + case ir.OMIN, ir.OMAX: + call := call.(*ir.CallExpr) + for _, arg := range call.Args { + argument(ks[0], arg) } e.discard(call.RType) diff --git a/src/cmd/compile/internal/escape/graph.go b/src/cmd/compile/internal/escape/graph.go index f3baa67223..75e2546a7b 100644 --- a/src/cmd/compile/internal/escape/graph.go +++ b/src/cmd/compile/internal/escape/graph.go @@ -38,7 +38,7 @@ import ( // e.value(k, n.Left) // } -// An location represents an abstract location that stores a Go +// A location represents an abstract location that stores a Go // variable. type location struct { n ir.Node // represented variable or expression, if any diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index a19962dabb..7e5069fced 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -9,10 +9,10 @@ import ( "bytes" "cmd/compile/internal/base" "cmd/compile/internal/coverage" - "cmd/compile/internal/devirtualize" "cmd/compile/internal/dwarfgen" "cmd/compile/internal/escape" "cmd/compile/internal/inline" + "cmd/compile/internal/inline/interleaved" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" "cmd/compile/internal/loopvar" @@ -224,30 +224,15 @@ func Main(archInit func(*ssagen.ArchInfo)) { } } - base.Timer.Start("fe", "pgo-devirtualization") - if profile != nil && base.Debug.PGODevirtualize > 0 { - // TODO(prattmic): No need to use bottom-up visit order. This - // is mirroring the PGO IRGraph visit order, which also need - // not be bottom-up. - ir.VisitFuncsBottomUp(typecheck.Target.Funcs, func(list []*ir.Func, recursive bool) { - for _, fn := range list { - devirtualize.ProfileGuided(fn, profile) - } - }) - ir.CurFunc = nil - } + // Interleaved devirtualization and inlining. + base.Timer.Start("fe", "devirtualize-and-inline") + interleaved.DevirtualizeAndInlinePackage(typecheck.Target, profile) - // Inlining - base.Timer.Start("fe", "inlining") - if base.Flag.LowerL != 0 { - inline.InlinePackage(profile) - } noder.MakeWrappers(typecheck.Target) // must happen after inlining - // Devirtualize and get variable capture right in for loops + // Get variable capture right in for loops. var transformed []loopvar.VarAndLoop for _, fn := range typecheck.Target.Funcs { - devirtualize.Static(fn) transformed = append(transformed, loopvar.ForCapture(fn)...) } ir.CurFunc = nil diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index 2677ae3086..b365008c76 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -29,6 +29,7 @@ package inline import ( "fmt" "go/constant" + "internal/buildcfg" "strconv" "cmd/compile/internal/base" @@ -76,8 +77,8 @@ var ( inlineHotMaxBudget int32 = 2000 ) -// pgoInlinePrologue records the hot callsites from ir-graph. -func pgoInlinePrologue(p *pgo.Profile, funcs []*ir.Func) { +// PGOInlinePrologue records the hot callsites from ir-graph. +func PGOInlinePrologue(p *pgo.Profile, funcs []*ir.Func) { if base.Debug.PGOInlineCDFThreshold != "" { if s, err := strconv.ParseFloat(base.Debug.PGOInlineCDFThreshold, 64); err == nil && s >= 0 && s <= 100 { inlineCDFHotCallSiteThresholdPercent = s @@ -134,79 +135,52 @@ func hotNodesFromCDF(p *pgo.Profile) (float64, []pgo.NamedCallEdge) { return 0, p.NamedEdgeMap.ByWeight } -// InlinePackage finds functions that can be inlined and clones them before walk expands them. -func InlinePackage(p *pgo.Profile) { - if base.Debug.PGOInline == 0 { - p = nil +// CanInlineFuncs computes whether a batch of functions are inlinable. +func CanInlineFuncs(funcs []*ir.Func, profile *pgo.Profile) { + if profile != nil { + PGOInlinePrologue(profile, funcs) } - inlheur.SetupScoreAdjustments() - - InlineDecls(p, typecheck.Target.Funcs, true) - - // Perform a garbage collection of hidden closures functions that - // are no longer reachable from top-level functions following - // inlining. See #59404 and #59638 for more context. - garbageCollectUnreferencedHiddenClosures() - - if base.Debug.DumpInlFuncProps != "" { - inlheur.DumpFuncProps(nil, base.Debug.DumpInlFuncProps) - } - if inlheur.Enabled() { - postProcessCallSites(p) - inlheur.TearDown() - } + ir.VisitFuncsBottomUp(funcs, func(list []*ir.Func, recursive bool) { + CanInlineSCC(list, recursive, profile) + }) } -// InlineDecls applies inlining to the given batch of declarations. -func InlineDecls(p *pgo.Profile, funcs []*ir.Func, doInline bool) { - if p != nil { - pgoInlinePrologue(p, funcs) +// CanInlineSCC computes the inlinability of functions within an SCC +// (strongly connected component). +// +// CanInlineSCC is designed to be used by ir.VisitFuncsBottomUp +// callbacks. +func CanInlineSCC(funcs []*ir.Func, recursive bool, profile *pgo.Profile) { + if base.Flag.LowerL == 0 { + return } - doCanInline := func(n *ir.Func, recursive bool, numfns int) { + numfns := numNonClosures(funcs) + + for _, fn := range funcs { if !recursive || numfns > 1 { // We allow inlining if there is no // recursion, or the recursion cycle is // across more than one function. - CanInline(n, p) + CanInline(fn, profile) } else { - if base.Flag.LowerM > 1 && n.OClosure == nil { - fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname) + if base.Flag.LowerM > 1 && fn.OClosure == nil { + fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(fn), fn.Nname) } } if inlheur.Enabled() { - analyzeFuncProps(n, p) + analyzeFuncProps(fn, profile) } } - - ir.VisitFuncsBottomUp(funcs, func(list []*ir.Func, recursive bool) { - numfns := numNonClosures(list) - // We visit functions within an SCC in fairly arbitrary order, - // so by computing inlinability for all functions in the SCC - // before performing any inlining, the results are less - // sensitive to the order within the SCC (see #58905 for an - // example). - - // First compute inlinability for all functions in the SCC ... - for _, n := range list { - doCanInline(n, recursive, numfns) - } - // ... then make a second pass to do inlining of calls. - if doInline { - for _, n := range list { - InlineCalls(n, p) - } - } - }) } -// garbageCollectUnreferencedHiddenClosures makes a pass over all the +// GarbageCollectUnreferencedHiddenClosures makes a pass over all the // top-level (non-hidden-closure) functions looking for nested closure // functions that are reachable, then sweeps through the Target.Decls // list and marks any non-reachable hidden closure function as dead. // See issues #59404 and #59638 for more context. -func garbageCollectUnreferencedHiddenClosures() { +func GarbageCollectUnreferencedHiddenClosures() { liveFuncs := make(map[*ir.Func]bool) @@ -336,7 +310,7 @@ func CanInline(fn *ir.Func, profile *pgo.Profile) { visitor := hairyVisitor{ curFunc: fn, - isBigFunc: isBigFunc(fn), + isBigFunc: IsBigFunc(fn), budget: budget, maxBudget: budget, extraCallCost: cc, @@ -354,14 +328,22 @@ func CanInline(fn *ir.Func, profile *pgo.Profile) { CanDelayResults: canDelayResults(fn), } + if base.Flag.LowerM != 0 || logopt.Enabled() { + noteInlinableFunc(n, fn, budget-visitor.budget) + } +} +// noteInlinableFunc issues a message to the user that the specified +// function is inlinable. +func noteInlinableFunc(n *ir.Name, fn *ir.Func, cost int32) { if base.Flag.LowerM > 1 { - fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, budget-visitor.budget, fn.Type(), ir.Nodes(fn.Body)) + fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, cost, fn.Type(), ir.Nodes(fn.Body)) } else if base.Flag.LowerM != 0 { fmt.Printf("%v: can inline %v\n", ir.Line(fn), n) } + // JSON optimization log output. if logopt.Enabled() { - logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", budget-visitor.budget)) + logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", cost)) } } @@ -585,7 +567,7 @@ opSwitch: // Check whether we'd actually inline this call. Set // log == false since we aren't actually doing inlining // yet. - if canInlineCallExpr(v.curFunc, n, callee, v.isBigFunc, false) { + if ok, _ := canInlineCallExpr(v.curFunc, n, callee, v.isBigFunc, false); ok { // mkinlcall would inline this call [1], so use // the cost of the inline body as the cost of // the call, as that is what will actually @@ -732,14 +714,16 @@ opSwitch: // particular, to avoid breaking the existing inlinability regress // tests), we need to compensate for this here. // - // See also identical logic in isBigFunc. - if init := n.Rhs[0].Init(); len(init) == 1 { - if _, ok := init[0].(*ir.AssignListStmt); ok { - // 4 for each value, because each temporary variable now - // appears 3 times (DCL, LHS, RHS), plus an extra DCL node. - // - // 1 for the extra "tmp1, tmp2 = f()" assignment statement. - v.budget += 4*int32(len(n.Lhs)) + 1 + // See also identical logic in IsBigFunc. + if len(n.Rhs) > 0 { + if init := n.Rhs[0].Init(); len(init) == 1 { + if _, ok := init[0].(*ir.AssignListStmt); ok { + // 4 for each value, because each temporary variable now + // appears 3 times (DCL, LHS, RHS), plus an extra DCL node. + // + // 1 for the extra "tmp1, tmp2 = f()" assignment statement. + v.budget += 4*int32(len(n.Lhs)) + 1 + } } } @@ -771,12 +755,15 @@ opSwitch: return ir.DoChildren(n, v.do) } -func isBigFunc(fn *ir.Func) bool { +// IsBigFunc reports whether fn is a "big" function. +// +// Note: The criteria for "big" is heuristic and subject to change. +func IsBigFunc(fn *ir.Func) bool { budget := inlineBigFunctionNodes return ir.Any(fn, func(n ir.Node) bool { // See logic in hairyVisitor.doNode, explaining unified IR's // handling of "a, b = f()" assignments. - if n, ok := n.(*ir.AssignListStmt); ok && n.Op() == ir.OAS2 { + if n, ok := n.(*ir.AssignListStmt); ok && n.Op() == ir.OAS2 && len(n.Rhs) > 0 { if init := n.Rhs[0].Init(); len(init) == 1 { if _, ok := init[0].(*ir.AssignListStmt); ok { budget += 4*len(n.Lhs) + 1 @@ -789,128 +776,40 @@ func isBigFunc(fn *ir.Func) bool { }) } -// InlineCalls/inlnode walks fn's statements and expressions and substitutes any -// calls made to inlineable functions. This is the external entry point. -func InlineCalls(fn *ir.Func, profile *pgo.Profile) { - if inlheur.Enabled() && !fn.Wrapper() { - inlheur.ScoreCalls(fn) - defer inlheur.ScoreCallsCleanup() +// TryInlineCall returns an inlined call expression for call, or nil +// if inlining is not possible. +func TryInlineCall(callerfn *ir.Func, call *ir.CallExpr, bigCaller bool, profile *pgo.Profile) *ir.InlinedCallExpr { + if base.Flag.LowerL == 0 { + return nil } - if base.Debug.DumpInlFuncProps != "" && !fn.Wrapper() { - inlheur.DumpFuncProps(fn, base.Debug.DumpInlFuncProps) + if call.Op() != ir.OCALLFUNC { + return nil } - savefn := ir.CurFunc - ir.CurFunc = fn - bigCaller := isBigFunc(fn) - if bigCaller && base.Flag.LowerM > 1 { - fmt.Printf("%v: function %v considered 'big'; reducing max cost of inlinees\n", ir.Line(fn), fn) - } - var inlCalls []*ir.InlinedCallExpr - var edit func(ir.Node) ir.Node - edit = func(n ir.Node) ir.Node { - return inlnode(fn, n, bigCaller, &inlCalls, edit, profile) - } - ir.EditChildren(fn, edit) - - // If we inlined any calls, we want to recursively visit their - // bodies for further inlining. However, we need to wait until - // *after* the original function body has been expanded, or else - // inlCallee can have false positives (e.g., #54632). - for len(inlCalls) > 0 { - call := inlCalls[0] - inlCalls = inlCalls[1:] - ir.EditChildren(call, edit) + if call.GoDefer || call.NoInline { + return nil } - ir.CurFunc = savefn -} - -// inlnode recurses over the tree to find inlineable calls, which will -// be turned into OINLCALLs by mkinlcall. When the recursion comes -// back up will examine left, right, list, rlist, ninit, ntest, nincr, -// nbody and nelse and use one of the 4 inlconv/glue functions above -// to turn the OINLCALL into an expression, a statement, or patch it -// in to this nodes list or rlist as appropriate. -// NOTE it makes no sense to pass the glue functions down the -// recursion to the level where the OINLCALL gets created because they -// have to edit /this/ n, so you'd have to push that one down as well, -// but then you may as well do it here. so this is cleaner and -// shorter and less complicated. -// The result of inlnode MUST be assigned back to n, e.g. -// -// n.Left = inlnode(n.Left) -func inlnode(callerfn *ir.Func, n ir.Node, bigCaller bool, inlCalls *[]*ir.InlinedCallExpr, edit func(ir.Node) ir.Node, profile *pgo.Profile) ir.Node { - if n == nil { - return n - } - - switch n.Op() { - case ir.ODEFER, ir.OGO: - n := n.(*ir.GoDeferStmt) - switch call := n.Call; call.Op() { - case ir.OCALLMETH: - base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") - case ir.OCALLFUNC: - call := call.(*ir.CallExpr) - call.NoInline = true - } - case ir.OTAILCALL: - n := n.(*ir.TailCallStmt) - n.Call.NoInline = true // Not inline a tail call for now. Maybe we could inline it just like RETURN fn(arg)? - - // TODO do them here (or earlier), - // so escape analysis can avoid more heapmoves. - case ir.OCLOSURE: - return n - case ir.OCALLMETH: - base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") - case ir.OCALLFUNC: - n := n.(*ir.CallExpr) - if n.Fun.Op() == ir.OMETHEXPR { - // Prevent inlining some reflect.Value methods when using checkptr, - // even when package reflect was compiled without it (#35073). - if meth := ir.MethodExprName(n.Fun); meth != nil { - s := meth.Sym() - if base.Debug.Checkptr != 0 { - switch types.ReflectSymName(s) { - case "Value.UnsafeAddr", "Value.Pointer": - return n - } - } + // Prevent inlining some reflect.Value methods when using checkptr, + // even when package reflect was compiled without it (#35073). + if base.Debug.Checkptr != 0 && call.Fun.Op() == ir.OMETHEXPR { + if method := ir.MethodExprName(call.Fun); method != nil { + switch types.ReflectSymName(method.Sym()) { + case "Value.UnsafeAddr", "Value.Pointer": + return nil } } } - lno := ir.SetPos(n) - - ir.EditChildren(n, edit) - - // with all the branches out of the way, it is now time to - // transmogrify this node itself unless inhibited by the - // switch at the top of this function. - switch n.Op() { - case ir.OCALLMETH: - base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck") - - case ir.OCALLFUNC: - call := n.(*ir.CallExpr) - if call.NoInline { - break - } - if base.Flag.LowerM > 3 { - fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.Fun) - } - if ir.IsIntrinsicCall(call) { - break - } - if fn := inlCallee(callerfn, call.Fun, profile); fn != nil && typecheck.HaveInlineBody(fn) { - n = mkinlcall(callerfn, call, fn, bigCaller, inlCalls) - } + if base.Flag.LowerM > 3 { + fmt.Printf("%v:call to func %+v\n", ir.Line(call), call.Fun) } - - base.Pos = lno - - return n + if ir.IsIntrinsicCall(call) { + return nil + } + if fn := inlCallee(callerfn, call.Fun, profile); fn != nil && typecheck.HaveInlineBody(fn) { + return mkinlcall(callerfn, call, fn, bigCaller) + } + return nil } // inlCallee takes a function-typed expression and returns the underlying function ONAME @@ -961,9 +860,10 @@ var InlineCall = func(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlInde // inlineCostOK returns true if call n from caller to callee is cheap enough to // inline. bigCaller indicates that caller is a big function. // -// If inlineCostOK returns false, it also returns the max cost that the callee -// exceeded. -func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool, int32) { +// In addition to the "cost OK" boolean, it also returns the "max +// cost" limit used to make the decision (which may differ depending +// on func size), and the score assigned to this specific callsite. +func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool, int32, int32) { maxCost := int32(inlineMaxBudget) if bigCaller { // We use this to restrict inlining into very big functions. @@ -977,12 +877,11 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool if ok { metric = int32(score) } - } if metric <= maxCost { // Simple case. Function is already cheap enough. - return true, 0 + return true, 0, metric } // We'll also allow inlining of hot functions below inlineHotMaxBudget, @@ -992,7 +891,7 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool csi := pgo.CallSiteInfo{LineOffset: lineOffset, Caller: caller} if _, ok := candHotEdgeMap[csi]; !ok { // Cold - return false, maxCost + return false, maxCost, metric } // Hot @@ -1001,47 +900,49 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool if base.Debug.PGODebug > 0 { fmt.Printf("hot-big check disallows inlining for call %s (cost %d) at %v in big function %s\n", ir.PkgFuncName(callee), callee.Inl.Cost, ir.Line(n), ir.PkgFuncName(caller)) } - return false, maxCost + return false, maxCost, metric } if metric > inlineHotMaxBudget { - return false, inlineHotMaxBudget + return false, inlineHotMaxBudget, metric } if !base.PGOHash.MatchPosWithInfo(n.Pos(), "inline", nil) { // De-selected by PGO Hash. - return false, maxCost + return false, maxCost, metric } if base.Debug.PGODebug > 0 { fmt.Printf("hot-budget check allows inlining for call %s (cost %d) at %v in function %s\n", ir.PkgFuncName(callee), callee.Inl.Cost, ir.Line(n), ir.PkgFuncName(caller)) } - return true, 0 + return true, 0, metric } -// canInlineCallsite returns true if the call n from caller to callee can be -// inlined. bigCaller indicates that caller is a big function. log indicates -// that the 'cannot inline' reason should be logged. +// canInlineCallsite returns true if the call n from caller to callee +// can be inlined, plus the score computed for the call expr in +// question. bigCaller indicates that caller is a big function. log +// indicates that the 'cannot inline' reason should be logged. // // Preconditions: CanInline(callee) has already been called. -func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCaller bool, log bool) bool { +func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCaller bool, log bool) (bool, int32) { if callee.Inl == nil { // callee is never inlinable. if log && logopt.Enabled() { logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(callee))) } - return false + return false, 0 } - if ok, maxCost := inlineCostOK(n, callerfn, callee, bigCaller); !ok { + ok, maxCost, callSiteScore := inlineCostOK(n, callerfn, callee, bigCaller) + if !ok { // callee cost too high for this call site. if log && logopt.Enabled() { logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf("cost %d of %s exceeds max caller cost %d", callee.Inl.Cost, ir.PkgFuncName(callee), maxCost)) } - return false + return false, 0 } if callee == callerfn { @@ -1049,7 +950,7 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa if log && logopt.Enabled() { logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(callerfn))) } - return false + return false, 0 } if base.Flag.Cfg.Instrumenting && types.IsNoInstrumentPkg(callee.Sym().Pkg) { @@ -1063,7 +964,7 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf("call to runtime function %s in instrumented build", ir.PkgFuncName(callee))) } - return false + return false, 0 } if base.Flag.Race && types.IsNoRacePkg(callee.Sym().Pkg) { @@ -1071,7 +972,7 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn), fmt.Sprintf(`call to into "no-race" package function %s in race build`, ir.PkgFuncName(callee))) } - return false + return false, 0 } // Check if we've already inlined this function at this particular @@ -1094,24 +995,24 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa fmt.Sprintf("repeated recursive cycle to %s", ir.PkgFuncName(callee))) } } - return false + return false, 0 } } - return true + return true, callSiteScore } -// If n is a OCALLFUNC node, and fn is an ONAME node for a -// function with an inlinable body, return an OINLCALL node that can replace n. -// The returned node's Ninit has the parameter assignments, the Nbody is the -// inlined function body, and (List, Rlist) contain the (input, output) -// parameters. +// mkinlcall returns an OINLCALL node that can replace OCALLFUNC n, or +// nil if it cannot be inlined. callerfn is the function that contains +// n, and fn is the function being called. +// // The result of mkinlcall MUST be assigned back to n, e.g. // // n.Left = mkinlcall(n.Left, fn, isddd) -func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool, inlCalls *[]*ir.InlinedCallExpr) ir.Node { - if !canInlineCallExpr(callerfn, n, fn, bigCaller, true) { - return n +func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool) *ir.InlinedCallExpr { + ok, score := canInlineCallExpr(callerfn, n, fn, bigCaller, true) + if !ok { + return nil } typecheck.AssertFixedCall(n) @@ -1169,7 +1070,12 @@ func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool, i } if base.Flag.LowerM != 0 { - fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn) + if buildcfg.Experiment.NewInliner { + fmt.Printf("%v: inlining call to %v with score %d\n", + ir.Line(n), fn, score) + } else { + fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn) + } } if base.Flag.LowerM > 2 { fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n) @@ -1189,8 +1095,6 @@ func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool, i inlheur.UpdateCallsiteTable(callerfn, n, res) } - *inlCalls = append(*inlCalls, res) - return res } @@ -1294,7 +1198,7 @@ func isAtomicCoverageCounterUpdate(cn *ir.CallExpr) bool { return v } -func postProcessCallSites(profile *pgo.Profile) { +func PostProcessCallSites(profile *pgo.Profile) { if base.Debug.DumpInlCallSiteScores != 0 { budgetCallback := func(fn *ir.Func, prof *pgo.Profile) (int32, bool) { v := inlineBudget(fn, prof, false, false) diff --git a/src/cmd/compile/internal/inline/inlheur/analyze.go b/src/cmd/compile/internal/inline/inlheur/analyze.go index 6c3db92afe..a1b6f358e1 100644 --- a/src/cmd/compile/internal/inline/inlheur/analyze.go +++ b/src/cmd/compile/internal/inline/inlheur/analyze.go @@ -98,12 +98,13 @@ func AnalyzeFunc(fn *ir.Func, canInline func(*ir.Func), budgetForFunc func(*ir.F // inlinable; if it is over the default hairyness limit and it // doesn't have any interesting properties, then we don't want // the overhead of writing out its inline body. + nameFinder := newNameFinder(fn) for i := len(funcs) - 1; i >= 0; i-- { f := funcs[i] if f.OClosure != nil && !f.InlinabilityChecked() { canInline(f) } - funcProps := analyzeFunc(f, inlineMaxBudget) + funcProps := analyzeFunc(f, inlineMaxBudget, nameFinder) revisitInlinability(f, funcProps, budgetForFunc) if f.Inl != nil { f.Inl.Properties = funcProps.SerializeToString() @@ -122,11 +123,11 @@ func TearDown() { scoreCallsCache.csl = nil } -func analyzeFunc(fn *ir.Func, inlineMaxBudget int) *FuncProps { +func analyzeFunc(fn *ir.Func, inlineMaxBudget int, nf *nameFinder) *FuncProps { if funcInlHeur, ok := fpmap[fn]; ok { return funcInlHeur.props } - funcProps, fcstab := computeFuncProps(fn, inlineMaxBudget) + funcProps, fcstab := computeFuncProps(fn, inlineMaxBudget, nf) file, line := fnFileLine(fn) entry := fnInlHeur{ fname: fn.Sym().Name, @@ -153,7 +154,7 @@ func revisitInlinability(fn *ir.Func, funcProps *FuncProps, budgetForFunc func(* if fn.Inl == nil { return } - maxAdj := int32(largestScoreAdjustment(fn, funcProps)) + maxAdj := int32(LargestNegativeScoreAdjustment(fn, funcProps)) budget := budgetForFunc(fn) if fn.Inl.Cost+maxAdj > budget { fn.Inl = nil @@ -163,7 +164,7 @@ func revisitInlinability(fn *ir.Func, funcProps *FuncProps, budgetForFunc func(* // computeFuncProps examines the Go function 'fn' and computes for it // a function "properties" object, to be used to drive inlining // heuristics. See comments on the FuncProps type for more info. -func computeFuncProps(fn *ir.Func, inlineMaxBudget int) (*FuncProps, CallSiteTab) { +func computeFuncProps(fn *ir.Func, inlineMaxBudget int, nf *nameFinder) (*FuncProps, CallSiteTab) { if debugTrace&debugTraceFuncs != 0 { fmt.Fprintf(os.Stderr, "=-= starting analysis of func %v:\n%+v\n", fn, fn) @@ -171,13 +172,13 @@ func computeFuncProps(fn *ir.Func, inlineMaxBudget int) (*FuncProps, CallSiteTab funcProps := new(FuncProps) ffa := makeFuncFlagsAnalyzer(fn) analyzers := []propAnalyzer{ffa} - analyzers = addResultsAnalyzer(fn, analyzers, funcProps, inlineMaxBudget) - analyzers = addParamsAnalyzer(fn, analyzers, funcProps) + analyzers = addResultsAnalyzer(fn, analyzers, funcProps, inlineMaxBudget, nf) + analyzers = addParamsAnalyzer(fn, analyzers, funcProps, nf) runAnalyzersOnFunction(fn, analyzers) for _, a := range analyzers { a.setResults(funcProps) } - cstab := computeCallSiteTable(fn, fn.Body, nil, ffa.panicPathTable(), 0) + cstab := computeCallSiteTable(fn, fn.Body, nil, ffa.panicPathTable(), 0, nf) return funcProps, cstab } diff --git a/src/cmd/compile/internal/inline/inlheur/analyze_func_callsites.go b/src/cmd/compile/internal/inline/inlheur/analyze_func_callsites.go index 3e285d5181..36ebe18b82 100644 --- a/src/cmd/compile/internal/inline/inlheur/analyze_func_callsites.go +++ b/src/cmd/compile/internal/inline/inlheur/analyze_func_callsites.go @@ -14,23 +14,37 @@ import ( ) type callSiteAnalyzer struct { + fn *ir.Func + *nameFinder +} + +type callSiteTableBuilder struct { + fn *ir.Func + *nameFinder cstab CallSiteTab - fn *ir.Func ptab map[ir.Node]pstate nstack []ir.Node loopNest int isInit bool } -func makeCallSiteAnalyzer(fn *ir.Func, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int) *callSiteAnalyzer { - isInit := fn.IsPackageInit() || strings.HasPrefix(fn.Sym().Name, "init.") +func makeCallSiteAnalyzer(fn *ir.Func) *callSiteAnalyzer { return &callSiteAnalyzer{ - fn: fn, - cstab: cstab, - ptab: ptab, - isInit: isInit, - loopNest: loopNestingLevel, - nstack: []ir.Node{fn}, + fn: fn, + nameFinder: newNameFinder(fn), + } +} + +func makeCallSiteTableBuilder(fn *ir.Func, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int, nf *nameFinder) *callSiteTableBuilder { + isInit := fn.IsPackageInit() || strings.HasPrefix(fn.Sym().Name, "init.") + return &callSiteTableBuilder{ + fn: fn, + cstab: cstab, + ptab: ptab, + isInit: isInit, + loopNest: loopNestingLevel, + nstack: []ir.Node{fn}, + nameFinder: nf, } } @@ -39,22 +53,22 @@ func makeCallSiteAnalyzer(fn *ir.Func, cstab CallSiteTab, ptab map[ir.Node]pstat // specific subtree within the AST for a function. The main intended // use cases are for 'region' to be either A) an entire function body, // or B) an inlined call expression. -func computeCallSiteTable(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int) CallSiteTab { - csa := makeCallSiteAnalyzer(fn, cstab, ptab, loopNestingLevel) +func computeCallSiteTable(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, ptab map[ir.Node]pstate, loopNestingLevel int, nf *nameFinder) CallSiteTab { + cstb := makeCallSiteTableBuilder(fn, cstab, ptab, loopNestingLevel, nf) var doNode func(ir.Node) bool doNode = func(n ir.Node) bool { - csa.nodeVisitPre(n) + cstb.nodeVisitPre(n) ir.DoChildren(n, doNode) - csa.nodeVisitPost(n) + cstb.nodeVisitPost(n) return false } for _, n := range region { doNode(n) } - return csa.cstab + return cstb.cstab } -func (csa *callSiteAnalyzer) flagsForNode(call *ir.CallExpr) CSPropBits { +func (cstb *callSiteTableBuilder) flagsForNode(call *ir.CallExpr) CSPropBits { var r CSPropBits if debugTrace&debugTraceCalls != 0 { @@ -63,21 +77,21 @@ func (csa *callSiteAnalyzer) flagsForNode(call *ir.CallExpr) CSPropBits { } // Set a bit if this call is within a loop. - if csa.loopNest > 0 { + if cstb.loopNest > 0 { r |= CallSiteInLoop } // Set a bit if the call is within an init function (either // compiler-generated or user-written). - if csa.isInit { + if cstb.isInit { r |= CallSiteInInitFunc } // Decide whether to apply the panic path heuristic. Hack: don't // apply this heuristic in the function "main.main" (mostly just // to avoid annoying users). - if !isMainMain(csa.fn) { - r = csa.determinePanicPathBits(call, r) + if !isMainMain(cstb.fn) { + r = cstb.determinePanicPathBits(call, r) } return r @@ -88,15 +102,15 @@ func (csa *callSiteAnalyzer) flagsForNode(call *ir.CallExpr) CSPropBits { // panic/exit. Do this by walking back up the node stack to see if we // can find either A) an enclosing panic, or B) a statement node that // we've determined leads to a panic/exit. -func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits) CSPropBits { - csa.nstack = append(csa.nstack, call) +func (cstb *callSiteTableBuilder) determinePanicPathBits(call ir.Node, r CSPropBits) CSPropBits { + cstb.nstack = append(cstb.nstack, call) defer func() { - csa.nstack = csa.nstack[:len(csa.nstack)-1] + cstb.nstack = cstb.nstack[:len(cstb.nstack)-1] }() - for ri := range csa.nstack[:len(csa.nstack)-1] { - i := len(csa.nstack) - ri - 1 - n := csa.nstack[i] + for ri := range cstb.nstack[:len(cstb.nstack)-1] { + i := len(cstb.nstack) - ri - 1 + n := cstb.nstack[i] _, isCallExpr := n.(*ir.CallExpr) _, isStmt := n.(ir.Stmt) if isCallExpr { @@ -104,7 +118,7 @@ func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits) } if debugTrace&debugTraceCalls != 0 { - ps, inps := csa.ptab[n] + ps, inps := cstb.ptab[n] fmt.Fprintf(os.Stderr, "=-= callpar %d op=%s ps=%s inptab=%v stmt=%v\n", i, n.Op().String(), ps.String(), inps, isStmt) } @@ -112,7 +126,7 @@ func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits) r |= CallSiteOnPanicPath break } - if v, ok := csa.ptab[n]; ok { + if v, ok := cstb.ptab[n]; ok { if v == psCallsPanic { r |= CallSiteOnPanicPath break @@ -126,16 +140,15 @@ func (csa *callSiteAnalyzer) determinePanicPathBits(call ir.Node, r CSPropBits) } // propsForArg returns property bits for a given call argument expression arg. -func (csa *callSiteAnalyzer) propsForArg(arg ir.Node) ActualExprPropBits { - _, islit := isLiteral(arg) - if islit { +func (cstb *callSiteTableBuilder) propsForArg(arg ir.Node) ActualExprPropBits { + if cval := cstb.constValue(arg); cval != nil { return ActualExprConstant } - if isConcreteConvIface(arg) { + if cstb.isConcreteConvIface(arg) { return ActualExprIsConcreteConvIface } - fname, isfunc, _ := isFuncName(arg) - if isfunc { + fname := cstb.funcName(arg) + if fname != nil { if fn := fname.Func; fn != nil && typecheck.HaveInlineBody(fn) { return ActualExprIsInlinableFunc } @@ -149,11 +162,11 @@ func (csa *callSiteAnalyzer) propsForArg(arg ir.Node) ActualExprPropBits { // expression; these will be stored in the CallSite object for a given // call and then consulted when scoring. If no arg has any interesting // properties we try to save some space and return a nil slice. -func (csa *callSiteAnalyzer) argPropsForCall(ce *ir.CallExpr) []ActualExprPropBits { +func (cstb *callSiteTableBuilder) argPropsForCall(ce *ir.CallExpr) []ActualExprPropBits { rv := make([]ActualExprPropBits, len(ce.Args)) somethingInteresting := false for idx := range ce.Args { - argProp := csa.propsForArg(ce.Args[idx]) + argProp := cstb.propsForArg(ce.Args[idx]) somethingInteresting = somethingInteresting || (argProp != 0) rv[idx] = argProp } @@ -163,9 +176,9 @@ func (csa *callSiteAnalyzer) argPropsForCall(ce *ir.CallExpr) []ActualExprPropBi return rv } -func (csa *callSiteAnalyzer) addCallSite(callee *ir.Func, call *ir.CallExpr) { - flags := csa.flagsForNode(call) - argProps := csa.argPropsForCall(call) +func (cstb *callSiteTableBuilder) addCallSite(callee *ir.Func, call *ir.CallExpr) { + flags := cstb.flagsForNode(call) + argProps := cstb.argPropsForCall(call) if debugTrace&debugTraceCalls != 0 { fmt.Fprintf(os.Stderr, "=-= props %+v for call %v\n", argProps, call) } @@ -173,12 +186,12 @@ func (csa *callSiteAnalyzer) addCallSite(callee *ir.Func, call *ir.CallExpr) { cs := &CallSite{ Call: call, Callee: callee, - Assign: csa.containingAssignment(call), + Assign: cstb.containingAssignment(call), ArgProps: argProps, Flags: flags, - ID: uint(len(csa.cstab)), + ID: uint(len(cstb.cstab)), } - if _, ok := csa.cstab[call]; ok { + if _, ok := cstb.cstab[call]; ok { fmt.Fprintf(os.Stderr, "*** cstab duplicate entry at: %s\n", fmtFullPos(call.Pos())) fmt.Fprintf(os.Stderr, "*** call: %+v\n", call) @@ -189,38 +202,38 @@ func (csa *callSiteAnalyzer) addCallSite(callee *ir.Func, call *ir.CallExpr) { // on heuristics. cs.Score = int(callee.Inl.Cost) - if csa.cstab == nil { - csa.cstab = make(CallSiteTab) + if cstb.cstab == nil { + cstb.cstab = make(CallSiteTab) } - csa.cstab[call] = cs + cstb.cstab[call] = cs if debugTrace&debugTraceCalls != 0 { fmt.Fprintf(os.Stderr, "=-= added callsite: caller=%v callee=%v n=%s\n", - csa.fn, callee, fmtFullPos(call.Pos())) + cstb.fn, callee, fmtFullPos(call.Pos())) } } -func (csa *callSiteAnalyzer) nodeVisitPre(n ir.Node) { +func (cstb *callSiteTableBuilder) nodeVisitPre(n ir.Node) { switch n.Op() { case ir.ORANGE, ir.OFOR: if !hasTopLevelLoopBodyReturnOrBreak(loopBody(n)) { - csa.loopNest++ + cstb.loopNest++ } case ir.OCALLFUNC: ce := n.(*ir.CallExpr) callee := pgo.DirectCallee(ce.Fun) if callee != nil && callee.Inl != nil { - csa.addCallSite(callee, ce) + cstb.addCallSite(callee, ce) } } - csa.nstack = append(csa.nstack, n) + cstb.nstack = append(cstb.nstack, n) } -func (csa *callSiteAnalyzer) nodeVisitPost(n ir.Node) { - csa.nstack = csa.nstack[:len(csa.nstack)-1] +func (cstb *callSiteTableBuilder) nodeVisitPost(n ir.Node) { + cstb.nstack = cstb.nstack[:len(cstb.nstack)-1] switch n.Op() { case ir.ORANGE, ir.OFOR: if !hasTopLevelLoopBodyReturnOrBreak(loopBody(n)) { - csa.loopNest-- + cstb.loopNest-- } } } @@ -281,8 +294,8 @@ func hasTopLevelLoopBodyReturnOrBreak(loopBody ir.Nodes) bool { // call to a pair of auto-temps, then the second one assigning the // auto-temps to the user-visible vars. This helper will return the // second (outer) of these two. -func (csa *callSiteAnalyzer) containingAssignment(n ir.Node) ir.Node { - parent := csa.nstack[len(csa.nstack)-1] +func (cstb *callSiteTableBuilder) containingAssignment(n ir.Node) ir.Node { + parent := cstb.nstack[len(cstb.nstack)-1] // assignsOnlyAutoTemps returns TRUE of the specified OAS2FUNC // node assigns only auto-temps. @@ -315,12 +328,12 @@ func (csa *callSiteAnalyzer) containingAssignment(n ir.Node) ir.Node { // OAS1({x,y},OCONVNOP(OAS2FUNC({auto1,auto2},OCALLFUNC(bar)))) // if assignsOnlyAutoTemps(parent) { - par2 := csa.nstack[len(csa.nstack)-2] + par2 := cstb.nstack[len(cstb.nstack)-2] if par2.Op() == ir.OAS2 { return par2 } if par2.Op() == ir.OCONVNOP { - par3 := csa.nstack[len(csa.nstack)-3] + par3 := cstb.nstack[len(cstb.nstack)-3] if par3.Op() == ir.OAS2 { return par3 } @@ -378,18 +391,23 @@ func UpdateCallsiteTable(callerfn *ir.Func, n *ir.CallExpr, ic *ir.InlinedCallEx loopNestLevel = 1 } ptab := map[ir.Node]pstate{ic: icp} - icstab := computeCallSiteTable(callerfn, ic.Body, nil, ptab, loopNestLevel) + nf := newNameFinder(nil) + icstab := computeCallSiteTable(callerfn, ic.Body, nil, ptab, loopNestLevel, nf) // Record parent callsite. This is primarily for debug output. for _, cs := range icstab { cs.parent = oldcs } - // Score the calls in the inlined body. Note the setting of "doCallResults" - // to false here: at the moment there isn't any easy way to localize - // or region-ize the work done by "rescoreBasedOnCallResultUses", which - // currently does a walk over the entire function to look for uses - // of a given set of results. + // Score the calls in the inlined body. Note the setting of + // "doCallResults" to false here: at the moment there isn't any + // easy way to localize or region-ize the work done by + // "rescoreBasedOnCallResultUses", which currently does a walk + // over the entire function to look for uses of a given set of + // results. Similarly we're passing nil to makeCallSiteAnalyzer, + // so as to run name finding without the use of static value & + // friends. + csa := makeCallSiteAnalyzer(nil) const doCallResults = false - scoreCallsRegion(callerfn, ic.Body, icstab, doCallResults, ic) + csa.scoreCallsRegion(callerfn, ic.Body, icstab, doCallResults, ic) } diff --git a/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go b/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go index 588d2f4f59..b7403a4f8c 100644 --- a/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go +++ b/src/cmd/compile/internal/inline/inlheur/analyze_func_flags.go @@ -66,34 +66,24 @@ func (ffa *funcFlagsAnalyzer) setResults(funcProps *FuncProps) { funcProps.Flags = rv } -func (ffa *funcFlagsAnalyzer) getstate(n ir.Node) pstate { - val, ok := ffa.nstate[n] - if !ok { - base.Fatalf("funcFlagsAnalyzer: fn %q node %s line %s: internal error, no setting for node:\n%+v\n", ffa.fn.Sym().Name, n.Op().String(), ir.Line(n), n) - } - return val +func (ffa *funcFlagsAnalyzer) getState(n ir.Node) pstate { + return ffa.nstate[n] } -func (ffa *funcFlagsAnalyzer) setstate(n ir.Node, st pstate) { - if _, ok := ffa.nstate[n]; ok { - base.Fatalf("funcFlagsAnalyzer: fn %q internal error, existing setting for node:\n%+v\n", ffa.fn.Sym().Name, n) - } else { +func (ffa *funcFlagsAnalyzer) setState(n ir.Node, st pstate) { + if st != psNoInfo { ffa.nstate[n] = st } } -func (ffa *funcFlagsAnalyzer) updatestate(n ir.Node, st pstate) { - if _, ok := ffa.nstate[n]; !ok { - base.Fatalf("funcFlagsAnalyzer: fn %q internal error, expected existing setting for node:\n%+v\n", ffa.fn.Sym().Name, n) +func (ffa *funcFlagsAnalyzer) updateState(n ir.Node, st pstate) { + if st == psNoInfo { + delete(ffa.nstate, n) } else { ffa.nstate[n] = st } } -func (ffa *funcFlagsAnalyzer) setstateSoft(n ir.Node, st pstate) { - ffa.nstate[n] = st -} - func (ffa *funcFlagsAnalyzer) panicPathTable() map[ir.Node]pstate { return ffa.nstate } @@ -164,13 +154,13 @@ func (ffa *funcFlagsAnalyzer) stateForList(list ir.Nodes) pstate { // line 10 will be on a panic path). for i := len(list) - 1; i >= 0; i-- { n := list[i] - psi := ffa.getstate(n) + psi := ffa.getState(n) if debugTrace&debugTraceFuncFlags != 0 { fmt.Fprintf(os.Stderr, "=-= %v: stateForList n=%s ps=%s\n", ir.Line(n), n.Op().String(), psi.String()) } st = blockCombine(psi, st) - ffa.updatestate(n, st) + ffa.updateState(n, st) } if st == psTop { st = psNoInfo @@ -237,8 +227,6 @@ func (ffa *funcFlagsAnalyzer) nodeVisitPost(n ir.Node) { ir.Line(n), n.Op().String(), shouldVisit(n)) } if !shouldVisit(n) { - // invoke soft set, since node may be shared (e.g. ONAME) - ffa.setstateSoft(n, psNoInfo) return } var st pstate @@ -361,7 +349,7 @@ func (ffa *funcFlagsAnalyzer) nodeVisitPost(n ir.Node) { fmt.Fprintf(os.Stderr, "=-= %v: visit n=%s returns %s\n", ir.Line(n), n.Op().String(), st.String()) } - ffa.setstate(n, st) + ffa.setState(n, st) } func (ffa *funcFlagsAnalyzer) nodeVisitPre(n ir.Node) { diff --git a/src/cmd/compile/internal/inline/inlheur/analyze_func_params.go b/src/cmd/compile/internal/inline/inlheur/analyze_func_params.go index 0ce0af43a2..d85d73b2ef 100644 --- a/src/cmd/compile/internal/inline/inlheur/analyze_func_params.go +++ b/src/cmd/compile/internal/inline/inlheur/analyze_func_params.go @@ -19,6 +19,7 @@ type paramsAnalyzer struct { params []*ir.Name top []bool *condLevelTracker + *nameFinder } // getParams returns an *ir.Name slice containing all params for the @@ -34,8 +35,8 @@ func getParams(fn *ir.Func) []*ir.Name { // new list. If the function in question doesn't have any interesting // parameters then the analyzer list is returned unchanged, and the // params flags in "fp" are updated accordingly. -func addParamsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps) []propAnalyzer { - pa, props := makeParamsAnalyzer(fn) +func addParamsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, nf *nameFinder) []propAnalyzer { + pa, props := makeParamsAnalyzer(fn, nf) if pa != nil { analyzers = append(analyzers, pa) } else { @@ -48,7 +49,7 @@ func addParamsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps) []p // of function fn. If the function doesn't have any interesting // params, a nil helper is returned along with a set of default param // flags for the func. -func makeParamsAnalyzer(fn *ir.Func) (*paramsAnalyzer, []ParamPropBits) { +func makeParamsAnalyzer(fn *ir.Func, nf *nameFinder) (*paramsAnalyzer, []ParamPropBits) { params := getParams(fn) // includes receiver if applicable if len(params) == 0 { return nil, nil @@ -98,6 +99,7 @@ func makeParamsAnalyzer(fn *ir.Func) (*paramsAnalyzer, []ParamPropBits) { params: params, top: top, condLevelTracker: new(condLevelTracker), + nameFinder: nf, } return pa, nil } @@ -162,7 +164,7 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) { return } sel := ce.Fun.(*ir.SelectorExpr) - r := ir.StaticValue(sel.X) + r := pa.staticValue(sel.X) if r.Op() != ir.ONAME { return } @@ -193,8 +195,8 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) { return name == p, false }) } else { - cname, isFunc, _ := isFuncName(called) - if isFunc { + cname := pa.funcName(called) + if cname != nil { pa.deriveFlagsFromCallee(ce, cname.Func) } } @@ -238,7 +240,7 @@ func (pa *paramsAnalyzer) deriveFlagsFromCallee(ce *ir.CallExpr, callee *ir.Func } // See if one of the caller's parameters is flowing unmodified // into this actual expression. - r := ir.StaticValue(arg) + r := pa.staticValue(arg) if r.Op() != ir.ONAME { return } @@ -247,7 +249,13 @@ func (pa *paramsAnalyzer) deriveFlagsFromCallee(ce *ir.CallExpr, callee *ir.Func return } callerParamIdx := pa.findParamIdx(name) - if callerParamIdx == -1 || pa.params[callerParamIdx] == nil { + // note that callerParamIdx may return -1 in the case where + // the param belongs not to the current closure func we're + // analyzing but to an outer enclosing func. + if callerParamIdx == -1 { + return + } + if pa.params[callerParamIdx] == nil { panic("something went wrong") } if !pa.top[callerParamIdx] && diff --git a/src/cmd/compile/internal/inline/inlheur/analyze_func_returns.go b/src/cmd/compile/internal/inline/inlheur/analyze_func_returns.go index 58b0f54697..2aaa68d1b7 100644 --- a/src/cmd/compile/internal/inline/inlheur/analyze_func_returns.go +++ b/src/cmd/compile/internal/inline/inlheur/analyze_func_returns.go @@ -20,6 +20,7 @@ type resultsAnalyzer struct { props []ResultPropBits values []resultVal inlineMaxBudget int + *nameFinder } // resultVal captures information about a specific result returned from @@ -28,7 +29,7 @@ type resultsAnalyzer struct { // the same function, etc. This container stores info on a the specific // scenarios we're looking for. type resultVal struct { - lit constant.Value + cval constant.Value fn *ir.Name fnClo bool top bool @@ -40,8 +41,8 @@ type resultVal struct { // new list. If the function in question doesn't have any returns (or // any interesting returns) then the analyzer list is left as is, and // the result flags in "fp" are updated accordingly. -func addResultsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, inlineMaxBudget int) []propAnalyzer { - ra, props := makeResultsAnalyzer(fn, inlineMaxBudget) +func addResultsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, inlineMaxBudget int, nf *nameFinder) []propAnalyzer { + ra, props := makeResultsAnalyzer(fn, inlineMaxBudget, nf) if ra != nil { analyzers = append(analyzers, ra) } else { @@ -54,7 +55,7 @@ func addResultsAnalyzer(fn *ir.Func, analyzers []propAnalyzer, fp *FuncProps, in // in function fn. If the function doesn't have any interesting // results, a nil helper is returned along with a set of default // result flags for the func. -func makeResultsAnalyzer(fn *ir.Func, inlineMaxBudget int) (*resultsAnalyzer, []ResultPropBits) { +func makeResultsAnalyzer(fn *ir.Func, inlineMaxBudget int, nf *nameFinder) (*resultsAnalyzer, []ResultPropBits) { results := fn.Type().Results() if len(results) == 0 { return nil, nil @@ -84,6 +85,7 @@ func makeResultsAnalyzer(fn *ir.Func, inlineMaxBudget int) (*resultsAnalyzer, [] props: props, values: vals, inlineMaxBudget: inlineMaxBudget, + nameFinder: nf, } return ra, nil } @@ -143,29 +145,6 @@ func (ra *resultsAnalyzer) nodeVisitPost(n ir.Node) { } } -// isFuncName returns the *ir.Name for the func or method -// corresponding to node 'n', along with a boolean indicating success, -// and another boolean indicating whether the func is closure. -func isFuncName(n ir.Node) (*ir.Name, bool, bool) { - sv := ir.StaticValue(n) - if sv.Op() == ir.ONAME { - name := sv.(*ir.Name) - if name.Sym() != nil && name.Class == ir.PFUNC { - return name, true, false - } - } - if sv.Op() == ir.OCLOSURE { - cloex := sv.(*ir.ClosureExpr) - return cloex.Func.Nname, true, true - } - if sv.Op() == ir.OMETHEXPR { - if mn := ir.MethodExprName(sv); mn != nil { - return mn, true, false - } - } - return nil, false, false -} - // analyzeResult examines the expression 'n' being returned as the // 'ii'th argument in some return statement to see whether has // interesting characteristics (for example, returns a constant), then @@ -173,18 +152,22 @@ func isFuncName(n ir.Node) (*ir.Name, bool, bool) { // previous result (for the given return slot) that we've already // processed. func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) { - isAllocMem := isAllocatedMem(n) - isConcConvItf := isConcreteConvIface(n) - lit, isConst := isLiteral(n) - rfunc, isFunc, isClo := isFuncName(n) + isAllocMem := ra.isAllocatedMem(n) + isConcConvItf := ra.isConcreteConvIface(n) + constVal := ra.constValue(n) + isConst := (constVal != nil) + isNil := ra.isNil(n) + rfunc := ra.funcName(n) + isFunc := (rfunc != nil) + isClo := (rfunc != nil && rfunc.Func.OClosure != nil) curp := ra.props[ii] - dprops, isDerivedFromCall := deriveReturnFlagsFromCallee(n) + dprops, isDerivedFromCall := ra.deriveReturnFlagsFromCallee(n) newp := ResultNoInfo - var newlit constant.Value + var newcval constant.Value var newfunc *ir.Name if debugTrace&debugTraceResults != 0 { - fmt.Fprintf(os.Stderr, "=-= %v: analyzeResult n=%s ismem=%v isconcconv=%v isconst=%v isfunc=%v isclo=%v\n", ir.Line(n), n.Op().String(), isAllocMem, isConcConvItf, isConst, isFunc, isClo) + fmt.Fprintf(os.Stderr, "=-= %v: analyzeResult n=%s ismem=%v isconcconv=%v isconst=%v isnil=%v isfunc=%v isclo=%v\n", ir.Line(n), n.Op().String(), isAllocMem, isConcConvItf, isConst, isNil, isFunc, isClo) } if ra.values[ii].top { @@ -201,7 +184,10 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) { newfunc = rfunc case isConst: newp = ResultAlwaysSameConstant - newlit = lit + newcval = constVal + case isNil: + newp = ResultAlwaysSameConstant + newcval = nil case isDerivedFromCall: newp = dprops ra.values[ii].derived = true @@ -214,17 +200,20 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) { // the previous returns. switch curp { case ResultIsAllocatedMem: - if isAllocatedMem(n) { + if isAllocMem { newp = ResultIsAllocatedMem } case ResultIsConcreteTypeConvertedToInterface: - if isConcreteConvIface(n) { + if isConcConvItf { newp = ResultIsConcreteTypeConvertedToInterface } case ResultAlwaysSameConstant: - if isConst && isSameLiteral(lit, ra.values[ii].lit) { + if isNil && ra.values[ii].cval == nil { newp = ResultAlwaysSameConstant - newlit = lit + newcval = nil + } else if isConst && constant.Compare(constVal, token.EQL, ra.values[ii].cval) { + newp = ResultAlwaysSameConstant + newcval = constVal } case ResultAlwaysSameFunc: if isFunc && isSameFuncName(rfunc, ra.values[ii].fn) { @@ -236,7 +225,7 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) { } ra.values[ii].fn = newfunc ra.values[ii].fnClo = isClo - ra.values[ii].lit = newlit + ra.values[ii].cval = newcval ra.props[ii] = newp if debugTrace&debugTraceResults != 0 { @@ -245,15 +234,6 @@ func (ra *resultsAnalyzer) analyzeResult(ii int, n ir.Node) { } } -func isAllocatedMem(n ir.Node) bool { - sv := ir.StaticValue(n) - switch sv.Op() { - case ir.OMAKESLICE, ir.ONEW, ir.OPTRLIT, ir.OSLICELIT: - return true - } - return false -} - // deriveReturnFlagsFromCallee tries to set properties for a given // return result where we're returning call expression; return value // is a return property value and a boolean indicating whether the @@ -270,7 +250,7 @@ func isAllocatedMem(n ir.Node) bool { // set foo's return property to that of bar. In the case of "two", however, // even though each return path returns a constant, we don't know // whether the constants are identical, hence we need to be conservative. -func deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) { +func (ra *resultsAnalyzer) deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) { if n.Op() != ir.OCALLFUNC { return 0, false } @@ -282,8 +262,8 @@ func deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) { if called.Op() != ir.ONAME { return 0, false } - cname, isFunc, _ := isFuncName(called) - if !isFunc { + cname := ra.funcName(called) + if cname == nil { return 0, false } calleeProps := propsForFunc(cname.Func) @@ -295,41 +275,3 @@ func deriveReturnFlagsFromCallee(n ir.Node) (ResultPropBits, bool) { } return calleeProps.ResultFlags[0], true } - -func isLiteral(n ir.Node) (constant.Value, bool) { - sv := ir.StaticValue(n) - switch sv.Op() { - case ir.ONIL: - return nil, true - case ir.OLITERAL: - return sv.Val(), true - } - return nil, false -} - -// isSameLiteral checks to see if 'v1' and 'v2' correspond to the same -// literal value, or if they are both nil. -func isSameLiteral(v1, v2 constant.Value) bool { - if v1 == nil && v2 == nil { - return true - } - if v1 == nil || v2 == nil { - return false - } - return constant.Compare(v1, token.EQL, v2) -} - -func isConcreteConvIface(n ir.Node) bool { - sv := ir.StaticValue(n) - if sv.Op() != ir.OCONVIFACE { - return false - } - return !sv.(*ir.ConvExpr).X.Type().IsInterface() -} - -func isSameFuncName(v1, v2 *ir.Name) bool { - // NB: there are a few corner cases where pointer equality - // doesn't work here, but this should be good enough for - // our purposes here. - return v1 == v2 -} diff --git a/src/cmd/compile/internal/inline/inlheur/names.go b/src/cmd/compile/internal/inline/inlheur/names.go new file mode 100644 index 0000000000..022385087b --- /dev/null +++ b/src/cmd/compile/internal/inline/inlheur/names.go @@ -0,0 +1,129 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package inlheur + +import ( + "cmd/compile/internal/ir" + "go/constant" +) + +// nameFinder provides a set of "isXXX" query methods for clients to +// ask whether a given AST node corresponds to a function, a constant +// value, and so on. These methods use an underlying ir.ReassignOracle +// to return more precise results in cases where an "interesting" +// value is assigned to a singly-defined local temp. Example: +// +// const q = 101 +// fq := func() int { return q } +// copyOfConstant := q +// copyOfFunc := f +// interestingCall(copyOfConstant, copyOfFunc) +// +// A name finder query method invoked on the arguments being passed to +// "interestingCall" will be able detect that 'copyOfConstant' always +// evaluates to a constant (even though it is in fact a PAUTO local +// variable). A given nameFinder can also operate without using +// ir.ReassignOracle (in cases where it is not practical to look +// at the entire function); in such cases queries will still work +// for explicit constant values and functions. +type nameFinder struct { + ro *ir.ReassignOracle +} + +// newNameFinder returns a new nameFinder object with a reassignment +// oracle initialized based on the function fn, or if fn is nil, +// without an underlying ReassignOracle. +func newNameFinder(fn *ir.Func) *nameFinder { + var ro *ir.ReassignOracle + if fn != nil { + ro = &ir.ReassignOracle{} + ro.Init(fn) + } + return &nameFinder{ro: ro} +} + +// funcName returns the *ir.Name for the func or method +// corresponding to node 'n', or nil if n can't be proven +// to contain a function value. +func (nf *nameFinder) funcName(n ir.Node) *ir.Name { + sv := n + if nf.ro != nil { + sv = nf.ro.StaticValue(n) + } + if name := ir.StaticCalleeName(sv); name != nil { + return name + } + return nil +} + +// isAllocatedMem returns true if node n corresponds to a memory +// allocation expression (make, new, or equivalent). +func (nf *nameFinder) isAllocatedMem(n ir.Node) bool { + sv := n + if nf.ro != nil { + sv = nf.ro.StaticValue(n) + } + switch sv.Op() { + case ir.OMAKESLICE, ir.ONEW, ir.OPTRLIT, ir.OSLICELIT: + return true + } + return false +} + +// constValue returns the underlying constant.Value for an AST node n +// if n is itself a constant value/expr, or if n is a singly assigned +// local containing constant expr/value (or nil not constant). +func (nf *nameFinder) constValue(n ir.Node) constant.Value { + sv := n + if nf.ro != nil { + sv = nf.ro.StaticValue(n) + } + if sv.Op() == ir.OLITERAL { + return sv.Val() + } + return nil +} + +// isNil returns whether n is nil (or singly +// assigned local containing nil). +func (nf *nameFinder) isNil(n ir.Node) bool { + sv := n + if nf.ro != nil { + sv = nf.ro.StaticValue(n) + } + return sv.Op() == ir.ONIL +} + +func (nf *nameFinder) staticValue(n ir.Node) ir.Node { + if nf.ro == nil { + return n + } + return nf.ro.StaticValue(n) +} + +func (nf *nameFinder) reassigned(n *ir.Name) bool { + if nf.ro == nil { + return true + } + return nf.ro.Reassigned(n) +} + +func (nf *nameFinder) isConcreteConvIface(n ir.Node) bool { + sv := n + if nf.ro != nil { + sv = nf.ro.StaticValue(n) + } + if sv.Op() != ir.OCONVIFACE { + return false + } + return !sv.(*ir.ConvExpr).X.Type().IsInterface() +} + +func isSameFuncName(v1, v2 *ir.Name) bool { + // NB: there are a few corner cases where pointer equality + // doesn't work here, but this should be good enough for + // our purposes here. + return v1 == v2 +} diff --git a/src/cmd/compile/internal/inline/inlheur/score_callresult_uses.go b/src/cmd/compile/internal/inline/inlheur/score_callresult_uses.go index 1d31f09ac0..b95ea37d59 100644 --- a/src/cmd/compile/internal/inline/inlheur/score_callresult_uses.go +++ b/src/cmd/compile/internal/inline/inlheur/score_callresult_uses.go @@ -46,7 +46,7 @@ type resultUseAnalyzer struct { // rescoreBasedOnCallResultUses examines how call results are used, // and tries to update the scores of calls based on how their results // are used in the function. -func rescoreBasedOnCallResultUses(fn *ir.Func, resultNameTab map[*ir.Name]resultPropAndCS, cstab CallSiteTab) { +func (csa *callSiteAnalyzer) rescoreBasedOnCallResultUses(fn *ir.Func, resultNameTab map[*ir.Name]resultPropAndCS, cstab CallSiteTab) { enableDebugTraceIfEnv() rua := &resultUseAnalyzer{ resultNameTab: resultNameTab, @@ -65,7 +65,7 @@ func rescoreBasedOnCallResultUses(fn *ir.Func, resultNameTab map[*ir.Name]result disableDebugTrace() } -func examineCallResults(cs *CallSite, resultNameTab map[*ir.Name]resultPropAndCS) map[*ir.Name]resultPropAndCS { +func (csa *callSiteAnalyzer) examineCallResults(cs *CallSite, resultNameTab map[*ir.Name]resultPropAndCS) map[*ir.Name]resultPropAndCS { if debugTrace&debugTraceScoring != 0 { fmt.Fprintf(os.Stderr, "=-= examining call results for %q\n", EncodeCallSiteKey(cs)) @@ -103,7 +103,7 @@ func examineCallResults(cs *CallSite, resultNameTab map[*ir.Name]resultPropAndCS if rprop&interesting == 0 { continue } - if ir.Reassigned(n) { + if csa.nameFinder.reassigned(n) { continue } if resultNameTab == nil { diff --git a/src/cmd/compile/internal/inline/inlheur/scoring.go b/src/cmd/compile/internal/inline/inlheur/scoring.go index 2b210fce8e..623ba8adf0 100644 --- a/src/cmd/compile/internal/inline/inlheur/scoring.go +++ b/src/cmd/compile/internal/inline/inlheur/scoring.go @@ -182,13 +182,14 @@ func mustToMay(x scoreAdjustTyp) scoreAdjustTyp { return 0 } -// computeCallSiteScore takes a given call site whose ir node is 'call' and -// callee function is 'callee' and with previously computed call site -// properties 'csflags', then computes a score for the callsite that -// combines the size cost of the callee with heuristics based on -// previously parameter and function properties, then stores the score -// and the adjustment mask in the appropriate fields in 'cs' -func (cs *CallSite) computeCallSiteScore(calleeProps *FuncProps) { +// computeCallSiteScore takes a given call site whose ir node is +// 'call' and callee function is 'callee' and with previously computed +// call site properties 'csflags', then computes a score for the +// callsite that combines the size cost of the callee with heuristics +// based on previously computed argument and function properties, +// then stores the score and the adjustment mask in the appropriate +// fields in 'cs' +func (cs *CallSite) computeCallSiteScore(csa *callSiteAnalyzer, calleeProps *FuncProps) { callee := cs.Callee csflags := cs.Flags call := cs.Call @@ -353,7 +354,7 @@ func setupFlagToAdjMaps() { } } -// largestScoreAdjustment tries to estimate the largest possible +// LargestNegativeScoreAdjustment tries to estimate the largest possible // negative score adjustment that could be applied to a call of the // function with the specified props. Example: // @@ -372,7 +373,7 @@ func setupFlagToAdjMaps() { // given call _could_ be rescored down as much as -35 points-- thus if // the size of "bar" is 100 (for example) then there is at least a // chance that scoring will enable inlining. -func largestScoreAdjustment(fn *ir.Func, props *FuncProps) int { +func LargestNegativeScoreAdjustment(fn *ir.Func, props *FuncProps) int { if resultFlagToPositiveAdj == nil { setupFlagToAdjMaps() } @@ -397,6 +398,14 @@ func largestScoreAdjustment(fn *ir.Func, props *FuncProps) int { return score } +// LargestPositiveScoreAdjustment tries to estimate the largest possible +// positive score adjustment that could be applied to a given callsite. +// At the moment we don't have very many positive score adjustments, so +// this is just hard-coded, not table-driven. +func LargestPositiveScoreAdjustment(fn *ir.Func) int { + return adjValues[panicPathAdj] + adjValues[initFuncAdj] +} + // callSiteTab contains entries for each call in the function // currently being processed by InlineCalls; this variable will either // be set to 'cstabCache' below (for non-inlinable routines) or to the @@ -438,8 +447,13 @@ type scoreCallsCacheType struct { // after foo has been analyzed, but it's conceivable that CanInline // might visit bar before foo for this SCC. func ScoreCalls(fn *ir.Func) { + if len(fn.Body) == 0 { + return + } enableDebugTraceIfEnv() + nameFinder := newNameFinder(fn) + if debugTrace&debugTraceScoring != 0 { fmt.Fprintf(os.Stderr, "=-= ScoreCalls(%v)\n", ir.FuncName(fn)) } @@ -461,21 +475,25 @@ func ScoreCalls(fn *ir.Func) { fmt.Fprintf(os.Stderr, "=-= building cstab for non-inl func %s\n", ir.FuncName(fn)) } - cstab = computeCallSiteTable(fn, fn.Body, scoreCallsCache.tab, nil, 0) + cstab = computeCallSiteTable(fn, fn.Body, scoreCallsCache.tab, nil, 0, + nameFinder) } + csa := makeCallSiteAnalyzer(fn) const doCallResults = true - scoreCallsRegion(fn, fn.Body, cstab, doCallResults, nil) + csa.scoreCallsRegion(fn, fn.Body, cstab, doCallResults, nil) + + disableDebugTrace() } // scoreCallsRegion assigns numeric scores to each of the callsites in // region 'region' within function 'fn'. This can be called on // an entire function, or with 'region' set to a chunk of // code corresponding to an inlined call. -func scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallResults bool, ic *ir.InlinedCallExpr) { +func (csa *callSiteAnalyzer) scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallResults bool, ic *ir.InlinedCallExpr) { if debugTrace&debugTraceScoring != 0 { - fmt.Fprintf(os.Stderr, "=-= scoreCallsRegion(%v, %s)\n", - ir.FuncName(fn), region[0].Op().String()) + fmt.Fprintf(os.Stderr, "=-= scoreCallsRegion(%v, %s) len(cstab)=%d\n", + ir.FuncName(fn), region[0].Op().String(), len(cstab)) } // Sort callsites to avoid any surprises with non deterministic @@ -510,13 +528,13 @@ func scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallRes continue } } - cs.computeCallSiteScore(cprops) + cs.computeCallSiteScore(csa, cprops) if doCallResults { if debugTrace&debugTraceScoring != 0 { fmt.Fprintf(os.Stderr, "=-= examineCallResults at %s: flags=%d score=%d funcInlHeur=%v deser=%v\n", fmtFullPos(cs.Call.Pos()), cs.Flags, cs.Score, fihcprops, desercprops) } - resultNameTab = examineCallResults(cs, resultNameTab) + resultNameTab = csa.examineCallResults(cs, resultNameTab) } if debugTrace&debugTraceScoring != 0 { @@ -525,7 +543,7 @@ func scoreCallsRegion(fn *ir.Func, region ir.Nodes, cstab CallSiteTab, doCallRes } if resultNameTab != nil { - rescoreBasedOnCallResultUses(fn, resultNameTab, cstab) + csa.rescoreBasedOnCallResultUses(fn, resultNameTab, cstab) } disableDebugTrace() diff --git a/src/cmd/compile/internal/inline/interleaved/interleaved.go b/src/cmd/compile/internal/inline/interleaved/interleaved.go new file mode 100644 index 0000000000..a6f19d470d --- /dev/null +++ b/src/cmd/compile/internal/inline/interleaved/interleaved.go @@ -0,0 +1,132 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package interleaved implements the interleaved devirtualization and +// inlining pass. +package interleaved + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/devirtualize" + "cmd/compile/internal/inline" + "cmd/compile/internal/inline/inlheur" + "cmd/compile/internal/ir" + "cmd/compile/internal/pgo" + "cmd/compile/internal/typecheck" + "fmt" +) + +// DevirtualizeAndInlinePackage interleaves devirtualization and inlining on +// all functions within pkg. +func DevirtualizeAndInlinePackage(pkg *ir.Package, profile *pgo.Profile) { + if profile != nil && base.Debug.PGODevirtualize > 0 { + // TODO(mdempsky): Integrate into DevirtualizeAndInlineFunc below. + ir.VisitFuncsBottomUp(typecheck.Target.Funcs, func(list []*ir.Func, recursive bool) { + for _, fn := range list { + devirtualize.ProfileGuided(fn, profile) + } + }) + ir.CurFunc = nil + } + + if base.Flag.LowerL != 0 { + inlheur.SetupScoreAdjustments() + } + + var inlProfile *pgo.Profile // copy of profile for inlining + if base.Debug.PGOInline != 0 { + inlProfile = profile + } + if inlProfile != nil { + inline.PGOInlinePrologue(inlProfile, pkg.Funcs) + } + + ir.VisitFuncsBottomUp(pkg.Funcs, func(funcs []*ir.Func, recursive bool) { + // We visit functions within an SCC in fairly arbitrary order, + // so by computing inlinability for all functions in the SCC + // before performing any inlining, the results are less + // sensitive to the order within the SCC (see #58905 for an + // example). + + // First compute inlinability for all functions in the SCC ... + inline.CanInlineSCC(funcs, recursive, inlProfile) + + // ... then make a second pass to do devirtualization and inlining + // of calls. + for _, fn := range funcs { + DevirtualizeAndInlineFunc(fn, inlProfile) + } + }) + + if base.Flag.LowerL != 0 { + // Perform a garbage collection of hidden closures functions that + // are no longer reachable from top-level functions following + // inlining. See #59404 and #59638 for more context. + inline.GarbageCollectUnreferencedHiddenClosures() + + if base.Debug.DumpInlFuncProps != "" { + inlheur.DumpFuncProps(nil, base.Debug.DumpInlFuncProps) + } + if inlheur.Enabled() { + inline.PostProcessCallSites(inlProfile) + inlheur.TearDown() + } + } +} + +// DevirtualizeAndInlineFunc interleaves devirtualization and inlining +// on a single function. +func DevirtualizeAndInlineFunc(fn *ir.Func, profile *pgo.Profile) { + ir.WithFunc(fn, func() { + if base.Flag.LowerL != 0 { + if inlheur.Enabled() && !fn.Wrapper() { + inlheur.ScoreCalls(fn) + defer inlheur.ScoreCallsCleanup() + } + if base.Debug.DumpInlFuncProps != "" && !fn.Wrapper() { + inlheur.DumpFuncProps(fn, base.Debug.DumpInlFuncProps) + } + } + + bigCaller := base.Flag.LowerL != 0 && inline.IsBigFunc(fn) + if bigCaller && base.Flag.LowerM > 1 { + fmt.Printf("%v: function %v considered 'big'; reducing max cost of inlinees\n", ir.Line(fn), fn) + } + + // Walk fn's body and apply devirtualization and inlining. + var inlCalls []*ir.InlinedCallExpr + var edit func(ir.Node) ir.Node + edit = func(n ir.Node) ir.Node { + switch n := n.(type) { + case *ir.TailCallStmt: + n.Call.NoInline = true // can't inline yet + } + + ir.EditChildren(n, edit) + + if call, ok := n.(*ir.CallExpr); ok { + devirtualize.StaticCall(call) + + if inlCall := inline.TryInlineCall(fn, call, bigCaller, profile); inlCall != nil { + inlCalls = append(inlCalls, inlCall) + n = inlCall + } + } + + return n + } + ir.EditChildren(fn, edit) + + // If we inlined any calls, we want to recursively visit their + // bodies for further devirtualization and inlining. However, we + // need to wait until *after* the original function body has been + // expanded, or else inlCallee can have false positives (e.g., + // #54632). + for len(inlCalls) > 0 { + call := inlCalls[0] + inlCalls = inlCalls[1:] + ir.EditChildren(call, edit) + } + }) +} diff --git a/src/cmd/compile/internal/ir/check_reassign_no.go b/src/cmd/compile/internal/ir/check_reassign_no.go new file mode 100644 index 0000000000..8290a7da7e --- /dev/null +++ b/src/cmd/compile/internal/ir/check_reassign_no.go @@ -0,0 +1,9 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !checknewoldreassignment + +package ir + +const consistencyCheckEnabled = false diff --git a/src/cmd/compile/internal/ir/check_reassign_yes.go b/src/cmd/compile/internal/ir/check_reassign_yes.go new file mode 100644 index 0000000000..30876cca20 --- /dev/null +++ b/src/cmd/compile/internal/ir/check_reassign_yes.go @@ -0,0 +1,9 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build checknewoldreassignment + +package ir + +const consistencyCheckEnabled = true diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index ca2a2d5008..da5b437f99 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -190,7 +190,8 @@ type CallExpr struct { RType Node `mknode:"-"` // see reflectdata/helpers.go KeepAlive []*Name // vars to be kept alive until call returns IsDDD bool - NoInline bool + GoDefer bool // whether this call is part of a go or defer statement + NoInline bool // whether this call must not be inlined } func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr { @@ -349,7 +350,7 @@ func NewKeyExpr(pos src.XPos, key, value Node) *KeyExpr { return n } -// A StructKeyExpr is an Field: Value composite literal key. +// A StructKeyExpr is a Field: Value composite literal key. type StructKeyExpr struct { miniExpr Field *types.Field @@ -922,6 +923,8 @@ FindRHS: // NB: global variables are always considered to be re-assigned. // TODO: handle initial declaration not including an assignment and // followed by a single assignment? +// NOTE: any changes made here should also be made in the corresponding +// code in the ReassignOracle.Init method. func Reassigned(name *Name) bool { if name.Op() != ONAME { base.Fatalf("reassigned %v", name) diff --git a/src/cmd/compile/internal/ir/reassign_consistency_check.go b/src/cmd/compile/internal/ir/reassign_consistency_check.go new file mode 100644 index 0000000000..e4d928d132 --- /dev/null +++ b/src/cmd/compile/internal/ir/reassign_consistency_check.go @@ -0,0 +1,46 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ir + +import ( + "cmd/compile/internal/base" + "cmd/internal/src" + "fmt" + "path/filepath" + "strings" +) + +// checkStaticValueResult compares the result from ReassignOracle.StaticValue +// with the corresponding result from ir.StaticValue to make sure they agree. +// This method is called only when turned on via build tag. +func checkStaticValueResult(n Node, newres Node) { + oldres := StaticValue(n) + if oldres != newres { + base.Fatalf("%s: new/old static value disagreement on %v:\nnew=%v\nold=%v", fmtFullPos(n.Pos()), n, newres, oldres) + } +} + +// checkStaticValueResult compares the result from ReassignOracle.Reassigned +// with the corresponding result from ir.Reassigned to make sure they agree. +// This method is called only when turned on via build tag. +func checkReassignedResult(n *Name, newres bool) { + origres := Reassigned(n) + if newres != origres { + base.Fatalf("%s: new/old reassigned disagreement on %v (class %s) newres=%v oldres=%v", fmtFullPos(n.Pos()), n, n.Class.String(), newres, origres) + } +} + +// fmtFullPos returns a verbose dump for pos p, including inlines. +func fmtFullPos(p src.XPos) string { + var sb strings.Builder + sep := "" + base.Ctxt.AllPos(p, func(pos src.Pos) { + fmt.Fprintf(&sb, sep) + sep = "|" + file := filepath.Base(pos.Filename()) + fmt.Fprintf(&sb, "%s:%d:%d", file, pos.Line(), pos.Col()) + }) + return sb.String() +} diff --git a/src/cmd/compile/internal/ir/reassignment.go b/src/cmd/compile/internal/ir/reassignment.go new file mode 100644 index 0000000000..9974292471 --- /dev/null +++ b/src/cmd/compile/internal/ir/reassignment.go @@ -0,0 +1,205 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ir + +import ( + "cmd/compile/internal/base" +) + +// A ReassignOracle efficiently answers queries about whether local +// variables are reassigned. This helper works by looking for function +// params and short variable declarations (e.g. +// https://go.dev/ref/spec#Short_variable_declarations) that are +// neither address taken nor subsequently re-assigned. It is intended +// to operate much like "ir.StaticValue" and "ir.Reassigned", but in a +// way that does just a single walk of the containing function (as +// opposed to a new walk on every call). +type ReassignOracle struct { + fn *Func + // maps candidate name to its defining assignment (or for + // for params, defining func). + singleDef map[*Name]Node +} + +// Init initializes the oracle based on the IR in function fn, laying +// the groundwork for future calls to the StaticValue and Reassigned +// methods. If the fn's IR is subsequently modified, Init must be +// called again. +func (ro *ReassignOracle) Init(fn *Func) { + ro.fn = fn + + // Collect candidate map. Start by adding function parameters + // explicitly. + ro.singleDef = make(map[*Name]Node) + sig := fn.Type() + numParams := sig.NumRecvs() + sig.NumParams() + for _, param := range fn.Dcl[:numParams] { + if IsBlank(param) { + continue + } + // For params, use func itself as defining node. + ro.singleDef[param] = fn + } + + // Walk the function body to discover any locals assigned + // via ":=" syntax (e.g. "a := "). + var findLocals func(n Node) bool + findLocals = func(n Node) bool { + if nn, ok := n.(*Name); ok { + if nn.Defn != nil && !nn.Addrtaken() && nn.Class == PAUTO { + ro.singleDef[nn] = nn.Defn + } + } else if nn, ok := n.(*ClosureExpr); ok { + Any(nn.Func, findLocals) + } + return false + } + Any(fn, findLocals) + + outerName := func(x Node) *Name { + if x == nil { + return nil + } + n, ok := OuterValue(x).(*Name) + if ok { + return n.Canonical() + } + return nil + } + + // pruneIfNeeded examines node nn appearing on the left hand side + // of assignment statement asn to see if it contains a reassignment + // to any nodes in our candidate map ro.singleDef; if a reassignment + // is found, the corresponding name is deleted from singleDef. + pruneIfNeeded := func(nn Node, asn Node) { + oname := outerName(nn) + if oname == nil { + return + } + defn, ok := ro.singleDef[oname] + if !ok { + return + } + // any assignment to a param invalidates the entry. + paramAssigned := oname.Class == PPARAM + // assignment to local ok iff assignment is its orig def. + localAssigned := (oname.Class == PAUTO && asn != defn) + if paramAssigned || localAssigned { + // We found an assignment to name N that doesn't + // correspond to its original definition; remove + // from candidates. + delete(ro.singleDef, oname) + } + } + + // Prune away anything that looks assigned. This code modeled after + // similar code in ir.Reassigned; any changes there should be made + // here as well. + var do func(n Node) bool + do = func(n Node) bool { + switch n.Op() { + case OAS: + asn := n.(*AssignStmt) + pruneIfNeeded(asn.X, n) + case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV, OSELRECV2: + asn := n.(*AssignListStmt) + for _, p := range asn.Lhs { + pruneIfNeeded(p, n) + } + case OASOP: + asn := n.(*AssignOpStmt) + pruneIfNeeded(asn.X, n) + case ORANGE: + rs := n.(*RangeStmt) + pruneIfNeeded(rs.Key, n) + pruneIfNeeded(rs.Value, n) + case OCLOSURE: + n := n.(*ClosureExpr) + Any(n.Func, do) + } + return false + } + Any(fn, do) +} + +// StaticValue method has the same semantics as the ir package function +// of the same name; see comments on [StaticValue]. +func (ro *ReassignOracle) StaticValue(n Node) Node { + arg := n + for { + if n.Op() == OCONVNOP { + n = n.(*ConvExpr).X + continue + } + + if n.Op() == OINLCALL { + n = n.(*InlinedCallExpr).SingleResult() + continue + } + + n1 := ro.staticValue1(n) + if n1 == nil { + if consistencyCheckEnabled { + checkStaticValueResult(arg, n) + } + return n + } + n = n1 + } +} + +func (ro *ReassignOracle) staticValue1(nn Node) Node { + if nn.Op() != ONAME { + return nil + } + n := nn.(*Name).Canonical() + if n.Class != PAUTO { + return nil + } + + defn := n.Defn + if defn == nil { + return nil + } + + var rhs Node +FindRHS: + switch defn.Op() { + case OAS: + defn := defn.(*AssignStmt) + rhs = defn.Y + case OAS2: + defn := defn.(*AssignListStmt) + for i, lhs := range defn.Lhs { + if lhs == n { + rhs = defn.Rhs[i] + break FindRHS + } + } + base.Fatalf("%v missing from LHS of %v", n, defn) + default: + return nil + } + if rhs == nil { + base.Fatalf("RHS is nil: %v", defn) + } + + if _, ok := ro.singleDef[n]; !ok { + return nil + } + + return rhs +} + +// Reassigned method has the same semantics as the ir package function +// of the same name; see comments on [Reassigned] for more info. +func (ro *ReassignOracle) Reassigned(n *Name) bool { + _, ok := ro.singleDef[n] + result := !ok + if consistencyCheckEnabled { + checkReassignedResult(n, result) + } + return result +} diff --git a/src/cmd/compile/internal/loong64/galign.go b/src/cmd/compile/internal/loong64/galign.go index 99ab7bdfb5..a613165054 100644 --- a/src/cmd/compile/internal/loong64/galign.go +++ b/src/cmd/compile/internal/loong64/galign.go @@ -20,4 +20,6 @@ func Init(arch *ssagen.ArchInfo) { arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {} arch.SSAGenValue = ssaGenValue arch.SSAGenBlock = ssaGenBlock + arch.LoadRegResult = loadRegResult + arch.SpillArgReg = spillArgReg } diff --git a/src/cmd/compile/internal/loong64/ssa.go b/src/cmd/compile/internal/loong64/ssa.go index 6e81da3ef8..e7298bdb9f 100644 --- a/src/cmd/compile/internal/loong64/ssa.go +++ b/src/cmd/compile/internal/loong64/ssa.go @@ -10,6 +10,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" + "cmd/compile/internal/objw" "cmd/compile/internal/ssa" "cmd/compile/internal/ssagen" "cmd/compile/internal/types" @@ -144,6 +145,18 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Type = obj.TYPE_REG p.From.Reg = r ssagen.AddrAuto(&p.To, v) + case ssa.OpArgIntReg, ssa.OpArgFloatReg: + // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill + // The loop only runs once. + for _, a := range v.Block.Func.RegArgs { + // Pass the spill/unspill information along to the assembler, offset by size of + // the saved LR slot. + addr := ssagen.SpillSlotAddr(a, loong64.REGSP, base.Ctxt.Arch.FixedFrameSize) + s.FuncInfo().AddSpill( + obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type, a.Reg), Spill: storeByType(a.Type, a.Reg)}) + } + v.Block.Func.RegArgs = nil + ssagen.CheckArgReg(v) case ssa.OpLOONG64ADDV, ssa.OpLOONG64SUBV, ssa.OpLOONG64AND, @@ -362,13 +375,12 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpLOONG64DUFFZERO: - // runtime.duffzero expects start address in R19 + // runtime.duffzero expects start address in R20 p := s.Prog(obj.ADUFFZERO) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = ir.Syms.Duffzero p.To.Offset = v.AuxInt - case ssa.OpLOONG64LoweredZero: // MOVx R0, (Rarg0) // ADDV $sz, Rarg0 @@ -797,3 +809,22 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { b.Fatalf("branch not implemented: %s", b.LongString()) } } + +func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p := s.Prog(loadByType(t, reg)) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_AUTO + p.From.Sym = n.Linksym() + p.From.Offset = n.FrameOffset() + off + p.To.Type = obj.TYPE_REG + p.To.Reg = reg + return p +} + +func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { + p = pp.Append(p, storeByType(t, reg), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off) + p.To.Name = obj.NAME_PARAM + p.To.Sym = n.Linksym() + p.Pos = p.Pos.WithNotStmt() + return p +} diff --git a/src/cmd/compile/internal/loopvar/loopvar_test.go b/src/cmd/compile/internal/loopvar/loopvar_test.go index c8e11dbd07..64cfdb77d9 100644 --- a/src/cmd/compile/internal/loopvar/loopvar_test.go +++ b/src/cmd/compile/internal/loopvar/loopvar_test.go @@ -251,7 +251,7 @@ func TestLoopVarVersionEnableFlag(t *testing.T) { t.Logf(m) - yCount := strings.Count(m, "opt.go:16:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt.go:30)") + yCount := strings.Count(m, "opt.go:16:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt.go:29)") nCount := strings.Count(m, "shared") if yCount != 1 { @@ -290,7 +290,7 @@ func TestLoopVarVersionEnableGoBuild(t *testing.T) { t.Logf(m) - yCount := strings.Count(m, "opt-122.go:18:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt-122.go:32)") + yCount := strings.Count(m, "opt-122.go:18:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt-122.go:31)") nCount := strings.Count(m, "shared") if yCount != 1 { @@ -329,7 +329,7 @@ func TestLoopVarVersionDisableFlag(t *testing.T) { t.Logf(m) // expect error - yCount := strings.Count(m, "opt.go:16:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt.go:30)") + yCount := strings.Count(m, "opt.go:16:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt.go:29)") nCount := strings.Count(m, "shared") if yCount != 0 { @@ -368,7 +368,7 @@ func TestLoopVarVersionDisableGoBuild(t *testing.T) { t.Logf(m) // expect error - yCount := strings.Count(m, "opt-121.go:18:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt-121.go:32)") + yCount := strings.Count(m, "opt-121.go:18:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt-121.go:31)") nCount := strings.Count(m, "shared") if yCount != 0 { diff --git a/src/cmd/compile/internal/loopvar/testdata/opt-121.go b/src/cmd/compile/internal/loopvar/testdata/opt-121.go index 131033b13c..4afb658fc8 100644 --- a/src/cmd/compile/internal/loopvar/testdata/opt-121.go +++ b/src/cmd/compile/internal/loopvar/testdata/opt-121.go @@ -19,7 +19,6 @@ func inline(j, k int) []*int { a = append(a, &private) } return a - } //go:noinline diff --git a/src/cmd/compile/internal/loopvar/testdata/opt-122.go b/src/cmd/compile/internal/loopvar/testdata/opt-122.go index 0ed6feee04..9dceab9175 100644 --- a/src/cmd/compile/internal/loopvar/testdata/opt-122.go +++ b/src/cmd/compile/internal/loopvar/testdata/opt-122.go @@ -19,7 +19,6 @@ func inline(j, k int) []*int { a = append(a, &private) } return a - } //go:noinline diff --git a/src/cmd/compile/internal/loopvar/testdata/opt.go b/src/cmd/compile/internal/loopvar/testdata/opt.go index 1bcd73614d..82c8616bcd 100644 --- a/src/cmd/compile/internal/loopvar/testdata/opt.go +++ b/src/cmd/compile/internal/loopvar/testdata/opt.go @@ -17,7 +17,6 @@ func inline(j, k int) []*int { a = append(a, &private) } return a - } //go:noinline diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go index 1f7b497599..f9e3838fd9 100644 --- a/src/cmd/compile/internal/noder/helpers.go +++ b/src/cmd/compile/internal/noder/helpers.go @@ -99,6 +99,8 @@ func idealType(tv syntax.TypeAndValue) types2.Type { typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition case types2.UntypedString: typ = types2.Typ[types2.String] // argument to "append" or "copy" calls + case types2.UntypedRune: + typ = types2.Typ[types2.Int32] // range over rune default: return nil } diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index 46511d1f97..d909f3467b 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -92,23 +92,22 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) { } // Check for anonymous interface cycles (#56103). - if base.Debug.InterfaceCycles == 0 { - var f cycleFinder - for _, file := range files { - syntax.Inspect(file, func(n syntax.Node) bool { - if n, ok := n.(*syntax.InterfaceType); ok { - if f.hasCycle(n.GetTypeInfo().Type.(*types2.Interface)) { - base.ErrorfAt(m.makeXPos(n.Pos()), errors.InvalidTypeCycle, "invalid recursive type: anonymous interface refers to itself (see https://go.dev/issue/56103)") + // TODO(gri) move this code into the type checkers (types2 and go/types) + var f cycleFinder + for _, file := range files { + syntax.Inspect(file, func(n syntax.Node) bool { + if n, ok := n.(*syntax.InterfaceType); ok { + if f.hasCycle(n.GetTypeInfo().Type.(*types2.Interface)) { + base.ErrorfAt(m.makeXPos(n.Pos()), errors.InvalidTypeCycle, "invalid recursive type: anonymous interface refers to itself (see https://go.dev/issue/56103)") - for typ := range f.cyclic { - f.cyclic[typ] = false // suppress duplicate errors - } + for typ := range f.cyclic { + f.cyclic[typ] = false // suppress duplicate errors } - return false } - return true - }) - } + return false + } + return true + }) } base.ExitIfErrors() diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index c1145f980e..99e778fd70 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -15,6 +15,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/dwarfgen" "cmd/compile/internal/inline" + "cmd/compile/internal/inline/interleaved" "cmd/compile/internal/ir" "cmd/compile/internal/objw" "cmd/compile/internal/reflectdata" @@ -3794,7 +3795,7 @@ func finishWrapperFunc(fn *ir.Func, target *ir.Package) { // We generate wrappers after the global inlining pass, // so we're responsible for applying inlining ourselves here. // TODO(prattmic): plumb PGO. - inline.InlineCalls(fn, nil) + interleaved.DevirtualizeAndInlineFunc(fn, nil) // The body of wrapper function after inlining may reveal new ir.OMETHVALUE node, // we don't know whether wrapper function has been generated for it or not, so diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index a803e53502..d2ca1f37a9 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -280,7 +280,7 @@ func readBodies(target *ir.Package, duringInlining bool) { oldLowerM := base.Flag.LowerM base.Flag.LowerM = 0 - inline.InlineDecls(nil, inlDecls, false) + inline.CanInlineFuncs(inlDecls, nil) base.Flag.LowerM = oldLowerM for _, fn := range inlDecls { diff --git a/src/cmd/compile/internal/pgo/irgraph.go b/src/cmd/compile/internal/pgo/irgraph.go index 7a7cd20f2b..96485e33ab 100644 --- a/src/cmd/compile/internal/pgo/irgraph.go +++ b/src/cmd/compile/internal/pgo/irgraph.go @@ -46,6 +46,7 @@ import ( "cmd/compile/internal/pgo/internal/graph" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "errors" "fmt" "internal/profile" "os" @@ -129,7 +130,7 @@ type Profile struct { // the percentage threshold for hot/cold partitioning. TotalWeight int64 - // EdgeMap contains all unique call edges in the profile and their + // NamedEdgeMap contains all unique call edges in the profile and their // edge weight. NamedEdgeMap NamedEdgeMap @@ -145,18 +146,22 @@ func New(profileFile string) (*Profile, error) { return nil, fmt.Errorf("error opening profile: %w", err) } defer f.Close() - profile, err := profile.Parse(f) - if err != nil { + p, err := profile.Parse(f) + if errors.Is(err, profile.ErrNoData) { + // Treat a completely empty file the same as a profile with no + // samples: nothing to do. + return nil, nil + } else if err != nil { return nil, fmt.Errorf("error parsing profile: %w", err) } - if len(profile.Sample) == 0 { + if len(p.Sample) == 0 { // We accept empty profiles, but there is nothing to do. return nil, nil } valueIndex := -1 - for i, s := range profile.SampleType { + for i, s := range p.SampleType { // Samples count is the raw data collected, and CPU nanoseconds is just // a scaled version of it, so either one we can find is fine. if (s.Type == "samples" && s.Unit == "count") || @@ -170,7 +175,7 @@ func New(profileFile string) (*Profile, error) { return nil, fmt.Errorf(`profile does not contain a sample index with value/type "samples/count" or cpu/nanoseconds"`) } - g := graph.NewGraph(profile, &graph.Options{ + g := graph.NewGraph(p, &graph.Options{ SampleValue: func(v []int64) int64 { return v[valueIndex] }, }) diff --git a/src/cmd/compile/internal/rangefunc/rewrite.go b/src/cmd/compile/internal/rangefunc/rewrite.go index 7475c570aa..d439412ea8 100644 --- a/src/cmd/compile/internal/rangefunc/rewrite.go +++ b/src/cmd/compile/internal/rangefunc/rewrite.go @@ -934,7 +934,7 @@ func (r *rewriter) endLoop(loop *forLoop) { if rfunc.Params().Len() != 1 { base.Fatalf("invalid typecheck of range func") } - ftyp := rfunc.Params().At(0).Type().(*types2.Signature) // func(...) bool + ftyp := types2.CoreType(rfunc.Params().At(0).Type()).(*types2.Signature) // func(...) bool if ftyp.Results().Len() != 1 { base.Fatalf("invalid typecheck of range func") } diff --git a/src/cmd/compile/internal/ssa/_gen/ARM.rules b/src/cmd/compile/internal/ssa/_gen/ARM.rules index a60afb000a..ed0ed80afa 100644 --- a/src/cmd/compile/internal/ssa/_gen/ARM.rules +++ b/src/cmd/compile/internal/ssa/_gen/ARM.rules @@ -66,17 +66,17 @@ // count trailing zero for ARMv5 and ARMv6 // 32 - CLZ(x&-x - 1) -(Ctz32 x) && buildcfg.GOARM<=6 => +(Ctz32 x) && buildcfg.GOARM.Version<=6 => (RSBconst [32] (CLZ (SUBconst (AND x (RSBconst [0] x)) [1]))) -(Ctz16 x) && buildcfg.GOARM<=6 => +(Ctz16 x) && buildcfg.GOARM.Version<=6 => (RSBconst [32] (CLZ (SUBconst (AND (ORconst [0x10000] x) (RSBconst [0] (ORconst [0x10000] x))) [1]))) -(Ctz8 x) && buildcfg.GOARM<=6 => +(Ctz8 x) && buildcfg.GOARM.Version<=6 => (RSBconst [32] (CLZ (SUBconst (AND (ORconst [0x100] x) (RSBconst [0] (ORconst [0x100] x))) [1]))) // count trailing zero for ARMv7 -(Ctz32 x) && buildcfg.GOARM==7 => (CLZ (RBIT x)) -(Ctz16 x) && buildcfg.GOARM==7 => (CLZ (RBIT (ORconst [0x10000] x))) -(Ctz8 x) && buildcfg.GOARM==7 => (CLZ (RBIT (ORconst [0x100] x))) +(Ctz32 x) && buildcfg.GOARM.Version==7 => (CLZ (RBIT x)) +(Ctz16 x) && buildcfg.GOARM.Version==7 => (CLZ (RBIT (ORconst [0x10000] x))) +(Ctz8 x) && buildcfg.GOARM.Version==7 => (CLZ (RBIT (ORconst [0x100] x))) // bit length (BitLen32 x) => (RSBconst [32] (CLZ x)) @@ -90,13 +90,13 @@ // t5 = x right rotate 8 bits -- (d, a, b, c ) // result = t4 ^ t5 -- (d, c, b, a ) // using shifted ops this can be done in 4 instructions. -(Bswap32 x) && buildcfg.GOARM==5 => +(Bswap32 x) && buildcfg.GOARM.Version==5 => (XOR (SRLconst (BICconst (XOR x (SRRconst [16] x)) [0xff0000]) [8]) (SRRconst x [8])) // byte swap for ARMv6 and above -(Bswap32 x) && buildcfg.GOARM>=6 => (REV x) +(Bswap32 x) && buildcfg.GOARM.Version>=6 => (REV x) // boolean ops -- booleans are represented with 0=false, 1=true (AndB ...) => (AND ...) @@ -741,10 +741,10 @@ (SUBconst [c] x) && !isARMImmRot(uint32(c)) && isARMImmRot(uint32(-c)) => (ADDconst [-c] x) (ANDconst [c] x) && !isARMImmRot(uint32(c)) && isARMImmRot(^uint32(c)) => (BICconst [int32(^uint32(c))] x) (BICconst [c] x) && !isARMImmRot(uint32(c)) && isARMImmRot(^uint32(c)) => (ANDconst [int32(^uint32(c))] x) -(ADDconst [c] x) && buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff => (SUBconst [-c] x) -(SUBconst [c] x) && buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff => (ADDconst [-c] x) -(ANDconst [c] x) && buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff => (BICconst [int32(^uint32(c))] x) -(BICconst [c] x) && buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff => (ANDconst [int32(^uint32(c))] x) +(ADDconst [c] x) && buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff => (SUBconst [-c] x) +(SUBconst [c] x) && buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff => (ADDconst [-c] x) +(ANDconst [c] x) && buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff => (BICconst [int32(^uint32(c))] x) +(BICconst [c] x) && buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff => (ANDconst [int32(^uint32(c))] x) (ADDconst [c] (MOVWconst [d])) => (MOVWconst [c+d]) (ADDconst [c] (ADDconst [d] x)) => (ADDconst [c+d] x) (ADDconst [c] (SUBconst [d] x)) => (ADDconst [c-d] x) @@ -1139,7 +1139,7 @@ // UBFX instruction is supported by ARMv6T2, ARMv7 and above versions, REV16 is supported by // ARMv6 and above versions. So for ARMv6, we need to match SLLconst, SRLconst and ORshiftLL. ((ADDshiftLL|ORshiftLL|XORshiftLL) [8] (BFXU [int32(armBFAuxInt(8, 8))] x) x) => (REV16 x) -((ADDshiftLL|ORshiftLL|XORshiftLL) [8] (SRLconst [24] (SLLconst [16] x)) x) && buildcfg.GOARM>=6 => (REV16 x) +((ADDshiftLL|ORshiftLL|XORshiftLL) [8] (SRLconst [24] (SLLconst [16] x)) x) && buildcfg.GOARM.Version>=6 => (REV16 x) // use indexed loads and stores (MOVWload [0] {sym} (ADD ptr idx) mem) && sym == nil => (MOVWloadidx ptr idx mem) @@ -1209,25 +1209,25 @@ (BIC x x) => (MOVWconst [0]) (ADD (MUL x y) a) => (MULA x y a) -(SUB a (MUL x y)) && buildcfg.GOARM == 7 => (MULS x y a) -(RSB (MUL x y) a) && buildcfg.GOARM == 7 => (MULS x y a) +(SUB a (MUL x y)) && buildcfg.GOARM.Version == 7 => (MULS x y a) +(RSB (MUL x y) a) && buildcfg.GOARM.Version == 7 => (MULS x y a) -(NEGF (MULF x y)) && buildcfg.GOARM >= 6 => (NMULF x y) -(NEGD (MULD x y)) && buildcfg.GOARM >= 6 => (NMULD x y) -(MULF (NEGF x) y) && buildcfg.GOARM >= 6 => (NMULF x y) -(MULD (NEGD x) y) && buildcfg.GOARM >= 6 => (NMULD x y) +(NEGF (MULF x y)) && buildcfg.GOARM.Version >= 6 => (NMULF x y) +(NEGD (MULD x y)) && buildcfg.GOARM.Version >= 6 => (NMULD x y) +(MULF (NEGF x) y) && buildcfg.GOARM.Version >= 6 => (NMULF x y) +(MULD (NEGD x) y) && buildcfg.GOARM.Version >= 6 => (NMULD x y) (NMULF (NEGF x) y) => (MULF x y) (NMULD (NEGD x) y) => (MULD x y) // the result will overwrite the addend, since they are in the same register -(ADDF a (MULF x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULAF a x y) -(ADDF a (NMULF x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULSF a x y) -(ADDD a (MULD x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULAD a x y) -(ADDD a (NMULD x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULSD a x y) -(SUBF a (MULF x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULSF a x y) -(SUBF a (NMULF x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULAF a x y) -(SUBD a (MULD x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULSD a x y) -(SUBD a (NMULD x y)) && a.Uses == 1 && buildcfg.GOARM >= 6 => (MULAD a x y) +(ADDF a (MULF x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULAF a x y) +(ADDF a (NMULF x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULSF a x y) +(ADDD a (MULD x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULAD a x y) +(ADDD a (NMULD x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULSD a x y) +(SUBF a (MULF x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULSF a x y) +(SUBF a (NMULF x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULAF a x y) +(SUBD a (MULD x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULSD a x y) +(SUBD a (NMULD x y)) && a.Uses == 1 && buildcfg.GOARM.Version >= 6 => (MULAD a x y) (AND x (MVN y)) => (BIC x y) @@ -1259,8 +1259,8 @@ (CMPD x (MOVDconst [0])) => (CMPD0 x) // bit extraction -(SRAconst (SLLconst x [c]) [d]) && buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFX [(d-c)|(32-d)<<8] x) -(SRLconst (SLLconst x [c]) [d]) && buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFXU [(d-c)|(32-d)<<8] x) +(SRAconst (SLLconst x [c]) [d]) && buildcfg.GOARM.Version==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFX [(d-c)|(32-d)<<8] x) +(SRLconst (SLLconst x [c]) [d]) && buildcfg.GOARM.Version==7 && uint64(d)>=uint64(c) && uint64(d)<=31 => (BFXU [(d-c)|(32-d)<<8] x) // comparison simplification ((EQ|NE) (CMP x (RSBconst [0] y))) => ((EQ|NE) (CMN x y)) // sense of carry bit not preserved; see also #50854 diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules index 4a47c4cd47..2af9519113 100644 --- a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules +++ b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules @@ -416,7 +416,7 @@ (GetCallerSP ...) => (LoweredGetCallerSP ...) (GetCallerPC ...) => (LoweredGetCallerPC ...) -(If cond yes no) => (NE cond yes no) +(If cond yes no) => (NE (MOVBUreg cond) yes no) // Write barrier. (WB ...) => (LoweredWB ...) @@ -450,71 +450,37 @@ (EQ (SGTconst [0] x) yes no) => (GEZ x yes no) (NE (SGT x (MOVVconst [0])) yes no) => (GTZ x yes no) (EQ (SGT x (MOVVconst [0])) yes no) => (LEZ x yes no) +(MOVBUreg x:((SGT|SGTU) _ _)) => x // fold offset into address (ADDVconst [off1] (MOVVaddr [off2] {sym} ptr)) && is32Bit(off1+int64(off2)) => (MOVVaddr [int32(off1)+int32(off2)] {sym} ptr) // fold address into load/store -(MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBload [off1+int32(off2)] {sym} ptr mem) -(MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBUload [off1+int32(off2)] {sym} ptr mem) -(MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHload [off1+int32(off2)] {sym} ptr mem) -(MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHUload [off1+int32(off2)] {sym} ptr mem) -(MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWload [off1+int32(off2)] {sym} ptr mem) -(MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWUload [off1+int32(off2)] {sym} ptr mem) -(MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVload [off1+int32(off2)] {sym} ptr mem) -(MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVFload [off1+int32(off2)] {sym} ptr mem) -(MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVDload [off1+int32(off2)] {sym} ptr mem) +// Do not fold global variable access in -dynlink mode, where it will be rewritten +// to use the GOT via REGTMP, which currently cannot handle large offset. +(MOV(B|BU|H|HU|W|WU|V|F|D)load [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) + && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|BU|H|HU|W|WU|V|F|D)load [off1+int32(off2)] {sym} ptr mem) -(MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVBstore [off1+int32(off2)] {sym} ptr val mem) -(MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVHstore [off1+int32(off2)] {sym} ptr val mem) -(MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVWstore [off1+int32(off2)] {sym} ptr val mem) -(MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVVstore [off1+int32(off2)] {sym} ptr val mem) -(MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVFstore [off1+int32(off2)] {sym} ptr val mem) -(MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) => (MOVDstore [off1+int32(off2)] {sym} ptr val mem) -(MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBstorezero [off1+int32(off2)] {sym} ptr mem) -(MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHstorezero [off1+int32(off2)] {sym} ptr mem) -(MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWstorezero [off1+int32(off2)] {sym} ptr mem) -(MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVVstorezero [off1+int32(off2)] {sym} ptr mem) +(MOV(B|H|W|V|F|D)store [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(int64(off1)+off2) + && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|H|W|V|F|D)store [off1+int32(off2)] {sym} ptr val mem) -(MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVBload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVBUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVHload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVHUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVWload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVWUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVVload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVFload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVDload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOV(B|H|W|V)storezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(int64(off1)+off2) + && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|H|W|V)storezero [off1+int32(off2)] {sym} ptr mem) -(MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVBstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) -(MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVHstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) -(MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVWstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) -(MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVVstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) -(MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVFstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) -(MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVDstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) -(MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVBstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVHstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVWstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) -(MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVVstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) +(MOV(B|BU|H|HU|W|WU|V|F|D)load [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) + && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|BU|H|HU|W|WU|V|F|D)load [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) + +(MOV(B|H|W|V|F|D)store [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) + && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|H|W|V|F|D)store [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) + +(MOV(B|H|W|V)storezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) + && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|H|W|V)storezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) (LoweredAtomicStore(32|64) ptr (MOVVconst [0]) mem) => (LoweredAtomicStorezero(32|64) ptr mem) (LoweredAtomicAdd32 ptr (MOVVconst [c]) mem) && is32Bit(c) => (LoweredAtomicAddconst32 [int32(c)] ptr mem) diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go b/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go index 3442fc8d7c..3fbf5be499 100644 --- a/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go +++ b/src/cmd/compile/internal/ssa/_gen/LOONG64Ops.go @@ -123,17 +123,17 @@ func init() { // Common individual register masks var ( - gp = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R21-unused, R22 is g, R30 is REGTMP + gp = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R22 is g, R30 is REGTMP gpg = gp | buildReg("g") gpsp = gp | buildReg("SP") gpspg = gpg | buildReg("SP") gpspsbg = gpspg | buildReg("SB") 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 F31") callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g - r1 = buildReg("R19") - r2 = buildReg("R18") - r3 = buildReg("R17") - r4 = buildReg("R4") + r1 = buildReg("R20") + r2 = buildReg("R21") + r3 = buildReg("R23") + r4 = buildReg("R24") ) // Common regInfo var ( @@ -273,32 +273,32 @@ func init() { {name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"}, // float64 -> float32 // function calls - {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem - {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem - {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem + {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem + {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem + {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem // duffzero // arg0 = address of memory to zero // arg1 = mem // auxint = offset into duffzero code to start executing // returns mem - // R19 aka loong64.REGRT1 changed as side effect + // R20 aka loong64.REGRT1 changed as side effect { name: "DUFFZERO", aux: "Int64", argLength: 2, reg: regInfo{ - inputs: []regMask{buildReg("R19")}, - clobbers: buildReg("R19 R1"), + inputs: []regMask{buildReg("R20")}, + clobbers: buildReg("R20 R1"), }, typ: "Mem", faultOnNilArg0: true, }, // duffcopy - // arg0 = address of dst memory (in R20, changed as side effect) REGRT2 - // arg1 = address of src memory (in R19, changed as side effect) REGRT1 + // arg0 = address of dst memory (in R21, changed as side effect) + // arg1 = address of src memory (in R20, changed as side effect) // arg2 = mem // auxint = offset into duffcopy code to start executing // returns mem @@ -307,8 +307,8 @@ func init() { aux: "Int64", argLength: 3, reg: regInfo{ - inputs: []regMask{buildReg("R20"), buildReg("R19")}, - clobbers: buildReg("R19 R20 R1"), + inputs: []regMask{buildReg("R21"), buildReg("R20")}, + clobbers: buildReg("R20 R21 R1"), }, typ: "Mem", faultOnNilArg0: true, @@ -316,45 +316,45 @@ func init() { }, // large or unaligned zeroing - // arg0 = address of memory to zero (in R19, changed as side effect) + // arg0 = address of memory to zero (in R20, changed as side effect) // arg1 = address of the last element to zero // arg2 = mem // auxint = alignment // returns mem - // MOVx R0, (R19) - // ADDV $sz, R19 - // BGEU Rarg1, R19, -2(PC) + // MOVx R0, (R20) + // ADDV $sz, R20 + // BGEU Rarg1, R20, -2(PC) { name: "LoweredZero", aux: "Int64", argLength: 3, reg: regInfo{ - inputs: []regMask{buildReg("R19"), gp}, - clobbers: buildReg("R19"), + inputs: []regMask{buildReg("R20"), gp}, + clobbers: buildReg("R20"), }, typ: "Mem", faultOnNilArg0: true, }, // large or unaligned move - // arg0 = address of dst memory (in R20, changed as side effect) - // arg1 = address of src memory (in R19, changed as side effect) + // arg0 = address of dst memory (in R21, changed as side effect) + // arg1 = address of src memory (in R20, changed as side effect) // arg2 = address of the last element of src // arg3 = mem // auxint = alignment // returns mem - // MOVx (R19), Rtmp - // MOVx Rtmp, (R20) - // ADDV $sz, R19 + // MOVx (R20), Rtmp + // MOVx Rtmp, (R21) // ADDV $sz, R20 - // BGEU Rarg2, R19, -4(PC) + // ADDV $sz, R21 + // BGEU Rarg2, R20, -4(PC) { name: "LoweredMove", aux: "Int64", argLength: 4, reg: regInfo{ - inputs: []regMask{buildReg("R20"), buildReg("R19"), gp}, - clobbers: buildReg("R19 R20"), + inputs: []regMask{buildReg("R21"), buildReg("R20"), gp}, + clobbers: buildReg("R20 R21"), }, typ: "Mem", faultOnNilArg0: true, @@ -476,8 +476,8 @@ func init() { blocks: blocks, regnames: regNamesLOONG64, // TODO: support register ABI on loong64 - ParamIntRegNames: "R4 R5 R6 R7 R8 R9 R10 R11", - ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7", + ParamIntRegNames: "R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19", + ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15", gpregmask: gp, fpregmask: fp, framepointerreg: -1, // not used diff --git a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules index 9afe5995ae..fc206c42d3 100644 --- a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules +++ b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules @@ -153,27 +153,27 @@ // SRL only considers the bottom 6 bits of y, similarly SRLW only considers the // bottom 5 bits of y. Ensure that the result is always zero if the shift exceeds // the maximum value. See Lsh above for a detailed description. -(Rsh8Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) -(Rsh8Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) -(Rsh8Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) -(Rsh8Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] y))) -(Rsh16Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) -(Rsh16Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) -(Rsh16Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) -(Rsh16Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] y))) -(Rsh32Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] (ZeroExt8to64 y)))) -(Rsh32Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] (ZeroExt16to64 y)))) -(Rsh32Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] (ZeroExt32to64 y)))) -(Rsh32Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] y))) -(Rsh64Ux8 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) -(Rsh64Ux16 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) -(Rsh64Ux32 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) -(Rsh64Ux64 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] y))) +(Rsh8Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt8to64 y)))) +(Rsh8Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt16to64 y)))) +(Rsh8Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] (ZeroExt32to64 y)))) +(Rsh8Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt8to64 x) y) (Neg8 (SLTIU [64] y))) +(Rsh16Ux8 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt8to64 y)))) +(Rsh16Ux16 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt16to64 y)))) +(Rsh16Ux32 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] (ZeroExt32to64 y)))) +(Rsh16Ux64 x y) && !shiftIsBounded(v) => (AND (SRL (ZeroExt16to64 x) y) (Neg16 (SLTIU [64] y))) +(Rsh32Ux8 x y) && !shiftIsBounded(v) => (AND (SRLW x y) (Neg32 (SLTIU [32] (ZeroExt8to64 y)))) +(Rsh32Ux16 x y) && !shiftIsBounded(v) => (AND (SRLW x y) (Neg32 (SLTIU [32] (ZeroExt16to64 y)))) +(Rsh32Ux32 x y) && !shiftIsBounded(v) => (AND (SRLW x y) (Neg32 (SLTIU [32] (ZeroExt32to64 y)))) +(Rsh32Ux64 x y) && !shiftIsBounded(v) => (AND (SRLW x y) (Neg32 (SLTIU [32] y))) +(Rsh64Ux8 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt8to64 y)))) +(Rsh64Ux16 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt16to64 y)))) +(Rsh64Ux32 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] (ZeroExt32to64 y)))) +(Rsh64Ux64 x y) && !shiftIsBounded(v) => (AND (SRL x y) (Neg64 (SLTIU [64] y))) -(Rsh8Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt8to64 x) y) -(Rsh16Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt16to64 x) y) -(Rsh32Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt32to64 x) y) -(Rsh64Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL x y) +(Rsh8Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt8to64 x) y) +(Rsh16Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL (ZeroExt16to64 x) y) +(Rsh32Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRLW x y) +(Rsh64Ux(64|32|16|8) x y) && shiftIsBounded(v) => (SRL x y) // SRA only considers the bottom 6 bits of y, similarly SRAW only considers the // bottom 5 bits. If y is greater than the maximum value (either 63 or 31 @@ -188,27 +188,27 @@ // // We don't need to sign-extend the OR result, as it will be at minimum 8 bits, // more than the 5 or 6 bits SRAW and SRA care about. -(Rsh8x8 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) -(Rsh8x16 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) -(Rsh8x32 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) -(Rsh8x64 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) -(Rsh16x8 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) -(Rsh16x16 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) -(Rsh16x32 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) -(Rsh16x64 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) -(Rsh32x8 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] (ZeroExt8to64 y))))) -(Rsh32x16 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] (ZeroExt16to64 y))))) -(Rsh32x32 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] (ZeroExt32to64 y))))) -(Rsh32x64 x y) && !shiftIsBounded(v) => (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] y)))) -(Rsh64x8 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) -(Rsh64x16 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) -(Rsh64x32 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) -(Rsh64x64 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] y)))) +(Rsh8x8 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) +(Rsh8x16 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) +(Rsh8x32 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) +(Rsh8x64 x y) && !shiftIsBounded(v) => (SRA (SignExt8to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) +(Rsh16x8 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) +(Rsh16x16 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) +(Rsh16x32 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) +(Rsh16x64 x y) && !shiftIsBounded(v) => (SRA (SignExt16to64 x) (OR y (ADDI [-1] (SLTIU [64] y)))) +(Rsh32x8 x y) && !shiftIsBounded(v) => (SRAW x (OR y (ADDI [-1] (SLTIU [32] (ZeroExt8to64 y))))) +(Rsh32x16 x y) && !shiftIsBounded(v) => (SRAW x (OR y (ADDI [-1] (SLTIU [32] (ZeroExt16to64 y))))) +(Rsh32x32 x y) && !shiftIsBounded(v) => (SRAW x (OR y (ADDI [-1] (SLTIU [32] (ZeroExt32to64 y))))) +(Rsh32x64 x y) && !shiftIsBounded(v) => (SRAW x (OR y (ADDI [-1] (SLTIU [32] y)))) +(Rsh64x8 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt8to64 y))))) +(Rsh64x16 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt16to64 y))))) +(Rsh64x32 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y))))) +(Rsh64x64 x y) && !shiftIsBounded(v) => (SRA x (OR y (ADDI [-1] (SLTIU [64] y)))) -(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt8to64 x) y) -(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt16to64 x) y) -(Rsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt32to64 x) y) -(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA x y) +(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt8to64 x) y) +(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA (SignExt16to64 x) y) +(Rsh32x(64|32|16|8) x y) && shiftIsBounded(v) => (SRAW x y) +(Rsh64x(64|32|16|8) x y) && shiftIsBounded(v) => (SRA x y) // Rotates. (RotateLeft8 x (MOVDconst [c])) => (Or8 (Lsh8x64 x (MOVDconst [c&7])) (Rsh8Ux64 x (MOVDconst [-c&7]))) @@ -710,10 +710,18 @@ (MOVDnop (MOVDconst [c])) => (MOVDconst [c]) // Avoid unnecessary zero and sign extension when right shifting. -(SRL (MOVWUreg x) y) => (SRLW x y) -(SRLI [x] (MOVWUreg y)) => (SRLIW [int64(x&31)] y) -(SRA (MOVWreg x) y) => (SRAW x y) -(SRAI [x] (MOVWreg y)) => (SRAIW [int64(x&31)] y) +(SRAI [x] (MOVWreg y)) && x >= 0 && x <= 31 => (SRAIW [int64(x)] y) +(SRLI [x] (MOVWUreg y)) && x >= 0 && x <= 31 => (SRLIW [int64(x)] y) + +// Replace right shifts that exceed size of signed type. +(SRAI [x] (MOVBreg y)) && x >= 8 => (SRAI [63] (SLLI [56] y)) +(SRAI [x] (MOVHreg y)) && x >= 16 => (SRAI [63] (SLLI [48] y)) +(SRAI [x] (MOVWreg y)) && x >= 32 => (SRAIW [31] y) + +// Eliminate right shifts that exceed size of unsigned type. +(SRLI [x] (MOVBUreg y)) && x >= 8 => (MOVDconst [0]) +(SRLI [x] (MOVHUreg y)) && x >= 16 => (MOVDconst [0]) +(SRLI [x] (MOVWUreg y)) && x >= 32 => (MOVDconst [0]) // Fold constant into immediate instructions where possible. (ADD (MOVDconst [val]) x) && is32Bit(val) && !t.IsPtr() => (ADDI [val] x) diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go index 4a24a181e5..26af10b59c 100644 --- a/src/cmd/compile/internal/ssa/block.go +++ b/src/cmd/compile/internal/ssa/block.go @@ -297,6 +297,8 @@ func (b *Block) removePred(i int) { // removeSucc removes the ith output edge from b. // It is the responsibility of the caller to remove // the corresponding predecessor edge. +// Note that this potentially reorders successors of b, so it +// must be used very carefully. func (b *Block) removeSucc(i int) { n := len(b.Succs) - 1 if i != n { @@ -323,6 +325,19 @@ func (b *Block) swapSuccessors() { b.Likely *= -1 } +// Swaps b.Succs[x] and b.Succs[y]. +func (b *Block) swapSuccessorsByIdx(x, y int) { + if x == y { + return + } + ex := b.Succs[x] + ey := b.Succs[y] + b.Succs[x] = ey + b.Succs[y] = ex + ex.b.Preds[ex.i].i = y + ey.b.Preds[ey.i].i = x +} + // removePhiArg removes the ith arg from phi. // It must be called after calling b.removePred(i) to // adjust the corresponding phi value of the block: @@ -339,7 +354,7 @@ func (b *Block) swapSuccessors() { func (b *Block) removePhiArg(phi *Value, i int) { n := len(b.Preds) if numPhiArgs := len(phi.Args); numPhiArgs-1 != n { - b.Fatalf("inconsistent state, num predecessors: %d, num phi args: %d", n, numPhiArgs) + b.Fatalf("inconsistent state for %v, num predecessors: %d, num phi args: %d", phi, n, numPhiArgs) } phi.Args[i].Uses-- phi.Args[i] = phi.Args[n] diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index da4294d871..debcf1a0f4 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -283,6 +283,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo c.registers = registersLOONG64[:] c.gpRegMask = gpRegMaskLOONG64 c.fpRegMask = fpRegMaskLOONG64 + c.intParamRegs = paramIntRegLOONG64 + c.floatParamRegs = paramFloatRegLOONG64 c.FPReg = framepointerRegLOONG64 c.LinkReg = linkRegLOONG64 c.hasGReg = true diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go index ae9fd2ef24..3bd1737bab 100644 --- a/src/cmd/compile/internal/ssa/deadcode.go +++ b/src/cmd/compile/internal/ssa/deadcode.go @@ -312,6 +312,8 @@ func deadcode(f *Func) { // removeEdge removes the i'th outgoing edge from b (and // the corresponding incoming edge from b.Succs[i].b). +// Note that this potentially reorders successors of b, so it +// must be used very carefully. func (b *Block) removeEdge(i int) { e := b.Succs[i] c := e.b diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go index 7e0e1f34a8..05a72787f3 100644 --- a/src/cmd/compile/internal/ssa/debug.go +++ b/src/cmd/compile/internal/ssa/debug.go @@ -42,7 +42,10 @@ type FuncDebug struct { OptDcl []*ir.Name // Filled in by the user. Translates Block and Value ID to PC. - GetPC func(ID, ID) int64 + // + // NOTE: block is only used if value is BlockStart.ID or BlockEnd.ID. + // Otherwise, it is ignored. + GetPC func(block, value ID) int64 } type BlockDebug struct { @@ -1368,7 +1371,7 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) { // Flush any leftover entries live at the end of the last block. for varID := range state.lists { - state.writePendingEntry(VarID(varID), state.f.Blocks[len(state.f.Blocks)-1].ID, FuncEnd.ID) + state.writePendingEntry(VarID(varID), -1, FuncEnd.ID) list := state.lists[varID] if state.loggingLevel > 0 { if len(list) == 0 { diff --git a/src/cmd/compile/internal/ssa/debug_lines_test.go b/src/cmd/compile/internal/ssa/debug_lines_test.go index cf115107a1..af9e2a34cf 100644 --- a/src/cmd/compile/internal/ssa/debug_lines_test.go +++ b/src/cmd/compile/internal/ssa/debug_lines_test.go @@ -44,7 +44,7 @@ func testGoArch() string { func hasRegisterABI() bool { switch testGoArch() { - case "amd64", "arm64", "ppc64", "ppc64le", "riscv": + case "amd64", "arm64", "loong64", "ppc64", "ppc64le", "riscv": return true } return false diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 298e29ec56..b0788f1db4 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -411,7 +411,7 @@ func (x *expandState) decomposeAsNecessary(pos src.XPos, b *Block, a, m0 *Value, return mem case types.TSLICE: - mem = x.decomposeOne(pos, b, a, mem, x.typs.BytePtr, OpSlicePtr, &rc) + mem = x.decomposeOne(pos, b, a, mem, at.Elem().PtrTo(), OpSlicePtr, &rc) pos = pos.WithNotStmt() mem = x.decomposeOne(pos, b, a, mem, x.typs.Int, OpSliceLen, &rc) return x.decomposeOne(pos, b, a, mem, x.typs.Int, OpSliceCap, &rc) @@ -564,7 +564,7 @@ func (x *expandState) rewriteSelectOrArg(pos src.XPos, b *Block, container, a, m return a case types.TSLICE: - addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.BytePtr, rc.next(x.typs.BytePtr))) + addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, at.Elem().PtrTo(), rc.next(x.typs.BytePtr))) pos = pos.WithNotStmt() addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Int, rc.next(x.typs.Int))) addArg(x.rewriteSelectOrArg(pos, b, container, nil, m0, x.typs.Int, rc.next(x.typs.Int))) @@ -721,7 +721,7 @@ func (x *expandState) rewriteWideSelectToStores(pos src.XPos, b *Block, containe return m0 case types.TSLICE: - m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.BytePtr, rc.next(x.typs.BytePtr)) + m0 = x.rewriteWideSelectToStores(pos, b, container, m0, at.Elem().PtrTo(), rc.next(x.typs.BytePtr)) pos = pos.WithNotStmt() m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Int, rc.next(x.typs.Int)) m0 = x.rewriteWideSelectToStores(pos, b, container, m0, x.typs.Int, rc.next(x.typs.Int)) diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index e94cb77f92..031d94f90c 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -64,7 +64,7 @@ type Func struct { // RegArgs is a slice of register-memory pairs that must be spilled and unspilled in the uncommon path of function entry. RegArgs []Spill - // AuxCall describing parameters and results for this function. + // OwnAux describes parameters and results for this function. OwnAux *AuxCall freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil. @@ -721,7 +721,6 @@ func (f *Func) ConstOffPtrSP(t *types.Type, c int64, sp *Value) *Value { v.AddArg(sp) } return v - } func (f *Func) Frontend() Frontend { return f.fe } diff --git a/src/cmd/compile/internal/ssa/magic.go b/src/cmd/compile/internal/ssa/magic.go index df4b568134..235b0e5e5c 100644 --- a/src/cmd/compile/internal/ssa/magic.go +++ b/src/cmd/compile/internal/ssa/magic.go @@ -170,7 +170,7 @@ func smagicOK(n uint, c int64) bool { return c&(c-1) != 0 } -// smagicOKn reports whether we should strength reduce an signed n-bit divide by c. +// smagicOKn reports whether we should strength reduce a signed n-bit divide by c. func smagicOK8(c int8) bool { return smagicOK(8, int64(c)) } func smagicOK16(c int16) bool { return smagicOK(16, int64(c)) } func smagicOK32(c int32) bool { return smagicOK(32, int64(c)) } diff --git a/src/cmd/compile/internal/ssa/memcombine.go b/src/cmd/compile/internal/ssa/memcombine.go index 848b1e57a7..b1a47510be 100644 --- a/src/cmd/compile/internal/ssa/memcombine.go +++ b/src/cmd/compile/internal/ssa/memcombine.go @@ -313,8 +313,8 @@ func combineLoads(root *Value, n int64) bool { if isLittleEndian && shift0 != 0 { v = leftShift(loadBlock, pos, v, shift0) } - if isBigEndian && shift0-(n-1)*8 != 0 { - v = leftShift(loadBlock, pos, v, shift0-(n-1)*8) + if isBigEndian && shift0-(n-1)*size*8 != 0 { + v = leftShift(loadBlock, pos, v, shift0-(n-1)*size*8) } // Install with (Copy v). @@ -662,14 +662,14 @@ func combineStores(root *Value, n int64) bool { isLittleEndian := true shift0 := shift(a[0].store, shiftBase) for i := int64(1); i < n; i++ { - if shift(a[i].store, shiftBase) != shift0+i*8 { + if shift(a[i].store, shiftBase) != shift0+i*size*8 { isLittleEndian = false break } } isBigEndian := true for i := int64(1); i < n; i++ { - if shift(a[i].store, shiftBase) != shift0-i*8 { + if shift(a[i].store, shiftBase) != shift0-i*size*8 { isBigEndian = false break } @@ -692,8 +692,8 @@ func combineStores(root *Value, n int64) bool { if isLittleEndian && shift0 != 0 { sv = rightShift(root.Block, root.Pos, sv, shift0) } - if isBigEndian && shift0-(n-1)*8 != 0 { - sv = rightShift(root.Block, root.Pos, sv, shift0-(n-1)*8) + if isBigEndian && shift0-(n-1)*size*8 != 0 { + sv = rightShift(root.Block, root.Pos, sv, shift0-(n-1)*size*8) } if sv.Type.Size() > size*n { sv = truncate(root.Block, root.Pos, sv, sv.Type.Size(), size*n) diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 80ac8e4f8b..c552832520 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -23193,11 +23193,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AADDVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23208,10 +23208,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AADDVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693244}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741820}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23221,11 +23221,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASUBVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23236,10 +23236,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASUBVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23250,11 +23250,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMULV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23265,11 +23265,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMULHV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23280,11 +23280,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMULHVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23294,11 +23294,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ADIVV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23308,11 +23308,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ADIVVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23322,11 +23322,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AREMV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23336,11 +23336,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AREMVU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23467,11 +23467,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23482,10 +23482,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AAND, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23496,11 +23496,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23511,10 +23511,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23525,11 +23525,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AXOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23540,10 +23540,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AXOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23554,11 +23554,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ANOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23569,10 +23569,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.ANOR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23581,10 +23581,10 @@ var opcodeTable = [...]opInfo{ argLen: 1, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23646,11 +23646,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMASKEQZ, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23660,11 +23660,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMASKNEZ, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23674,11 +23674,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASLLV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23689,10 +23689,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASLLV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23702,11 +23702,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASRLV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23717,10 +23717,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASRLV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23730,11 +23730,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASRAV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23745,10 +23745,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASRAV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23758,11 +23758,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AROTR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23772,11 +23772,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.AROTRV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23787,10 +23787,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AROTR, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23801,10 +23801,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AROTRV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23814,11 +23814,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASGT, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23829,10 +23829,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASGT, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23842,11 +23842,11 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASGTU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23857,10 +23857,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.ASGTU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23938,7 +23938,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVV, reg: regInfo{ outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23978,7 +23978,7 @@ var opcodeTable = [...]opInfo{ {0, 4611686018427387908}, // SP SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -23991,10 +23991,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24007,10 +24007,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVBU, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24023,10 +24023,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24039,10 +24039,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24055,10 +24055,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24071,10 +24071,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24087,10 +24087,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24103,7 +24103,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVF, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ {0, 4611686017353646080}, // 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 F31 @@ -24119,7 +24119,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ {0, 4611686017353646080}, // 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 F31 @@ -24135,8 +24135,8 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24149,8 +24149,8 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24163,8 +24163,8 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24177,8 +24177,8 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24191,7 +24191,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVF, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB {1, 4611686017353646080}, // 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 F31 }, }, @@ -24205,7 +24205,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVD, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB {1, 4611686017353646080}, // 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 F31 }, }, @@ -24219,7 +24219,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24232,7 +24232,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24245,7 +24245,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24258,7 +24258,7 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24268,10 +24268,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVB, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24281,10 +24281,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVBU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24294,10 +24294,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVH, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24307,10 +24307,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVHU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24320,10 +24320,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVW, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24333,10 +24333,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVWU, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24346,10 +24346,10 @@ var opcodeTable = [...]opInfo{ asm: loong64.AMOVV, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24359,10 +24359,10 @@ var opcodeTable = [...]opInfo{ resultInArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24499,49 +24499,49 @@ var opcodeTable = [...]opInfo{ { name: "CALLstatic", auxType: auxCallOff, - argLen: 1, + argLen: -1, clobberFlags: true, call: true, reg: regInfo{ - clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g 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 F31 + clobbers: 4611686018427387896, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g 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 F31 }, }, { name: "CALLtail", auxType: auxCallOff, - argLen: 1, + argLen: -1, clobberFlags: true, call: true, tailCall: true, reg: regInfo{ - clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g 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 F31 + clobbers: 4611686018427387896, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g 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 F31 }, }, { name: "CALLclosure", auxType: auxCallOff, - argLen: 3, + argLen: -1, clobberFlags: true, call: true, reg: regInfo{ inputs: []inputInfo{ {1, 268435456}, // R29 - {0, 1070596092}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644668}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, - clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g 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 F31 + clobbers: 4611686018427387896, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g 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 F31 }, }, { name: "CALLinter", auxType: auxCallOff, - argLen: 2, + argLen: -1, clobberFlags: true, call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, - clobbers: 4611686018426339320, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g 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 F31 + clobbers: 4611686018427387896, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g 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 F31 }, }, { @@ -24551,9 +24551,9 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 262144}, // R19 + {0, 524288}, // R20 }, - clobbers: 262146, // R1 R19 + clobbers: 524290, // R1 R20 }, }, { @@ -24564,10 +24564,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg1: true, reg: regInfo{ inputs: []inputInfo{ - {0, 524288}, // R20 - {1, 262144}, // R19 + {0, 1048576}, // R21 + {1, 524288}, // R20 }, - clobbers: 786434, // R1 R19 R20 + clobbers: 1572866, // R1 R20 R21 }, }, { @@ -24577,10 +24577,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 262144}, // R19 - {1, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 524288}, // R20 + {1, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, - clobbers: 262144, // R19 + clobbers: 524288, // R20 }, }, { @@ -24591,11 +24591,11 @@ var opcodeTable = [...]opInfo{ faultOnNilArg1: true, reg: regInfo{ inputs: []inputInfo{ - {0, 524288}, // R20 - {1, 262144}, // R19 - {2, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1048576}, // R21 + {1, 524288}, // R20 + {2, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, - clobbers: 786432, // R19 R20 + clobbers: 1572864, // R20 R21 }, }, { @@ -24604,10 +24604,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24617,10 +24617,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24630,10 +24630,10 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24644,8 +24644,8 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24656,8 +24656,8 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24668,8 +24668,8 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24680,7 +24680,7 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24691,7 +24691,7 @@ var opcodeTable = [...]opInfo{ hasSideEffects: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, }, }, @@ -24704,11 +24704,11 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24721,11 +24721,11 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24738,11 +24738,11 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24755,11 +24755,11 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24773,10 +24773,10 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24790,10 +24790,10 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24806,12 +24806,12 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {2, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {2, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24824,12 +24824,12 @@ var opcodeTable = [...]opInfo{ unsafePoint: true, reg: regInfo{ inputs: []inputInfo{ - {1, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {2, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 - {0, 4611686019500081148}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 SB + {1, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {2, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 4611686019501129724}, // SP R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 SB }, outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24840,7 +24840,7 @@ var opcodeTable = [...]opInfo{ faultOnNilArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 1072693240}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 g R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1073741816}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 g R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24849,7 +24849,7 @@ var opcodeTable = [...]opInfo{ argLen: 1, reg: regInfo{ outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24858,7 +24858,7 @@ var opcodeTable = [...]opInfo{ argLen: 1, reg: regInfo{ outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24878,7 +24878,7 @@ var opcodeTable = [...]opInfo{ rematerializeable: true, reg: regInfo{ outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24888,7 +24888,7 @@ var opcodeTable = [...]opInfo{ rematerializeable: true, reg: regInfo{ outputs: []outputInfo{ - {0, 1070596088}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31 + {0, 1071644664}, // R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31 }, }, }, @@ -24911,8 +24911,8 @@ var opcodeTable = [...]opInfo{ call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 65536}, // R17 - {1, 8}, // R4 + {0, 4194304}, // R23 + {1, 8388608}, // R24 }, }, }, @@ -24923,8 +24923,8 @@ var opcodeTable = [...]opInfo{ call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 131072}, // R18 - {1, 65536}, // R17 + {0, 1048576}, // R21 + {1, 4194304}, // R23 }, }, }, @@ -24935,8 +24935,8 @@ var opcodeTable = [...]opInfo{ call: true, reg: regInfo{ inputs: []inputInfo{ - {0, 262144}, // R19 - {1, 131072}, // R18 + {0, 524288}, // R20 + {1, 1048576}, // R21 }, }, }, @@ -40707,16 +40707,16 @@ var registersLOONG64 = [...]Register{ {17, loong64.REG_R18, 14, "R18"}, {18, loong64.REG_R19, 15, "R19"}, {19, loong64.REG_R20, 16, "R20"}, - {20, loong64.REG_R21, -1, "R21"}, + {20, loong64.REG_R21, 17, "R21"}, {21, loong64.REGG, -1, "g"}, - {22, loong64.REG_R23, 17, "R23"}, - {23, loong64.REG_R24, 18, "R24"}, - {24, loong64.REG_R25, 19, "R25"}, - {25, loong64.REG_R26, 20, "R26"}, - {26, loong64.REG_R27, 21, "R27"}, - {27, loong64.REG_R28, 22, "R28"}, - {28, loong64.REG_R29, 23, "R29"}, - {29, loong64.REG_R31, 24, "R31"}, + {22, loong64.REG_R23, 18, "R23"}, + {23, loong64.REG_R24, 19, "R24"}, + {24, loong64.REG_R25, 20, "R25"}, + {25, loong64.REG_R26, 21, "R26"}, + {26, loong64.REG_R27, 22, "R27"}, + {27, loong64.REG_R28, 23, "R28"}, + {28, loong64.REG_R29, 24, "R29"}, + {29, loong64.REG_R31, 25, "R31"}, {30, loong64.REG_F0, -1, "F0"}, {31, loong64.REG_F1, -1, "F1"}, {32, loong64.REG_F2, -1, "F2"}, @@ -40751,9 +40751,9 @@ var registersLOONG64 = [...]Register{ {61, loong64.REG_F31, -1, "F31"}, {62, 0, -1, "SB"}, } -var paramIntRegLOONG64 = []int8{3, 4, 5, 6, 7, 8, 9, 10} -var paramFloatRegLOONG64 = []int8{30, 31, 32, 33, 34, 35, 36, 37} -var gpRegMaskLOONG64 = regMask(1070596088) +var paramIntRegLOONG64 = []int8{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18} +var paramFloatRegLOONG64 = []int8{30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45} +var gpRegMaskLOONG64 = regMask(1071644664) var fpRegMaskLOONG64 = regMask(4611686017353646080) var specialRegMaskLOONG64 = regMask(0) var framepointerRegLOONG64 = int8(-1) diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index 91f5fbe765..842719fb4c 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -100,10 +100,11 @@ func (d domain) String() string { } type pair struct { - v, w *Value // a pair of values, ordered by ID. + // a pair of values, ordered by ID. // v can be nil, to mean the zero value. // for booleans the zero value (v == nil) is false. - d domain + v, w *Value + d domain } // fact is a pair plus a relation for that pair. @@ -165,7 +166,7 @@ type factsTable struct { facts map[pair]relation // current known set of relation stack []fact // previous sets of relations - // order is a couple of partial order sets that record information + // order* is a couple of partial order sets that record information // about relations between SSA values in the signed and unsigned // domain. orderS *poset diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index fcd3f5c8b5..2325b9ee45 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -672,6 +672,8 @@ func (s *regAllocState) init(f *Func) { s.allocatable &^= 1 << 9 // R9 case "arm64": // nothing to do + case "loong64": // R2 (aka TP) already reserved. + // nothing to do case "ppc64le": // R2 already reserved. // nothing to do case "riscv64": // X3 (aka GP) and X4 (aka TP) already reserved. diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go index 70cacb90ed..971c9a5d55 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM.go +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -1496,7 +1496,7 @@ func rewriteValueARM_OpARMADDD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (ADDD a (MULD x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULAD a x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1506,7 +1506,7 @@ func rewriteValueARM_OpARMADDD(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { continue } v.reset(OpARMMULAD) @@ -1516,7 +1516,7 @@ func rewriteValueARM_OpARMADDD(v *Value) bool { break } // match: (ADDD a (NMULD x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULSD a x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1526,7 +1526,7 @@ func rewriteValueARM_OpARMADDD(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { continue } v.reset(OpARMMULSD) @@ -1541,7 +1541,7 @@ func rewriteValueARM_OpARMADDF(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (ADDF a (MULF x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULAF a x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1551,7 +1551,7 @@ func rewriteValueARM_OpARMADDF(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { continue } v.reset(OpARMMULAF) @@ -1561,7 +1561,7 @@ func rewriteValueARM_OpARMADDF(v *Value) bool { break } // match: (ADDF a (NMULF x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULSF a x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -1571,7 +1571,7 @@ func rewriteValueARM_OpARMADDF(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { continue } v.reset(OpARMMULSF) @@ -1979,12 +1979,12 @@ func rewriteValueARM_OpARMADDconst(v *Value) bool { return true } // match: (ADDconst [c] x) - // cond: buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff + // cond: buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff // result: (SUBconst [-c] x) for { c := auxIntToInt32(v.AuxInt) x := v_0 - if !(buildcfg.GOARM == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && uint32(-c) <= 0xffff) { + if !(buildcfg.GOARM.Version == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && uint32(-c) <= 0xffff) { break } v.reset(OpARMSUBconst) @@ -2099,7 +2099,7 @@ func rewriteValueARM_OpARMADDshiftLL(v *Value) bool { return true } // match: (ADDshiftLL [8] (SRLconst [24] (SLLconst [16] x)) x) - // cond: buildcfg.GOARM>=6 + // cond: buildcfg.GOARM.Version>=6 // result: (REV16 x) for { if v.Type != typ.UInt16 || auxIntToInt32(v.AuxInt) != 8 || v_0.Op != OpARMSRLconst || v_0.Type != typ.UInt16 || auxIntToInt32(v_0.AuxInt) != 24 { @@ -2110,7 +2110,7 @@ func rewriteValueARM_OpARMADDshiftLL(v *Value) bool { break } x := v_0_0.Args[0] - if x != v_1 || !(buildcfg.GOARM >= 6) { + if x != v_1 || !(buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMREV16) @@ -2551,12 +2551,12 @@ func rewriteValueARM_OpARMANDconst(v *Value) bool { return true } // match: (ANDconst [c] x) - // cond: buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff + // cond: buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff // result: (BICconst [int32(^uint32(c))] x) for { c := auxIntToInt32(v.AuxInt) x := v_0 - if !(buildcfg.GOARM == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && ^uint32(c) <= 0xffff) { + if !(buildcfg.GOARM.Version == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && ^uint32(c) <= 0xffff) { break } v.reset(OpARMBICconst) @@ -3052,12 +3052,12 @@ func rewriteValueARM_OpARMBICconst(v *Value) bool { return true } // match: (BICconst [c] x) - // cond: buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff + // cond: buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && ^uint32(c)<=0xffff // result: (ANDconst [int32(^uint32(c))] x) for { c := auxIntToInt32(v.AuxInt) x := v_0 - if !(buildcfg.GOARM == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && ^uint32(c) <= 0xffff) { + if !(buildcfg.GOARM.Version == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && ^uint32(c) <= 0xffff) { break } v.reset(OpARMANDconst) @@ -7590,7 +7590,7 @@ func rewriteValueARM_OpARMMULD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MULD (NEGD x) y) - // cond: buildcfg.GOARM >= 6 + // cond: buildcfg.GOARM.Version >= 6 // result: (NMULD x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -7599,7 +7599,7 @@ func rewriteValueARM_OpARMMULD(v *Value) bool { } x := v_0.Args[0] y := v_1 - if !(buildcfg.GOARM >= 6) { + if !(buildcfg.GOARM.Version >= 6) { continue } v.reset(OpARMNMULD) @@ -7614,7 +7614,7 @@ func rewriteValueARM_OpARMMULF(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MULF (NEGF x) y) - // cond: buildcfg.GOARM >= 6 + // cond: buildcfg.GOARM.Version >= 6 // result: (NMULF x y) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -7623,7 +7623,7 @@ func rewriteValueARM_OpARMMULF(v *Value) bool { } x := v_0.Args[0] y := v_1 - if !(buildcfg.GOARM >= 6) { + if !(buildcfg.GOARM.Version >= 6) { continue } v.reset(OpARMNMULF) @@ -8247,7 +8247,7 @@ func rewriteValueARM_OpARMMVNshiftRLreg(v *Value) bool { func rewriteValueARM_OpARMNEGD(v *Value) bool { v_0 := v.Args[0] // match: (NEGD (MULD x y)) - // cond: buildcfg.GOARM >= 6 + // cond: buildcfg.GOARM.Version >= 6 // result: (NMULD x y) for { if v_0.Op != OpARMMULD { @@ -8255,7 +8255,7 @@ func rewriteValueARM_OpARMNEGD(v *Value) bool { } y := v_0.Args[1] x := v_0.Args[0] - if !(buildcfg.GOARM >= 6) { + if !(buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMNMULD) @@ -8267,7 +8267,7 @@ func rewriteValueARM_OpARMNEGD(v *Value) bool { func rewriteValueARM_OpARMNEGF(v *Value) bool { v_0 := v.Args[0] // match: (NEGF (MULF x y)) - // cond: buildcfg.GOARM >= 6 + // cond: buildcfg.GOARM.Version >= 6 // result: (NMULF x y) for { if v_0.Op != OpARMMULF { @@ -8275,7 +8275,7 @@ func rewriteValueARM_OpARMNEGF(v *Value) bool { } y := v_0.Args[1] x := v_0.Args[0] - if !(buildcfg.GOARM >= 6) { + if !(buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMNMULF) @@ -8583,7 +8583,7 @@ func rewriteValueARM_OpARMORshiftLL(v *Value) bool { return true } // match: (ORshiftLL [8] (SRLconst [24] (SLLconst [16] x)) x) - // cond: buildcfg.GOARM>=6 + // cond: buildcfg.GOARM.Version>=6 // result: (REV16 x) for { if v.Type != typ.UInt16 || auxIntToInt32(v.AuxInt) != 8 || v_0.Op != OpARMSRLconst || v_0.Type != typ.UInt16 || auxIntToInt32(v_0.AuxInt) != 24 { @@ -8594,7 +8594,7 @@ func rewriteValueARM_OpARMORshiftLL(v *Value) bool { break } x := v_0_0.Args[0] - if x != v_1 || !(buildcfg.GOARM >= 6) { + if x != v_1 || !(buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMREV16) @@ -9048,7 +9048,7 @@ func rewriteValueARM_OpARMRSB(v *Value) bool { return true } // match: (RSB (MUL x y) a) - // cond: buildcfg.GOARM == 7 + // cond: buildcfg.GOARM.Version == 7 // result: (MULS x y a) for { if v_0.Op != OpARMMUL { @@ -9057,7 +9057,7 @@ func rewriteValueARM_OpARMRSB(v *Value) bool { y := v_0.Args[1] x := v_0.Args[0] a := v_1 - if !(buildcfg.GOARM == 7) { + if !(buildcfg.GOARM.Version == 7) { break } v.reset(OpARMMULS) @@ -10534,7 +10534,7 @@ func rewriteValueARM_OpARMSRAconst(v *Value) bool { return true } // match: (SRAconst (SLLconst x [c]) [d]) - // cond: buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 + // cond: buildcfg.GOARM.Version==7 && uint64(d)>=uint64(c) && uint64(d)<=31 // result: (BFX [(d-c)|(32-d)<<8] x) for { d := auxIntToInt32(v.AuxInt) @@ -10543,7 +10543,7 @@ func rewriteValueARM_OpARMSRAconst(v *Value) bool { } c := auxIntToInt32(v_0.AuxInt) x := v_0.Args[0] - if !(buildcfg.GOARM == 7 && uint64(d) >= uint64(c) && uint64(d) <= 31) { + if !(buildcfg.GOARM.Version == 7 && uint64(d) >= uint64(c) && uint64(d) <= 31) { break } v.reset(OpARMBFX) @@ -10590,7 +10590,7 @@ func rewriteValueARM_OpARMSRLconst(v *Value) bool { return true } // match: (SRLconst (SLLconst x [c]) [d]) - // cond: buildcfg.GOARM==7 && uint64(d)>=uint64(c) && uint64(d)<=31 + // cond: buildcfg.GOARM.Version==7 && uint64(d)>=uint64(c) && uint64(d)<=31 // result: (BFXU [(d-c)|(32-d)<<8] x) for { d := auxIntToInt32(v.AuxInt) @@ -10599,7 +10599,7 @@ func rewriteValueARM_OpARMSRLconst(v *Value) bool { } c := auxIntToInt32(v_0.AuxInt) x := v_0.Args[0] - if !(buildcfg.GOARM == 7 && uint64(d) >= uint64(c) && uint64(d) <= 31) { + if !(buildcfg.GOARM.Version == 7 && uint64(d) >= uint64(c) && uint64(d) <= 31) { break } v.reset(OpARMBFXU) @@ -10830,7 +10830,7 @@ func rewriteValueARM_OpARMSUB(v *Value) bool { return true } // match: (SUB a (MUL x y)) - // cond: buildcfg.GOARM == 7 + // cond: buildcfg.GOARM.Version == 7 // result: (MULS x y a) for { a := v_0 @@ -10839,7 +10839,7 @@ func rewriteValueARM_OpARMSUB(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(buildcfg.GOARM == 7) { + if !(buildcfg.GOARM.Version == 7) { break } v.reset(OpARMMULS) @@ -10852,7 +10852,7 @@ func rewriteValueARM_OpARMSUBD(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SUBD a (MULD x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULSD a x y) for { a := v_0 @@ -10861,7 +10861,7 @@ func rewriteValueARM_OpARMSUBD(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMMULSD) @@ -10869,7 +10869,7 @@ func rewriteValueARM_OpARMSUBD(v *Value) bool { return true } // match: (SUBD a (NMULD x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULAD a x y) for { a := v_0 @@ -10878,7 +10878,7 @@ func rewriteValueARM_OpARMSUBD(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMMULAD) @@ -10891,7 +10891,7 @@ func rewriteValueARM_OpARMSUBF(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SUBF a (MULF x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULSF a x y) for { a := v_0 @@ -10900,7 +10900,7 @@ func rewriteValueARM_OpARMSUBF(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMMULSF) @@ -10908,7 +10908,7 @@ func rewriteValueARM_OpARMSUBF(v *Value) bool { return true } // match: (SUBF a (NMULF x y)) - // cond: a.Uses == 1 && buildcfg.GOARM >= 6 + // cond: a.Uses == 1 && buildcfg.GOARM.Version >= 6 // result: (MULAF a x y) for { a := v_0 @@ -10917,7 +10917,7 @@ func rewriteValueARM_OpARMSUBF(v *Value) bool { } y := v_1.Args[1] x := v_1.Args[0] - if !(a.Uses == 1 && buildcfg.GOARM >= 6) { + if !(a.Uses == 1 && buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMMULAF) @@ -11383,12 +11383,12 @@ func rewriteValueARM_OpARMSUBconst(v *Value) bool { return true } // match: (SUBconst [c] x) - // cond: buildcfg.GOARM==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff + // cond: buildcfg.GOARM.Version==7 && !isARMImmRot(uint32(c)) && uint32(c)>0xffff && uint32(-c)<=0xffff // result: (ADDconst [-c] x) for { c := auxIntToInt32(v.AuxInt) x := v_0 - if !(buildcfg.GOARM == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && uint32(-c) <= 0xffff) { + if !(buildcfg.GOARM.Version == 7 && !isARMImmRot(uint32(c)) && uint32(c) > 0xffff && uint32(-c) <= 0xffff) { break } v.reset(OpARMADDconst) @@ -12710,7 +12710,7 @@ func rewriteValueARM_OpARMXORshiftLL(v *Value) bool { return true } // match: (XORshiftLL [8] (SRLconst [24] (SLLconst [16] x)) x) - // cond: buildcfg.GOARM>=6 + // cond: buildcfg.GOARM.Version>=6 // result: (REV16 x) for { if v.Type != typ.UInt16 || auxIntToInt32(v.AuxInt) != 8 || v_0.Op != OpARMSRLconst || v_0.Type != typ.UInt16 || auxIntToInt32(v_0.AuxInt) != 24 { @@ -12721,7 +12721,7 @@ func rewriteValueARM_OpARMXORshiftLL(v *Value) bool { break } x := v_0_0.Args[0] - if x != v_1 || !(buildcfg.GOARM >= 6) { + if x != v_1 || !(buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMREV16) @@ -13062,12 +13062,12 @@ func rewriteValueARM_OpBswap32(v *Value) bool { v_0 := v.Args[0] b := v.Block // match: (Bswap32 x) - // cond: buildcfg.GOARM==5 + // cond: buildcfg.GOARM.Version==5 // result: (XOR (SRLconst (BICconst (XOR x (SRRconst [16] x)) [0xff0000]) [8]) (SRRconst x [8])) for { t := v.Type x := v_0 - if !(buildcfg.GOARM == 5) { + if !(buildcfg.GOARM.Version == 5) { break } v.reset(OpARMXOR) @@ -13090,11 +13090,11 @@ func rewriteValueARM_OpBswap32(v *Value) bool { return true } // match: (Bswap32 x) - // cond: buildcfg.GOARM>=6 + // cond: buildcfg.GOARM.Version>=6 // result: (REV x) for { x := v_0 - if !(buildcfg.GOARM >= 6) { + if !(buildcfg.GOARM.Version >= 6) { break } v.reset(OpARMREV) @@ -13177,12 +13177,12 @@ func rewriteValueARM_OpCtz16(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Ctz16 x) - // cond: buildcfg.GOARM<=6 + // cond: buildcfg.GOARM.Version<=6 // result: (RSBconst [32] (CLZ (SUBconst (AND (ORconst [0x10000] x) (RSBconst [0] (ORconst [0x10000] x))) [1]))) for { t := v.Type x := v_0 - if !(buildcfg.GOARM <= 6) { + if !(buildcfg.GOARM.Version <= 6) { break } v.reset(OpARMRSBconst) @@ -13204,12 +13204,12 @@ func rewriteValueARM_OpCtz16(v *Value) bool { return true } // match: (Ctz16 x) - // cond: buildcfg.GOARM==7 + // cond: buildcfg.GOARM.Version==7 // result: (CLZ (RBIT (ORconst [0x10000] x))) for { t := v.Type x := v_0 - if !(buildcfg.GOARM == 7) { + if !(buildcfg.GOARM.Version == 7) { break } v.reset(OpARMCLZ) @@ -13228,12 +13228,12 @@ func rewriteValueARM_OpCtz32(v *Value) bool { v_0 := v.Args[0] b := v.Block // match: (Ctz32 x) - // cond: buildcfg.GOARM<=6 + // cond: buildcfg.GOARM.Version<=6 // result: (RSBconst [32] (CLZ (SUBconst (AND x (RSBconst [0] x)) [1]))) for { t := v.Type x := v_0 - if !(buildcfg.GOARM <= 6) { + if !(buildcfg.GOARM.Version <= 6) { break } v.reset(OpARMRSBconst) @@ -13252,12 +13252,12 @@ func rewriteValueARM_OpCtz32(v *Value) bool { return true } // match: (Ctz32 x) - // cond: buildcfg.GOARM==7 + // cond: buildcfg.GOARM.Version==7 // result: (CLZ (RBIT x)) for { t := v.Type x := v_0 - if !(buildcfg.GOARM == 7) { + if !(buildcfg.GOARM.Version == 7) { break } v.reset(OpARMCLZ) @@ -13274,12 +13274,12 @@ func rewriteValueARM_OpCtz8(v *Value) bool { b := v.Block typ := &b.Func.Config.Types // match: (Ctz8 x) - // cond: buildcfg.GOARM<=6 + // cond: buildcfg.GOARM.Version<=6 // result: (RSBconst [32] (CLZ (SUBconst (AND (ORconst [0x100] x) (RSBconst [0] (ORconst [0x100] x))) [1]))) for { t := v.Type x := v_0 - if !(buildcfg.GOARM <= 6) { + if !(buildcfg.GOARM.Version <= 6) { break } v.reset(OpARMRSBconst) @@ -13301,12 +13301,12 @@ func rewriteValueARM_OpCtz8(v *Value) bool { return true } // match: (Ctz8 x) - // cond: buildcfg.GOARM==7 + // cond: buildcfg.GOARM.Version==7 // result: (CLZ (RBIT (ORconst [0x100] x))) for { t := v.Type x := v_0 - if !(buildcfg.GOARM == 7) { + if !(buildcfg.GOARM.Version == 7) { break } v.reset(OpARMCLZ) diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64.go b/src/cmd/compile/internal/ssa/rewriteLOONG64.go index e88b74cb22..edd3ffe6b9 100644 --- a/src/cmd/compile/internal/ssa/rewriteLOONG64.go +++ b/src/cmd/compile/internal/ssa/rewriteLOONG64.go @@ -1724,8 +1724,10 @@ func rewriteValueLOONG64_OpLOONG64MASKNEZ(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBUload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -1736,7 +1738,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBUload) @@ -1746,7 +1748,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool { return true } // match: (MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -1758,7 +1760,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBUload) @@ -1771,6 +1773,26 @@ func rewriteValueLOONG64_OpLOONG64MOVBUload(v *Value) bool { } func rewriteValueLOONG64_OpLOONG64MOVBUreg(v *Value) bool { v_0 := v.Args[0] + // match: (MOVBUreg x:(SGT _ _)) + // result: x + for { + x := v_0 + if x.Op != OpLOONG64SGT { + break + } + v.copyOf(x) + return true + } + // match: (MOVBUreg x:(SGTU _ _)) + // result: x + for { + x := v_0 + if x.Op != OpLOONG64SGTU { + break + } + v.copyOf(x) + return true + } // match: (MOVBUreg x:(MOVBUload _ _)) // result: (MOVVreg x) for { @@ -1809,8 +1831,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBUreg(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVBload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -1821,7 +1845,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBload) @@ -1831,7 +1855,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool { return true } // match: (MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -1843,7 +1867,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBload) @@ -1895,8 +1919,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBstore [off1+int32(off2)] {sym} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -1908,7 +1934,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBstore) @@ -1918,7 +1944,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool { return true } // match: (MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -1931,7 +1957,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBstore) @@ -2047,8 +2073,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBstore(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBstorezero [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2059,7 +2087,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBstorezero) @@ -2069,7 +2097,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool { return true } // match: (MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2081,7 +2109,7 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVBstorezero) @@ -2095,8 +2123,10 @@ func rewriteValueLOONG64_OpLOONG64MOVBstorezero(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVDload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVDload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2107,7 +2137,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVDload) @@ -2117,7 +2147,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool { return true } // match: (MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVDload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2129,7 +2159,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVDload) @@ -2144,8 +2174,10 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVDstore [off1+int32(off2)] {sym} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2157,7 +2189,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVDstore) @@ -2167,7 +2199,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool { return true } // match: (MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVDstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2180,7 +2212,7 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVDstore) @@ -2194,8 +2226,10 @@ func rewriteValueLOONG64_OpLOONG64MOVDstore(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVFload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVFload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2206,7 +2240,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVFload) @@ -2216,7 +2250,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool { return true } // match: (MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVFload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2228,7 +2262,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVFload) @@ -2243,8 +2277,10 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVFstore [off1+int32(off2)] {sym} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2256,7 +2292,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVFstore) @@ -2266,7 +2302,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool { return true } // match: (MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVFstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2279,7 +2315,7 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVFstore) @@ -2293,8 +2329,10 @@ func rewriteValueLOONG64_OpLOONG64MOVFstore(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHUload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2305,7 +2343,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHUload) @@ -2315,7 +2353,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool { return true } // match: (MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2327,7 +2365,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHUload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHUload) @@ -2400,8 +2438,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHUreg(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2412,7 +2452,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHload) @@ -2422,7 +2462,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool { return true } // match: (MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2434,7 +2474,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHload) @@ -2530,8 +2570,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHstore [off1+int32(off2)] {sym} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2543,7 +2585,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHstore) @@ -2553,7 +2595,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool { return true } // match: (MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2566,7 +2608,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHstore) @@ -2648,8 +2690,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHstore(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHstorezero [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2660,7 +2704,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHstorezero) @@ -2670,7 +2714,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool { return true } // match: (MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2682,7 +2726,7 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVHstorezero) @@ -2696,8 +2740,10 @@ func rewriteValueLOONG64_OpLOONG64MOVHstorezero(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVVload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVVload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2708,7 +2754,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVVload) @@ -2718,7 +2764,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool { return true } // match: (MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVVload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2730,7 +2776,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVVload) @@ -2772,8 +2818,10 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVVstore [off1+int32(off2)] {sym} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2785,7 +2833,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVVstore) @@ -2795,7 +2843,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool { return true } // match: (MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVVstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2808,7 +2856,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVVstore) @@ -2822,8 +2870,10 @@ func rewriteValueLOONG64_OpLOONG64MOVVstore(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVVstorezero [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2834,7 +2884,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVVstorezero) @@ -2844,7 +2894,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool { return true } // match: (MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVVstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2856,7 +2906,7 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVVstorezero) @@ -2870,8 +2920,10 @@ func rewriteValueLOONG64_OpLOONG64MOVVstorezero(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWUload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2882,7 +2934,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWUload) @@ -2892,7 +2944,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool { return true } // match: (MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWUload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -2904,7 +2956,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWUload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWUload) @@ -2999,8 +3051,10 @@ func rewriteValueLOONG64_OpLOONG64MOVWUreg(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWload [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWload [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -3011,7 +3065,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWload) @@ -3021,7 +3075,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool { return true } // match: (MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWload [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -3033,7 +3087,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWload(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWload) @@ -3162,8 +3216,10 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWstore [off1+int32(off2)] {sym} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -3175,7 +3231,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWstore) @@ -3185,7 +3241,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool { return true } // match: (MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWstore [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -3198,7 +3254,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool { ptr := v_0.Args[0] val := v_1 mem := v_2 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWstore) @@ -3246,8 +3302,10 @@ func rewriteValueLOONG64_OpLOONG64MOVWstore(v *Value) bool { func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) - // cond: is32Bit(int64(off1)+off2) + // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWstorezero [off1+int32(off2)] {sym} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -3258,7 +3316,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool { off2 := auxIntToInt64(v_0.AuxInt) ptr := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1) + off2)) { + if !(is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWstorezero) @@ -3268,7 +3326,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool { return true } // match: (MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWstorezero [off1+int32(off2)] {mergeSym(sym1,sym2)} ptr mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -3280,7 +3338,7 @@ func rewriteValueLOONG64_OpLOONG64MOVWstorezero(v *Value) bool { sym2 := auxToSym(v_0.Aux) ptr := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (ptr.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpLOONG64MOVWstorezero) @@ -7570,6 +7628,7 @@ func rewriteValueLOONG64_OpZero(v *Value) bool { return false } func rewriteBlockLOONG64(b *Block) bool { + typ := &b.Func.Config.Types switch b.Kind { case BlockLOONG64EQ: // match: (EQ (FPFlagTrue cmp) yes no) @@ -7769,10 +7828,12 @@ func rewriteBlockLOONG64(b *Block) bool { } case BlockIf: // match: (If cond yes no) - // result: (NE cond yes no) + // result: (NE (MOVBUreg cond) yes no) for { cond := b.Controls[0] - b.resetWithControl(BlockLOONG64NE, cond) + v0 := b.NewValue0(cond.Pos, OpLOONG64MOVBUreg, typ.UInt64) + v0.AddArg(cond) + b.resetWithControl(BlockLOONG64NE, v0) return true } case BlockLOONG64LEZ: diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go index 6009c41f2d..52ddca1c7d 100644 --- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go +++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go @@ -6260,20 +6260,6 @@ func rewriteValueRISCV64_OpRISCV64SNEZ(v *Value) bool { func rewriteValueRISCV64_OpRISCV64SRA(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SRA (MOVWreg x) y) - // result: (SRAW x y) - for { - t := v.Type - if v_0.Op != OpRISCV64MOVWreg { - break - } - x := v_0.Args[0] - y := v_1 - v.reset(OpRISCV64SRAW) - v.Type = t - v.AddArg2(x, y) - return true - } // match: (SRA x (MOVDconst [val])) // result: (SRAI [int64(val&63)] x) for { @@ -6291,8 +6277,10 @@ func rewriteValueRISCV64_OpRISCV64SRA(v *Value) bool { } func rewriteValueRISCV64_OpRISCV64SRAI(v *Value) bool { v_0 := v.Args[0] + b := v.Block // match: (SRAI [x] (MOVWreg y)) - // result: (SRAIW [int64(x&31)] y) + // cond: x >= 0 && x <= 31 + // result: (SRAIW [int64(x)] y) for { t := v.Type x := auxIntToInt64(v.AuxInt) @@ -6300,9 +6288,71 @@ func rewriteValueRISCV64_OpRISCV64SRAI(v *Value) bool { break } y := v_0.Args[0] + if !(x >= 0 && x <= 31) { + break + } v.reset(OpRISCV64SRAIW) v.Type = t - v.AuxInt = int64ToAuxInt(int64(x & 31)) + v.AuxInt = int64ToAuxInt(int64(x)) + v.AddArg(y) + return true + } + // match: (SRAI [x] (MOVBreg y)) + // cond: x >= 8 + // result: (SRAI [63] (SLLI [56] y)) + for { + t := v.Type + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVBreg { + break + } + y := v_0.Args[0] + if !(x >= 8) { + break + } + v.reset(OpRISCV64SRAI) + v.AuxInt = int64ToAuxInt(63) + v0 := b.NewValue0(v.Pos, OpRISCV64SLLI, t) + v0.AuxInt = int64ToAuxInt(56) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (SRAI [x] (MOVHreg y)) + // cond: x >= 16 + // result: (SRAI [63] (SLLI [48] y)) + for { + t := v.Type + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVHreg { + break + } + y := v_0.Args[0] + if !(x >= 16) { + break + } + v.reset(OpRISCV64SRAI) + v.AuxInt = int64ToAuxInt(63) + v0 := b.NewValue0(v.Pos, OpRISCV64SLLI, t) + v0.AuxInt = int64ToAuxInt(48) + v0.AddArg(y) + v.AddArg(v0) + return true + } + // match: (SRAI [x] (MOVWreg y)) + // cond: x >= 32 + // result: (SRAIW [31] y) + for { + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVWreg { + break + } + y := v_0.Args[0] + if !(x >= 32) { + break + } + v.reset(OpRISCV64SRAIW) + v.AuxInt = int64ToAuxInt(31) v.AddArg(y) return true } @@ -6341,20 +6391,6 @@ func rewriteValueRISCV64_OpRISCV64SRAW(v *Value) bool { func rewriteValueRISCV64_OpRISCV64SRL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (SRL (MOVWUreg x) y) - // result: (SRLW x y) - for { - t := v.Type - if v_0.Op != OpRISCV64MOVWUreg { - break - } - x := v_0.Args[0] - y := v_1 - v.reset(OpRISCV64SRLW) - v.Type = t - v.AddArg2(x, y) - return true - } // match: (SRL x (MOVDconst [val])) // result: (SRLI [int64(val&63)] x) for { @@ -6373,7 +6409,8 @@ func rewriteValueRISCV64_OpRISCV64SRL(v *Value) bool { func rewriteValueRISCV64_OpRISCV64SRLI(v *Value) bool { v_0 := v.Args[0] // match: (SRLI [x] (MOVWUreg y)) - // result: (SRLIW [int64(x&31)] y) + // cond: x >= 0 && x <= 31 + // result: (SRLIW [int64(x)] y) for { t := v.Type x := auxIntToInt64(v.AuxInt) @@ -6381,12 +6418,66 @@ func rewriteValueRISCV64_OpRISCV64SRLI(v *Value) bool { break } y := v_0.Args[0] + if !(x >= 0 && x <= 31) { + break + } v.reset(OpRISCV64SRLIW) v.Type = t - v.AuxInt = int64ToAuxInt(int64(x & 31)) + v.AuxInt = int64ToAuxInt(int64(x)) v.AddArg(y) return true } + // match: (SRLI [x] (MOVBUreg y)) + // cond: x >= 8 + // result: (MOVDconst [0]) + for { + t := v.Type + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVBUreg { + break + } + if !(x >= 8) { + break + } + v.reset(OpRISCV64MOVDconst) + v.Type = t + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SRLI [x] (MOVHUreg y)) + // cond: x >= 16 + // result: (MOVDconst [0]) + for { + t := v.Type + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVHUreg { + break + } + if !(x >= 16) { + break + } + v.reset(OpRISCV64MOVDconst) + v.Type = t + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (SRLI [x] (MOVWUreg y)) + // cond: x >= 32 + // result: (MOVDconst [0]) + for { + t := v.Type + x := auxIntToInt64(v.AuxInt) + if v_0.Op != OpRISCV64MOVWUreg { + break + } + if !(x >= 32) { + break + } + v.reset(OpRISCV64MOVDconst) + v.Type = t + v.AuxInt = int64ToAuxInt(0) + return true + } // match: (SRLI [x] (MOVDconst [y])) // result: (MOVDconst [int64(uint64(y) >> uint32(x))]) for { @@ -7035,7 +7126,7 @@ func rewriteValueRISCV64_OpRsh32Ux16(v *Value) bool { typ := &b.Func.Config.Types // match: (Rsh32Ux16 x y) // cond: !shiftIsBounded(v) - // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] (ZeroExt16to64 y)))) + // result: (AND (SRLW x y) (Neg32 (SLTIU [32] (ZeroExt16to64 y)))) for { t := v.Type x := v_0 @@ -7044,33 +7135,29 @@ func rewriteValueRISCV64_OpRsh32Ux16(v *Value) bool { break } v.reset(OpRISCV64AND) - v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(x) - v0.AddArg2(v1, y) - v2 := b.NewValue0(v.Pos, OpNeg32, t) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) - v3.AuxInt = int64ToAuxInt(32) - v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v4.AddArg(y) - v3.AddArg(v4) + v0 := b.NewValue0(v.Pos, OpRISCV64SRLW, t) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpNeg32, t) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) + v2.AuxInt = int64ToAuxInt(32) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) v2.AddArg(v3) - v.AddArg2(v0, v2) + v1.AddArg(v2) + v.AddArg2(v0, v1) return true } // match: (Rsh32Ux16 x y) // cond: shiftIsBounded(v) - // result: (SRL (ZeroExt32to64 x) y) + // result: (SRLW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRL) - v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRLW) + v.AddArg2(x, y) return true } return false @@ -7082,7 +7169,7 @@ func rewriteValueRISCV64_OpRsh32Ux32(v *Value) bool { typ := &b.Func.Config.Types // match: (Rsh32Ux32 x y) // cond: !shiftIsBounded(v) - // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] (ZeroExt32to64 y)))) + // result: (AND (SRLW x y) (Neg32 (SLTIU [32] (ZeroExt32to64 y)))) for { t := v.Type x := v_0 @@ -7091,33 +7178,29 @@ func rewriteValueRISCV64_OpRsh32Ux32(v *Value) bool { break } v.reset(OpRISCV64AND) - v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(x) - v0.AddArg2(v1, y) - v2 := b.NewValue0(v.Pos, OpNeg32, t) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) - v3.AuxInt = int64ToAuxInt(32) - v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v4.AddArg(y) - v3.AddArg(v4) + v0 := b.NewValue0(v.Pos, OpRISCV64SRLW, t) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpNeg32, t) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) + v2.AuxInt = int64ToAuxInt(32) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) v2.AddArg(v3) - v.AddArg2(v0, v2) + v1.AddArg(v2) + v.AddArg2(v0, v1) return true } // match: (Rsh32Ux32 x y) // cond: shiftIsBounded(v) - // result: (SRL (ZeroExt32to64 x) y) + // result: (SRLW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRL) - v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRLW) + v.AddArg2(x, y) return true } return false @@ -7126,10 +7209,9 @@ func rewriteValueRISCV64_OpRsh32Ux64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - typ := &b.Func.Config.Types // match: (Rsh32Ux64 x y) // cond: !shiftIsBounded(v) - // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] y))) + // result: (AND (SRLW x y) (Neg32 (SLTIU [32] y))) for { t := v.Type x := v_0 @@ -7138,31 +7220,27 @@ func rewriteValueRISCV64_OpRsh32Ux64(v *Value) bool { break } v.reset(OpRISCV64AND) - v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(x) - v0.AddArg2(v1, y) - v2 := b.NewValue0(v.Pos, OpNeg32, t) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) - v3.AuxInt = int64ToAuxInt(32) - v3.AddArg(y) - v2.AddArg(v3) - v.AddArg2(v0, v2) + v0 := b.NewValue0(v.Pos, OpRISCV64SRLW, t) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpNeg32, t) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) + v2.AuxInt = int64ToAuxInt(32) + v2.AddArg(y) + v1.AddArg(v2) + v.AddArg2(v0, v1) return true } // match: (Rsh32Ux64 x y) // cond: shiftIsBounded(v) - // result: (SRL (ZeroExt32to64 x) y) + // result: (SRLW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRL) - v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRLW) + v.AddArg2(x, y) return true } return false @@ -7174,7 +7252,7 @@ func rewriteValueRISCV64_OpRsh32Ux8(v *Value) bool { typ := &b.Func.Config.Types // match: (Rsh32Ux8 x y) // cond: !shiftIsBounded(v) - // result: (AND (SRL (ZeroExt32to64 x) y) (Neg32 (SLTIU [32] (ZeroExt8to64 y)))) + // result: (AND (SRLW x y) (Neg32 (SLTIU [32] (ZeroExt8to64 y)))) for { t := v.Type x := v_0 @@ -7183,33 +7261,29 @@ func rewriteValueRISCV64_OpRsh32Ux8(v *Value) bool { break } v.reset(OpRISCV64AND) - v0 := b.NewValue0(v.Pos, OpRISCV64SRL, t) - v1 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v1.AddArg(x) - v0.AddArg2(v1, y) - v2 := b.NewValue0(v.Pos, OpNeg32, t) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) - v3.AuxInt = int64ToAuxInt(32) - v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v4.AddArg(y) - v3.AddArg(v4) + v0 := b.NewValue0(v.Pos, OpRISCV64SRLW, t) + v0.AddArg2(x, y) + v1 := b.NewValue0(v.Pos, OpNeg32, t) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, t) + v2.AuxInt = int64ToAuxInt(32) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) v2.AddArg(v3) - v.AddArg2(v0, v2) + v1.AddArg(v2) + v.AddArg2(v0, v1) return true } // match: (Rsh32Ux8 x y) // cond: shiftIsBounded(v) - // result: (SRL (ZeroExt32to64 x) y) + // result: (SRLW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRL) - v0 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRLW) + v.AddArg2(x, y) return true } return false @@ -7221,7 +7295,7 @@ func rewriteValueRISCV64_OpRsh32x16(v *Value) bool { typ := &b.Func.Config.Types // match: (Rsh32x16 x y) // cond: !shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] (ZeroExt16to64 y))))) + // result: (SRAW x (OR y (ADDI [-1] (SLTIU [32] (ZeroExt16to64 y))))) for { t := v.Type x := v_0 @@ -7229,36 +7303,32 @@ func rewriteValueRISCV64_OpRsh32x16(v *Value) bool { if !(!shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) + v.reset(OpRISCV64SRAW) v.Type = t - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v1 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) - v2 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) - v2.AuxInt = int64ToAuxInt(-1) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) - v3.AuxInt = int64ToAuxInt(32) - v4 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) - v4.AddArg(y) - v3.AddArg(v4) + v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) + v1 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) + v1.AuxInt = int64ToAuxInt(-1) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) + v2.AuxInt = int64ToAuxInt(32) + v3 := b.NewValue0(v.Pos, OpZeroExt16to64, typ.UInt64) + v3.AddArg(y) v2.AddArg(v3) - v1.AddArg2(y, v2) - v.AddArg2(v0, v1) + v1.AddArg(v2) + v0.AddArg2(y, v1) + v.AddArg2(x, v0) return true } // match: (Rsh32x16 x y) // cond: shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) y) + // result: (SRAW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRAW) + v.AddArg2(x, y) return true } return false @@ -7270,7 +7340,7 @@ func rewriteValueRISCV64_OpRsh32x32(v *Value) bool { typ := &b.Func.Config.Types // match: (Rsh32x32 x y) // cond: !shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] (ZeroExt32to64 y))))) + // result: (SRAW x (OR y (ADDI [-1] (SLTIU [32] (ZeroExt32to64 y))))) for { t := v.Type x := v_0 @@ -7278,36 +7348,32 @@ func rewriteValueRISCV64_OpRsh32x32(v *Value) bool { if !(!shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) + v.reset(OpRISCV64SRAW) v.Type = t - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v1 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) - v2 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) - v2.AuxInt = int64ToAuxInt(-1) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) - v3.AuxInt = int64ToAuxInt(32) - v4 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) - v4.AddArg(y) - v3.AddArg(v4) + v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) + v1 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) + v1.AuxInt = int64ToAuxInt(-1) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) + v2.AuxInt = int64ToAuxInt(32) + v3 := b.NewValue0(v.Pos, OpZeroExt32to64, typ.UInt64) + v3.AddArg(y) v2.AddArg(v3) - v1.AddArg2(y, v2) - v.AddArg2(v0, v1) + v1.AddArg(v2) + v0.AddArg2(y, v1) + v.AddArg2(x, v0) return true } // match: (Rsh32x32 x y) // cond: shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) y) + // result: (SRAW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRAW) + v.AddArg2(x, y) return true } return false @@ -7316,10 +7382,9 @@ func rewriteValueRISCV64_OpRsh32x64(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - typ := &b.Func.Config.Types // match: (Rsh32x64 x y) // cond: !shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] y)))) + // result: (SRAW x (OR y (ADDI [-1] (SLTIU [32] y)))) for { t := v.Type x := v_0 @@ -7327,34 +7392,30 @@ func rewriteValueRISCV64_OpRsh32x64(v *Value) bool { if !(!shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) + v.reset(OpRISCV64SRAW) v.Type = t - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v1 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) - v2 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) - v2.AuxInt = int64ToAuxInt(-1) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) - v3.AuxInt = int64ToAuxInt(32) - v3.AddArg(y) - v2.AddArg(v3) - v1.AddArg2(y, v2) - v.AddArg2(v0, v1) + v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) + v1 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) + v1.AuxInt = int64ToAuxInt(-1) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) + v2.AuxInt = int64ToAuxInt(32) + v2.AddArg(y) + v1.AddArg(v2) + v0.AddArg2(y, v1) + v.AddArg2(x, v0) return true } // match: (Rsh32x64 x y) // cond: shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) y) + // result: (SRAW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRAW) + v.AddArg2(x, y) return true } return false @@ -7366,7 +7427,7 @@ func rewriteValueRISCV64_OpRsh32x8(v *Value) bool { typ := &b.Func.Config.Types // match: (Rsh32x8 x y) // cond: !shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) (OR y (ADDI [-1] (SLTIU [32] (ZeroExt8to64 y))))) + // result: (SRAW x (OR y (ADDI [-1] (SLTIU [32] (ZeroExt8to64 y))))) for { t := v.Type x := v_0 @@ -7374,36 +7435,32 @@ func rewriteValueRISCV64_OpRsh32x8(v *Value) bool { if !(!shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) + v.reset(OpRISCV64SRAW) v.Type = t - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v1 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) - v2 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) - v2.AuxInt = int64ToAuxInt(-1) - v3 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) - v3.AuxInt = int64ToAuxInt(32) - v4 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) - v4.AddArg(y) - v3.AddArg(v4) + v0 := b.NewValue0(v.Pos, OpRISCV64OR, y.Type) + v1 := b.NewValue0(v.Pos, OpRISCV64ADDI, y.Type) + v1.AuxInt = int64ToAuxInt(-1) + v2 := b.NewValue0(v.Pos, OpRISCV64SLTIU, y.Type) + v2.AuxInt = int64ToAuxInt(32) + v3 := b.NewValue0(v.Pos, OpZeroExt8to64, typ.UInt64) + v3.AddArg(y) v2.AddArg(v3) - v1.AddArg2(y, v2) - v.AddArg2(v0, v1) + v1.AddArg(v2) + v0.AddArg2(y, v1) + v.AddArg2(x, v0) return true } // match: (Rsh32x8 x y) // cond: shiftIsBounded(v) - // result: (SRA (SignExt32to64 x) y) + // result: (SRAW x y) for { x := v_0 y := v_1 if !(shiftIsBounded(v)) { break } - v.reset(OpRISCV64SRA) - v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64) - v0.AddArg(x) - v.AddArg2(v0, y) + v.reset(OpRISCV64SRAW) + v.AddArg2(x, y) return true } return false diff --git a/src/cmd/compile/internal/ssa/sccp.go b/src/cmd/compile/internal/ssa/sccp.go index 3c109548ab..77a6f50961 100644 --- a/src/cmd/compile/internal/ssa/sccp.go +++ b/src/cmd/compile/internal/ssa/sccp.go @@ -533,12 +533,19 @@ func rewireSuccessor(block *Block, constVal *Value) bool { block.ResetControls() return true case BlockJumpTable: + // Remove everything but the known taken branch. idx := int(constVal.AuxInt) - targetBlock := block.Succs[idx].b - for len(block.Succs) > 0 { - block.removeEdge(0) + if idx < 0 || idx >= len(block.Succs) { + // This can only happen in unreachable code, + // as an invariant of jump tables is that their + // input index is in range. + // See issue 64826. + return false + } + block.swapSuccessorsByIdx(0, idx) + for len(block.Succs) > 1 { + block.removeEdge(1) } - block.AddEdgeTo(targetBlock) block.Kind = BlockPlain block.Likely = BranchUnknown block.ResetControls() diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 8d1e30e1e6..c794d6ffd9 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -7133,7 +7133,7 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym { n := 0 writebyte := func(o uint8) { wOff = objw.Uint8(x, wOff, o) } - // Write one non-aggrgate arg/field/element. + // Write one non-aggregate arg/field/element. write1 := func(sz, offset int64) { if offset >= _special { writebyte(_offsetTooLarge) @@ -7578,9 +7578,9 @@ func genssa(f *ssa.Func, pp *objw.Progs) { for i, b := range f.Blocks { idToIdx[b.ID] = i } - // Note that at this moment, Prog.Pc is a sequence number; it's - // not a real PC until after assembly, so this mapping has to - // be done later. + // Register a callback that will be used later to fill in PCs into location + // lists. At the moment, Prog.Pc is a sequence number; it's not a real PC + // until after assembly, so the translation needs to be deferred. debugInfo.GetPC = func(b, v ssa.ID) int64 { switch v { case ssa.BlockStart.ID: diff --git a/src/cmd/compile/internal/test/inl_test.go b/src/cmd/compile/internal/test/inl_test.go index 6d10f6c54c..0ccc7b3761 100644 --- a/src/cmd/compile/internal/test/inl_test.go +++ b/src/cmd/compile/internal/test/inl_test.go @@ -44,7 +44,6 @@ func TestIntendedInlining(t *testing.T) { "chanbuf", "evacuated", "fastlog2", - "fastrand", "float64bits", "funcspdelta", "getm", @@ -54,6 +53,7 @@ func TestIntendedInlining(t *testing.T) { "nextslicecap", "noescape", "pcvalueCacheKey", + "rand32", "readUnaligned32", "readUnaligned64", "releasem", diff --git a/src/cmd/compile/internal/test/memcombine_test.go b/src/cmd/compile/internal/test/memcombine_test.go index c7e7a208dd..3fc4a004a3 100644 --- a/src/cmd/compile/internal/test/memcombine_test.go +++ b/src/cmd/compile/internal/test/memcombine_test.go @@ -71,3 +71,129 @@ func readUint32be(b []byte) uint64 { //go:noinline func nop() { } + +type T32 struct { + a, b uint32 +} + +//go:noinline +func (t *T32) bigEndianLoad() uint64 { + return uint64(t.a)<<32 | uint64(t.b) +} + +//go:noinline +func (t *T32) littleEndianLoad() uint64 { + return uint64(t.a) | (uint64(t.b) << 32) +} + +//go:noinline +func (t *T32) bigEndianStore(x uint64) { + t.a = uint32(x >> 32) + t.b = uint32(x) +} + +//go:noinline +func (t *T32) littleEndianStore(x uint64) { + t.a = uint32(x) + t.b = uint32(x >> 32) +} + +type T16 struct { + a, b uint16 +} + +//go:noinline +func (t *T16) bigEndianLoad() uint32 { + return uint32(t.a)<<16 | uint32(t.b) +} + +//go:noinline +func (t *T16) littleEndianLoad() uint32 { + return uint32(t.a) | (uint32(t.b) << 16) +} + +//go:noinline +func (t *T16) bigEndianStore(x uint32) { + t.a = uint16(x >> 16) + t.b = uint16(x) +} + +//go:noinline +func (t *T16) littleEndianStore(x uint32) { + t.a = uint16(x) + t.b = uint16(x >> 16) +} + +type T8 struct { + a, b uint8 +} + +//go:noinline +func (t *T8) bigEndianLoad() uint16 { + return uint16(t.a)<<8 | uint16(t.b) +} + +//go:noinline +func (t *T8) littleEndianLoad() uint16 { + return uint16(t.a) | (uint16(t.b) << 8) +} + +//go:noinline +func (t *T8) bigEndianStore(x uint16) { + t.a = uint8(x >> 8) + t.b = uint8(x) +} + +//go:noinline +func (t *T8) littleEndianStore(x uint16) { + t.a = uint8(x) + t.b = uint8(x >> 8) +} + +func TestIssue64468(t *testing.T) { + t32 := T32{1, 2} + if got, want := t32.bigEndianLoad(), uint64(1<<32+2); got != want { + t.Errorf("T32.bigEndianLoad got %x want %x\n", got, want) + } + if got, want := t32.littleEndianLoad(), uint64(1+2<<32); got != want { + t.Errorf("T32.littleEndianLoad got %x want %x\n", got, want) + } + t16 := T16{1, 2} + if got, want := t16.bigEndianLoad(), uint32(1<<16+2); got != want { + t.Errorf("T16.bigEndianLoad got %x want %x\n", got, want) + } + if got, want := t16.littleEndianLoad(), uint32(1+2<<16); got != want { + t.Errorf("T16.littleEndianLoad got %x want %x\n", got, want) + } + t8 := T8{1, 2} + if got, want := t8.bigEndianLoad(), uint16(1<<8+2); got != want { + t.Errorf("T8.bigEndianLoad got %x want %x\n", got, want) + } + if got, want := t8.littleEndianLoad(), uint16(1+2<<8); got != want { + t.Errorf("T8.littleEndianLoad got %x want %x\n", got, want) + } + t32.bigEndianStore(1<<32 + 2) + if got, want := t32, (T32{1, 2}); got != want { + t.Errorf("T32.bigEndianStore got %x want %x\n", got, want) + } + t32.littleEndianStore(1<<32 + 2) + if got, want := t32, (T32{2, 1}); got != want { + t.Errorf("T32.littleEndianStore got %x want %x\n", got, want) + } + t16.bigEndianStore(1<<16 + 2) + if got, want := t16, (T16{1, 2}); got != want { + t.Errorf("T16.bigEndianStore got %x want %x\n", got, want) + } + t16.littleEndianStore(1<<16 + 2) + if got, want := t16, (T16{2, 1}); got != want { + t.Errorf("T16.littleEndianStore got %x want %x\n", got, want) + } + t8.bigEndianStore(1<<8 + 2) + if got, want := t8, (T8{1, 2}); got != want { + t.Errorf("T8.bigEndianStore got %x want %x\n", got, want) + } + t8.littleEndianStore(1<<8 + 2) + if got, want := t8, (T8{2, 1}); got != want { + t.Errorf("T8.littleEndianStore got %x want %x\n", got, want) + } +} diff --git a/src/cmd/compile/internal/test/testdata/arith_test.go b/src/cmd/compile/internal/test/testdata/arith_test.go index 2b8cd9fad3..cd7b5bc2c4 100644 --- a/src/cmd/compile/internal/test/testdata/arith_test.go +++ b/src/cmd/compile/internal/test/testdata/arith_test.go @@ -268,6 +268,70 @@ func testOverflowConstShift(t *testing.T) { } } +//go:noinline +func rsh64x64ConstOverflow8(x int8) int64 { + return int64(x) >> 9 +} + +//go:noinline +func rsh64x64ConstOverflow16(x int16) int64 { + return int64(x) >> 17 +} + +//go:noinline +func rsh64x64ConstOverflow32(x int32) int64 { + return int64(x) >> 33 +} + +func testArithRightShiftConstOverflow(t *testing.T) { + allSet := int64(-1) + if got, want := rsh64x64ConstOverflow8(0x7f), int64(0); got != want { + t.Errorf("rsh64x64ConstOverflow8 failed: got %v, want %v", got, want) + } + if got, want := rsh64x64ConstOverflow16(0x7fff), int64(0); got != want { + t.Errorf("rsh64x64ConstOverflow16 failed: got %v, want %v", got, want) + } + if got, want := rsh64x64ConstOverflow32(0x7ffffff), int64(0); got != want { + t.Errorf("rsh64x64ConstOverflow32 failed: got %v, want %v", got, want) + } + if got, want := rsh64x64ConstOverflow8(int8(-1)), allSet; got != want { + t.Errorf("rsh64x64ConstOverflow8 failed: got %v, want %v", got, want) + } + if got, want := rsh64x64ConstOverflow16(int16(-1)), allSet; got != want { + t.Errorf("rsh64x64ConstOverflow16 failed: got %v, want %v", got, want) + } + if got, want := rsh64x64ConstOverflow32(int32(-1)), allSet; got != want { + t.Errorf("rsh64x64ConstOverflow32 failed: got %v, want %v", got, want) + } +} + +//go:noinline +func rsh64Ux64ConstOverflow8(x uint8) uint64 { + return uint64(x) >> 9 +} + +//go:noinline +func rsh64Ux64ConstOverflow16(x uint16) uint64 { + return uint64(x) >> 17 +} + +//go:noinline +func rsh64Ux64ConstOverflow32(x uint32) uint64 { + return uint64(x) >> 33 +} + +func testRightShiftConstOverflow(t *testing.T) { + if got, want := rsh64Ux64ConstOverflow8(0xff), uint64(0); got != want { + t.Errorf("rsh64Ux64ConstOverflow8 failed: got %v, want %v", got, want) + } + if got, want := rsh64Ux64ConstOverflow16(0xffff), uint64(0); got != want { + t.Errorf("rsh64Ux64ConstOverflow16 failed: got %v, want %v", got, want) + } + if got, want := rsh64Ux64ConstOverflow32(0xffffffff), uint64(0); got != want { + t.Errorf("rsh64Ux64ConstOverflow32 failed: got %v, want %v", got, want) + } +} + // test64BitConstMult tests that rewrite rules don't fold 64 bit constants // into multiply instructions. func test64BitConstMult(t *testing.T) { @@ -918,6 +982,8 @@ func TestArithmetic(t *testing.T) { testShiftCX(t) testSubConst(t) testOverflowConstShift(t) + testArithRightShiftConstOverflow(t) + testRightShiftConstOverflow(t) testArithConstShift(t) testArithRshConst(t) testLargeConst(t) diff --git a/src/cmd/compile/internal/test/testdata/ctl_test.go b/src/cmd/compile/internal/test/testdata/ctl_test.go index ff3a1609c5..501f79eee1 100644 --- a/src/cmd/compile/internal/test/testdata/ctl_test.go +++ b/src/cmd/compile/internal/test/testdata/ctl_test.go @@ -70,7 +70,6 @@ func switch_ssa(a int) int { ret += 1 } return ret - } func fallthrough_ssa(a int) int { @@ -92,7 +91,6 @@ func fallthrough_ssa(a int) int { ret++ } return ret - } func testFallthrough(t *testing.T) { diff --git a/src/cmd/compile/internal/typecheck/_builtin/runtime.go b/src/cmd/compile/internal/typecheck/_builtin/runtime.go index f27a773a88..421152967c 100644 --- a/src/cmd/compile/internal/typecheck/_builtin/runtime.go +++ b/src/cmd/compile/internal/typecheck/_builtin/runtime.go @@ -122,7 +122,7 @@ func panicrangeexit() // defer in range over func func deferrangefunc() interface{} -func fastrand() uint32 +func rand32() uint32 // *byte is really *runtime.Type func makemap64(mapType *byte, hint int64, mapbuf *any) (hmap map[any]any) diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go index 142fc26d2e..09f60c68c0 100644 --- a/src/cmd/compile/internal/typecheck/builtin.go +++ b/src/cmd/compile/internal/typecheck/builtin.go @@ -104,7 +104,7 @@ var runtimeDecls = [...]struct { {"efaceeq", funcTag, 72}, {"panicrangeexit", funcTag, 9}, {"deferrangefunc", funcTag, 73}, - {"fastrand", funcTag, 74}, + {"rand32", funcTag, 74}, {"makemap64", funcTag, 76}, {"makemap", funcTag, 77}, {"makemap_small", funcTag, 78}, diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go index e54d5256e6..8d792485d8 100644 --- a/src/cmd/compile/internal/typecheck/stmt.go +++ b/src/cmd/compile/internal/typecheck/stmt.go @@ -198,32 +198,36 @@ func tcFor(n *ir.ForStmt) ir.Node { return n } -// tcGoDefer typechecks an OGO/ODEFER statement. +// tcGoDefer typechecks (normalizes) an OGO/ODEFER statement. +func tcGoDefer(n *ir.GoDeferStmt) { + call := normalizeGoDeferCall(n.Pos(), n.Op(), n.Call, n.PtrInit()) + call.GoDefer = true + n.Call = call +} + +// normalizeGoDeferCall normalizes call into a normal function call +// with no arguments and no results, suitable for use in an OGO/ODEFER +// statement. // -// Really, this means normalizing the statement to always use a simple -// function call with no arguments and no results. For example, it -// rewrites: +// For example, it normalizes: // -// defer f(x, y) +// f(x, y) // // into: // -// x1, y1 := x, y -// defer func() { f(x1, y1) }() -func tcGoDefer(n *ir.GoDeferStmt) { - call := n.Call - - init := n.PtrInit() +// x1, y1 := x, y // added to init +// func() { f(x1, y1) }() // result +func normalizeGoDeferCall(pos src.XPos, op ir.Op, call ir.Node, init *ir.Nodes) *ir.CallExpr { init.Append(ir.TakeInit(call)...) - if call, ok := n.Call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC { + if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC { if sig := call.Fun.Type(); sig.NumParams()+sig.NumResults() == 0 { - return // already in normal form + return call // already in normal form } } // Create a new wrapper function without parameters or results. - wrapperFn := ir.NewClosureFunc(n.Pos(), n.Pos(), n.Op(), types.NewSignature(nil, nil, nil), ir.CurFunc, Target) + wrapperFn := ir.NewClosureFunc(pos, pos, op, types.NewSignature(nil, nil, nil), ir.CurFunc, Target) wrapperFn.DeclareParams(true) wrapperFn.SetWrapper(true) @@ -372,8 +376,8 @@ func tcGoDefer(n *ir.GoDeferStmt) { // evaluate there. wrapperFn.Body = []ir.Node{call} - // Finally, rewrite the go/defer statement to call the wrapper. - n.Call = Call(call.Pos(), wrapperFn.OClosure, nil, false) + // Finally, construct a call to the wrapper. + return Call(call.Pos(), wrapperFn.OClosure, nil, false).(*ir.CallExpr) } // tcIf typechecks an OIF node. diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index 6628174428..bb02d9198e 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -268,6 +268,15 @@ type Info struct { // scope, the function scopes are embedded in the file scope of the file // containing the function declaration. // + // The Scope of a function contains the declarations of any + // type parameters, parameters, and named results, plus any + // local declarations in the body block. + // It is coextensive with the complete extent of the + // function's syntax ([*ast.FuncDecl] or [*ast.FuncLit]). + // The Scopes mapping does not contain an entry for the + // function body ([*ast.BlockStmt]); the function's scope is + // associated with the [*ast.FuncType]. + // // The following node types may appear in Scopes: // // *syntax.File diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 56cddf6b29..c70d914453 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -1892,12 +1892,12 @@ const Pi = 3.1415 type T struct{} var Y, _ = lib.X, X -func F(){ +func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ { const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/ type /*t=undef*/ t /*t=typename:14*/ *t print(Y) /*Y=var:10*/ x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y - var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F + var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F var a []int for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x } @@ -1916,6 +1916,10 @@ func F(){ println(int) default /*int=var:31*/ : } + + _ = param1 + _ = res1 + return } /*main=undef*/ ` @@ -1981,7 +1985,29 @@ func F(){ _, gotObj := inner.LookupParent(id.Value, id.Pos()) if gotObj != wantObj { - t.Errorf("%s: got %v, want %v", id.Pos(), gotObj, wantObj) + // Print the scope tree of mainScope in case of error. + var printScopeTree func(indent string, s *Scope) + printScopeTree = func(indent string, s *Scope) { + t.Logf("%sscope %s %v-%v = %v", + indent, + ScopeComment(s), + s.Pos(), + s.End(), + s.Names()) + for i := range s.NumChildren() { + printScopeTree(indent+" ", s.Child(i)) + } + } + printScopeTree("", mainScope) + + t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]", + id.Pos(), + ScopeComment(inner), + id.Value, + id.Pos(), + gotObj, + wantObj, + ObjectScopePos(wantObj)) continue } } diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go index 079802b0b0..338a114ff9 100644 --- a/src/cmd/compile/internal/types2/assignments.go +++ b/src/cmd/compile/internal/types2/assignments.go @@ -244,8 +244,15 @@ func (check *Checker) assignVar(lhs, rhs syntax.Expr, x *operand) { } if x == nil { + var target *target + // avoid calling syntax.String if not needed + if T != nil { + if _, ok := under(T).(*Signature); ok { + target = newTarget(T, syntax.String(lhs)) + } + } x = new(operand) - check.expr(T, x, rhs) + check.expr(target, x, rhs) } context := "assignment" @@ -369,7 +376,11 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt sy if l == r && !isCall { var x operand for i, lhs := range lhs { - check.expr(lhs.typ, &x, orig_rhs[i]) + desc := lhs.name + if returnStmt != nil && desc == "" { + desc = "result variable" + } + check.expr(newTarget(lhs.typ, desc), &x, orig_rhs[i]) check.initVar(lhs, &x, context) } return diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index 01b8e46304..60f6d7f415 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -799,7 +799,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( // unsafe.Slice(ptr *T, len IntegerType) []T check.verifyVersionf(call.Fun, go1_17, "unsafe.Slice") - ptr, _ := under(x.typ).(*Pointer) // TODO(gri) should this be coreType rather than under? + ptr, _ := coreType(x.typ).(*Pointer) if ptr == nil { check.errorf(x, InvalidUnsafeSlice, invalidArg+"%s is not a pointer", x) return @@ -820,7 +820,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( // unsafe.SliceData(slice []T) *T check.verifyVersionf(call.Fun, go1_20, "unsafe.SliceData") - slice, _ := under(x.typ).(*Slice) // TODO(gri) should this be coreType rather than under? + slice, _ := coreType(x.typ).(*Slice) if slice == nil { check.errorf(x, InvalidUnsafeSliceData, invalidArg+"%s is not a slice", x) return @@ -954,7 +954,7 @@ func hasVarSize(t Type, seen map[*Named]bool) (varSized bool) { } // applyTypeFunc applies f to x. If x is a type parameter, -// the result is a type parameter constrained by an new +// the result is a type parameter constrained by a new // interface bound. The type bounds for that interface // are computed by applying f to each of the type bounds // of x. If any of these applications of f return nil, diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 439f515265..db7d86e3d3 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -16,8 +16,8 @@ import ( // funcInst type-checks a function instantiation. // The incoming x must be a generic function. // If inst != nil, it provides some or all of the type arguments (inst.Index). -// If target type tsig != nil, the signature may be used to infer missing type -// arguments of x, if any. At least one of tsig or inst must be provided. +// If target != nil, it may be used to infer missing type arguments of x, if any. +// At least one of T or inst must be provided. // // There are two modes of operation: // @@ -32,8 +32,8 @@ import ( // // If an error (other than a version error) occurs in any case, it is reported // and x.mode is set to invalid. -func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst *syntax.IndexExpr, infer bool) ([]Type, []syntax.Expr) { - assert(tsig != nil || inst != nil) +func (check *Checker) funcInst(T *target, pos syntax.Pos, x *operand, inst *syntax.IndexExpr, infer bool) ([]Type, []syntax.Expr) { + assert(T != nil || inst != nil) var instErrPos poser if inst != nil { @@ -87,7 +87,8 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst // var args []*operand var params []*Var - if tsig != nil && sig.tparams != nil { + var reverse bool + if T != nil && sig.tparams != nil { if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) { if inst != nil { check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment") @@ -100,15 +101,16 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst // The type of the argument operand is tsig, which is the type of the LHS in an assignment // or the result type in a return statement. Create a pseudo-expression for that operand // that makes sense when reported in error messages from infer, below. - expr := syntax.NewName(x.Pos(), "variable in assignment") - args = []*operand{{mode: value, expr: expr, typ: tsig}} + expr := syntax.NewName(x.Pos(), T.desc) + args = []*operand{{mode: value, expr: expr, typ: T.sig}} + reverse = true } // Rename type parameters to avoid problems with recursive instantiations. // Note that NewTuple(params...) below is (*Tuple)(nil) if len(params) == 0, as desired. tparams, params2 := check.renameTParams(pos, sig.TypeParams().list(), NewTuple(params...)) - targs = check.infer(pos, tparams, targs, params2.(*Tuple), args) + targs = check.infer(pos, tparams, targs, params2.(*Tuple), args, reverse) if targs == nil { // error was already reported x.mode = invalid @@ -608,7 +610,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T // infer missing type arguments of callee and function arguments if len(tparams) > 0 { - targs = check.infer(call.Pos(), tparams, targs, sigParams, args) + targs = check.infer(call.Pos(), tparams, targs, sigParams, args, false) if targs == nil { // TODO(gri) If infer inferred the first targs[:n], consider instantiating // the call signature for better error messages/gopls behavior. diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 3abde44c71..f3e3418f4f 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -449,7 +449,7 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) { if lhs == nil || len(lhs) == 1 { assert(lhs == nil || lhs[0] == obj) var x operand - check.expr(obj.typ, &x, init) + check.expr(newTarget(obj.typ, obj.name), &x, init) check.initVar(obj, &x, "variable declaration") return } @@ -570,8 +570,11 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Fiel // Declare type parameters up-front. // The scope of type parameters starts at the beginning of the type parameter // list (so we can have mutually recursive parameterized type bounds). - for i, f := range list { - tparams[i] = check.declareTypeParam(f.Name) + if len(list) > 0 { + scopePos := list[0].Pos() + for i, f := range list { + tparams[i] = check.declareTypeParam(f.Name, scopePos) + } } // Set the type parameters before collecting the type constraints because @@ -628,7 +631,7 @@ func (check *Checker) bound(x syntax.Expr) Type { return check.typ(x) } -func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam { +func (check *Checker) declareTypeParam(name *syntax.Name, scopePos syntax.Pos) *TypeParam { // Use Typ[Invalid] for the type constraint to ensure that a type // is present even if the actual constraint has not been assigned // yet. @@ -636,8 +639,8 @@ func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam { // constraints to make sure we don't rely on them if they // are not properly set yet. tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil) - tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect - check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position + tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect + check.declare(check.scope, name, tname, scopePos) return tpar } @@ -750,6 +753,11 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type) obj.color_ = saved + // Set the scope's extent to the complete "func (...) { ... }" + // so that Scope.Innermost works correctly. + sig.scope.pos = fdecl.Pos() + sig.scope.end = syntax.EndPos(fdecl) + if len(fdecl.TParamList) > 0 && fdecl.Body == nil { check.softErrorf(fdecl, BadDecl, "generic function is missing function body") } diff --git a/src/cmd/compile/internal/types2/errorcalls_test.go b/src/cmd/compile/internal/types2/errorcalls_test.go index 6153b42a34..ba4dc87b6a 100644 --- a/src/cmd/compile/internal/types2/errorcalls_test.go +++ b/src/cmd/compile/internal/types2/errorcalls_test.go @@ -1,6 +1,6 @@ // 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 ast. +// license that can be found in the LICENSE file. package types2_test diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 321b0c4762..124d9701d6 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -956,18 +956,32 @@ const ( statement ) -// TODO(gri) In rawExpr below, consider using T instead of hint and -// some sort of "operation mode" instead of allowGeneric. -// May be clearer and less error-prone. +// target represent the (signature) type and description of the LHS +// variable of an assignment, or of a function result variable. +type target struct { + sig *Signature + desc string +} + +// newTarget creates a new target for the given type and description. +// The result is nil if typ is not a signature. +func newTarget(typ Type, desc string) *target { + if typ != nil { + if sig, _ := under(typ).(*Signature); sig != nil { + return &target{sig, desc} + } + } + return nil +} // rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. -// If a non-nil target type T is given and e is a generic function -// or function call, T is used to infer the type arguments for e. +// If a non-nil target T is given and e is a generic function, +// T is used to infer the type arguments for e. // 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(T Type, x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind { +func (check *Checker) rawExpr(T *target, x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind { if check.conf.Trace { check.trace(e.Pos(), "-- expr %s", e) check.indent++ @@ -989,9 +1003,9 @@ func (check *Checker) rawExpr(T Type, x *operand, e syntax.Expr, hint Type, allo } // If x is a generic type, or a generic function whose type arguments cannot be inferred -// from a non-nil target type T, nonGeneric reports an error and invalidates x.mode and x.typ. +// from a non-nil target T, nonGeneric reports an error and invalidates x.mode and x.typ. // Otherwise it leaves x alone. -func (check *Checker) nonGeneric(T Type, x *operand) { +func (check *Checker) nonGeneric(T *target, x *operand) { if x.mode == invalid || x.mode == novalue { return } @@ -1004,10 +1018,8 @@ func (check *Checker) nonGeneric(T Type, x *operand) { case *Signature: if t.tparams != nil { if enableReverseTypeInference && T != nil { - if tsig, _ := under(T).(*Signature); tsig != nil { - check.funcInst(tsig, x.Pos(), x, nil, true) - return - } + check.funcInst(T, x.Pos(), x, nil, true) + return } what = "function" } @@ -1022,7 +1034,7 @@ func (check *Checker) nonGeneric(T Type, x *operand) { // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. // (See rawExpr for an explanation of the parameters.) -func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type) exprKind { +func (check *Checker) exprInternal(T *target, x *operand, e syntax.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was go.dev/issue/5770) x.mode = invalid @@ -1081,6 +1093,10 @@ func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type) case *syntax.FuncLit: if sig, ok := check.typ(e.Type).(*Signature); ok { + // Set the Scope's extent to the complete "func (...) {...}" + // so that Scope.Innermost works correctly. + sig.scope.pos = e.Pos() + sig.scope.end = syntax.EndPos(e) if !check.conf.IgnoreFuncBodies && e.Body != nil { // Anonymous functions are considered part of the // init expression/func declaration which contains @@ -1328,11 +1344,10 @@ func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type) case *syntax.IndexExpr: if check.indexExpr(x, e) { - var tsig *Signature - if enableReverseTypeInference && T != nil { - tsig, _ = under(T).(*Signature) + if !enableReverseTypeInference { + T = nil } - check.funcInst(tsig, e.Pos(), x, e, true) + check.funcInst(T, e.Pos(), x, e, true) } if x.mode == invalid { goto Error @@ -1543,11 +1558,11 @@ func (check *Checker) typeAssertion(e syntax.Expr, x *operand, T Type, typeSwitc } // expr typechecks expression e and initializes x with the expression value. -// If a non-nil target type T is given and e is a generic function -// or function call, T is used to infer the type arguments for e. +// If a non-nil target T is given and e is a generic function or +// a function call, T is used to infer the type arguments for e. // The result must be a single value. // If an error occurred, x.mode is set to invalid. -func (check *Checker) expr(T Type, x *operand, e syntax.Expr) { +func (check *Checker) expr(T *target, x *operand, e syntax.Expr) { check.rawExpr(T, x, e, nil, false) check.exclude(x, 1< want: + qual = "too many" + default: + return true } - return true + + msg := check.sprintf("%s type arguments for type %s: have %d, want %d", qual, name, got, want) + if check != nil { + check.error(atPos(pos), WrongTypeArgCount, msg) + return false + } + + panic(fmt.Sprintf("%v: %s", pos, msg)) } func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type, ctxt *Context) (int, error) { diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go index 95b9f94078..a8893cf6de 100644 --- a/src/cmd/compile/internal/types2/issues_test.go +++ b/src/cmd/compile/internal/types2/issues_test.go @@ -998,8 +998,81 @@ type S struct{ A } } got := S.String() - const want = "type p.S struct{p.A /* = []int */}" + const want = "type p.S struct{p.A}" if got != want { t.Fatalf("got %q; want %q", got, want) } } + +func TestIssue59831(t *testing.T) { + // Package a exports a type S with an unexported method m; + // the tests check the error messages when m is not found. + const asrc = `package a; type S struct{}; func (S) m() {}` + apkg := mustTypecheck(asrc, nil, nil) + + // Package b exports a type S with an exported method m; + // the tests check the error messages when M is not found. + const bsrc = `package b; type S struct{}; func (S) M() {}` + bpkg := mustTypecheck(bsrc, nil, nil) + + tests := []struct { + imported *Package + src, err string + }{ + // tests importing a (or nothing) + {apkg, `package a1; import "a"; var _ interface { M() } = a.S{}`, + "a.S does not implement interface{M()} (missing method M) have m() want M()"}, + + {apkg, `package a2; import "a"; var _ interface { m() } = a.S{}`, + "a.S does not implement interface{m()} (unexported method m)"}, // test for issue + + {nil, `package a3; type S struct{}; func (S) m(); var _ interface { M() } = S{}`, + "S does not implement interface{M()} (missing method M) have m() want M()"}, + + {nil, `package a4; type S struct{}; func (S) m(); var _ interface { m() } = S{}`, + ""}, // no error expected + + {nil, `package a5; type S struct{}; func (S) m(); var _ interface { n() } = S{}`, + "S does not implement interface{n()} (missing method n)"}, + + // tests importing b (or nothing) + {bpkg, `package b1; import "b"; var _ interface { m() } = b.S{}`, + "b.S does not implement interface{m()} (missing method m) have M() want m()"}, + + {bpkg, `package b2; import "b"; var _ interface { M() } = b.S{}`, + ""}, // no error expected + + {nil, `package b3; type S struct{}; func (S) M(); var _ interface { M() } = S{}`, + ""}, // no error expected + + {nil, `package b4; type S struct{}; func (S) M(); var _ interface { m() } = S{}`, + "S does not implement interface{m()} (missing method m) have M() want m()"}, + + {nil, `package b5; type S struct{}; func (S) M(); var _ interface { n() } = S{}`, + "S does not implement interface{n()} (missing method n)"}, + } + + for _, test := range tests { + // typecheck test source + conf := Config{Importer: importHelper{pkg: test.imported}} + pkg, err := typecheck(test.src, &conf, nil) + if err == nil { + if test.err != "" { + t.Errorf("package %s: got no error, want %q", pkg.Name(), test.err) + } + continue + } + if test.err == "" { + t.Errorf("package %s: got %q, want not error", pkg.Name(), err.Error()) + } + + // flatten reported error message + errmsg := strings.ReplaceAll(err.Error(), "\n", " ") + errmsg = strings.ReplaceAll(errmsg, "\t", "") + + // verify error message + if !strings.Contains(errmsg, test.err) { + t.Errorf("package %s: got %q, want %q", pkg.Name(), errmsg, test.err) + } + } +} diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 893cdb157d..bc47c15060 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -96,7 +96,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o // and missingMethod (the latter doesn't care about struct fields). // // If foldCase is true, method names are considered equal if they are equal -// with case folding. +// with case folding, irrespective of which package they are in. // // The resulting object may not be fully type-checked. func lookupFieldOrMethodImpl(T Type, addressable bool, pkg *Package, name string, foldCase bool) (obj Object, index []int, indirect bool) { @@ -343,6 +343,7 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y ok = iota notFound wrongName + unexported wrongSig ambigSel ptrRecv @@ -388,6 +389,11 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y f, _ = obj.(*Func) if f != nil { state = wrongName + if f.name == m.name { + // If the names are equal, f must be unexported + // (otherwise the package wouldn't matter). + state = unexported + } } } break @@ -436,8 +442,9 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y } case wrongName: fs, ms := check.funcString(f, false), check.funcString(m, false) - *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s", - m.Name(), fs, ms) + *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s", m.Name(), fs, ms) + case unexported: + *cause = check.sprintf("(unexported method %s)", m.Name()) case wrongSig: fs, ms := check.funcString(f, false), check.funcString(m, false) if fs == ms { @@ -445,8 +452,18 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y // Add package information to disambiguate (go.dev/issue/54258). fs, ms = check.funcString(f, true), check.funcString(m, true) } - *cause = check.sprintf("(wrong type for method %s)\n\t\thave %s\n\t\twant %s", - m.Name(), fs, ms) + if fs == ms { + // We still have "want Foo, have Foo". + // This is most likely due to different type parameters with + // the same name appearing in the instantiated signatures + // (go.dev/issue/61685). + // Rather than reporting this misleading error cause, for now + // just point out that the method signature is incorrect. + // TODO(gri) should find a good way to report the root cause + *cause = check.sprintf("(wrong type for method %s)", m.Name()) + break + } + *cause = check.sprintf("(wrong type for method %s)\n\t\thave %s\n\t\twant %s", m.Name(), fs, ms) case ambigSel: *cause = check.sprintf("(ambiguous selector %s.%s)", V, m.Name()) case ptrRecv: @@ -572,11 +589,12 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int { } // lookupMethod returns the index of and method with matching package and name, or (-1, nil). -// If foldCase is true, method names are considered equal if they are equal with case folding. +// If foldCase is true, method names are considered equal if they are equal with case folding +// and their packages are ignored (e.g., pkg1.m, pkg1.M, pkg2.m, and pkg2.M are all equal). func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) { if name != "_" { for i, m := range methods { - if (m.name == name || foldCase && strings.EqualFold(m.name, name)) && m.sameId(pkg, m.name) { + if m.sameId(pkg, name) || foldCase && strings.EqualFold(m.name, name) { return i, m } } diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index 8b896f7a90..18a64ec1a0 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -108,9 +108,12 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] // - the receiver specification acts as local declaration for its type parameters, which may be blank _, rname, rparams := check.unpackRecv(recvPar.Type, true) if len(rparams) > 0 { + // The scope of the type parameter T in "func (r T[T]) f()" + // starts after f, not at "r"; see #52038. + scopePos := ftyp.Pos() tparams := make([]*TypeParam, len(rparams)) for i, rparam := range rparams { - tparams[i] = check.declareTypeParam(rparam) + tparams[i] = check.declareTypeParam(rparam, scopePos) } sig.rparams = bindTParams(tparams) // Blank identifiers don't get declared, so naive type-checking of the @@ -167,16 +170,21 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] check.collectTypeParams(&sig.tparams, tparams) } - // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their - // declarations and then squash that scope into the parent scope (and report any redeclarations at - // that time). + // Use a temporary scope for all parameter declarations and then + // squash that scope into the parent scope (and report any + // redeclarations at that time). + // + // TODO(adonovan): now that each declaration has the correct + // scopePos, there should be no need for scope squashing. + // Audit to ensure all lookups honor scopePos and simplify. scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)") - var recvList []*Var // TODO(gri) remove the need for making a list here + scopePos := syntax.EndPos(ftyp) // all parameters' scopes start after the signature + var recvList []*Var // TODO(gri) remove the need for making a list here if recvPar != nil { - recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false) // use rewritten receiver type, if any + recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false, scopePos) // use rewritten receiver type, if any } - params, variadic := check.collectParams(scope, ftyp.ParamList, true) - results, _ := check.collectParams(scope, ftyp.ResultList, false) + params, variadic := check.collectParams(scope, ftyp.ParamList, true, scopePos) + results, _ := check.collectParams(scope, ftyp.ResultList, false, scopePos) scope.Squash(func(obj, alt Object) { var err error_ err.code = DuplicateDecl @@ -259,7 +267,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] // collectParams declares the parameters of list in scope and returns the corresponding // variable list. -func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool) (params []*Var, variadic bool) { +func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool, scopePos syntax.Pos) (params []*Var, variadic bool) { if list == nil { return } @@ -294,7 +302,7 @@ func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadic // ok to continue } par := NewParam(field.Name.Pos(), check.pkg, name, typ) - check.declare(scope, field.Name, par, scope.pos) + check.declare(scope, field.Name, par, scopePos) params = append(params, par) named = true } else { diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go index 7c14e3476e..405af78572 100644 --- a/src/cmd/compile/internal/types2/stdlib_test.go +++ b/src/cmd/compile/internal/types2/stdlib_test.go @@ -311,6 +311,7 @@ func TestStdFixed(t *testing.T) { testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"), "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore + "bug398.go", // types2 doesn't check for anonymous interface cycles (go.dev/issue/56103) "issue6889.go", // gc-specific test "issue11362.go", // canonical import path check "issue16369.go", // types2 handles this correctly - not an issue diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index e4bda49c52..a07bc9370a 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -23,10 +23,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body check.trace(body.Pos(), "-- %s: %s", name, sig) } - // set function scope extent - sig.scope.pos = body.Pos() - sig.scope.end = syntax.EndPos(body) - // save/restore current environment and set up function environment // (and use 0 indentation at function start) defer func(env environment, indent int) { @@ -865,7 +861,9 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s var key, val Type if x.mode != invalid { // Ranging over a type parameter is permitted if it has a core type. - k, v, cause, isFunc, ok := rangeKeyVal(x.typ) + k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool { + return check.allowVersion(check.pkg, x.expr, v) + }) switch { case !ok && cause != "": check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s: %s", &x, cause) @@ -968,16 +966,18 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s } // RangeKeyVal returns the key and value types for a range over typ. +// Exported for use by the compiler (does not exist in go/types). func RangeKeyVal(typ Type) (Type, Type) { - key, val, _, _, _ := rangeKeyVal(typ) + key, val, _, _, _ := rangeKeyVal(typ, nil) return key, val } // rangeKeyVal returns the key and value type produced by a range clause -// over an expression of type typ. If the range clause is not permitted, -// rangeKeyVal returns ok = false. When ok = false, rangeKeyVal may also -// return a reason in cause. -func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) { +// over an expression of type typ. +// If allowVersion != nil, it is used to check the required language version. +// If the range clause is not permitted, rangeKeyVal returns ok = false. +// When ok = false, rangeKeyVal may also return a reason in cause. +func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, cause string, isFunc, ok bool) { bad := func(cause string) (Type, Type, string, bool, bool) { return Typ[Invalid], Typ[Invalid], cause, false, false } @@ -995,6 +995,9 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) { return Typ[Int], universeRune, "", false, true // use 'rune' name } if isInteger(typ) { + if allowVersion != nil && !allowVersion(go1_22) { + return bad("requires go1.22 or later") + } return orig, nil, "", false, true } case *Array: @@ -1009,6 +1012,7 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) { } return typ.elem, nil, "", false, true case *Signature: + // TODO(gri) when this becomes enabled permanently, add version check if !buildcfg.Experiment.RangeFunc { break } diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index 0e0da0f7f6..4b410af6b7 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -331,8 +331,6 @@ func (w *typeWriter) typ(typ Type) { if w.ctxt != nil { // TODO(gri) do we need to print the alias type name, too? w.typ(Unalias(t.obj.typ)) - } else { - w.string(fmt.Sprintf(" /* = %s */", Unalias(t.obj.typ))) } default: diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index 0ee92be6ee..81adcbd9cf 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -463,7 +463,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * // errors. check.recordInstance(x, inst.TypeArgs().list(), inst) - if check.validateTArgLen(x.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) { + if check.validateTArgLen(x.Pos(), inst.obj.name, inst.TypeParams().Len(), inst.TypeArgs().Len()) { if i, err := check.verify(x.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil { // best position for error reporting pos := x.Pos() diff --git a/src/cmd/compile/internal/types2/util_test.go b/src/cmd/compile/internal/types2/util_test.go index 4cbd002355..70058aad84 100644 --- a/src/cmd/compile/internal/types2/util_test.go +++ b/src/cmd/compile/internal/types2/util_test.go @@ -7,6 +7,11 @@ package types2 -import "cmd/compile/internal/syntax" +import ( + "cmd/compile/internal/syntax" +) func CmpPos(p, q syntax.Pos) int { return cmpPos(p, q) } + +func ScopeComment(s *Scope) string { return s.comment } +func ObjectScopePos(obj Object) syntax.Pos { return obj.scopePos() } diff --git a/src/cmd/compile/internal/types2/version.go b/src/cmd/compile/internal/types2/version.go index 12c86ef9fe..5aa3c803b5 100644 --- a/src/cmd/compile/internal/types2/version.go +++ b/src/cmd/compile/internal/types2/version.go @@ -43,6 +43,7 @@ var ( go1_18 = asGoVersion("go1.18") go1_20 = asGoVersion("go1.20") go1_21 = asGoVersion("go1.21") + go1_22 = asGoVersion("go1.22") // current (deployed) Go version go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version)) diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go index afd1a326d3..fc3b858a80 100644 --- a/src/cmd/compile/internal/walk/assign.go +++ b/src/cmd/compile/internal/walk/assign.go @@ -6,6 +6,7 @@ package walk import ( "go/constant" + "internal/abi" "cmd/compile/internal/base" "cmd/compile/internal/ir" @@ -168,7 +169,7 @@ func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node { a := n.Lhs[0] var call *ir.CallExpr - if w := t.Elem().Size(); w <= zeroValSize { + if w := t.Elem().Size(); w <= abi.ZeroValSize { fn := mapfn(mapaccess2[fast], t, false) call = mkcall1(fn, fn.Type().ResultsTuple(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key) } else { diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index 90c32154b9..37143baa28 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -358,8 +358,8 @@ func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node { if n.Esc() == ir.EscNone { // Only need to initialize h.hash0 since // hmap h has been allocated on the stack already. - // h.hash0 = fastrand() - rand := mkcall("fastrand", types.Types[types.TUINT32], init) + // h.hash0 = rand32() + rand := mkcall("rand32", types.Types[types.TUINT32], init) hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, hashsym), rand)) return typecheck.ConvNop(h, t) diff --git a/src/cmd/compile/internal/walk/closure.go b/src/cmd/compile/internal/walk/closure.go index 6fc2317afb..38c6c03dc4 100644 --- a/src/cmd/compile/internal/walk/closure.go +++ b/src/cmd/compile/internal/walk/closure.go @@ -144,7 +144,7 @@ func walkClosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node { return walkExpr(cfn, init) } -// closureArgs returns a slice of expressions that an be used to +// closureArgs returns a slice of expressions that can be used to // initialize the given closure's free variables. These correspond // one-to-one with the variables in clo.Func.ClosureVars, and will be // either an ONAME node (if the variable is captured by value) or an diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go index 64d20b555e..268f793dc9 100644 --- a/src/cmd/compile/internal/walk/expr.go +++ b/src/cmd/compile/internal/walk/expr.go @@ -7,6 +7,7 @@ package walk import ( "fmt" "go/constant" + "internal/abi" "internal/buildcfg" "strings" @@ -825,7 +826,7 @@ func walkIndexMap(n *ir.IndexExpr, init *ir.Nodes) ir.Node { switch { case n.Assigned: mapFn = mapfn(mapassign[fast], t, false) - case t.Elem().Size() > zeroValSize: + case t.Elem().Size() > abi.ZeroValSize: args = append(args, reflectdata.ZeroAddr(t.Elem().Size())) mapFn = mapfn("mapaccess1_fat", t, true) default: diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index 4d9b2fbee5..179fbdb99e 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -11,6 +11,7 @@ import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/reflectdata" + "cmd/compile/internal/ssa" "cmd/compile/internal/staticinit" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" @@ -231,14 +232,29 @@ func (o *orderState) addrTemp(n ir.Node) ir.Node { vstat = typecheck.Expr(vstat).(*ir.Name) return vstat } + + // Prevent taking the address of an SSA-able local variable (#63332). + // + // TODO(mdempsky): Note that OuterValue unwraps OCONVNOPs, but + // IsAddressable does not. It should be possible to skip copying for + // at least some of these OCONVNOPs (e.g., reinsert them after the + // OADDR operation), but at least walkCompare needs to be fixed to + // support that (see trybot failures on go.dev/cl/541715, PS1). if ir.IsAddressable(n) { + if name, ok := ir.OuterValue(n).(*ir.Name); ok && name.Op() == ir.ONAME { + if name.Class == ir.PAUTO && !name.Addrtaken() && ssa.CanSSA(name.Type()) { + goto Copy + } + } + return n } + +Copy: return o.copyExpr(n) } // mapKeyTemp prepares n to be a key in a map runtime call and returns n. -// It should only be used for map runtime calls which have *_fast* versions. // The first parameter is the position of n's containing node, for use in case // that n's position is not unique (e.g., if n is an ONAME). func (o *orderState) mapKeyTemp(outerPos src.XPos, t *types.Type, n ir.Node) ir.Node { @@ -603,8 +619,38 @@ func (o *orderState) stmt(n ir.Node) { case ir.OAS: n := n.(*ir.AssignStmt) t := o.markTemp() + + // There's a delicate interaction here between two OINDEXMAP + // optimizations. + // + // First, we want to handle m[k] = append(m[k], ...) with a single + // runtime call to mapassign. This requires the m[k] expressions to + // satisfy ir.SameSafeExpr in walkAssign. + // + // But if k is a slow map key type that's passed by reference (e.g., + // byte), then we want to avoid marking user variables as addrtaken, + // if that might prevent the compiler from keeping k in a register. + // + // TODO(mdempsky): It would be better if walk was responsible for + // inserting temporaries as needed. + mapAppend := n.X.Op() == ir.OINDEXMAP && n.Y.Op() == ir.OAPPEND && + ir.SameSafeExpr(n.X, n.Y.(*ir.CallExpr).Args[0]) + n.X = o.expr(n.X, nil) - n.Y = o.expr(n.Y, n.X) + if mapAppend { + indexLHS := n.X.(*ir.IndexExpr) + indexLHS.X = o.cheapExpr(indexLHS.X) + indexLHS.Index = o.cheapExpr(indexLHS.Index) + + call := n.Y.(*ir.CallExpr) + indexRHS := call.Args[0].(*ir.IndexExpr) + indexRHS.X = indexLHS.X + indexRHS.Index = indexLHS.Index + + o.exprList(call.Args[1:]) + } else { + n.Y = o.expr(n.Y, n.X) + } o.mapAssign(n) o.popTemp(t) @@ -1158,7 +1204,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node { } } - // key must be addressable + // key may need to be be addressable n.Index = o.mapKeyTemp(n.Pos(), n.X.Type(), n.Index) if needCopy { return o.copyExpr(n) diff --git a/src/cmd/compile/internal/walk/walk.go b/src/cmd/compile/internal/walk/walk.go index 8be5804616..001edcc332 100644 --- a/src/cmd/compile/internal/walk/walk.go +++ b/src/cmd/compile/internal/walk/walk.go @@ -18,7 +18,6 @@ import ( // The constant is known to runtime. const tmpstringbufsize = 32 -const zeroValSize = 1024 // must match value of runtime/map.go:maxZero func Walk(fn *ir.Func) { ir.CurFunc = fn diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 9635c4fb61..5e62bbf4c2 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -719,6 +719,17 @@ func (t *tester) registerTests() { }) } + // GOEXPERIMENT=rangefunc tests + if !t.compileOnly { + t.registerTest("GOEXPERIMENT=rangefunc go test iter", + &goTest{ + variant: "iter", + short: t.short, + env: []string{"GOEXPERIMENT=rangefunc"}, + pkg: "iter", + }) + } + // GODEBUG=gcstoptheworld=2 tests. We only run these in long-test // mode (with GO_TEST_SHORT=0) because this is just testing a // non-critical debug setting. @@ -1627,7 +1638,7 @@ func buildModeSupported(compiler, buildmode, goos, goarch string) bool { case "plugin": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le", + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le", "android/amd64", "android/386", "darwin/amd64", "darwin/arm64", "freebsd/amd64": diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go index dfdc5674e9..a21d8a4688 100644 --- a/src/cmd/doc/pkg.go +++ b/src/cmd/doc/pkg.go @@ -43,9 +43,9 @@ type Package struct { buf pkgBuffer } -func (p *Package) ToText(w io.Writer, text, prefix, codePrefix string) { - d := p.doc.Parser().Parse(text) - pr := p.doc.Printer() +func (pkg *Package) ToText(w io.Writer, text, prefix, codePrefix string) { + d := pkg.doc.Parser().Parse(text) + pr := pkg.doc.Printer() pr.TextPrefix = prefix pr.TextCodePrefix = codePrefix w.Write(pr.Text(d)) diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 1e94f5dab0..7a426887b4 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -4,12 +4,12 @@ go 1.22 require ( github.com/google/pprof v0.0.0-20230811205829-9131a7e9cc17 - golang.org/x/arch v0.5.1-0.20231011141335-a6bdeed49307 - golang.org/x/mod v0.13.1-0.20231025225536-6e58e47c7bd6 - golang.org/x/sync v0.4.1-0.20231011140417-10739b037d36 - golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3 - golang.org/x/term v0.13.1-0.20231011140651-6a610bc55bff - golang.org/x/tools v0.14.1-0.20231019165902-71f6a46884ab + golang.org/x/arch v0.6.0 + golang.org/x/mod v0.14.0 + golang.org/x/sync v0.5.0 + golang.org/x/sys v0.15.0 + golang.org/x/term v0.15.0 + golang.org/x/tools v0.16.2-0.20231218185909-83bceaf2424d ) require github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab // indirect diff --git a/src/cmd/go.sum b/src/cmd/go.sum index ab476f84f9..8ea3d75bd1 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -2,15 +2,15 @@ github.com/google/pprof v0.0.0-20230811205829-9131a7e9cc17 h1:0h35ESZ02+hN/MFZb7 github.com/google/pprof v0.0.0-20230811205829-9131a7e9cc17/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab h1:BA4a7pe6ZTd9F8kXETBoijjFJ/ntaa//1wiH9BZu4zU= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -golang.org/x/arch v0.5.1-0.20231011141335-a6bdeed49307 h1:1nIbNxjxQ3+dss3xYMxayoIZONazUTg8/BENwc19sAQ= -golang.org/x/arch v0.5.1-0.20231011141335-a6bdeed49307/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/mod v0.13.1-0.20231025225536-6e58e47c7bd6 h1:YSyE+/SK6vfYAxf27iVtUZ/tTZOHGN6epnMgE1al/+M= -golang.org/x/mod v0.13.1-0.20231025225536-6e58e47c7bd6/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/sync v0.4.1-0.20231011140417-10739b037d36 h1:+lDu3sHZVY5Qqb7ynMbjaT4IsYicvoxypEOIE4aYlYE= -golang.org/x/sync v0.4.1-0.20231011140417-10739b037d36/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3 h1:G9se7UpoI67yWrFY0IIFGf6H3nwLLUZFDBCyOJwWeSc= -golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.13.1-0.20231011140651-6a610bc55bff h1:4lCCwEX5qbLiqBk8cqIlwrDbmkzfggoqDXYLTU+jr30= -golang.org/x/term v0.13.1-0.20231011140651-6a610bc55bff/go.mod h1:tfGnZ3a6ww9diaioGSzdPRgIfpk6Odt1UPXNhRcgfag= -golang.org/x/tools v0.14.1-0.20231019165902-71f6a46884ab h1:wNyFWaRZ6iFNvDL/8TK0HF5x3mGttsqpXPuHeKN88G8= -golang.org/x/tools v0.14.1-0.20231019165902-71f6a46884ab/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= +golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/tools v0.16.2-0.20231218185909-83bceaf2424d h1:9YOyUBubvYqtjjtZBnI62JT9/QB9jfPwOQ7xLeyuOIU= +golang.org/x/tools v0.16.2-0.20231218185909-83bceaf2424d/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index a5148ad486..e61e865c84 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1324,9 +1324,6 @@ // using import comments in .go files, vendoring tool configuration files (like // Gopkg.lock), and the current directory (if in GOPATH). // -// If a configuration file for a vendoring tool is present, init will attempt to -// import module requirements from it. -// // See https://golang.org/ref/mod#go-mod-init for more about 'go mod init'. // // # Add missing and remove unused modules @@ -2285,6 +2282,8 @@ // GOARM // For GOARCH=arm, the ARM architecture for which to compile. // Valid values are 5, 6, 7. +// The value can be followed by an option specifying how to implement floating point instructions. +// Valid options are ,softfloat (default for 5) and ,hardfloat (default for 6 and 7). // GO386 // For GOARCH=386, how to implement floating point instructions. // Valid values are sse2 (default), softfloat. diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index bae83eb92f..32822950f1 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -881,22 +881,17 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { // Copy the runtime packages into a temporary GOROOT // so that we can change files. - for _, copydir := range []string{ - "src/runtime", - "src/internal/abi", - "src/internal/bytealg", - "src/internal/coverage/rtcov", - "src/internal/cpu", - "src/internal/goarch", - "src/internal/godebugs", - "src/internal/goexperiment", - "src/internal/goos", - "src/internal/coverage/rtcov", - "src/math/bits", - "src/unsafe", + var dirs []string + tg.run("list", "-deps", "runtime") + pkgs := strings.Split(strings.TrimSpace(tg.getStdout()), "\n") + for _, pkg := range pkgs { + dirs = append(dirs, filepath.Join("src", pkg)) + } + dirs = append(dirs, filepath.Join("pkg/tool", goHostOS+"_"+goHostArch), "pkg/include", - } { + ) + for _, copydir := range dirs { srcdir := filepath.Join(testGOROOT, copydir) tg.tempDir(filepath.Join("goroot", copydir)) err := filepath.WalkDir(srcdir, @@ -912,6 +907,9 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) { return err } dest := filepath.Join("goroot", copydir, srcrel) + if _, err := os.Stat(dest); err == nil { + return nil + } data, err := os.ReadFile(path) if err != nil { return err diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 93613ac65e..c5d1e2af16 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -601,6 +601,8 @@ Architecture-specific environment variables: GOARM For GOARCH=arm, the ARM architecture for which to compile. Valid values are 5, 6, 7. + The value can be followed by an option specifying how to implement floating point instructions. + Valid options are ,softfloat (default for 5) and ,hardfloat (default for 6 and 7). GO386 For GOARCH=386, how to implement floating point instructions. Valid values are sse2 (default), softfloat. diff --git a/src/cmd/go/internal/mmap/mmap.go b/src/cmd/go/internal/mmap/mmap.go index 0cad9caf27..fcbd3e08c1 100644 --- a/src/cmd/go/internal/mmap/mmap.go +++ b/src/cmd/go/internal/mmap/mmap.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. diff --git a/src/cmd/go/internal/mmap/mmap_other.go b/src/cmd/go/internal/mmap/mmap_other.go index 22e9395b21..4d2844fc37 100644 --- a/src/cmd/go/internal/mmap/mmap_other.go +++ b/src/cmd/go/internal/mmap/mmap_other.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Go Authors. All rights reserved. +// 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. diff --git a/src/cmd/go/internal/mmap/mmap_unix.go b/src/cmd/go/internal/mmap/mmap_unix.go index 53bcbb92a8..5dce872368 100644 --- a/src/cmd/go/internal/mmap/mmap_unix.go +++ b/src/cmd/go/internal/mmap/mmap_unix.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. diff --git a/src/cmd/go/internal/mmap/mmap_windows.go b/src/cmd/go/internal/mmap/mmap_windows.go index 1cf62feca3..d00bef71e5 100644 --- a/src/cmd/go/internal/mmap/mmap_windows.go +++ b/src/cmd/go/internal/mmap/mmap_windows.go @@ -1,4 +1,4 @@ -// Copyright 2011 The Go Authors. All rights reserved. +// Copyright 2011 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. diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go index e4be73fab0..facdaa9911 100644 --- a/src/cmd/go/internal/modcmd/init.go +++ b/src/cmd/go/internal/modcmd/init.go @@ -25,9 +25,6 @@ module path argument is omitted, init will attempt to infer the module path using import comments in .go files, vendoring tool configuration files (like Gopkg.lock), and the current directory (if in GOPATH). -If a configuration file for a vendoring tool is present, init will attempt to -import module requirements from it. - See https://golang.org/ref/mod#go-mod-init for more about 'go mod init'. `, Run: runInit, diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go index d1a18a8d58..d3a713223c 100644 --- a/src/cmd/go/internal/modfetch/codehost/git.go +++ b/src/cmd/go/internal/modfetch/codehost/git.go @@ -18,6 +18,7 @@ import ( "os/exec" "path/filepath" "runtime" + "slices" "sort" "strconv" "strings" @@ -154,7 +155,7 @@ type gitRepo struct { refsErr error localTagsOnce sync.Once - localTags map[string]bool + localTags sync.Map // map[string]bool } const ( @@ -166,7 +167,6 @@ const ( // loadLocalTags loads tag references from the local git cache // into the map r.localTags. -// Should only be called as r.localTagsOnce.Do(r.loadLocalTags). func (r *gitRepo) loadLocalTags(ctx context.Context) { // The git protocol sends all known refs and ls-remote filters them on the client side, // so we might as well record both heads and tags in one shot. @@ -176,10 +176,9 @@ func (r *gitRepo) loadLocalTags(ctx context.Context) { return } - r.localTags = make(map[string]bool) for _, line := range strings.Split(string(out), "\n") { if line != "" { - r.localTags[line] = true + r.localTags.Store(line, true) } } } @@ -430,7 +429,7 @@ func (r *gitRepo) stat(ctx context.Context, rev string) (info *RevInfo, err erro // Maybe rev is a tag we already have locally. // (Note that we're excluding branches, which can be stale.) r.localTagsOnce.Do(func() { r.loadLocalTags(ctx) }) - if r.localTags[rev] { + if _, ok := r.localTags.Load(rev); ok { return r.statLocal(ctx, rev, "refs/tags/"+rev) } @@ -506,11 +505,18 @@ func (r *gitRepo) stat(ctx context.Context, rev string) (info *RevInfo, err erro // Either way, try a local stat before falling back to network I/O. if !didStatLocal { if info, err := r.statLocal(ctx, rev, hash); err == nil { - if after, found := strings.CutPrefix(ref, "refs/tags/"); found { - // Make sure tag exists, so it will be in localTags next time the go command is run. - Run(ctx, r.dir, "git", "tag", after, hash) + tag, fromTag := strings.CutPrefix(ref, "refs/tags/") + if fromTag && !slices.Contains(info.Tags, tag) { + // The local repo includes the commit hash we want, but it is missing + // the corresponding tag. Add that tag and try again. + _, err := Run(ctx, r.dir, "git", "tag", tag, hash) + if err != nil { + return nil, err + } + r.localTags.Store(tag, true) + return r.statLocal(ctx, rev, ref) } - return info, nil + return info, err } } @@ -524,13 +530,7 @@ func (r *gitRepo) stat(ctx context.Context, rev string) (info *RevInfo, err erro if r.fetchLevel <= fetchSome && ref != "" && hash != "" && !r.local { r.fetchLevel = fetchSome var refspec string - if ref != "" && ref != "HEAD" { - // If we do know the ref name, save the mapping locally - // so that (if it is a tag) it can show up in localTags - // on a future call. Also, some servers refuse to allow - // full hashes in ref specs, so prefer a ref name if known. - refspec = ref + ":" + ref - } else { + if ref == "HEAD" { // Fetch the hash but give it a local name (refs/dummy), // because that triggers the fetch behavior of creating any // other known remote tags for the hash. We never use @@ -538,6 +538,12 @@ func (r *gitRepo) stat(ctx context.Context, rev string) (info *RevInfo, err erro // overwritten in the next command, and that's fine. ref = hash refspec = hash + ":refs/dummy" + } else { + // If we do know the ref name, save the mapping locally + // so that (if it is a tag) it can show up in localTags + // on a future call. Also, some servers refuse to allow + // full hashes in ref specs, so prefer a ref name if known. + refspec = ref + ":" + ref } release, err := base.AcquireNet() diff --git a/src/cmd/go/internal/modfetch/codehost/git_test.go b/src/cmd/go/internal/modfetch/codehost/git_test.go index 328ab5bf58..dba9935b58 100644 --- a/src/cmd/go/internal/modfetch/codehost/git_test.go +++ b/src/cmd/go/internal/modfetch/codehost/git_test.go @@ -280,9 +280,6 @@ func TestLatest(t *testing.T) { t.Fatal(err) } if !reflect.DeepEqual(info, tt.info) { - if !reflect.DeepEqual(info.Tags, tt.info.Tags) { - testenv.SkipFlaky(t, 56881) - } t.Errorf("Latest: incorrect info\nhave %+v (origin %+v)\nwant %+v (origin %+v)", info, info.Origin, tt.info, tt.info.Origin) } } @@ -661,9 +658,6 @@ func TestStat(t *testing.T) { } info.Origin = nil // TestLatest and ../../../testdata/script/reuse_git.txt test Origin well enough if !reflect.DeepEqual(info, tt.info) { - if !reflect.DeepEqual(info.Tags, tt.info.Tags) { - testenv.SkipFlaky(t, 56881) - } t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info) } } diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go index 7d83ac6971..75c34e9fcb 100644 --- a/src/cmd/go/internal/modfetch/coderepo.go +++ b/src/cmd/go/internal/modfetch/coderepo.go @@ -514,10 +514,20 @@ func (r *codeRepo) convert(ctx context.Context, info *codehost.RevInfo, statVers // Determine version. if module.IsPseudoVersion(statVers) { + // Validate the go.mod location and major version before + // we check for an ancestor tagged with the pseude-version base. + // + // We can rule out an invalid subdirectory or major version with only + // shallow commit information, but checking the pseudo-version base may + // require downloading a (potentially more expensive) full history. + revInfo, err = checkCanonical(statVers) + if err != nil { + return revInfo, err + } if err := r.validatePseudoVersion(ctx, info, statVers); err != nil { return nil, err } - return checkCanonical(statVers) + return revInfo, nil } // statVers is not a pseudo-version, so we need to either resolve it to a diff --git a/src/cmd/go/internal/modget/query.go b/src/cmd/go/internal/modget/query.go index b78c1c4621..498ba6c2ff 100644 --- a/src/cmd/go/internal/modget/query.go +++ b/src/cmd/go/internal/modget/query.go @@ -55,7 +55,7 @@ type query struct { // path. matchWildcard func(path string) bool - // canMatchWildcard, if non-nil, reports whether the module with the given + // canMatchWildcardInModule, if non-nil, reports whether the module with the given // path could lexically contain a package matching pattern, which must be a // wildcard. canMatchWildcardInModule func(mPath string) bool diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index 895c6c0032..c4cf55442b 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -891,11 +891,12 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod // is most likely to find helpful: the most useful class of error at the // longest matching path. var ( - noPackage *PackageNotInModuleError - noVersion *NoMatchingVersionError - noPatchBase *NoPatchBaseError - invalidPath *module.InvalidPathError // see comment in case below - notExistErr error + noPackage *PackageNotInModuleError + noVersion *NoMatchingVersionError + noPatchBase *NoPatchBaseError + invalidPath *module.InvalidPathError // see comment in case below + invalidVersion error + notExistErr error ) for _, r := range results { switch rErr := r.err.(type) { @@ -931,6 +932,10 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod if notExistErr == nil { notExistErr = rErr } + } else if iv := (*module.InvalidVersionError)(nil); errors.As(rErr, &iv) { + if invalidVersion == nil { + invalidVersion = rErr + } } else if err == nil { if len(found) > 0 || noPackage != nil { // golang.org/issue/34094: If we have already found a module that @@ -961,6 +966,8 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod err = noPatchBase case invalidPath != nil: err = invalidPath + case invalidVersion != nil: + err = invalidVersion case notExistErr != nil: err = notExistErr default: diff --git a/src/cmd/go/internal/trace/trace.go b/src/cmd/go/internal/trace/trace.go index 17d3ee9e7f..f96aa40002 100644 --- a/src/cmd/go/internal/trace/trace.go +++ b/src/cmd/go/internal/trace/trace.go @@ -5,10 +5,10 @@ package trace import ( - "cmd/internal/traceviewer" "context" "encoding/json" "errors" + "internal/trace/traceviewer/format" "os" "strings" "sync/atomic" @@ -47,7 +47,7 @@ func StartSpan(ctx context.Context, name string) (context.Context, *Span) { return ctx, nil } childSpan := &Span{t: tc.t, name: name, tid: tc.tid, start: time.Now()} - tc.t.writeEvent(&traceviewer.Event{ + tc.t.writeEvent(&format.Event{ Name: childSpan.name, Time: float64(childSpan.start.UnixNano()) / float64(time.Microsecond), TID: childSpan.tid, @@ -77,7 +77,7 @@ func Flow(ctx context.Context, from *Span, to *Span) { } id := tc.t.getNextFlowID() - tc.t.writeEvent(&traceviewer.Event{ + tc.t.writeEvent(&format.Event{ Name: from.name + " -> " + to.name, Category: "flow", ID: id, @@ -85,7 +85,7 @@ func Flow(ctx context.Context, from *Span, to *Span) { Phase: phaseFlowStart, TID: from.tid, }) - tc.t.writeEvent(&traceviewer.Event{ + tc.t.writeEvent(&format.Event{ Name: from.name + " -> " + to.name, Category: "flow", // TODO(matloob): Add Category to Flow? ID: id, @@ -110,7 +110,7 @@ func (s *Span) Done() { return } s.end = time.Now() - s.t.writeEvent(&traceviewer.Event{ + s.t.writeEvent(&format.Event{ Name: s.name, Time: float64(s.end.UnixNano()) / float64(time.Microsecond), TID: s.tid, @@ -125,7 +125,7 @@ type tracer struct { nextFlowID atomic.Uint64 } -func (t *tracer) writeEvent(ev *traceviewer.Event) error { +func (t *tracer) writeEvent(ev *format.Event) error { f := <-t.file defer func() { t.file <- f }() var err error diff --git a/src/cmd/go/internal/vcweb/git.go b/src/cmd/go/internal/vcweb/git.go index 316c2382ba..d1e0563bed 100644 --- a/src/cmd/go/internal/vcweb/git.go +++ b/src/cmd/go/internal/vcweb/git.go @@ -37,16 +37,35 @@ func (h *gitHandler) Handler(dir string, env []string, logger *log.Logger) (http return nil, ServerNotInstalledError{name: "git"} } - handler := &cgi.Handler{ - Path: h.gitPath, - Logger: logger, - Args: []string{"http-backend"}, - Dir: dir, - Env: append(slices.Clip(env), - "GIT_PROJECT_ROOT="+dir, - "GIT_HTTP_EXPORT_ALL=1", - ), - } + baseEnv := append(slices.Clip(env), + "GIT_PROJECT_ROOT="+dir, + "GIT_HTTP_EXPORT_ALL=1", + ) + + handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + // The Git client sends the requested Git protocol version as a + // "Git-Protocol" HTTP request header, which the CGI host then converts + // to an environment variable (HTTP_GIT_PROTOCOL). + // + // However, versions of Git older that 2.34.0 don't recognize the + // HTTP_GIT_PROTOCOL variable, and instead need that value to be set in the + // GIT_PROTOCOL variable. We do so here so that vcweb can work reliably + // with older Git releases. (As of the time of writing, the Go project's + // builders were on Git version 2.30.2.) + env := slices.Clip(baseEnv) + if p := req.Header.Get("Git-Protocol"); p != "" { + env = append(env, "GIT_PROTOCOL="+p) + } + + h := &cgi.Handler{ + Path: h.gitPath, + Logger: logger, + Args: []string{"http-backend"}, + Dir: dir, + Env: env, + } + h.ServeHTTP(w, req) + }) return handler, nil } diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go index 276f524afa..bf923d0d5e 100644 --- a/src/cmd/go/internal/work/buildid.go +++ b/src/cmd/go/internal/work/buildid.go @@ -238,8 +238,28 @@ func (b *Builder) gccToolID(name, language string) (id, exe string, err error) { version := "" lines := strings.Split(string(out), "\n") for _, line := range lines { - if fields := strings.Fields(line); len(fields) > 1 && fields[1] == "version" || len(fields) > 2 && fields[2] == "version" { - version = line + fields := strings.Fields(line) + for i, field := range fields { + if strings.HasSuffix(field, ":") { + // Avoid parsing fields of lines like "Configured with: …", which may + // contain arbitrary substrings. + break + } + if field == "version" && i < len(fields)-1 { + // Check that the next field is plausibly a version number. + // We require only that it begins with an ASCII digit, + // since we don't know what version numbering schemes a given + // C compiler may use. (Clang and GCC mostly seem to follow the scheme X.Y.Z, + // but in https://go.dev/issue/64619 we saw "8.3 [DragonFly]", and who knows + // what other C compilers like "zig cc" might report?) + next := fields[i+1] + if len(next) > 0 && next[0] >= '0' && next[0] <= '9' { + version = line + break + } + } + } + if version != "" { break } } diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index ecad324886..e05471b06c 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -629,19 +629,6 @@ OverlayLoop: } } - // Run SWIG on each .swig and .swigcxx file. - // Each run will generate two files, a .go file and a .c or .cxx file. - // The .go file will use import "C" and is to be processed by cgo. - if p.UsesSwig() { - outGo, outC, outCXX, err := b.swig(a, objdir, pcCFLAGS) - if err != nil { - return err - } - cgofiles = append(cgofiles, outGo...) - cfiles = append(cfiles, outC...) - cxxfiles = append(cxxfiles, outCXX...) - } - // If we're doing coverage, preprocess the .go files and put them in the work directory if p.Internal.Cover.Mode != "" { outfiles := []string{} @@ -722,6 +709,22 @@ OverlayLoop: } } + // Run SWIG on each .swig and .swigcxx file. + // Each run will generate two files, a .go file and a .c or .cxx file. + // The .go file will use import "C" and is to be processed by cgo. + // For -cover test or build runs, this needs to happen after the cover + // tool is run; we don't want to instrument swig-generated Go files, + // see issue #64661. + if p.UsesSwig() { + outGo, outC, outCXX, err := b.swig(a, objdir, pcCFLAGS) + if err != nil { + return err + } + cgofiles = append(cgofiles, outGo...) + cfiles = append(cfiles, outC...) + cxxfiles = append(cxxfiles, outCXX...) + } + // Run cgo. if p.UsesCgo() || p.UsesSwig() { // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc. diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 7d4dedc1bb..d380aae489 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -16,7 +16,6 @@ import ( "log" "os" "path/filepath" - "runtime" rtrace "runtime/trace" "slices" "strings" @@ -107,10 +106,19 @@ func main() { return } + if cfg.GOROOT == "" { + fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: 'go' binary is trimmed and GOROOT is not set\n") + os.Exit(2) + } + if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() { + fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT) + os.Exit(2) + } + // Diagnose common mistake: GOPATH==GOROOT. // This setting is equivalent to not setting GOPATH at all, // which is not what most people want when they do it. - if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(runtime.GOROOT()) { + if gopath := cfg.BuildContext.GOPATH; filepath.Clean(gopath) == filepath.Clean(cfg.GOROOT) { fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) } else { for _, p := range filepath.SplitList(gopath) { @@ -139,15 +147,6 @@ func main() { } } - if cfg.GOROOT == "" { - fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: 'go' binary is trimmed and GOROOT is not set\n") - os.Exit(2) - } - if fi, err := os.Stat(cfg.GOROOT); err != nil || !fi.IsDir() { - fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", cfg.GOROOT) - os.Exit(2) - } - cmd, used := lookupCmd(args) cfg.CmdName = strings.Join(args[:used], " ") if len(cmd.Commands) > 0 { diff --git a/src/cmd/go/testdata/script/build_cc_cache_issue64423.txt b/src/cmd/go/testdata/script/build_cc_cache_issue64423.txt new file mode 100644 index 0000000000..f1bc2c3108 --- /dev/null +++ b/src/cmd/go/testdata/script/build_cc_cache_issue64423.txt @@ -0,0 +1,121 @@ +# Regression test for https://go.dev/issue/64423: +# +# When we parse the version for a Clang binary, we should accept +# an arbitrary vendor prefix, which (as of 2023) may be injected +# by defining CLANG_VENDOR when building clang itself. +# +# Since we don't want to actually rebuild the Clang toolchain in +# this test, we instead simulate it by injecting a fake "clang" +# binary that runs the real one as a subprocess. + +[!cgo] skip +[short] skip 'builds and links a fake clang binary' +[!cc:clang] skip 'test is specific to clang version parsing' + +# Save the location of the real clang command for our fake one to use. +go run ./which clang +cp stdout $WORK/.realclang + +# Build a fake clang and ensure that it is the one in $PATH. +mkdir $WORK/bin +go build -o $WORK/bin/clang$GOEXE ./fakeclang +[!GOOS:plan9] env PATH=$WORK${/}bin +[GOOS:plan9] env path=$WORK${/}bin + +# Force CGO_ENABLED=1 so that the following commands should error +# out if the fake clang doesn't work. +env CGO_ENABLED=1 + +# The bug in https://go.dev/issue/64423 resulted in cache keys that +# didn't contain any information about the C compiler. +# Since the bug was in cache key computation, isolate the cache: +# if we change the way caching works, we want the test to fail +# instead of accidentally reusing the cached information from a +# previous test run. +env GOCACHE=$WORK${/}.cache +mkdir $GOCACHE + +go build -x runtime/cgo + + # Tell our fake clang to stop working. + # Previously, 'go build -x runtime/cgo' would continue to + # succeed because both the broken clang and the non-broken one + # resulted in a cache key with no clang version information. +env GO_BREAK_CLANG=1 +! go build -x runtime/cgo +stderr '# runtime/cgo\nGO_BREAK_CLANG is set' + +-- go.mod -- +module example/issue64423 +go 1.20 +-- which/main.go -- +package main + +import ( + "os" + "os/exec" +) + +func main() { + path, err := exec.LookPath(os.Args[1]) + if err != nil { + panic(err) + } + os.Stdout.WriteString(path) +} +-- fakeclang/main.go -- +package main + +import ( + "bufio" + "bytes" + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func main() { + if os.Getenv("GO_BREAK_CLANG") != "" { + os.Stderr.WriteString("GO_BREAK_CLANG is set\n") + os.Exit(1) + } + + b, err := os.ReadFile(filepath.Join(os.Getenv("WORK"), ".realclang")) + if err != nil { + log.Fatal(err) + } + clang := string(bytes.TrimSpace(b)) + cmd := exec.Command(clang, os.Args[1:]...) + cmd.Stdout = os.Stdout + stderr, err := cmd.StderrPipe() + if err != nil { + log.Fatal(err) + } + + if err := cmd.Start(); err != nil { + log.Fatal(err) + } + + r := bufio.NewReader(stderr) + for { + line, err := r.ReadString('\n') + if line != "" { + if strings.Contains(line, "clang version") { + // Simulate a clang version string with an arbitrary vendor prefix. + const vendorString = "Gopher Solutions Unlimited " + os.Stderr.WriteString(vendorString) + } + os.Stderr.WriteString(line) + } + if err != nil { + break + } + } + os.Stderr.Close() + + if err := cmd.Wait(); err != nil { + os.Exit(1) + } +} diff --git a/src/cmd/go/testdata/script/cgo_stale_precompiled.txt b/src/cmd/go/testdata/script/cgo_stale_precompiled.txt index eb7e10557b..b2a0e0c8d5 100644 --- a/src/cmd/go/testdata/script/cgo_stale_precompiled.txt +++ b/src/cmd/go/testdata/script/cgo_stale_precompiled.txt @@ -29,8 +29,12 @@ env GOROOT_FINAL=$oldGOROOT_FINAL # no longer installed anyway! Since we're requiring a C compiler in order to # build and use cgo libraries in the standard library, we should make sure it # matches what's in the cache. -[!abscc] env CGO_ENABLED=1 -[!abscc] [!GOOS:plan9] env PATH='' # Guaranteed not to include $(go env CC)! -[!abscc] [GOOS:plan9] env path='' -[!abscc] ! go build -x runtime/cgo -[!abscc] stderr 'C compiler .* not found' + +[abscc] stop + +env CGO_ENABLED=1 +env CC='' +[!GOOS:plan9] env PATH='' # Guaranteed not to include $(go env CC)! +[GOOS:plan9] env path='' +! go build -x runtime/cgo +stderr 'C compiler .* not found' diff --git a/src/cmd/go/testdata/script/cover_statements.txt b/src/cmd/go/testdata/script/cover_statements.txt index 24b5751154..030177cb8b 100644 --- a/src/cmd/go/testdata/script/cover_statements.txt +++ b/src/cmd/go/testdata/script/cover_statements.txt @@ -1,5 +1,13 @@ [short] skip +# Workaround for issue 64014 -- for the portion of this test that +# verifies that caching works correctly, the cache should theoretically +# always behave reliably/deterministically, however if other tests are +# concurrently accessing the cache while this test is running, it can +# lead to cache lookup failures, which manifest as test failures here. +# To avoid such flakes, use a separate isolated GOCACHE for this test. +env GOCACHE=$WORK/cache + # Initial run with simple coverage. go test -cover ./pkg1 ./pkg2 ./pkg3 ./pkg4 [!GOEXPERIMENT:coverageredesign] stdout 'pkg1 \[no test files\]' diff --git a/src/cmd/go/testdata/script/cover_swig.txt b/src/cmd/go/testdata/script/cover_swig.txt new file mode 100644 index 0000000000..decb29aaec --- /dev/null +++ b/src/cmd/go/testdata/script/cover_swig.txt @@ -0,0 +1,72 @@ + +# Testcase for issue 64661. This testcase is intended to verify that +# we don't try to send swig-generated Go files through the cover tool +# for "go test -cover" runs on packages that have *.swig source files. + +[!exec:swig] skip +[!cgo] skip + +go test -v -count=1 -coverprofile=foo.p +stdout 'coverage: 100.0% of statements' + +-- go.mod -- +module simple + +go 1.21 +-- main.c -- +/* A global variable */ +double Foo = 3.0; + +/* Compute the greatest common divisor of positive integers */ +int gcd(int x, int y) { + int g; + g = y; + while (x > 0) { + g = x; + x = y % x; + y = g; + } + return g; +} + + +-- main.go -- +package main + +import ( + "fmt" +) + +func main() { + // Call our gcd() function + x := 42 + y := 105 + g := Gcd(x, y) + fmt.Println("The gcd of", x, "and", y, "is", g) + + // Manipulate the Foo global variable + + // Output its current value + fmt.Println("Foo =", GetFoo()) + + // Change its value + SetFoo(3.1415926) + + // See if the change took effect + fmt.Println("Foo =", GetFoo()) +} +-- main.swig -- +%module main + +%inline %{ +extern int gcd(int x, int y); +extern double Foo; +%} +-- main_test.go -- +package main + +import "testing" + +func TestSwigFuncs(t *testing.T) { + main() +} diff --git a/src/cmd/go/testdata/script/get_issue53955.txt b/src/cmd/go/testdata/script/get_issue53955.txt new file mode 100644 index 0000000000..685c6facaa --- /dev/null +++ b/src/cmd/go/testdata/script/get_issue53955.txt @@ -0,0 +1,79 @@ +# Regression test for https://go.dev/issue/53955. +# New remote tags were erroneously added to the local clone of a repo +# only *after* extracting version information for a locally-cached commit, +# causing the version information to have incomplete Tags and Version fields. + +[short] skip 'constructs a local git repo' +[!git] skip +[!net:github.com] skip 'does not actually use github.com because of insteadOf, but silence network check just in case' + +# Redirect git to a test-specific .gitconfig. +# GIT_CONFIG_GLOBAL suffices for git 2.32.0 and newer. +# For older git versions we also set $HOME. +env GIT_CONFIG_GLOBAL=$WORK${/}home${/}gopher${/}.gitconfig +env HOME=$WORK${/}home${/}gopher +exec git config --global --show-origin user.name +stdout 'Go Gopher' + +# Inject a local repo in place of a remote one, so that we can +# add commits to the repo partway through the test. +env GIT_ALLOW_PROTOCOL=file +env GOPRIVATE=github.com/golang/issue53955 + +[!GOOS:windows] exec git config --global 'url.file://'$WORK'/repo.insteadOf' 'https://github.com/golang/issue53955' +[GOOS:windows] exec git config --global 'url.file:///'$WORK'/repo.insteadOf' 'https://github.com/golang/issue53955' + +cd $WORK/repo + +env GIT_AUTHOR_NAME='Go Gopher' +env GIT_AUTHOR_EMAIL='gopher@golang.org' +env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME +env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL + +exec git init + +env GIT_COMMITTER_DATE=2022-07-19T11:07:00-04:00 +env GIT_AUTHOR_DATE=2022-07-19T11:07:00-04:00 +exec git add go.mod issue53955.go +exec git commit -m 'initial commit' +exec git branch -m main +exec git tag v1.0.9 + +env GIT_COMMITTER_DATE=2022-07-19T11:07:01-04:00 +env GIT_AUTHOR_DATE=2022-07-19T11:07:01-04:00 +exec git add extra.go +exec git commit -m 'next commit' +exec git show-ref --tags --heads +cmp stdout $WORK/.git-refs-1 + +cd $WORK/m +go get -x github.com/golang/issue53955@2cb3d49f +stderr '^go: added github.com/golang/issue53955 v1.0.10-0.20220719150701-2cb3d49f8874$' + +cd $WORK/repo +exec git tag v1.0.10 + +cd $WORK/m +go get -x github.com/golang/issue53955@v1.0.10 +! stderr 'v1\.0\.10 is not a tag' +stderr '^go: upgraded github.com/golang/issue53955 v.* => v1\.0\.10$' + +-- $WORK/repo/go.mod -- +module github.com/golang/issue53955 + +go 1.18 +-- $WORK/repo/issue53955.go -- +package issue53955 +-- $WORK/repo/extra.go -- +package issue53955 +-- $WORK/.git-refs-1 -- +2cb3d49f8874b9362ed0ddd2a6512e4108bbf6b1 refs/heads/main +050526ebf5883191e990529eb3cc9345abaf838c refs/tags/v1.0.9 +-- $WORK/m/go.mod -- +module m + +go 1.18 +-- $WORK/home/gopher/.gitconfig -- +[user] + name = Go Gopher + email = gopher@golang.org diff --git a/src/cmd/go/testdata/script/goroot_executable_trimpath.txt b/src/cmd/go/testdata/script/goroot_executable_trimpath.txt index dc1e25e606..a3f0c39a83 100644 --- a/src/cmd/go/testdata/script/goroot_executable_trimpath.txt +++ b/src/cmd/go/testdata/script/goroot_executable_trimpath.txt @@ -29,12 +29,20 @@ wait env TESTGOROOT=$GOROOT env GOROOT= +# Unset GOPATH and any variables that its default may be derived from, +# so that we can check for a spurious warning. +env GOPATH= +env HOME='' +env USERPROFILE='' +env home='' + # Relocated Executable # Since we built with -trimpath and the binary isn't installed in a # normal-looking GOROOT, this command should fail. ! exec $WORK/new/bin/go$GOEXE env GOROOT stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$' +! stderr 'GOPATH set to GOROOT' # Cross-compiled binaries in cmd are installed to a ${GOOS}_${GOARCH} subdirectory, # so we also want to try a copy there. @@ -44,6 +52,7 @@ stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT i cp $WORK/new/bin/go$GOEXE $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE ! exec $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE env GOROOT stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$' +! stderr 'GOPATH set to GOROOT' # Relocated Tree: # If the binary is sitting in a bin dir next to ../pkg/tool, that counts as a GOROOT, @@ -51,6 +60,7 @@ stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT i mkdir $WORK/new/pkg/tool exec $WORK/bin/check$GOEXE $WORK/new/bin/go$GOEXE $WORK/new exec $WORK/bin/check$GOEXE $WORK/new/bin/${GOOS}_${GOARCH}/go$GOEXE $WORK/new +! stderr 'GOPATH set to GOROOT' -- check.go -- package main diff --git a/src/cmd/go/testdata/script/mod_download_git_bareRepository.txt b/src/cmd/go/testdata/script/mod_download_git_bareRepository.txt index 8050461c65..a61283ca49 100644 --- a/src/cmd/go/testdata/script/mod_download_git_bareRepository.txt +++ b/src/cmd/go/testdata/script/mod_download_git_bareRepository.txt @@ -1,8 +1,14 @@ [short] skip [!git] skip -[!GOOS:linux] skip # Uses XDG_CONFIG_HOME -env GIT_CONFIG_GLOBAL=$WORK/.gitconfig +# Redirect git to a test-specific .gitconfig. +# GIT_CONFIG_GLOBAL suffices for git 2.32.0 and newer. +# For older git versions we also set $HOME. +env GIT_CONFIG_GLOBAL=$WORK${/}home${/}gopher${/}.gitconfig +env HOME=$WORK${/}home${/}gopher +exec git config --global --show-origin user.name +stdout 'Go Gopher' + env GOPRIVATE=vcs-test.golang.org go mod download -x @@ -14,6 +20,9 @@ go 1.18 require vcs-test.golang.org/git/gitrepo1.git v1.2.3 --- $WORK/.gitconfig -- +-- $WORK/home/gopher/.gitconfig -- +[user] + name = Go Gopher + email = gopher@golang.org [safe] -bareRepository = explicit + bareRepository = explicit diff --git a/src/cmd/go/testdata/script/mod_download_git_decorate_full.txt b/src/cmd/go/testdata/script/mod_download_git_decorate_full.txt index 080ccf072e..9afd347746 100644 --- a/src/cmd/go/testdata/script/mod_download_git_decorate_full.txt +++ b/src/cmd/go/testdata/script/mod_download_git_decorate_full.txt @@ -3,12 +3,15 @@ env GO111MODULE=on [short] skip [!git] skip +# Redirect git to a test-specific .gitconfig. +# GIT_CONFIG_GLOBAL suffices for git 2.32.0 and newer. +# For older git versions we also set $HOME. +env GIT_CONFIG_GLOBAL=$WORK${/}home${/}gopher${/}.gitconfig +env HOME=$WORK${/}home${/}gopher +exec git config --global --show-origin user.name +stdout 'Go Gopher' + env GOPROXY=direct -env HOME=$WORK/home/gopher - - -go env GOPROXY -stdout 'direct' exec git config --get log.decorate stdout 'full' @@ -24,5 +27,8 @@ go list -m vcs-test.golang.org/git/gitrepo1.git@v1.2.3 stdout 'vcs-test.golang.org/git/gitrepo1.git v1.2.3' -- $WORK/home/gopher/.gitconfig -- +[user] + name = Go Gopher + email = gopher@golang.org [log] decorate = full diff --git a/src/cmd/go/testdata/script/mod_download_issue51114.txt b/src/cmd/go/testdata/script/mod_download_issue51114.txt index 4d274d61a9..a28d467bb8 100644 --- a/src/cmd/go/testdata/script/mod_download_issue51114.txt +++ b/src/cmd/go/testdata/script/mod_download_issue51114.txt @@ -1,8 +1,14 @@ [!net:github.com] skip [!git] skip -[!GOOS:linux] skip # Uses XDG_CONFIG_HOME -env GIT_CONFIG_GLOBAL=$WORK/.gitconfig +# Redirect git to a test-specific .gitconfig. +# GIT_CONFIG_GLOBAL suffices for git 2.32.0 and newer. +# For older git versions we also set $HOME. +env GIT_CONFIG_GLOBAL=$WORK${/}home${/}gopher${/}.gitconfig +env HOME=$WORK${/}home${/}gopher +exec git config --global --show-origin user.name +stdout 'Go Gopher' + env GOPROXY=direct ! go mod download @@ -15,6 +21,9 @@ go 1.18 require github.com/golang/notexist/subdir v0.1.0 --- $WORK/.gitconfig -- +-- $WORK/home/gopher/.gitconfig -- +[user] + name = Go Gopher + email = gopher@golang.org [url "git@github.com:"] insteadOf = https://github.com/ diff --git a/src/cmd/go/testdata/script/mod_download_private_vcs.txt b/src/cmd/go/testdata/script/mod_download_private_vcs.txt index 2f72a4213a..5c8d93a978 100644 --- a/src/cmd/go/testdata/script/mod_download_private_vcs.txt +++ b/src/cmd/go/testdata/script/mod_download_private_vcs.txt @@ -5,6 +5,14 @@ env GO111MODULE=on [!git] skip env GOPROXY=direct +# Redirect git to a test-specific .gitconfig. +# GIT_CONFIG_GLOBAL suffices for git 2.32.0 and newer. +# For older git versions we also set $HOME. +env GIT_CONFIG_GLOBAL=$WORK${/}home${/}gopher${/}.gitconfig +env HOME=$WORK${/}home${/}gopher +exec git config --global --show-origin user.name +stdout 'Go Gopher' + ! go mod download github.com/golang/nonexist@latest stderr 'Confirm the import path was entered correctly.' stderr 'If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.' @@ -27,7 +35,7 @@ stderr '^If this is a private repository, see https://golang.org/doc/faq#git_htt # Test that Git clone errors will be shown to the user instead of a generic # "unknown revision" error. To do this we want to force git ls-remote to return # an error we don't already have special handling for. See golang/go#42751. -env HOME=$WORK${/}home${/}gopher +exec git config --global url.git@github.com.insteadOf https://github.com/ env GIT_SSH_COMMAND=false ! go install github.com/golang/nonexist@master stderr 'fatal: Could not read from remote repository.' @@ -35,5 +43,6 @@ stderr 'fatal: Could not read from remote repository.' ! stdout . -- $WORK/home/gopher/.gitconfig -- -[url "git@github.com:"] - insteadOf = https://github.com/ +[user] + name = Go Gopher + email = gopher@golang.org diff --git a/src/cmd/go/testdata/script/mod_get_issue47650.txt b/src/cmd/go/testdata/script/mod_get_issue47650.txt new file mode 100644 index 0000000000..8561b21df0 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_get_issue47650.txt @@ -0,0 +1,29 @@ +# Regression test for https://go.dev/issue/47650: +# 'go get' with a pseudo-version of a non-root package within a module +# erroneously rejected the pseudo-version as invalid, because it did not fetch +# enough commit history to validate the pseudo-version base. + +[short] skip 'creates and uses a git repository' +[!git] skip + +env GOPRIVATE=vcs-test.golang.org + +# If we request a package in a subdirectory of a module by commit hash, we +# successfully resolve it to a pseudo-version derived from a tag on the parent +# commit. +cp go.mod go.mod.orig +go get -x vcs-test.golang.org/git/issue47650.git/cmd/issue47650@21535ef346c3 +stderr '^go: added vcs-test.golang.org/git/issue47650.git v0.1.1-0.20210811175200-21535ef346c3$' + +# Explicitly requesting that same version should succeed, fetching additional +# history for the requested commit as needed in order to validate the +# pseudo-version base. +go clean -modcache +cp go.mod.orig go.mod +go get -x vcs-test.golang.org/git/issue47650.git/cmd/issue47650@v0.1.1-0.20210811175200-21535ef346c3 +stderr '^go: added vcs-test.golang.org/git/issue47650.git v0.1.1-0.20210811175200-21535ef346c3$' + +-- go.mod -- +module example + +go 1.20 diff --git a/src/cmd/go/testdata/vcstest/git/issue47650.txt b/src/cmd/go/testdata/vcstest/git/issue47650.txt new file mode 100644 index 0000000000..52040787c8 --- /dev/null +++ b/src/cmd/go/testdata/vcstest/git/issue47650.txt @@ -0,0 +1,42 @@ +handle git + +env GIT_AUTHOR_NAME='Bryan C. Mills' +env GIT_AUTHOR_EMAIL='bcmills@google.com' +env GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME +env GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL + +git init + +at 2021-08-11T13:52:00-04:00 +git add cmd +git commit -m 'add cmd/issue47650' +git branch -m main +git tag v0.1.0 + +git add go.mod +git commit -m 'add go.mod' + +git show-ref --tags --heads +cmp stdout .git-refs + +git log --oneline --decorate=short +cmp stdout .git-log + +-- .git-refs -- +21535ef346c3e79fd09edd75bd4725f06c828e43 refs/heads/main +4d237df2dbfc8a443af2f5e84be774f08a2aed0c refs/tags/v0.1.0 +-- .git-log -- +21535ef (HEAD -> main) add go.mod +4d237df (tag: v0.1.0) add cmd/issue47650 +-- go.mod -- +module vcs-test.golang.org/git/issue47650.git + +go 1.17 +-- cmd/issue47650/main.go -- +package main + +import "os" + +func main() { + os.Stdout.WriteString("Hello, world!") +} diff --git a/src/cmd/internal/goobj/builtinlist.go b/src/cmd/internal/goobj/builtinlist.go index 03982d54f2..fb729f512e 100644 --- a/src/cmd/internal/goobj/builtinlist.go +++ b/src/cmd/internal/goobj/builtinlist.go @@ -83,7 +83,7 @@ var builtins = [...]struct { {"runtime.efaceeq", 1}, {"runtime.panicrangeexit", 1}, {"runtime.deferrangefunc", 1}, - {"runtime.fastrand", 1}, + {"runtime.rand32", 1}, {"runtime.makemap64", 1}, {"runtime.makemap", 1}, {"runtime.makemap_small", 1}, diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go index ae890b66cb..3d4c99eecb 100644 --- a/src/cmd/internal/moddeps/moddeps_test.go +++ b/src/cmd/internal/moddeps/moddeps_test.go @@ -443,7 +443,14 @@ func findGorootModules(t *testing.T) []gorootModule { goBin := testenv.GoToolPath(t) goroot.once.Do(func() { - goroot.err = filepath.WalkDir(testenv.GOROOT(t), func(path string, info fs.DirEntry, err error) error { + // If the root itself is a symlink to a directory, + // we want to follow it (see https://go.dev/issue/64375). + // Add a trailing separator to force that to happen. + root := testenv.GOROOT(t) + if !os.IsPathSeparator(root[len(root)-1]) { + root += string(filepath.Separator) + } + goroot.err = filepath.WalkDir(root, func(path string, info fs.DirEntry, err error) error { if err != nil { return err } diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go index 177ffd9797..4e6eff9e17 100644 --- a/src/cmd/internal/obj/arm/asm5.go +++ b/src/cmd/internal/obj/arm/asm5.go @@ -979,7 +979,7 @@ func (c *ctxt5) aclass(a *obj.Addr) int { if immrot(^uint32(c.instoffset)) != 0 { return C_NCON } - if uint32(c.instoffset) <= 0xffff && buildcfg.GOARM == 7 { + if uint32(c.instoffset) <= 0xffff && buildcfg.GOARM.Version == 7 { return C_SCON } if x, y := immrot2a(uint32(c.instoffset)); x != 0 && y != 0 { @@ -1099,6 +1099,32 @@ func (c *ctxt5) oplook(p *obj.Prog) *Optab { fmt.Printf("\t\t%d %d\n", p.From.Type, p.To.Type) } + if (p.As == ASRL || p.As == ASRA) && p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 { + // Right shifts are weird - a shift that looks like "shift by constant 0" actually + // means "shift by constant 32". Use left shift in this situation instead. + // See issue 64715. + // TODO: rotate by 0? Not currently supported, but if we ever do then include it here. + p.As = ASLL + } + if p.As != AMOVB && p.As != AMOVBS && p.As != AMOVBU && p.As != AMOVH && p.As != AMOVHS && p.As != AMOVHU && p.As != AXTAB && p.As != AXTABU && p.As != AXTAH && p.As != AXTAHU { + // Same here, but for shifts encoded in Addrs. + // Don't do it for the extension ops, which + // need to keep their RR shifts. + fixShift := func(a *obj.Addr) { + if a.Type == obj.TYPE_SHIFT { + typ := a.Offset & SHIFT_RR + isConst := a.Offset&(1<<4) == 0 + amount := a.Offset >> 7 & 0x1f + if isConst && amount == 0 && (typ == SHIFT_LR || typ == SHIFT_AR || typ == SHIFT_RR) { + a.Offset -= typ + a.Offset += SHIFT_LL + } + } + } + fixShift(&p.From) + fixShift(&p.To) + } + ops := oprange[p.As&obj.AMask] c1 := &xcmp[a1] c3 := &xcmp[a3] @@ -3044,16 +3070,16 @@ func (c *ctxt5) omvl(p *obj.Prog, a *obj.Addr, dr int) uint32 { } func (c *ctxt5) chipzero5(e float64) int { - // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. - if buildcfg.GOARM < 7 || math.Float64bits(e) != 0 { + // We use GOARM.Version=7 and !GOARM.SoftFloat to gate the use of VFPv3 vmov (imm) instructions. + if buildcfg.GOARM.Version < 7 || buildcfg.GOARM.SoftFloat || math.Float64bits(e) != 0 { return -1 } return 0 } func (c *ctxt5) chipfloat5(e float64) int { - // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. - if buildcfg.GOARM < 7 { + // We use GOARM.Version=7 and !GOARM.SoftFloat to gate the use of VFPv3 vmov (imm) instructions. + if buildcfg.GOARM.Version < 7 || buildcfg.GOARM.SoftFloat { return -1 } diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go index fb7c260f89..def4f526ad 100644 --- a/src/cmd/internal/obj/arm/obj5.go +++ b/src/cmd/internal/obj/arm/obj5.go @@ -66,7 +66,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) } - if buildcfg.GOARM < 7 { + if buildcfg.GOARM.Version < 7 { // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. if progedit_tlsfallback == nil { progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback") diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 0991ec9201..03f0fb06da 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -7741,7 +7741,7 @@ func (c *ctxt7) opldpstp(p *obj.Prog, o *Optab, vo int32, rbase, rl, rh int16, l c.ctxt.Diag("invalid register pair %v\n", p) } case ALDP, ALDPW, ALDPSW: - if rl < REG_R0 || REG_R30 < rl || rh < REG_R0 || REG_R30 < rh { + if rl < REG_R0 || REG_R31 < rl || rh < REG_R0 || REG_R31 < rh { c.ctxt.Diag("invalid register pair %v\n", p) } case ASTP, ASTPW: diff --git a/src/cmd/internal/obj/loong64/a.out.go b/src/cmd/internal/obj/loong64/a.out.go index 99a7da388f..d944fcfcb8 100644 --- a/src/cmd/internal/obj/loong64/a.out.go +++ b/src/cmd/internal/obj/loong64/a.out.go @@ -157,14 +157,14 @@ const ( REGZERO = REG_R0 // set to zero REGLINK = REG_R1 REGSP = REG_R3 - REGRET = REG_R19 + REGRET = REG_R20 // not use REGARG = -1 // -1 disables passing the first argument in register - REGRT1 = REG_R19 // reserved for runtime, duffzero and duffcopy - REGRT2 = REG_R20 // reserved for runtime, duffcopy + REGRT1 = REG_R20 // reserved for runtime, duffzero and duffcopy + REGRT2 = REG_R21 // reserved for runtime, duffcopy REGCTXT = REG_R29 // context for closures REGG = REG_R22 // G in loong64 REGTMP = REG_R30 // used by the assembler - FREGRET = REG_F0 + FREGRET = REG_F0 // not use ) var LOONG64DWARFRegisters = map[int16]int16{} @@ -227,6 +227,7 @@ const ( C_ADDR C_TLS_LE C_TLS_IE + C_GOTADDR C_TEXTSIZE C_NCLASS // must be the last diff --git a/src/cmd/internal/obj/loong64/asm.go b/src/cmd/internal/obj/loong64/asm.go index 19250c94ee..64c9226079 100644 --- a/src/cmd/internal/obj/loong64/asm.go +++ b/src/cmd/internal/obj/loong64/asm.go @@ -349,6 +349,8 @@ var optab = []Optab{ {AWORD, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 40, 4, 0, 0}, {AWORD, C_DCON, C_NONE, C_NONE, C_NONE, C_NONE, 61, 4, 0, 0}, + {AMOVV, C_GOTADDR, C_NONE, C_NONE, C_REG, C_NONE, 65, 8, 0, 0}, + {ATEQ, C_SCON, C_REG, C_NONE, C_REG, C_NONE, 15, 8, 0, 0}, {ATEQ, C_SCON, C_NONE, C_NONE, C_REG, C_NONE, 15, 8, 0, 0}, @@ -676,6 +678,9 @@ func (c *ctxt0) aclass(a *obj.Addr) int { return C_SOREG } return C_LOREG + + case obj.NAME_GOTREF: + return C_GOTADDR } return C_GOK @@ -1776,6 +1781,22 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { case 64: // movv c_reg, c_fcc0 ==> movgr2cf cd, rj a := OP_TEN(8, 1334) o1 = OP_RR(a, uint32(p.From.Reg), uint32(p.To.Reg)) + + case 65: // mov sym@GOT, r ==> pcalau12i + ld.d + o1 = OP_IR(c.opir(APCALAU12I), uint32(0), uint32(p.To.Reg)) + rel := obj.Addrel(c.cursym) + rel.Off = int32(c.pc) + rel.Siz = 4 + rel.Sym = p.From.Sym + rel.Type = objabi.R_LOONG64_GOT_HI + rel.Add = 0x0 + o2 = OP_12IRR(c.opirr(-p.As), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg)) + rel2 := obj.Addrel(c.cursym) + rel2.Off = int32(c.pc + 4) + rel2.Siz = 4 + rel2.Sym = p.From.Sym + rel2.Type = objabi.R_LOONG64_GOT_LO + rel2.Add = 0x0 } out[0] = o1 diff --git a/src/cmd/internal/obj/loong64/cnames.go b/src/cmd/internal/obj/loong64/cnames.go index 8b8af6ba31..94b1b54c93 100644 --- a/src/cmd/internal/obj/loong64/cnames.go +++ b/src/cmd/internal/obj/loong64/cnames.go @@ -39,6 +39,7 @@ var cnames0 = []string{ "ADDR", "TLS_LE", "TLS_IE", + "GOTADDR", "TEXTSIZE", "NCLASS", } diff --git a/src/cmd/internal/obj/loong64/obj.go b/src/cmd/internal/obj/loong64/obj.go index 1eedd46c69..5fa67f3acd 100644 --- a/src/cmd/internal/obj/loong64/obj.go +++ b/src/cmd/internal/obj/loong64/obj.go @@ -6,6 +6,7 @@ package loong64 import ( "cmd/internal/obj" + "cmd/internal/objabi" "cmd/internal/sys" "internal/abi" "log" @@ -84,6 +85,122 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { p.As = AADDVU } } + + if ctxt.Flag_dynlink { + rewriteToUseGot(ctxt, p, newprog) + } +} + +func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { + // ADUFFxxx $offset + // becomes + // MOVV runtime.duffxxx@GOT, REGTMP + // ADD $offset, REGTMP + // JAL REGTMP + if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { + var sym *obj.LSym + if p.As == obj.ADUFFZERO { + sym = ctxt.Lookup("runtime.duffzero") + } else { + sym = ctxt.Lookup("runtime.duffcopy") + } + offset := p.To.Offset + p.As = AMOVV + p.From.Type = obj.TYPE_MEM + p.From.Sym = sym + p.From.Name = obj.NAME_GOTREF + p.To.Type = obj.TYPE_REG + p.To.Reg = REGTMP + p.To.Name = obj.NAME_NONE + p.To.Offset = 0 + p.To.Sym = nil + p1 := obj.Appendp(p, newprog) + p1.As = AADDV + p1.From.Type = obj.TYPE_CONST + p1.From.Offset = offset + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REGTMP + p2 := obj.Appendp(p1, newprog) + p2.As = AJAL + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = REGTMP + } + + // We only care about global data: NAME_EXTERN means a global + // symbol in the Go sense, and p.Sym.Local is true for a few + // internally defined symbols. + if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { + // MOVV $sym, Rx becomes MOVV sym@GOT, Rx + // MOVV $sym+, Rx becomes MOVV sym@GOT, Rx; ADD , Rx + if p.As != AMOVV { + ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -shared", p) + } + if p.To.Type != obj.TYPE_REG { + ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -shared", p) + } + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_GOTREF + if p.From.Offset != 0 { + q := obj.Appendp(p, newprog) + q.As = AADDV + q.From.Type = obj.TYPE_CONST + q.From.Offset = p.From.Offset + q.To = p.To + p.From.Offset = 0 + } + } + if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { + ctxt.Diag("don't know how to handle %v with -shared", p) + } + + var source *obj.Addr + // MOVx sym, Ry becomes MOVV sym@GOT, REGTMP; MOVx (REGTMP), Ry + // MOVx Ry, sym becomes MOVV sym@GOT, REGTMP; MOVx Ry, (REGTMP) + // An addition may be inserted between the two MOVs if there is an offset. + if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { + if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { + ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -shared", p) + } + source = &p.From + } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { + source = &p.To + } else { + return + } + if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { + return + } + if source.Sym.Type == objabi.STLSBSS { + return + } + if source.Type != obj.TYPE_MEM { + ctxt.Diag("don't know how to handle %v with -shared", p) + } + p1 := obj.Appendp(p, newprog) + p2 := obj.Appendp(p1, newprog) + p1.As = AMOVV + p1.From.Type = obj.TYPE_MEM + p1.From.Sym = source.Sym + p1.From.Name = obj.NAME_GOTREF + p1.To.Type = obj.TYPE_REG + p1.To.Reg = REGTMP + + p2.As = p.As + p2.From = p.From + p2.To = p.To + if p.From.Name == obj.NAME_EXTERN { + p2.From.Reg = REGTMP + p2.From.Name = obj.NAME_NONE + p2.From.Sym = nil + } else if p.To.Name == obj.NAME_EXTERN { + p2.To.Reg = REGTMP + p2.To.Name = obj.NAME_NONE + p2.To.Sym = nil + } else { + return + } + + obj.Nopout(p) } func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { @@ -279,18 +396,18 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // - // MOV g_panic(g), R1 - // BEQ R1, end - // MOV panic_argp(R1), R2 - // ADD $(autosize+FIXED_FRAME), R29, R3 - // BNE R2, R3, end - // ADD $FIXED_FRAME, R29, R2 - // MOV R2, panic_argp(R1) + // MOV g_panic(g), R20 + // BEQ R20, end + // MOV panic_argp(R20), R24 + // ADD $(autosize+FIXED_FRAME), R3, R30 + // BNE R24, R30, end + // ADD $FIXED_FRAME, R3, R24 + // MOV R24, panic_argp(R20) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. - // It is a liblink NOP, not an hardware NOP: it encodes to 0 instruction bytes. + // It is a liblink NOP, not a hardware NOP: it encodes to 0 instruction bytes. // // We don't generate this for leafs because that means the wrapped // function was inlined into the wrapper. @@ -302,12 +419,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.From.Reg = REGG q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R19 + q.To.Reg = REG_R20 q = obj.Appendp(q, newprog) q.As = ABEQ q.From.Type = obj.TYPE_REG - q.From.Reg = REG_R19 + q.From.Reg = REG_R20 q.To.Type = obj.TYPE_BRANCH q.Mark |= BRANCH p1 = q @@ -315,10 +432,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q = obj.Appendp(q, newprog) q.As = mov q.From.Type = obj.TYPE_MEM - q.From.Reg = REG_R19 + q.From.Reg = REG_R20 q.From.Offset = 0 // Panic.argp q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R4 + q.To.Reg = REG_R24 q = obj.Appendp(q, newprog) q.As = add @@ -326,13 +443,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize q.Reg = REGSP q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R5 + q.To.Reg = REG_R30 q = obj.Appendp(q, newprog) q.As = ABNE q.From.Type = obj.TYPE_REG - q.From.Reg = REG_R4 - q.Reg = REG_R5 + q.From.Reg = REG_R24 + q.Reg = REG_R30 q.To.Type = obj.TYPE_BRANCH q.Mark |= BRANCH p2 = q @@ -343,14 +460,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.From.Offset = ctxt.Arch.FixedFrameSize q.Reg = REGSP q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R4 + q.To.Reg = REG_R24 q = obj.Appendp(q, newprog) q.As = mov q.From.Type = obj.TYPE_REG - q.From.Reg = REG_R4 + q.From.Reg = REG_R24 q.To.Type = obj.TYPE_MEM - q.To.Reg = REG_R19 + q.To.Reg = REG_R20 q.To.Offset = 0 // Panic.argp q = obj.Appendp(q, newprog) @@ -503,6 +620,10 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p = c.ctxt.StartUnsafePoint(p, c.newprog) + // Spill Arguments. This has to happen before we open + // any more frame space. + p = c.cursym.Func().SpillRegisterArgs(p, c.newprog) + // MOV REGLINK, -8/-16(SP) p = obj.Appendp(p, c.newprog) p.As = mov @@ -567,13 +688,15 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.To.Reg = REGSP p.Spadj = int32(-frameSize) + // Unspill arguments + p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog) p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) } // Jump back to here after morestack returns. startPred := p - // MOV g_stackguard(g), R19 + // MOV g_stackguard(g), R20 p = obj.Appendp(p, c.newprog) p.As = mov @@ -584,7 +707,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 } p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R19 + p.To.Reg = REG_R20 // Mark the stack bound check and morestack call async nonpreemptible. // If we get preempted here, when resumed the preemption request is @@ -595,15 +718,15 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { var q *obj.Prog if framesize <= abi.StackSmall { // small stack: SP < stackguard - // AGTU SP, stackguard, R19 + // AGTU SP, stackguard, R20 p = obj.Appendp(p, c.newprog) p.As = ASGTU p.From.Type = obj.TYPE_REG p.From.Reg = REGSP - p.Reg = REG_R19 + p.Reg = REG_R20 p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R19 + p.To.Reg = REG_R20 } else { // large stack: SP-framesize < stackguard-StackSmall offset := int64(framesize) - abi.StackSmall @@ -615,8 +738,8 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { // stack guard to incorrectly succeed. We explicitly // guard against underflow. // - // SGTU $(framesize-StackSmall), SP, R4 - // BNE R4, label-of-call-to-morestack + // SGTU $(framesize-StackSmall), SP, R24 + // BNE R24, label-of-call-to-morestack p = obj.Appendp(p, c.newprog) p.As = ASGTU @@ -624,13 +747,13 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.From.Offset = offset p.Reg = REGSP p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R4 + p.To.Reg = REG_R24 p = obj.Appendp(p, c.newprog) q = p p.As = ABNE p.From.Type = obj.TYPE_REG - p.From.Reg = REG_R4 + p.From.Reg = REG_R24 p.To.Type = obj.TYPE_BRANCH p.Mark |= BRANCH } @@ -642,35 +765,35 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.From.Offset = -offset p.Reg = REGSP p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R4 + p.To.Reg = REG_R24 p = obj.Appendp(p, c.newprog) p.As = ASGTU p.From.Type = obj.TYPE_REG - p.From.Reg = REG_R4 - p.Reg = REG_R19 + p.From.Reg = REG_R24 + p.Reg = REG_R20 p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R19 + p.To.Reg = REG_R20 } - // q1: BNE R19, done + // q1: BNE R20, done p = obj.Appendp(p, c.newprog) q1 := p p.As = ABNE p.From.Type = obj.TYPE_REG - p.From.Reg = REG_R19 + p.From.Reg = REG_R20 p.To.Type = obj.TYPE_BRANCH p.Mark |= BRANCH - // MOV LINK, R5 + // MOV LINK, R31 p = obj.Appendp(p, c.newprog) p.As = mov p.From.Type = obj.TYPE_REG p.From.Reg = REGLINK p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R5 + p.To.Reg = REG_R31 if q != nil { q.To.SetTarget(p) p.Mark |= LABEL @@ -678,6 +801,10 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog) + // Spill the register args that could be clobbered by the + // morestack code + p = c.cursym.Func().SpillRegisterArgs(p, c.newprog) + // JAL runtime.morestack(SB) p = obj.Appendp(p, c.newprog) @@ -692,6 +819,7 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { } p.Mark |= BRANCH + p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog) p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) // JMP start diff --git a/src/cmd/internal/obj/riscv/asm_test.go b/src/cmd/internal/obj/riscv/asm_test.go index afe0525532..96ea230841 100644 --- a/src/cmd/internal/obj/riscv/asm_test.go +++ b/src/cmd/internal/obj/riscv/asm_test.go @@ -9,8 +9,10 @@ import ( "fmt" "internal/testenv" "os" + "os/exec" "path/filepath" "runtime" + "strings" "testing" ) @@ -277,3 +279,33 @@ func TestBranch(t *testing.T) { t.Errorf("Branch test failed: %v\n%s", err, out) } } + +func TestPCAlign(t *testing.T) { + dir := t.TempDir() + tmpfile := filepath.Join(dir, "x.s") + asm := ` +TEXT _stub(SB),$0-0 + FENCE + PCALIGN $8 + FENCE + RET +` + if err := os.WriteFile(tmpfile, []byte(asm), 0644); err != nil { + t.Fatal(err) + } + cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile) + cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux") + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("Failed to assemble: %v\n%s", err, out) + } + // The expected instruction sequence after alignment: + // FENCE + // NOP + // FENCE + // RET + want := "0f 00 f0 0f 13 00 00 00 0f 00 f0 0f 67 80 00 00" + if !strings.Contains(string(out), want) { + t.Errorf("PCALIGN test failed - got %s\nwant %s", out, want) + } +} diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 93bda45096..11d6c202ea 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -308,6 +308,12 @@ func setPCs(p *obj.Prog, pc int64) int64 { for _, ins := range instructionsForProg(p) { pc += int64(ins.length()) } + + if p.As == obj.APCALIGN { + alignedValue := p.From.Offset + v := pcAlignPadLength(pc, alignedValue) + pc += int64(v) + } } return pc } @@ -733,6 +739,16 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high, Sym: cursym} p.Link.To.Offset = low } + + case obj.APCALIGN: + alignedValue := p.From.Offset + if (alignedValue&(alignedValue-1) != 0) || 4 > alignedValue || alignedValue > 2048 { + ctxt.Diag("alignment value of an instruction must be a power of two and in the range [4, 2048], got %d\n", alignedValue) + } + // Update the current text symbol alignment value. + if int32(alignedValue) > cursym.Func().Align { + cursym.Func().Align = int32(alignedValue) + } } } @@ -744,6 +760,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } } +func pcAlignPadLength(pc int64, alignedValue int64) int { + return int(-pc & (alignedValue - 1)) +} + func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgAlloc, framesize int64) *obj.Prog { // Leaf function with no frame is effectively NOSPLIT. if framesize == 0 { @@ -1708,6 +1728,7 @@ var encodings = [ALAST & obj.AMask]encoding{ obj.ANOP: pseudoOpEncoding, obj.ADUFFZERO: pseudoOpEncoding, obj.ADUFFCOPY: pseudoOpEncoding, + obj.APCALIGN: pseudoOpEncoding, } // encodingForAs returns the encoding for an obj.As. @@ -2425,6 +2446,17 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { rel.Sym = addr.Sym rel.Add = addr.Offset rel.Type = rt + + case obj.APCALIGN: + alignedValue := p.From.Offset + v := pcAlignPadLength(p.Pc, alignedValue) + offset := p.Pc + for ; v >= 4; v -= 4 { + // NOP + cursym.WriteBytes(ctxt, offset, []byte{0x13, 0, 0, 0}) + offset += 4 + } + continue } offset := p.Pc diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go index bf6d48e305..7b560e0053 100644 --- a/src/cmd/internal/obj/s390x/asmz.go +++ b/src/cmd/internal/obj/s390x/asmz.go @@ -4434,7 +4434,7 @@ func (c *ctxtz) asmout(p *obj.Prog, asm *[]byte) { } zRRE(op_KDSA, uint32(p.From.Reg), uint32(p.To.Reg), asm) - case 126: // KMA and KMCTR - CIPHER MESSAGE WITH AUTHENTICATION; CIPHER MESSAGE WITH + case 126: // KMA and KMCTR - CIPHER MESSAGE WITH AUTHENTICATION; CIPHER MESSAGE WITH COUNTER var opcode uint32 switch p.As { default: @@ -4458,16 +4458,13 @@ func (c *ctxtz) asmout(p *obj.Prog, asm *[]byte) { if p.Reg&1 != 0 { c.ctxt.Diag("third argument must be even register in %v", p) } - if p.Reg == p.To.Reg || p.Reg == p.From.Reg { - c.ctxt.Diag("third argument must not be input or output argument registers in %v", p) - } if p.As == AKMA { opcode = op_KMA } else if p.As == AKMCTR { opcode = op_KMCTR } } - zRRF(opcode, uint32(p.From.Reg), 0, uint32(p.Reg), uint32(p.To.Reg), asm) + zRRF(opcode, uint32(p.Reg), 0, uint32(p.From.Reg), uint32(p.To.Reg), asm) } } diff --git a/src/cmd/internal/objabi/funcid.go b/src/cmd/internal/objabi/funcid.go index 007107e778..d9b47f1ec9 100644 --- a/src/cmd/internal/objabi/funcid.go +++ b/src/cmd/internal/objabi/funcid.go @@ -14,6 +14,7 @@ var funcIDs = map[string]abi.FuncID{ "asmcgocall": abi.FuncID_asmcgocall, "asyncPreempt": abi.FuncID_asyncPreempt, "cgocallback": abi.FuncID_cgocallback, + "corostart": abi.FuncID_corostart, "debugCallV2": abi.FuncID_debugCallV2, "gcBgMarkWorker": abi.FuncID_gcBgMarkWorker, "rt0_go": abi.FuncID_rt0_go, diff --git a/src/cmd/internal/objabi/pkgspecial.go b/src/cmd/internal/objabi/pkgspecial.go index 9bf07153a4..6df95f33f9 100644 --- a/src/cmd/internal/objabi/pkgspecial.go +++ b/src/cmd/internal/objabi/pkgspecial.go @@ -50,6 +50,7 @@ var runtimePkgs = []string{ "internal/abi", "internal/bytealg", + "internal/chacha8rand", "internal/coverage/rtcov", "internal/cpu", "internal/goarch", @@ -79,6 +80,7 @@ var allowAsmABIPkgs = []string{ "reflect", "syscall", "internal/bytealg", + "internal/chacha8rand", "runtime/internal/syscall", "runtime/internal/startlinetest", } diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go index e3e042a511..54429840b0 100644 --- a/src/cmd/internal/objabi/reloctype.go +++ b/src/cmd/internal/objabi/reloctype.go @@ -345,6 +345,11 @@ const ( R_LOONG64_TLS_IE_PCREL_HI R_LOONG64_TLS_IE_LO + // R_LOONG64_GOT_HI and R_LOONG64_GOT_LO resolves a GOT-relative instruction sequence, + // usually an pcalau12i followed by another ld or addi instruction. + R_LOONG64_GOT_HI + R_LOONG64_GOT_LO + // R_JMPLOONG64 resolves to non-PC-relative target address of a JMP instruction, // by encoding the address into the instruction. R_JMPLOONG64 diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go index e8793dedc1..c8923c0f4a 100644 --- a/src/cmd/internal/objabi/reloctype_string.go +++ b/src/cmd/internal/objabi/reloctype_string.go @@ -89,19 +89,21 @@ func _() { _ = x[R_CALLLOONG64-79] _ = x[R_LOONG64_TLS_IE_PCREL_HI-80] _ = x[R_LOONG64_TLS_IE_LO-81] - _ = x[R_JMPLOONG64-82] - _ = x[R_ADDRMIPSU-83] - _ = x[R_ADDRMIPSTLS-84] - _ = x[R_ADDRCUOFF-85] - _ = x[R_WASMIMPORT-86] - _ = x[R_XCOFFREF-87] - _ = x[R_PEIMAGEOFF-88] - _ = x[R_INITORDER-89] + _ = x[R_LOONG64_GOT_HI-82] + _ = x[R_LOONG64_GOT_LO-83] + _ = x[R_JMPLOONG64-84] + _ = x[R_ADDRMIPSU-85] + _ = x[R_ADDRMIPSTLS-86] + _ = x[R_ADDRCUOFF-87] + _ = x[R_WASMIMPORT-88] + _ = x[R_XCOFFREF-89] + _ = x[R_PEIMAGEOFF-90] + _ = x[R_INITORDER-91] } -const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_ADDRLOONG64R_ADDRLOONG64UR_ADDRLOONG64TLSR_ADDRLOONG64TLSUR_CALLLOONG64R_LOONG64_TLS_IE_PCREL_HIR_LOONG64_TLS_IE_LOR_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDER" +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_ADDRLOONG64R_ADDRLOONG64UR_ADDRLOONG64TLSR_ADDRLOONG64TLSUR_CALLLOONG64R_LOONG64_TLS_IE_PCREL_HIR_LOONG64_TLS_IE_LOR_LOONG64_GOT_HIR_LOONG64_GOT_LOR_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDER" -var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 328, 344, 355, 368, 387, 407, 427, 447, 460, 474, 488, 502, 517, 531, 545, 556, 578, 600, 614, 629, 652, 669, 687, 708, 723, 742, 753, 770, 782, 801, 820, 834, 848, 864, 882, 902, 922, 936, 954, 970, 980, 993, 1007, 1023, 1040, 1053, 1078, 1097, 1109, 1120, 1133, 1144, 1156, 1166, 1178, 1189} +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 328, 344, 355, 368, 387, 407, 427, 447, 460, 474, 488, 502, 517, 531, 545, 556, 578, 600, 614, 629, 652, 669, 687, 708, 723, 742, 753, 770, 782, 801, 820, 834, 848, 864, 882, 902, 922, 936, 954, 970, 980, 993, 1007, 1023, 1040, 1053, 1078, 1097, 1113, 1129, 1141, 1152, 1165, 1176, 1188, 1198, 1210, 1221} func (i RelocType) String() string { i -= 1 diff --git a/src/cmd/internal/testdir/testdir_test.go b/src/cmd/internal/testdir/testdir_test.go index 1b91dbe3ce..0fb56e6c78 100644 --- a/src/cmd/internal/testdir/testdir_test.go +++ b/src/cmd/internal/testdir/testdir_test.go @@ -1458,7 +1458,7 @@ var ( archVariants = map[string][]string{ "386": {"GO386", "sse2", "softfloat"}, "amd64": {"GOAMD64", "v1", "v2", "v3", "v4"}, - "arm": {"GOARM", "5", "6", "7"}, + "arm": {"GOARM", "5", "6", "7", "7,softfloat"}, "arm64": {}, "loong64": {}, "mips": {"GOMIPS", "hardfloat", "softfloat"}, diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go index c5f43a2954..b0f2700ac1 100644 --- a/src/cmd/link/doc.go +++ b/src/cmd/link/doc.go @@ -43,10 +43,10 @@ Flags: or initialized to a constant string expression. -X will not work if the initializer makes a function call or refers to other variables. Note that before Go 1.5 this option took two separate arguments. - -a - Disassemble output. -asan Link with C/C++ address sanitizer support. + -aslr + Enable ASLR for buildmode=c-shared on windows (default true). -buildid id Record id as Go toolchain build id. -buildmode mode @@ -64,8 +64,6 @@ Flags: The dynamic header is on by default, even without any references to dynamic libraries, because many common system tools now assume the presence of the header. - -debugtramp int - Debug trampolines. -dumpdep Dump symbol dependency graph. -extar ar @@ -104,8 +102,6 @@ Flags: Set runtime.MemProfileRate to rate. -msan Link with C/C++ memory sanitizer support. - -n - Dump symbol table. -o file Write output to file (default a.out, or a.out.exe on Windows). -pluginpath path @@ -116,13 +112,9 @@ Flags: Link with race detection libraries. -s Omit the symbol table and debug information. - -shared - Generated shared object (implies -linkmode external; experimental). -tmpdir dir Write temporary files to dir. Temporary files are only used in external linking mode. - -u - Reject unsafe packages. -v Print trace of linker operations. -w diff --git a/src/cmd/link/internal/dwtest/dwtest.go b/src/cmd/link/internal/dwtest/dwtest.go index c68edf4187..3fb02ee1db 100644 --- a/src/cmd/link/internal/dwtest/dwtest.go +++ b/src/cmd/link/internal/dwtest/dwtest.go @@ -90,7 +90,7 @@ func (ex *Examiner) DumpEntry(idx int, dumpKids bool, ilevel int) { fmt.Printf("0x%x: %v\n", idx, entry.Tag) for _, f := range entry.Field { indent(ilevel) - fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val) + fmt.Printf("at=%v val=%v\n", f.Attr, f.Val) } if dumpKids { ksl := ex.kids[idx] diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 2d761c7ee7..f4ea8407c8 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -56,10 +56,11 @@ import ( func isRuntimeDepPkg(pkg string) bool { switch pkg { case "runtime", - "sync/atomic", // runtime may call to sync/atomic, due to go:linkname - "internal/abi", // used by reflectcall (and maybe more) - "internal/bytealg", // for IndexByte - "internal/cpu": // for cpu features + "sync/atomic", // runtime may call to sync/atomic, due to go:linkname + "internal/abi", // used by reflectcall (and maybe more) + "internal/bytealg", // for IndexByte + "internal/chacha8rand", // for rand + "internal/cpu": // for cpu features return true } return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test") diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index dcbe719c96..17f2803ebf 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -748,6 +748,7 @@ func (d *dwctxt) defptrto(dwtype loader.Sym) loader.Sym { // pointers of slices. Link to the ones we can find. gts := d.ldr.Lookup("type:"+ptrname, 0) if gts != 0 && d.ldr.AttrReachable(gts) { + newattr(pdie, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(objabi.KindPtr), 0) newattr(pdie, dwarf.DW_AT_go_runtime_type, dwarf.DW_CLS_GO_TYPEREF, 0, dwSym(gts)) } diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 6ca2a844f5..e431427249 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -1985,17 +1985,7 @@ func main() { } } -func TestZeroSizedVariable(t *testing.T) { - testenv.MustHaveGoBuild(t) - - mustHaveDWARF(t) - t.Parallel() - - // This test verifies that the compiler emits DIEs for zero sized variables - // (for example variables of type 'struct {}'). - // See go.dev/issues/54615. - - const prog = ` +const zeroSizedVarProg = ` package main import ( @@ -2008,10 +1998,24 @@ func main() { } ` +func TestZeroSizedVariable(t *testing.T) { + testenv.MustHaveGoBuild(t) + + mustHaveDWARF(t) + t.Parallel() + + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + // This test verifies that the compiler emits DIEs for zero sized variables + // (for example variables of type 'struct {}'). + // See go.dev/issues/54615. + for _, opt := range []string{NoOpt, DefaultOpt} { opt := opt t.Run(opt, func(t *testing.T) { - _, ex := gobuildAndExamine(t, prog, opt) + _, ex := gobuildAndExamine(t, zeroSizedVarProg, opt) // Locate the main.zeroSizedVariable DIE abcs := ex.Named("zeroSizedVariable") @@ -2024,3 +2028,46 @@ func main() { }) } } + +func TestConsistentGoKindAndRuntimeType(t *testing.T) { + testenv.MustHaveGoBuild(t) + + mustHaveDWARF(t) + t.Parallel() + + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + // Ensure that if we emit a "go runtime type" attr on a type DIE, + // we also include the "go kind" attribute. See issue #64231. + _, ex := gobuildAndExamine(t, zeroSizedVarProg, DefaultOpt) + + // Walk all dies. + typesChecked := 0 + failures := 0 + for _, die := range ex.DIEs() { + // For any type DIE with DW_AT_go_runtime_type set... + rtt, hasRT := die.Val(intdwarf.DW_AT_go_runtime_type).(uint64) + if !hasRT || rtt == 0 { + continue + } + typesChecked++ + // ... we want to see a meaningful DW_AT_go_kind value. + if val, ok := die.Val(intdwarf.DW_AT_go_kind).(int64); !ok || val == 0 { + failures++ + // dump DIEs for first 10 failures. + if failures <= 10 { + idx := ex.IdxFromOffset(die.Offset) + t.Logf("type DIE has DW_AT_go_runtime_type but invalid DW_AT_go_kind:\n") + ex.DumpEntry(idx, false, 0) + } + t.Errorf("bad type DIE at offset %d\n", die.Offset) + } + } + if typesChecked == 0 { + t.Fatalf("something went wrong, 0 types checked") + } else { + t.Logf("%d types checked\n", typesChecked) + } +} diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index b603fba6c7..eab74dc328 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -878,7 +878,17 @@ func (ctxt *Link) linksetup() { sb := ctxt.loader.MakeSymbolUpdater(goarm) sb.SetType(sym.SDATA) sb.SetSize(0) - sb.AddUint8(uint8(buildcfg.GOARM)) + sb.AddUint8(uint8(buildcfg.GOARM.Version)) + + goarmsoftfp := ctxt.loader.LookupOrCreateSym("runtime.goarmsoftfp", 0) + sb2 := ctxt.loader.MakeSymbolUpdater(goarmsoftfp) + sb2.SetType(sym.SDATA) + sb2.SetSize(0) + if buildcfg.GOARM.SoftFloat { + sb2.AddUint8(1) + } else { + sb2.AddUint8(0) + } } // Set runtime.disableMemoryProfiling bool if diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index e120f90a22..feb4ba5c17 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -90,7 +90,7 @@ var ( flagF = flag.Bool("f", false, "ignore version mismatch") flagG = flag.Bool("g", false, "disable go package data checks") flagH = flag.Bool("h", false, "halt on error") - flagN = flag.Bool("n", false, "dump symbol table") + flagN = flag.Bool("n", false, "no-op (deprecated)") FlagS = flag.Bool("s", false, "disable symbol table") flag8 bool // use 64-bit addresses in symbol table flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker") diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go index 2f887366b7..d915ab393b 100644 --- a/src/cmd/link/internal/ld/xcoff.go +++ b/src/cmd/link/internal/ld/xcoff.go @@ -1140,7 +1140,7 @@ func (f *xcoffFile) asmaixsym(ctxt *Link) { putaixsym(ctxt, s, TextSym) } - if ctxt.Debugvlog != 0 || *flagN { + if ctxt.Debugvlog != 0 { ctxt.Logf("symsize = %d\n", uint32(symSize)) } xfile.updatePreviousFile(ctxt, true) diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 5dd657b4d7..3edb5e2f6f 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -988,7 +988,7 @@ func (l *Loader) AttrExternal(i Sym) bool { return l.attrExternal.Has(l.extIndex(i)) } -// SetAttrExternal sets the "external" property for an host object +// SetAttrExternal sets the "external" property for a host object // symbol (see AttrExternal). func (l *Loader) SetAttrExternal(i Sym, v bool) { if !l.IsExternal(i) { diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go index e4134607c4..1ba6debb4e 100644 --- a/src/cmd/link/internal/loadpe/ldpe.go +++ b/src/cmd/link/internal/loadpe/ldpe.go @@ -493,17 +493,10 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read continue } if pesym.SectionNumber == IMAGE_SYM_ABSOLUTE && bytes.Equal(pesym.Name[:], []byte("@feat.00")) { - // Microsoft's linker looks at whether all input objects have an empty - // section called @feat.00. If all of them do, then it enables SEH; - // otherwise it doesn't enable that feature. So, since around the Windows - // XP SP2 era, most tools that make PE objects just tack on that section, - // so that it won't gimp Microsoft's linker logic. Go doesn't support SEH, - // so in theory, none of this really matters to us. But actually, if the - // linker tries to ingest an object with @feat.00 -- which are produced by - // LLVM's resource compiler, for example -- it chokes because of the - // IMAGE_SYM_ABSOLUTE section that it doesn't know how to deal with. Since - // @feat.00 is just a marking anyway, skip IMAGE_SYM_ABSOLUTE sections that - // are called @feat.00. + // The PE documentation says that, on x86 platforms, the absolute symbol named @feat.00 + // is used to indicate that the COFF object supports SEH. + // Go doesn't support SEH on windows/386, so we can ignore this symbol. + // See https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-sxdata-section continue } var sect *pe.Section diff --git a/src/cmd/link/internal/loadpe/seh.go b/src/cmd/link/internal/loadpe/seh.go index 0e2cda21dd..545958f1d6 100644 --- a/src/cmd/link/internal/loadpe/seh.go +++ b/src/cmd/link/internal/loadpe/seh.go @@ -16,8 +16,9 @@ import ( const ( UNW_FLAG_EHANDLER = 1 << 3 UNW_FLAG_UHANDLER = 2 << 3 - UNW_FLAG_CHAININFO = 3 << 3 - unwStaticDataSize = 8 + UNW_FLAG_CHAININFO = 4 << 3 + unwStaticDataSize = 4 // Bytes of unwind data before the variable length part. + unwCodeSize = 2 // Bytes per unwind code. ) // processSEH walks all pdata relocations looking for exception handler function symbols. @@ -81,14 +82,14 @@ func findHandlerInXDataAMD64(ldr *loader.Loader, xsym sym.LoaderSym, add int64) // Nothing to do. return 0 } - codes := data[3] + codes := data[2] if codes%2 != 0 { // There are always an even number of unwind codes, even if the last one is unused. codes += 1 } // The exception handler relocation is the first relocation after the unwind codes, // unless it is chained, but we will handle this case later. - targetOff := add + unwStaticDataSize*(1+int64(codes)) + targetOff := add + unwStaticDataSize + unwCodeSize*int64(codes) xrels := ldr.Relocs(xsym) xrelsCount := xrels.Count() idx := sort.Search(xrelsCount, func(i int) bool { diff --git a/src/cmd/link/internal/loadxcoff/ldxcoff.go b/src/cmd/link/internal/loadxcoff/ldxcoff.go index 920e1c85fd..29d162596a 100644 --- a/src/cmd/link/internal/loadxcoff/ldxcoff.go +++ b/src/cmd/link/internal/loadxcoff/ldxcoff.go @@ -155,7 +155,6 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read } } return textp, nil - } // Convert symbol xcoff type to sym.SymKind diff --git a/src/cmd/link/internal/loong64/asm.go b/src/cmd/link/internal/loong64/asm.go index 3c58c27d82..6607e5dc64 100644 --- a/src/cmd/link/internal/loong64/asm.go +++ b/src/cmd/link/internal/loong64/asm.go @@ -14,7 +14,47 @@ import ( "log" ) -func gentext(ctxt *ld.Link, ldr *loader.Loader) {} +func gentext(ctxt *ld.Link, ldr *loader.Loader) { + initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt) + if initfunc == nil { + return + } + + o := func(op uint32) { + initfunc.AddUint32(ctxt.Arch, op) + } + + // Emit the following function: + // + // local.dso_init: + // la.pcrel $a0, local.moduledata + // b runtime.addmoduledata + + // 0000000000000000 : + // 0: 1a000004 pcalau12i $a0, 0 + // 0: R_LARCH_PCALA_HI20 local.moduledata + o(0x1a000004) + rel, _ := initfunc.AddRel(objabi.R_ADDRLOONG64U) + rel.SetOff(0) + rel.SetSiz(4) + rel.SetSym(ctxt.Moduledata) + + // 4: 02c00084 addi.d $a0, $a0, 0 + // 4: R_LARCH_PCALA_LO12 local.moduledata + o(0x02c00084) + rel2, _ := initfunc.AddRel(objabi.R_ADDRLOONG64) + rel2.SetOff(4) + rel2.SetSiz(4) + rel2.SetSym(ctxt.Moduledata) + + // 8: 50000000 b 0 + // 8: R_LARCH_B26 runtime.addmoduledata + o(0x50000000) + rel3, _ := initfunc.AddRel(objabi.R_CALLLOONG64) + rel3.SetOff(8) + rel3.SetSiz(4) + rel3.SetSym(addmoduledata) +} func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { log.Fatalf("adddynrel not implemented") @@ -78,6 +118,16 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, out.Write64(uint64(sectoff)) out.Write64(uint64(elf.R_LARCH_PCALA_HI20) | uint64(elfsym)<<32) out.Write64(uint64(r.Xadd)) + + case objabi.R_LOONG64_GOT_HI: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_GOT_PC_HI20) | uint64(elfsym)<<32) + out.Write64(uint64(0x0)) + + case objabi.R_LOONG64_GOT_LO: + out.Write64(uint64(sectoff)) + out.Write64(uint64(elf.R_LARCH_GOT_PC_LO12) | uint64(elfsym)<<32) + out.Write64(uint64(0x0)) } return true @@ -111,7 +161,9 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade objabi.R_CALLLOONG64, objabi.R_JMPLOONG64, objabi.R_LOONG64_TLS_IE_PCREL_HI, - objabi.R_LOONG64_TLS_IE_LO: + objabi.R_LOONG64_TLS_IE_LO, + objabi.R_LOONG64_GOT_HI, + objabi.R_LOONG64_GOT_LO: return val, 1, true } } @@ -156,7 +208,10 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) { switch r.Type() { case objabi.R_ADDRLOONG64, - objabi.R_ADDRLOONG64U: + objabi.R_ADDRLOONG64U, + objabi.R_LOONG64_GOT_HI, + objabi.R_LOONG64_GOT_LO: + return ld.ExtrelocViaOuterSym(ldr, r, s), true case objabi.R_ADDRLOONG64TLS, diff --git a/src/cmd/link/internal/mips/obj.go b/src/cmd/link/internal/mips/obj.go index e07ee0b208..c76e267cc2 100644 --- a/src/cmd/link/internal/mips/obj.go +++ b/src/cmd/link/internal/mips/obj.go @@ -103,5 +103,4 @@ func archinit(ctxt *ld.Link) { func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool { ld.Exitf("adddynrel currently unimplemented for MIPS") return false - } diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 91eef5e461..09647d84b1 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -917,7 +917,6 @@ func xcoffreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy emitReloc(ld.XCOFF_R_REF|0x3F<<8, 0) } return true - } func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool { diff --git a/src/cmd/trace/annotations.go b/src/cmd/trace/annotations.go index 0addc240be..df194a7598 100644 --- a/src/cmd/trace/annotations.go +++ b/src/cmd/trace/annotations.go @@ -9,8 +9,8 @@ import ( "fmt" "html/template" "internal/trace" + "internal/trace/traceviewer" "log" - "math" "net/http" "net/url" "reflect" @@ -808,122 +808,9 @@ func newRegionFilter(r *http.Request) (*regionFilter, error) { }, nil } -type durationHistogram struct { - Count int - Buckets []int - MinBucket, MaxBucket int -} - -// Five buckets for every power of 10. -var logDiv = math.Log(math.Pow(10, 1.0/5)) - -func (h *durationHistogram) add(d time.Duration) { - var bucket int - if d > 0 { - bucket = int(math.Log(float64(d)) / logDiv) - } - if len(h.Buckets) <= bucket { - h.Buckets = append(h.Buckets, make([]int, bucket-len(h.Buckets)+1)...) - h.Buckets = h.Buckets[:cap(h.Buckets)] - } - h.Buckets[bucket]++ - if bucket < h.MinBucket || h.MaxBucket == 0 { - h.MinBucket = bucket - } - if bucket > h.MaxBucket { - h.MaxBucket = bucket - } - h.Count++ -} - -func (h *durationHistogram) BucketMin(bucket int) time.Duration { - return time.Duration(math.Exp(float64(bucket) * logDiv)) -} - -func niceDuration(d time.Duration) string { - var rnd time.Duration - var unit string - switch { - case d < 10*time.Microsecond: - rnd, unit = time.Nanosecond, "ns" - case d < 10*time.Millisecond: - rnd, unit = time.Microsecond, "µs" - case d < 10*time.Second: - rnd, unit = time.Millisecond, "ms" - default: - rnd, unit = time.Second, "s " - } - return fmt.Sprintf("%d%s", d/rnd, unit) -} - -func (h *durationHistogram) ToHTML(urlmaker func(min, max time.Duration) string) template.HTML { - if h == nil || h.Count == 0 { - return template.HTML("") - } - - const barWidth = 400 - - maxCount := 0 - for _, count := range h.Buckets { - if count > maxCount { - maxCount = count - } - } - - w := new(strings.Builder) - fmt.Fprintf(w, ``) - for i := h.MinBucket; i <= h.MaxBucket; i++ { - // Tick label. - if h.Buckets[i] > 0 { - fmt.Fprintf(w, ``, urlmaker(h.BucketMin(i), h.BucketMin(i+1)), niceDuration(h.BucketMin(i))) - } else { - fmt.Fprintf(w, ``, niceDuration(h.BucketMin(i))) - } - // Bucket bar. - width := h.Buckets[i] * barWidth / maxCount - fmt.Fprintf(w, ``, width) - // Bucket count. - fmt.Fprintf(w, ``, h.Buckets[i]) - fmt.Fprintf(w, "\n") - - } - // Final tick label. - fmt.Fprintf(w, ``, niceDuration(h.BucketMin(h.MaxBucket+1))) - fmt.Fprintf(w, `
    %s
    %s
     
    %d
    %s
    `) - return template.HTML(w.String()) -} - -func (h *durationHistogram) String() string { - const barWidth = 40 - - labels := []string{} - maxLabel := 0 - maxCount := 0 - for i := h.MinBucket; i <= h.MaxBucket; i++ { - // TODO: This formatting is pretty awful. - label := fmt.Sprintf("[%-12s%-11s)", h.BucketMin(i).String()+",", h.BucketMin(i+1)) - labels = append(labels, label) - if len(label) > maxLabel { - maxLabel = len(label) - } - count := h.Buckets[i] - if count > maxCount { - maxCount = count - } - } - - w := new(strings.Builder) - for i := h.MinBucket; i <= h.MaxBucket; i++ { - count := h.Buckets[i] - bar := count * barWidth / maxCount - fmt.Fprintf(w, "%*s %-*s %d\n", maxLabel, labels[i-h.MinBucket], barWidth, strings.Repeat("█", bar), count) - } - return w.String() -} - type regionStats struct { regionTypeID - Histogram durationHistogram + Histogram traceviewer.TimeHistogram } func (s *regionStats) UserRegionURL() func(min, max time.Duration) string { @@ -933,7 +820,7 @@ func (s *regionStats) UserRegionURL() func(min, max time.Duration) string { } func (s *regionStats) add(region regionDesc) { - s.Histogram.add(region.duration()) + s.Histogram.Add(region.duration()) } var templUserRegionTypes = template.Must(template.New("").Parse(` @@ -966,8 +853,8 @@ var templUserRegionTypes = template.Must(template.New("").Parse(` type taskStats struct { Type string - Count int // Complete + incomplete tasks - Histogram durationHistogram // Complete tasks only + Count int // Complete + incomplete tasks + Histogram traceviewer.TimeHistogram // Complete tasks only } func (s *taskStats) UserTaskURL(complete bool) func(min, max time.Duration) string { @@ -979,7 +866,7 @@ func (s *taskStats) UserTaskURL(complete bool) func(min, max time.Duration) stri func (s *taskStats) add(task *taskDesc) { s.Count++ if task.complete() { - s.Histogram.add(task.duration()) + s.Histogram.Add(task.duration()) } } @@ -1169,7 +1056,7 @@ func isUserAnnotationEvent(ev *trace.Event) (taskID uint64, ok bool) { var templUserRegionType = template.Must(template.New("").Funcs(template.FuncMap{ "prettyDuration": func(nsec int64) template.HTML { d := time.Duration(nsec) * time.Nanosecond - return template.HTML(niceDuration(d)) + return template.HTML(d.String()) }, "percent": func(dividend, divisor int64) template.HTML { if divisor == 0 { diff --git a/src/cmd/trace/goroutines.go b/src/cmd/trace/goroutines.go index 7850fc0ff1..28eace82fa 100644 --- a/src/cmd/trace/goroutines.go +++ b/src/cmd/trace/goroutines.go @@ -169,7 +169,7 @@ func httpGoroutine(w http.ResponseWriter, r *http.Request) { var templGoroutine = template.Must(template.New("").Funcs(template.FuncMap{ "prettyDuration": func(nsec int64) template.HTML { d := time.Duration(nsec) * time.Nanosecond - return template.HTML(niceDuration(d)) + return template.HTML(d.String()) }, "percent": func(dividend, divisor int64) template.HTML { if divisor == 0 { diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index 9e9e7f3e49..5f0d6f612b 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -7,11 +7,11 @@ package main import ( "bufio" "cmd/internal/browser" + cmdv2 "cmd/trace/v2" "flag" "fmt" - "html/template" "internal/trace" - "io" + "internal/trace/traceviewer" "log" "net" "net/http" @@ -46,7 +46,7 @@ Supported profile types are: Flags: -http=addr: HTTP service address (e.g., ':6060') -pprof=type: print a pprof-like profile instead - -d: print debug info such as parsed events + -d=int: print debug info such as parsed events (1 for high-level, 2 for low-level) Note that while the various profiles available when launching 'go tool trace' work on every browser, the trace viewer itself @@ -57,7 +57,7 @@ and is only actively tested on that browser. var ( httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')") pprofFlag = flag.String("pprof", "", "print a pprof-like profile instead") - debugFlag = flag.Bool("d", false, "print debug information such as parsed events list") + debugFlag = flag.Int("d", 0, "print debug information (1 for basic debug info, 2 for lower-level info)") // The binary file name, left here for serveSVGProfile. programBinary string @@ -83,7 +83,14 @@ func main() { flag.Usage() } - var pprofFunc func(io.Writer, *http.Request) error + if isTraceV2(traceFile) { + if err := cmdv2.Main(traceFile, *httpFlag, *pprofFlag, *debugFlag); err != nil { + dief("%s\n", err) + } + return + } + + var pprofFunc traceviewer.ProfileFunc switch *pprofFlag { case "net": pprofFunc = pprofByGoroutine(computePprofIO) @@ -95,7 +102,11 @@ func main() { pprofFunc = pprofByGoroutine(computePprofSched) } if pprofFunc != nil { - if err := pprofFunc(os.Stdout, &http.Request{}); err != nil { + records, err := pprofFunc(&http.Request{}) + if err != nil { + dief("failed to generate pprof: %v\n", err) + } + if err := traceviewer.BuildProfile(records).Write(os.Stdout); err != nil { dief("failed to generate pprof: %v\n", err) } os.Exit(0) @@ -115,7 +126,7 @@ func main() { dief("%v\n", err) } - if *debugFlag { + if *debugFlag != 0 { trace.Print(res.Events) os.Exit(0) } @@ -131,13 +142,35 @@ func main() { log.Printf("Opening browser. Trace viewer is listening on %s", addr) browser.Open(addr) + // Install MMU handler. + http.HandleFunc("/mmu", traceviewer.MMUHandlerFunc(ranges, mutatorUtil)) + + // Install main handler. + http.Handle("/", traceviewer.MainHandler([]traceviewer.View{ + {Type: traceviewer.ViewProc, Ranges: ranges}, + })) + // Start http server. - http.HandleFunc("/", httpMain) err = http.Serve(ln, nil) dief("failed to start http server: %v\n", err) } -var ranges []Range +// isTraceV2 returns true if filename holds a v2 trace. +func isTraceV2(filename string) bool { + file, err := os.Open(filename) + if err != nil { + return false + } + defer file.Close() + + ver, _, err := trace.ReadVersion(file) + if err != nil { + return false + } + return ver >= 1022 +} + +var ranges []traceviewer.Range var loader struct { once sync.Once @@ -175,209 +208,6 @@ func parseTrace() (trace.ParseResult, error) { return loader.res, loader.err } -// httpMain serves the starting page. -func httpMain(w http.ResponseWriter, r *http.Request) { - if err := templMain.Execute(w, ranges); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -var templMain = template.Must(template.New("").Parse(` - - - -

    cmd/trace: the Go trace event viewer

    -

    - This web server provides various visualizations of an event log gathered during - the execution of a Go program that uses the runtime/trace package. -

    - -

    Event timelines for running goroutines

    -{{if $}} -

    - Large traces are split into multiple sections of equal data size - (not duration) to avoid overwhelming the visualizer. -

    - -{{else}} - -{{end}} -

    - This view displays a timeline for each of the GOMAXPROCS logical - processors, showing which goroutine (if any) was running on that - logical processor at each moment. - - Each goroutine has an identifying number (e.g. G123), main function, - and color. - - A colored bar represents an uninterrupted span of execution. - - Execution of a goroutine may migrate from one logical processor to another, - causing a single colored bar to be horizontally continuous but - vertically displaced. -

    -

    - Clicking on a span reveals information about it, such as its - duration, its causal predecessors and successors, and the stack trace - at the final moment when it yielded the logical processor, for example - because it made a system call or tried to acquire a mutex. - - Directly underneath each bar, a smaller bar or more commonly a fine - vertical line indicates an event occurring during its execution. - Some of these are related to garbage collection; most indicate that - a goroutine yielded its logical processor but then immediately resumed execution - on the same logical processor. Clicking on the event displays the stack trace - at the moment it occurred. -

    -

    - The causal relationships between spans of goroutine execution - can be displayed by clicking the Flow Events button at the top. -

    -

    - At the top ("STATS"), there are three additional timelines that - display statistical information. - - "Goroutines" is a time series of the count of existing goroutines; - clicking on it displays their breakdown by state at that moment: - running, runnable, or waiting. - - "Heap" is a time series of the amount of heap memory allocated (in orange) - and (in green) the allocation limit at which the next GC cycle will begin. - - "Threads" shows the number of kernel threads in existence: there is - always one kernel thread per logical processor, and additional threads - are created for calls to non-Go code such as a system call or a - function written in C. -

    -

    - Above the event trace for the first logical processor are - traces for various runtime-internal events. - - The "GC" bar shows when the garbage collector is running, and in which stage. - Garbage collection may temporarily affect all the logical processors - and the other metrics. - - The "Network", "Timers", and "Syscalls" traces indicate events in - the runtime that cause goroutines to wake up. -

    -

    - The visualization allows you to navigate events at scales ranging from several - seconds to a handful of nanoseconds. - - Consult the documentation for the Chromium Trace Event Profiling Tool - for help navigating the view. -

    - - -

    - This view displays information about each set of goroutines that - shares the same main function. - - Clicking on a main function shows links to the four types of - blocking profile (see below) applied to that subset of goroutines. - - It also shows a table of specific goroutine instances, with various - execution statistics and a link to the event timeline for each one. - - The timeline displays only the selected goroutine and any others it - interacts with via block/unblock events. (The timeline is - goroutine-oriented rather than logical processor-oriented.) -

    - -

    Profiles

    -

    - Each link below displays a global profile in zoomable graph form as - produced by pprof's "web" command. - - In addition there is a link to download the profile for offline - analysis with pprof. - - All four profiles represent causes of delay that prevent a goroutine - from running on a logical processor: because it was waiting for the network, - for a synchronization operation on a mutex or channel, for a system call, - or for a logical processor to become available. -

    - - -

    User-defined tasks and regions

    -

    - The trace API allows a target program to annotate a region of code - within a goroutine, such as a key function, so that its performance - can be analyzed. - - Log events may be - associated with a region to record progress and relevant values. - - The API also allows annotation of higher-level - tasks, - which may involve work across many goroutines. -

    -

    - The links below display, for each region and task, a histogram of its execution times. - - Each histogram bucket contains a sample trace that records the - sequence of events such as goroutine creations, log events, and - subregion start/end times. - - For each task, you can click through to a logical-processor or - goroutine-oriented view showing the tasks and regions on the - timeline. - - Such information may help uncover which steps in a region are - unexpectedly slow, or reveal relationships between the data values - logged in a request and its running time. -

    - - -

    Garbage collection metrics

    - -

    - This chart indicates the maximum GC pause time (the largest x value - for which y is zero), and more generally, the fraction of time that - the processors are available to application goroutines ("mutators"), - for any time window of a specified size, in the worst case. -

    - - -`)) - func dief(msg string, args ...any) { fmt.Fprintf(os.Stderr, msg, args...) os.Exit(1) @@ -408,3 +238,11 @@ func reportMemoryUsage(msg string) { fmt.Printf("Enter to continue...") fmt.Scanf("%s", &dummy) } + +func mutatorUtil(flags trace.UtilFlags) ([][]trace.MutatorUtil, error) { + events, err := parseEvents() + if err != nil { + return nil, err + } + return trace.MutatorUtilization(events, flags), nil +} diff --git a/src/cmd/trace/pprof.go b/src/cmd/trace/pprof.go index a73ff5336a..3722b37ab8 100644 --- a/src/cmd/trace/pprof.go +++ b/src/cmd/trace/pprof.go @@ -7,51 +7,25 @@ package main import ( - "bufio" "fmt" "internal/trace" - "io" + "internal/trace/traceviewer" "net/http" - "os" - "os/exec" - "path/filepath" - "runtime" "sort" "strconv" "time" - - "github.com/google/pprof/profile" ) -func goCmd() string { - var exeSuffix string - if runtime.GOOS == "windows" { - exeSuffix = ".exe" - } - path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) - if _, err := os.Stat(path); err == nil { - return path - } - return "go" -} - func init() { - http.HandleFunc("/io", serveSVGProfile(pprofByGoroutine(computePprofIO))) - http.HandleFunc("/block", serveSVGProfile(pprofByGoroutine(computePprofBlock))) - http.HandleFunc("/syscall", serveSVGProfile(pprofByGoroutine(computePprofSyscall))) - http.HandleFunc("/sched", serveSVGProfile(pprofByGoroutine(computePprofSched))) + http.HandleFunc("/io", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofIO))) + http.HandleFunc("/block", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofBlock))) + http.HandleFunc("/syscall", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSyscall))) + http.HandleFunc("/sched", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSched))) - http.HandleFunc("/regionio", serveSVGProfile(pprofByRegion(computePprofIO))) - http.HandleFunc("/regionblock", serveSVGProfile(pprofByRegion(computePprofBlock))) - http.HandleFunc("/regionsyscall", serveSVGProfile(pprofByRegion(computePprofSyscall))) - http.HandleFunc("/regionsched", serveSVGProfile(pprofByRegion(computePprofSched))) -} - -// Record represents one entry in pprof-like profiles. -type Record struct { - stk []*trace.Frame - n uint64 - time int64 + http.HandleFunc("/regionio", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofIO))) + http.HandleFunc("/regionblock", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofBlock))) + http.HandleFunc("/regionsyscall", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSyscall))) + http.HandleFunc("/regionsched", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSched))) } // interval represents a time interval in the trace. @@ -59,34 +33,34 @@ type interval struct { begin, end int64 // nanoseconds. } -func pprofByGoroutine(compute func(io.Writer, map[uint64][]interval, []*trace.Event) error) func(w io.Writer, r *http.Request) error { - return func(w io.Writer, r *http.Request) error { +func pprofByGoroutine(compute computePprofFunc) traceviewer.ProfileFunc { + return func(r *http.Request) ([]traceviewer.ProfileRecord, error) { id := r.FormValue("id") events, err := parseEvents() if err != nil { - return err + return nil, err } gToIntervals, err := pprofMatchingGoroutines(id, events) if err != nil { - return err + return nil, err } - return compute(w, gToIntervals, events) + return compute(gToIntervals, events) } } -func pprofByRegion(compute func(io.Writer, map[uint64][]interval, []*trace.Event) error) func(w io.Writer, r *http.Request) error { - return func(w io.Writer, r *http.Request) error { +func pprofByRegion(compute computePprofFunc) traceviewer.ProfileFunc { + return func(r *http.Request) ([]traceviewer.ProfileRecord, error) { filter, err := newRegionFilter(r) if err != nil { - return err + return nil, err } gToIntervals, err := pprofMatchingRegions(filter) if err != nil { - return err + return nil, err } events, _ := parseEvents() - return compute(w, gToIntervals, events) + return compute(gToIntervals, events) } } @@ -170,9 +144,11 @@ func pprofMatchingRegions(filter *regionFilter) (map[uint64][]interval, error) { return gToIntervals, nil } +type computePprofFunc func(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) + // computePprofIO generates IO pprof-like profile (time spent in IO wait, currently only network blocking event). -func computePprofIO(w io.Writer, gToIntervals map[uint64][]interval, events []*trace.Event) error { - prof := make(map[uint64]Record) +func computePprofIO(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) { + prof := make(map[uint64]traceviewer.ProfileRecord) for _, ev := range events { if ev.Type != trace.EvGoBlockNet || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { continue @@ -180,18 +156,18 @@ func computePprofIO(w io.Writer, gToIntervals map[uint64][]interval, events []*t overlapping := pprofOverlappingDuration(gToIntervals, ev) if overlapping > 0 { rec := prof[ev.StkID] - rec.stk = ev.Stk - rec.n++ - rec.time += overlapping.Nanoseconds() + rec.Stack = ev.Stk + rec.Count++ + rec.Time += overlapping prof[ev.StkID] = rec } } - return buildProfile(prof).Write(w) + return recordsOf(prof), nil } // computePprofBlock generates blocking pprof-like profile (time spent blocked on synchronization primitives). -func computePprofBlock(w io.Writer, gToIntervals map[uint64][]interval, events []*trace.Event) error { - prof := make(map[uint64]Record) +func computePprofBlock(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) { + prof := make(map[uint64]traceviewer.ProfileRecord) for _, ev := range events { switch ev.Type { case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect, @@ -208,18 +184,18 @@ func computePprofBlock(w io.Writer, gToIntervals map[uint64][]interval, events [ overlapping := pprofOverlappingDuration(gToIntervals, ev) if overlapping > 0 { rec := prof[ev.StkID] - rec.stk = ev.Stk - rec.n++ - rec.time += overlapping.Nanoseconds() + rec.Stack = ev.Stk + rec.Count++ + rec.Time += overlapping prof[ev.StkID] = rec } } - return buildProfile(prof).Write(w) + return recordsOf(prof), nil } // computePprofSyscall generates syscall pprof-like profile (time spent blocked in syscalls). -func computePprofSyscall(w io.Writer, gToIntervals map[uint64][]interval, events []*trace.Event) error { - prof := make(map[uint64]Record) +func computePprofSyscall(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) { + prof := make(map[uint64]traceviewer.ProfileRecord) for _, ev := range events { if ev.Type != trace.EvGoSysCall || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { continue @@ -227,19 +203,19 @@ func computePprofSyscall(w io.Writer, gToIntervals map[uint64][]interval, events overlapping := pprofOverlappingDuration(gToIntervals, ev) if overlapping > 0 { rec := prof[ev.StkID] - rec.stk = ev.Stk - rec.n++ - rec.time += overlapping.Nanoseconds() + rec.Stack = ev.Stk + rec.Count++ + rec.Time += overlapping prof[ev.StkID] = rec } } - return buildProfile(prof).Write(w) + return recordsOf(prof), nil } // computePprofSched generates scheduler latency pprof-like profile // (time between a goroutine become runnable and actually scheduled for execution). -func computePprofSched(w io.Writer, gToIntervals map[uint64][]interval, events []*trace.Event) error { - prof := make(map[uint64]Record) +func computePprofSched(gToIntervals map[uint64][]interval, events []*trace.Event) ([]traceviewer.ProfileRecord, error) { + prof := make(map[uint64]traceviewer.ProfileRecord) for _, ev := range events { if (ev.Type != trace.EvGoUnblock && ev.Type != trace.EvGoCreate) || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 { @@ -248,13 +224,13 @@ func computePprofSched(w io.Writer, gToIntervals map[uint64][]interval, events [ overlapping := pprofOverlappingDuration(gToIntervals, ev) if overlapping > 0 { rec := prof[ev.StkID] - rec.stk = ev.Stk - rec.n++ - rec.time += overlapping.Nanoseconds() + rec.Stack = ev.Stk + rec.Count++ + rec.Time += overlapping prof[ev.StkID] = rec } } - return buildProfile(prof).Write(w) + return recordsOf(prof), nil } // pprofOverlappingDuration returns the overlapping duration between @@ -278,100 +254,10 @@ func pprofOverlappingDuration(gToIntervals map[uint64][]interval, ev *trace.Even return overlapping } -// serveSVGProfile serves pprof-like profile generated by prof as svg. -func serveSVGProfile(prof func(w io.Writer, r *http.Request) error) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - - if r.FormValue("raw") != "" { - w.Header().Set("Content-Type", "application/octet-stream") - if err := prof(w, r); err != nil { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.Header().Set("X-Go-Pprof", "1") - http.Error(w, fmt.Sprintf("failed to get profile: %v", err), http.StatusInternalServerError) - return - } - return - } - - blockf, err := os.CreateTemp("", "block") - if err != nil { - http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError) - return - } - defer func() { - blockf.Close() - os.Remove(blockf.Name()) - }() - blockb := bufio.NewWriter(blockf) - if err := prof(blockb, r); err != nil { - http.Error(w, fmt.Sprintf("failed to generate profile: %v", err), http.StatusInternalServerError) - return - } - if err := blockb.Flush(); err != nil { - http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError) - return - } - if err := blockf.Close(); err != nil { - http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError) - return - } - svgFilename := blockf.Name() + ".svg" - if output, err := exec.Command(goCmd(), "tool", "pprof", "-svg", "-output", svgFilename, blockf.Name()).CombinedOutput(); err != nil { - http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", err, output), http.StatusInternalServerError) - return - } - defer os.Remove(svgFilename) - w.Header().Set("Content-Type", "image/svg+xml") - http.ServeFile(w, r, svgFilename) +func recordsOf(records map[uint64]traceviewer.ProfileRecord) []traceviewer.ProfileRecord { + result := make([]traceviewer.ProfileRecord, 0, len(records)) + for _, record := range records { + result = append(result, record) } -} - -func buildProfile(prof map[uint64]Record) *profile.Profile { - p := &profile.Profile{ - PeriodType: &profile.ValueType{Type: "trace", Unit: "count"}, - Period: 1, - SampleType: []*profile.ValueType{ - {Type: "contentions", Unit: "count"}, - {Type: "delay", Unit: "nanoseconds"}, - }, - } - locs := make(map[uint64]*profile.Location) - funcs := make(map[string]*profile.Function) - for _, rec := range prof { - var sloc []*profile.Location - for _, frame := range rec.stk { - loc := locs[frame.PC] - if loc == nil { - fn := funcs[frame.File+frame.Fn] - if fn == nil { - fn = &profile.Function{ - ID: uint64(len(p.Function) + 1), - Name: frame.Fn, - SystemName: frame.Fn, - Filename: frame.File, - } - p.Function = append(p.Function, fn) - funcs[frame.File+frame.Fn] = fn - } - loc = &profile.Location{ - ID: uint64(len(p.Location) + 1), - Address: frame.PC, - Line: []profile.Line{ - { - Function: fn, - Line: int64(frame.Line), - }, - }, - } - p.Location = append(p.Location, loc) - locs[frame.PC] = loc - } - sloc = append(sloc, loc) - } - p.Sample = append(p.Sample, &profile.Sample{ - Value: []int64{int64(rec.n), rec.time}, - Location: sloc, - }) - } - return p + return result } diff --git a/src/cmd/trace/trace.go b/src/cmd/trace/trace.go index 618df42033..438b8dd328 100644 --- a/src/cmd/trace/trace.go +++ b/src/cmd/trace/trace.go @@ -5,29 +5,24 @@ package main import ( - "cmd/internal/traceviewer" - "embed" - "encoding/json" "fmt" "internal/trace" - "io" + "internal/trace/traceviewer" "log" "math" "net/http" "runtime/debug" "sort" "strconv" - "strings" "time" -) -//go:embed static/trace_viewer_full.html static/webcomponents.min.js -var staticContent embed.FS + "internal/trace/traceviewer/format" +) func init() { http.HandleFunc("/trace", httpTrace) http.HandleFunc("/jsontrace", httpJsonTrace) - http.Handle("/static/", http.FileServer(http.FS(staticContent))) + http.Handle("/static/", traceviewer.StaticHandler()) } // httpTrace serves either whole trace (goid==0) or trace for goid goroutine. @@ -37,143 +32,9 @@ func httpTrace(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } - if err := r.ParseForm(); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - html := strings.ReplaceAll(templTrace, "{{PARAMS}}", r.Form.Encode()) - w.Write([]byte(html)) - + traceviewer.TraceHandler().ServeHTTP(w, r) } -// https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962/tracing/docs/embedding-trace-viewer.md -// This is almost verbatim copy of https://chromium-review.googlesource.com/c/catapult/+/2062938/2/tracing/bin/index.html -var templTrace = ` - - - - - - - - - - - - - -` - // httpJsonTrace serves json trace, requested from within templTrace HTML. func httpJsonTrace(w http.ResponseWriter, r *http.Request) { defer debug.FreeOSMemory() @@ -203,7 +64,7 @@ func httpJsonTrace(w http.ResponseWriter, r *http.Request) { log.Printf("failed to find goroutine %d", goid) return } - params.mode = modeGoroutineOriented + params.mode = traceviewer.ModeGoroutineOriented params.startTime = g.StartTime if g.EndTime != 0 { params.endTime = g.EndTime @@ -225,7 +86,7 @@ func httpJsonTrace(w http.ResponseWriter, r *http.Request) { return } goid := task.events[0].G - params.mode = modeGoroutineOriented | modeTaskOriented + params.mode = traceviewer.ModeGoroutineOriented | traceviewer.ModeTaskOriented params.startTime = task.firstTimestamp() - 1 params.endTime = task.lastTimestamp() + 1 params.maing = goid @@ -250,7 +111,7 @@ func httpJsonTrace(w http.ResponseWriter, r *http.Request) { log.Printf("failed to find task with id %d", taskid) return } - params.mode = modeTaskOriented + params.mode = traceviewer.ModeTaskOriented params.startTime = task.firstTimestamp() - 1 params.endTime = task.lastTimestamp() + 1 params.tasks = task.descendants() @@ -272,247 +133,31 @@ func httpJsonTrace(w http.ResponseWriter, r *http.Request) { } } - c := viewerDataTraceConsumer(w, start, end) + c := traceviewer.ViewerDataTraceConsumer(w, start, end) if err := generateTrace(params, c); err != nil { log.Printf("failed to generate trace: %v", err) return } } -type Range struct { - Name string - Start int - End int - StartTime int64 - EndTime int64 -} - -func (r Range) URL() string { - return fmt.Sprintf("/trace?start=%d&end=%d", r.Start, r.End) -} - // splitTrace splits the trace into a number of ranges, // each resulting in approx 100MB of json output // (trace viewer can hardly handle more). -func splitTrace(res trace.ParseResult) []Range { +func splitTrace(res trace.ParseResult) []traceviewer.Range { params := &traceParams{ parsed: res, endTime: math.MaxInt64, } - s, c := splittingTraceConsumer(100 << 20) // 100M + s, c := traceviewer.SplittingTraceConsumer(100 << 20) // 100M if err := generateTrace(params, c); err != nil { dief("%v\n", err) } return s.Ranges } -type splitter struct { - Ranges []Range -} - -// walkStackFrames calls fn for id and all of its parent frames from allFrames. -func walkStackFrames(allFrames map[string]traceviewer.Frame, id int, fn func(id int)) { - for id != 0 { - f, ok := allFrames[strconv.Itoa(id)] - if !ok { - break - } - fn(id) - id = f.Parent - } -} - -func stackFrameEncodedSize(id uint, f traceviewer.Frame) int { - // We want to know the marginal size of traceviewer.Data.Frames for - // each event. Running full JSON encoding of the map for each event is - // far too slow. - // - // Since the format is fixed, we can easily compute the size without - // encoding. - // - // A single entry looks like one of the following: - // - // "1":{"name":"main.main:30"}, - // "10":{"name":"pkg.NewSession:173","parent":9}, - // - // The parent is omitted if 0. The trailing comma is omitted from the - // last entry, but we don't need that much precision. - const ( - baseSize = len(`"`) + len(`":{"name":"`) + len(`"},`) - - // Don't count the trailing quote on the name, as that is - // counted in baseSize. - parentBaseSize = len(`,"parent":`) - ) - - size := baseSize - - size += len(f.Name) - - // Bytes for id (always positive). - for id > 0 { - size += 1 - id /= 10 - } - - if f.Parent > 0 { - size += parentBaseSize - // Bytes for parent (always positive). - for f.Parent > 0 { - size += 1 - f.Parent /= 10 - } - } - - return size -} - -func splittingTraceConsumer(max int) (*splitter, traceConsumer) { - type eventSz struct { - Time float64 - Sz int - Frames []int - } - - var ( - // data.Frames contains only the frames for required events. - data = traceviewer.Data{Frames: make(map[string]traceviewer.Frame)} - - allFrames = make(map[string]traceviewer.Frame) - - sizes []eventSz - cw countingWriter - ) - - s := new(splitter) - - return s, traceConsumer{ - consumeTimeUnit: func(unit string) { - data.TimeUnit = unit - }, - consumeViewerEvent: func(v *traceviewer.Event, required bool) { - if required { - // Store required events inside data so flush - // can include them in the required part of the - // trace. - data.Events = append(data.Events, v) - walkStackFrames(allFrames, v.Stack, func(id int) { - s := strconv.Itoa(id) - data.Frames[s] = allFrames[s] - }) - walkStackFrames(allFrames, v.EndStack, func(id int) { - s := strconv.Itoa(id) - data.Frames[s] = allFrames[s] - }) - return - } - enc := json.NewEncoder(&cw) - enc.Encode(v) - size := eventSz{Time: v.Time, Sz: cw.size + 1} // +1 for ",". - // Add referenced stack frames. Their size is computed - // in flush, where we can dedup across events. - walkStackFrames(allFrames, v.Stack, func(id int) { - size.Frames = append(size.Frames, id) - }) - walkStackFrames(allFrames, v.EndStack, func(id int) { - size.Frames = append(size.Frames, id) // This may add duplicates. We'll dedup later. - }) - sizes = append(sizes, size) - cw.size = 0 - }, - consumeViewerFrame: func(k string, v traceviewer.Frame) { - allFrames[k] = v - }, - flush: func() { - // Calculate size of the mandatory part of the trace. - // This includes thread names and stack frames for - // required events. - cw.size = 0 - enc := json.NewEncoder(&cw) - enc.Encode(data) - requiredSize := cw.size - - // Then calculate size of each individual event and - // their stack frames, grouping them into ranges. We - // only include stack frames relevant to the events in - // the range to reduce overhead. - - var ( - start = 0 - - eventsSize = 0 - - frames = make(map[string]traceviewer.Frame) - framesSize = 0 - ) - for i, ev := range sizes { - eventsSize += ev.Sz - - // Add required stack frames. Note that they - // may already be in the map. - for _, id := range ev.Frames { - s := strconv.Itoa(id) - _, ok := frames[s] - if ok { - continue - } - f := allFrames[s] - frames[s] = f - framesSize += stackFrameEncodedSize(uint(id), f) - } - - total := requiredSize + framesSize + eventsSize - if total < max { - continue - } - - // Reached max size, commit this range and - // start a new range. - startTime := time.Duration(sizes[start].Time * 1000) - endTime := time.Duration(ev.Time * 1000) - ranges = append(ranges, Range{ - Name: fmt.Sprintf("%v-%v", startTime, endTime), - Start: start, - End: i + 1, - StartTime: int64(startTime), - EndTime: int64(endTime), - }) - start = i + 1 - frames = make(map[string]traceviewer.Frame) - framesSize = 0 - eventsSize = 0 - } - if len(ranges) <= 1 { - s.Ranges = nil - return - } - - if end := len(sizes) - 1; start < end { - ranges = append(ranges, Range{ - Name: fmt.Sprintf("%v-%v", time.Duration(sizes[start].Time*1000), time.Duration(sizes[end].Time*1000)), - Start: start, - End: end, - StartTime: int64(sizes[start].Time * 1000), - EndTime: int64(sizes[end].Time * 1000), - }) - } - s.Ranges = ranges - }, - } -} - -type countingWriter struct { - size int -} - -func (cw *countingWriter) Write(data []byte) (int, error) { - cw.size += len(data) - return len(data), nil -} - type traceParams struct { parsed trace.ParseResult - mode traceviewMode + mode traceviewer.Mode startTime int64 endTime int64 maing uint64 // for goroutine-oriented view, place this goroutine on the top row @@ -520,59 +165,18 @@ type traceParams struct { tasks []*taskDesc // Tasks to be displayed. tasks[0] is the top-most task } -type traceviewMode uint - -const ( - modeGoroutineOriented traceviewMode = 1 << iota - modeTaskOriented -) - type traceContext struct { *traceParams - consumer traceConsumer - frameTree frameNode - frameSeq int - arrowSeq uint64 - gcount uint64 - - heapStats, prevHeapStats heapStats - threadStats, prevThreadStats threadStats - gstates, prevGstates [gStateCount]int64 - + consumer traceviewer.TraceConsumer + emitter *traceviewer.Emitter + arrowSeq uint64 + gcount uint64 regionID int // last emitted region id. incremented in each emitRegion call. } -type heapStats struct { - heapAlloc uint64 - nextGC uint64 -} - -type threadStats struct { - insyscallRuntime int64 // system goroutine in syscall - insyscall int64 // user goroutine in syscall - prunning int64 // thread running P -} - -type frameNode struct { - id int - children map[uint64]frameNode -} - -type gState int - -const ( - gDead gState = iota - gRunnable - gRunning - gWaiting - gWaitingGC - - gStateCount -) - type gInfo struct { - state gState // current state - name string // name chosen for this goroutine at first EvGoStart + state traceviewer.GState // current state + name string // name chosen for this goroutine at first EvGoStart isSystemG bool start *trace.Event // most recent EvGoStart markAssist *trace.Event // if non-nil, the mark assist currently running. @@ -596,19 +200,6 @@ type SortIndexArg struct { Index int `json:"sort_index"` } -type traceConsumer struct { - consumeTimeUnit func(unit string) - consumeViewerEvent func(v *traceviewer.Event, required bool) - consumeViewerFrame func(key string, f traceviewer.Frame) - flush func() -} - -const ( - procsSection = 0 // where Goroutines or per-P timelines are presented. - statsSection = 1 // where counters are presented. - tasksSection = 2 // where Task hierarchy & timeline is presented. -) - // generateTrace generates json trace for trace-viewer: // https://github.com/google/trace-viewer // Trace format is described at: @@ -616,14 +207,22 @@ const ( // If mode==goroutineMode, generate trace for goroutine goid, otherwise whole trace. // startTime, endTime determine part of the trace that we are interested in. // gset restricts goroutines that are included in the resulting trace. -func generateTrace(params *traceParams, consumer traceConsumer) error { - defer consumer.flush() +func generateTrace(params *traceParams, consumer traceviewer.TraceConsumer) error { + emitter := traceviewer.NewEmitter( + consumer, + time.Duration(params.startTime), + time.Duration(params.endTime), + ) + if params.mode&traceviewer.ModeGoroutineOriented != 0 { + emitter.SetResourceType("G") + } else { + emitter.SetResourceType("PROCS") + } + defer emitter.Flush() - ctx := &traceContext{traceParams: params} - ctx.frameTree.children = make(map[uint64]frameNode) + ctx := &traceContext{traceParams: params, emitter: emitter} ctx.consumer = consumer - ctx.consumer.consumeTimeUnit("ns") maxProc := 0 ginfos := make(map[uint64]*gInfo) stacks := params.parsed.Stacks @@ -640,17 +239,17 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { // Since we make many calls to setGState, we record a sticky // error in setGStateErr and check it after every event. var setGStateErr error - setGState := func(ev *trace.Event, g uint64, oldState, newState gState) { + setGState := func(ev *trace.Event, g uint64, oldState, newState traceviewer.GState) { info := getGInfo(g) - if oldState == gWaiting && info.state == gWaitingGC { - // For checking, gWaiting counts as any gWaiting*. + if oldState == traceviewer.GWaiting && info.state == traceviewer.GWaitingGC { + // For checking, traceviewer.GWaiting counts as any traceviewer.GWaiting*. oldState = info.state } if info.state != oldState && setGStateErr == nil { setGStateErr = fmt.Errorf("expected G %d to be in state %d, but got state %d", g, oldState, info.state) } - ctx.gstates[info.state]-- - ctx.gstates[newState]++ + + emitter.GoroutineTransition(time.Duration(ev.Ts), info.state, newState) info.state = newState } @@ -658,13 +257,13 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { // Handle state transitions before we filter out events. switch ev.Type { case trace.EvGoStart, trace.EvGoStartLabel: - setGState(ev, ev.G, gRunnable, gRunning) + setGState(ev, ev.G, traceviewer.GRunnable, traceviewer.GRunning) info := getGInfo(ev.G) info.start = ev case trace.EvProcStart: - ctx.threadStats.prunning++ + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateRunning, 1) case trace.EvProcStop: - ctx.threadStats.prunning-- + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateRunning, -1) case trace.EvGoCreate: newG := ev.Args[0] info := getGInfo(newG) @@ -682,58 +281,59 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { info.isSystemG = trace.IsSystemGoroutine(fname) ctx.gcount++ - setGState(ev, newG, gDead, gRunnable) + setGState(ev, newG, traceviewer.GDead, traceviewer.GRunnable) case trace.EvGoEnd: ctx.gcount-- - setGState(ev, ev.G, gRunning, gDead) + setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GDead) case trace.EvGoUnblock: - setGState(ev, ev.Args[0], gWaiting, gRunnable) + setGState(ev, ev.Args[0], traceviewer.GWaiting, traceviewer.GRunnable) case trace.EvGoSysExit: - setGState(ev, ev.G, gWaiting, gRunnable) + setGState(ev, ev.G, traceviewer.GWaiting, traceviewer.GRunnable) if getGInfo(ev.G).isSystemG { - ctx.threadStats.insyscallRuntime-- + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscallRuntime, -1) } else { - ctx.threadStats.insyscall-- + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscall, -1) } case trace.EvGoSysBlock: - setGState(ev, ev.G, gRunning, gWaiting) + setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GWaiting) if getGInfo(ev.G).isSystemG { - ctx.threadStats.insyscallRuntime++ + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscallRuntime, 1) } else { - ctx.threadStats.insyscall++ + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscall, 1) } case trace.EvGoSched, trace.EvGoPreempt: - setGState(ev, ev.G, gRunning, gRunnable) + setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GRunnable) case trace.EvGoStop, trace.EvGoSleep, trace.EvGoBlock, trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect, trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockNet: - setGState(ev, ev.G, gRunning, gWaiting) + setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GWaiting) case trace.EvGoBlockGC: - setGState(ev, ev.G, gRunning, gWaitingGC) + setGState(ev, ev.G, traceviewer.GRunning, traceviewer.GWaitingGC) case trace.EvGCMarkAssistStart: getGInfo(ev.G).markAssist = ev case trace.EvGCMarkAssistDone: getGInfo(ev.G).markAssist = nil case trace.EvGoWaiting: - setGState(ev, ev.G, gRunnable, gWaiting) + setGState(ev, ev.G, traceviewer.GRunnable, traceviewer.GWaiting) case trace.EvGoInSyscall: // Cancel out the effect of EvGoCreate at the beginning. - setGState(ev, ev.G, gRunnable, gWaiting) + setGState(ev, ev.G, traceviewer.GRunnable, traceviewer.GWaiting) if getGInfo(ev.G).isSystemG { - ctx.threadStats.insyscallRuntime++ + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscallRuntime, 1) } else { - ctx.threadStats.insyscall++ + emitter.IncThreadStateCount(time.Duration(ev.Ts), traceviewer.ThreadStateInSyscall, 1) } case trace.EvHeapAlloc: - ctx.heapStats.heapAlloc = ev.Args[0] + emitter.HeapAlloc(time.Duration(ev.Ts), ev.Args[0]) case trace.EvHeapGoal: - ctx.heapStats.nextGC = ev.Args[0] + emitter.HeapGoal(time.Duration(ev.Ts), ev.Args[0]) } if setGStateErr != nil { return setGStateErr } - if ctx.gstates[gRunnable] < 0 || ctx.gstates[gRunning] < 0 || ctx.threadStats.insyscall < 0 || ctx.threadStats.insyscallRuntime < 0 { - return fmt.Errorf("invalid state after processing %v: runnable=%d running=%d insyscall=%d insyscallRuntime=%d", ev, ctx.gstates[gRunnable], ctx.gstates[gRunning], ctx.threadStats.insyscall, ctx.threadStats.insyscallRuntime) + + if err := emitter.Err(); err != nil { + return fmt.Errorf("invalid state after processing %v: %s", ev, err) } // Ignore events that are from uninteresting goroutines @@ -752,12 +352,12 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { // Emit trace objects. switch ev.Type { case trace.EvProcStart: - if ctx.mode&modeGoroutineOriented != 0 { + if ctx.mode&traceviewer.ModeGoroutineOriented != 0 { continue } ctx.emitInstant(ev, "proc start", "") case trace.EvProcStop: - if ctx.mode&modeGoroutineOriented != 0 { + if ctx.mode&traceviewer.ModeGoroutineOriented != 0 { continue } ctx.emitInstant(ev, "proc stop", "") @@ -765,7 +365,7 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { ctx.emitSlice(ev, "GC") case trace.EvGCDone: case trace.EvSTWStart: - if ctx.mode&modeGoroutineOriented != 0 { + if ctx.mode&traceviewer.ModeGoroutineOriented != 0 { continue } ctx.emitSlice(ev, fmt.Sprintf("STW (%s)", ev.SArgs[0])) @@ -832,46 +432,10 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { ctx.emitInstant(ev, "CPU profile sample", "") } } - // Emit any counter updates. - ctx.emitThreadCounters(ev) - ctx.emitHeapCounters(ev) - ctx.emitGoroutineCounters(ev) - } - - ctx.emitSectionFooter(statsSection, "STATS", 0) - - if ctx.mode&modeTaskOriented != 0 { - ctx.emitSectionFooter(tasksSection, "TASKS", 1) - } - - if ctx.mode&modeGoroutineOriented != 0 { - ctx.emitSectionFooter(procsSection, "G", 2) - } else { - ctx.emitSectionFooter(procsSection, "PROCS", 2) - } - - ctx.emitFooter(&traceviewer.Event{Name: "thread_name", Phase: "M", PID: procsSection, TID: trace.GCP, Arg: &NameArg{"GC"}}) - ctx.emitFooter(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: procsSection, TID: trace.GCP, Arg: &SortIndexArg{-6}}) - - ctx.emitFooter(&traceviewer.Event{Name: "thread_name", Phase: "M", PID: procsSection, TID: trace.NetpollP, Arg: &NameArg{"Network"}}) - ctx.emitFooter(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: procsSection, TID: trace.NetpollP, Arg: &SortIndexArg{-5}}) - - ctx.emitFooter(&traceviewer.Event{Name: "thread_name", Phase: "M", PID: procsSection, TID: trace.TimerP, Arg: &NameArg{"Timers"}}) - ctx.emitFooter(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: procsSection, TID: trace.TimerP, Arg: &SortIndexArg{-4}}) - - ctx.emitFooter(&traceviewer.Event{Name: "thread_name", Phase: "M", PID: procsSection, TID: trace.SyscallP, Arg: &NameArg{"Syscalls"}}) - ctx.emitFooter(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: procsSection, TID: trace.SyscallP, Arg: &SortIndexArg{-3}}) - - // Display rows for Ps if we are in the default trace view mode (not goroutine-oriented presentation) - if ctx.mode&modeGoroutineOriented == 0 { - for i := 0; i <= maxProc; i++ { - ctx.emitFooter(&traceviewer.Event{Name: "thread_name", Phase: "M", PID: procsSection, TID: uint64(i), Arg: &NameArg{fmt.Sprintf("Proc %v", i)}}) - ctx.emitFooter(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: procsSection, TID: uint64(i), Arg: &SortIndexArg{i}}) - } } // Display task and its regions if we are in task-oriented presentation mode. - if ctx.mode&modeTaskOriented != 0 { + if ctx.mode&traceviewer.ModeTaskOriented != 0 { // sort tasks based on the task start time. sortedTask := make([]*taskDesc, len(ctx.tasks)) copy(sortedTask, ctx.tasks) @@ -888,7 +452,7 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { // If we are in goroutine-oriented mode, we draw regions. // TODO(hyangah): add this for task/P-oriented mode (i.e., focustask view) too. - if ctx.mode&modeGoroutineOriented != 0 { + if ctx.mode&traceviewer.ModeGoroutineOriented != 0 { for _, s := range task.regions { ctx.emitRegion(s) } @@ -897,34 +461,34 @@ func generateTrace(params *traceParams, consumer traceConsumer) error { } // Display goroutine rows if we are either in goroutine-oriented mode. - if ctx.mode&modeGoroutineOriented != 0 { + if ctx.mode&traceviewer.ModeGoroutineOriented != 0 { for k, v := range ginfos { if !ctx.gs[k] { continue } - ctx.emitFooter(&traceviewer.Event{Name: "thread_name", Phase: "M", PID: procsSection, TID: k, Arg: &NameArg{v.name}}) + emitter.Resource(k, v.name) } - // Row for the main goroutine (maing) - ctx.emitFooter(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: procsSection, TID: ctx.maing, Arg: &SortIndexArg{-2}}) + emitter.Focus(ctx.maing) + // Row for GC or global state (specified with G=0) - ctx.emitFooter(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: procsSection, TID: 0, Arg: &SortIndexArg{-1}}) + ctx.emitFooter(&format.Event{Name: "thread_sort_index", Phase: "M", PID: format.ProcsSection, TID: 0, Arg: &SortIndexArg{-1}}) + } else { + // Display rows for Ps if we are in the default trace view mode. + for i := 0; i <= maxProc; i++ { + emitter.Resource(uint64(i), fmt.Sprintf("Proc %v", i)) + } } return nil } -func (ctx *traceContext) emit(e *traceviewer.Event) { - ctx.consumer.consumeViewerEvent(e, false) +func (ctx *traceContext) emit(e *format.Event) { + ctx.consumer.ConsumeViewerEvent(e, false) } -func (ctx *traceContext) emitFooter(e *traceviewer.Event) { - ctx.consumer.consumeViewerEvent(e, true) +func (ctx *traceContext) emitFooter(e *format.Event) { + ctx.consumer.ConsumeViewerEvent(e, true) } -func (ctx *traceContext) emitSectionFooter(sectionID uint64, name string, priority int) { - ctx.emitFooter(&traceviewer.Event{Name: "process_name", Phase: "M", PID: sectionID, Arg: &NameArg{name}}) - ctx.emitFooter(&traceviewer.Event{Name: "process_sort_index", Phase: "M", PID: sectionID, Arg: &SortIndexArg{priority}}) -} - func (ctx *traceContext) time(ev *trace.Event) float64 { // Trace viewer wants timestamps in microseconds. return float64(ev.Ts) / 1000 @@ -942,7 +506,7 @@ func tsWithinRange(ts, s, e int64) bool { } func (ctx *traceContext) proc(ev *trace.Event) uint64 { - if ctx.mode&modeGoroutineOriented != 0 && ev.P < trace.FakeP { + if ctx.mode&traceviewer.ModeGoroutineOriented != 0 && ev.P < trace.FakeP { return ev.G } else { return uint64(ev.P) @@ -953,7 +517,7 @@ func (ctx *traceContext) emitSlice(ev *trace.Event, name string) { ctx.emit(ctx.makeSlice(ev, name)) } -func (ctx *traceContext) makeSlice(ev *trace.Event, name string) *traceviewer.Event { +func (ctx *traceContext) makeSlice(ev *trace.Event, name string) *format.Event { // If ViewerEvent.Dur is not a positive value, // trace viewer handles it as a non-terminating time interval. // Avoid it by setting the field with a small value. @@ -961,18 +525,18 @@ func (ctx *traceContext) makeSlice(ev *trace.Event, name string) *traceviewer.Ev if ev.Link.Ts-ev.Ts <= 0 { durationUsec = 0.0001 // 0.1 nanoseconds } - sl := &traceviewer.Event{ + sl := &format.Event{ Name: name, Phase: "X", Time: ctx.time(ev), Dur: durationUsec, TID: ctx.proc(ev), - Stack: ctx.stack(ev.Stk), - EndStack: ctx.stack(ev.Link.Stk), + Stack: ctx.emitter.Stack(ev.Stk), + EndStack: ctx.emitter.Stack(ev.Link.Stk), } // grey out non-overlapping events if the event is not a global event (ev.G == 0) - if ctx.mode&modeTaskOriented != 0 && ev.G != 0 { + if ctx.mode&traceviewer.ModeTaskOriented != 0 && ev.G != 0 { // include P information. if t := ev.Type; t == trace.EvGoStart || t == trace.EvGoStartLabel { type Arg struct { @@ -1000,25 +564,24 @@ func (ctx *traceContext) emitTask(task *taskDesc, sortIndex int) { taskName := task.name durationUsec := float64(task.lastTimestamp()-task.firstTimestamp()) / 1e3 - ctx.emitFooter(&traceviewer.Event{Name: "thread_name", Phase: "M", PID: tasksSection, TID: taskRow, Arg: &NameArg{fmt.Sprintf("T%d %s", task.id, taskName)}}) - ctx.emit(&traceviewer.Event{Name: "thread_sort_index", Phase: "M", PID: tasksSection, TID: taskRow, Arg: &SortIndexArg{sortIndex}}) + ctx.emitter.Task(taskRow, taskName, sortIndex) ts := float64(task.firstTimestamp()) / 1e3 - sl := &traceviewer.Event{ + sl := &format.Event{ Name: taskName, Phase: "X", Time: ts, Dur: durationUsec, - PID: tasksSection, + PID: format.TasksSection, TID: taskRow, Cname: pickTaskColor(task.id), } targ := TaskArg{ID: task.id} if task.create != nil { - sl.Stack = ctx.stack(task.create.Stk) + sl.Stack = ctx.emitter.Stack(task.create.Stk) targ.StartG = task.create.G } if task.end != nil { - sl.EndStack = ctx.stack(task.end.Stk) + sl.EndStack = ctx.emitter.Stack(task.end.Stk) targ.EndG = task.end.G } sl.Arg = targ @@ -1026,8 +589,8 @@ func (ctx *traceContext) emitTask(task *taskDesc, sortIndex int) { if task.create != nil && task.create.Type == trace.EvUserTaskCreate && task.create.Args[1] != 0 { ctx.arrowSeq++ - ctx.emit(&traceviewer.Event{Name: "newTask", Phase: "s", TID: task.create.Args[1], ID: ctx.arrowSeq, Time: ts, PID: tasksSection}) - ctx.emit(&traceviewer.Event{Name: "newTask", Phase: "t", TID: taskRow, ID: ctx.arrowSeq, Time: ts, PID: tasksSection}) + ctx.emit(&format.Event{Name: "newTask", Phase: "s", TID: task.create.Args[1], ID: ctx.arrowSeq, Time: ts, PID: format.TasksSection}) + ctx.emit(&format.Event{Name: "newTask", Phase: "t", TID: taskRow, ID: ctx.arrowSeq, Time: ts, PID: format.TasksSection}) } } @@ -1048,7 +611,7 @@ func (ctx *traceContext) emitRegion(s regionDesc) { scopeID := fmt.Sprintf("%x", id) name := s.Name - sl0 := &traceviewer.Event{ + sl0 := &format.Event{ Category: "Region", Name: name, Phase: "b", @@ -1059,11 +622,11 @@ func (ctx *traceContext) emitRegion(s regionDesc) { Cname: pickTaskColor(s.TaskID), } if s.Start != nil { - sl0.Stack = ctx.stack(s.Start.Stk) + sl0.Stack = ctx.emitter.Stack(s.Start.Stk) } ctx.emit(sl0) - sl1 := &traceviewer.Event{ + sl1 := &format.Event{ Category: "Region", Name: name, Phase: "e", @@ -1075,70 +638,18 @@ func (ctx *traceContext) emitRegion(s regionDesc) { Arg: RegionArg{TaskID: s.TaskID}, } if s.End != nil { - sl1.Stack = ctx.stack(s.End.Stk) + sl1.Stack = ctx.emitter.Stack(s.End.Stk) } ctx.emit(sl1) } -type heapCountersArg struct { - Allocated uint64 - NextGC uint64 -} - -func (ctx *traceContext) emitHeapCounters(ev *trace.Event) { - if ctx.prevHeapStats == ctx.heapStats { - return - } - diff := uint64(0) - if ctx.heapStats.nextGC > ctx.heapStats.heapAlloc { - diff = ctx.heapStats.nextGC - ctx.heapStats.heapAlloc - } - if tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) { - ctx.emit(&traceviewer.Event{Name: "Heap", Phase: "C", Time: ctx.time(ev), PID: 1, Arg: &heapCountersArg{ctx.heapStats.heapAlloc, diff}}) - } - ctx.prevHeapStats = ctx.heapStats -} - -type goroutineCountersArg struct { - Running uint64 - Runnable uint64 - GCWaiting uint64 -} - -func (ctx *traceContext) emitGoroutineCounters(ev *trace.Event) { - if ctx.prevGstates == ctx.gstates { - return - } - if tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) { - ctx.emit(&traceviewer.Event{Name: "Goroutines", Phase: "C", Time: ctx.time(ev), PID: 1, Arg: &goroutineCountersArg{uint64(ctx.gstates[gRunning]), uint64(ctx.gstates[gRunnable]), uint64(ctx.gstates[gWaitingGC])}}) - } - ctx.prevGstates = ctx.gstates -} - -type threadCountersArg struct { - Running int64 - InSyscall int64 -} - -func (ctx *traceContext) emitThreadCounters(ev *trace.Event) { - if ctx.prevThreadStats == ctx.threadStats { - return - } - if tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) { - ctx.emit(&traceviewer.Event{Name: "Threads", Phase: "C", Time: ctx.time(ev), PID: 1, Arg: &threadCountersArg{ - Running: ctx.threadStats.prunning, - InSyscall: ctx.threadStats.insyscall}}) - } - ctx.prevThreadStats = ctx.threadStats -} - func (ctx *traceContext) emitInstant(ev *trace.Event, name, category string) { if !tsWithinRange(ev.Ts, ctx.startTime, ctx.endTime) { return } cname := "" - if ctx.mode&modeTaskOriented != 0 { + if ctx.mode&traceviewer.ModeTaskOriented != 0 { taskID, isUserAnnotation := isUserAnnotationEvent(ev) show := false @@ -1163,14 +674,14 @@ func (ctx *traceContext) emitInstant(ev *trace.Event, name, category string) { } arg = &Arg{ev.Args[0]} } - ctx.emit(&traceviewer.Event{ + ctx.emit(&format.Event{ Name: name, Category: category, Phase: "I", Scope: "t", Time: ctx.time(ev), TID: ctx.proc(ev), - Stack: ctx.stack(ev.Stk), + Stack: ctx.emitter.Stack(ev.Stk), Cname: cname, Arg: arg}) } @@ -1181,7 +692,7 @@ func (ctx *traceContext) emitArrow(ev *trace.Event, name string) { // For example, a goroutine was unblocked but was not scheduled before trace stop. return } - if ctx.mode&modeGoroutineOriented != 0 && (!ctx.gs[ev.Link.G] || ev.Link.Ts < ctx.startTime || ev.Link.Ts > ctx.endTime) { + if ctx.mode&traceviewer.ModeGoroutineOriented != 0 && (!ctx.gs[ev.Link.G] || ev.Link.Ts < ctx.startTime || ev.Link.Ts > ctx.endTime) { return } @@ -1192,7 +703,7 @@ func (ctx *traceContext) emitArrow(ev *trace.Event, name string) { } color := "" - if ctx.mode&modeTaskOriented != 0 { + if ctx.mode&traceviewer.ModeTaskOriented != 0 { overlapping := false // skip non-overlapping arrows. for _, task := range ctx.tasks { @@ -1207,32 +718,8 @@ func (ctx *traceContext) emitArrow(ev *trace.Event, name string) { } ctx.arrowSeq++ - ctx.emit(&traceviewer.Event{Name: name, Phase: "s", TID: ctx.proc(ev), ID: ctx.arrowSeq, Time: ctx.time(ev), Stack: ctx.stack(ev.Stk), Cname: color}) - ctx.emit(&traceviewer.Event{Name: name, Phase: "t", TID: ctx.proc(ev.Link), ID: ctx.arrowSeq, Time: ctx.time(ev.Link), Cname: color}) -} - -func (ctx *traceContext) stack(stk []*trace.Frame) int { - return ctx.buildBranch(ctx.frameTree, stk) -} - -// buildBranch builds one branch in the prefix tree rooted at ctx.frameTree. -func (ctx *traceContext) buildBranch(parent frameNode, stk []*trace.Frame) int { - if len(stk) == 0 { - return parent.id - } - last := len(stk) - 1 - frame := stk[last] - stk = stk[:last] - - node, ok := parent.children[frame.PC] - if !ok { - ctx.frameSeq++ - node.id = ctx.frameSeq - node.children = make(map[uint64]frameNode) - parent.children[frame.PC] = node - ctx.consumer.consumeViewerFrame(strconv.Itoa(node.id), traceviewer.Frame{Name: fmt.Sprintf("%v:%v", frame.Fn, frame.Line), Parent: parent.id}) - } - return ctx.buildBranch(node, stk) + ctx.emit(&format.Event{Name: name, Phase: "s", TID: ctx.proc(ev), ID: ctx.arrowSeq, Time: ctx.time(ev), Stack: ctx.emitter.Stack(ev.Stk), Cname: color}) + ctx.emit(&format.Event{Name: name, Phase: "t", TID: ctx.proc(ev.Link), ID: ctx.arrowSeq, Time: ctx.time(ev.Link), Cname: color}) } // firstTimestamp returns the timestamp of the first event record. @@ -1253,61 +740,6 @@ func lastTimestamp() int64 { return 0 } -type jsonWriter struct { - w io.Writer - enc *json.Encoder -} - -func viewerDataTraceConsumer(w io.Writer, start, end int64) traceConsumer { - allFrames := make(map[string]traceviewer.Frame) - requiredFrames := make(map[string]traceviewer.Frame) - enc := json.NewEncoder(w) - written := 0 - index := int64(-1) - - io.WriteString(w, "{") - return traceConsumer{ - consumeTimeUnit: func(unit string) { - io.WriteString(w, `"displayTimeUnit":`) - enc.Encode(unit) - io.WriteString(w, ",") - }, - consumeViewerEvent: func(v *traceviewer.Event, required bool) { - index++ - if !required && (index < start || index > end) { - // not in the range. Skip! - return - } - walkStackFrames(allFrames, v.Stack, func(id int) { - s := strconv.Itoa(id) - requiredFrames[s] = allFrames[s] - }) - walkStackFrames(allFrames, v.EndStack, func(id int) { - s := strconv.Itoa(id) - requiredFrames[s] = allFrames[s] - }) - if written == 0 { - io.WriteString(w, `"traceEvents": [`) - } - if written > 0 { - io.WriteString(w, ",") - } - enc.Encode(v) - // TODO: get rid of the extra \n inserted by enc.Encode. - // Same should be applied to splittingTraceConsumer. - written++ - }, - consumeViewerFrame: func(k string, v traceviewer.Frame) { - allFrames[k] = v - }, - flush: func() { - io.WriteString(w, `], "stackFrames":`) - enc.Encode(requiredFrames) - io.WriteString(w, `}`) - }, - } -} - // Mapping from more reasonable color names to the reserved color names in // https://github.com/catapult-project/catapult/blob/master/tracing/tracing/base/color_scheme.html#L50 // The chrome trace viewer allows only those as cname values. diff --git a/src/cmd/trace/trace_test.go b/src/cmd/trace/trace_test.go index 87fd3a3515..d315fad471 100644 --- a/src/cmd/trace/trace_test.go +++ b/src/cmd/trace/trace_test.go @@ -7,9 +7,10 @@ package main import ( - "cmd/internal/traceviewer" "context" "internal/trace" + "internal/trace/traceviewer" + "internal/trace/traceviewer/format" "io" rtrace "runtime/trace" "strings" @@ -78,10 +79,10 @@ func TestGoroutineCount(t *testing.T) { // Use the default viewerDataTraceConsumer but replace // consumeViewerEvent to intercept the ViewerEvents for testing. - c := viewerDataTraceConsumer(io.Discard, 0, 1<<63-1) - c.consumeViewerEvent = func(ev *traceviewer.Event, _ bool) { + c := traceviewer.ViewerDataTraceConsumer(io.Discard, 0, 1<<63-1) + c.ConsumeViewerEvent = func(ev *format.Event, _ bool) { if ev.Name == "Goroutines" { - cnt := ev.Arg.(*goroutineCountersArg) + cnt := ev.Arg.(*format.GoroutineCountersArg) if cnt.Runnable+cnt.Running > 2 { t.Errorf("goroutine count=%+v; want no more than 2 goroutines in runnable/running state", cnt) } @@ -131,7 +132,7 @@ func TestGoroutineFilter(t *testing.T) { gs: map[uint64]bool{10: true}, } - c := viewerDataTraceConsumer(io.Discard, 0, 1<<63-1) + c := traceviewer.ViewerDataTraceConsumer(io.Discard, 0, 1<<63-1) if err := generateTrace(params, c); err != nil { t.Fatalf("generateTrace failed: %v", err) } @@ -163,10 +164,10 @@ func TestPreemptedMarkAssist(t *testing.T) { endTime: int64(1<<63 - 1), } - c := viewerDataTraceConsumer(io.Discard, 0, 1<<63-1) + c := traceviewer.ViewerDataTraceConsumer(io.Discard, 0, 1<<63-1) marks := 0 - c.consumeViewerEvent = func(ev *traceviewer.Event, _ bool) { + c.ConsumeViewerEvent = func(ev *format.Event, _ bool) { if strings.Contains(ev.Name, "MARK ASSIST") { marks++ } @@ -208,16 +209,16 @@ func TestFoo(t *testing.T) { params := &traceParams{ parsed: res, - mode: modeTaskOriented, + mode: traceviewer.ModeTaskOriented, startTime: task.firstTimestamp() - 1, endTime: task.lastTimestamp() + 1, tasks: []*taskDesc{task}, } - c := viewerDataTraceConsumer(io.Discard, 0, 1<<63-1) + c := traceviewer.ViewerDataTraceConsumer(io.Discard, 0, 1<<63-1) var logBeforeTaskEnd, logAfterTaskEnd bool - c.consumeViewerEvent = func(ev *traceviewer.Event, _ bool) { + c.ConsumeViewerEvent = func(ev *format.Event, _ bool) { if ev.Name == "log before task ends" { logBeforeTaskEnd = true } diff --git a/src/cmd/trace/trace_unix_test.go b/src/cmd/trace/trace_unix_test.go index 87ad86fce8..e634635427 100644 --- a/src/cmd/trace/trace_unix_test.go +++ b/src/cmd/trace/trace_unix_test.go @@ -8,9 +8,10 @@ package main import ( "bytes" - "cmd/internal/traceviewer" "internal/goexperiment" traceparser "internal/trace" + "internal/trace/traceviewer" + "internal/trace/traceviewer/format" "io" "runtime" "runtime/trace" @@ -87,10 +88,10 @@ func TestGoroutineInSyscall(t *testing.T) { // Check only one thread for the pipe read goroutine is // considered in-syscall. - c := viewerDataTraceConsumer(io.Discard, 0, 1<<63-1) - c.consumeViewerEvent = func(ev *traceviewer.Event, _ bool) { + c := traceviewer.ViewerDataTraceConsumer(io.Discard, 0, 1<<63-1) + c.ConsumeViewerEvent = func(ev *format.Event, _ bool) { if ev.Name == "Threads" { - arg := ev.Arg.(*threadCountersArg) + arg := ev.Arg.(*format.ThreadCountersArg) if arg.InSyscall > 1 { t.Errorf("%d threads in syscall at time %v; want less than 1 thread in syscall", arg.InSyscall, ev.Time) } diff --git a/src/cmd/trace/v2/gen.go b/src/cmd/trace/v2/gen.go new file mode 100644 index 0000000000..ad1599db92 --- /dev/null +++ b/src/cmd/trace/v2/gen.go @@ -0,0 +1,359 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "fmt" + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" + "strings" +) + +// generator is an interface for generating a JSON trace for the trace viewer +// from a trace. Each method in this interface is a handler for a kind of event +// that is interesting to render in the UI via the JSON trace. +type generator interface { + // Global parts. + Sync() // Notifies the generator of an EventSync event. + StackSample(ctx *traceContext, ev *tracev2.Event) + GlobalRange(ctx *traceContext, ev *tracev2.Event) + GlobalMetric(ctx *traceContext, ev *tracev2.Event) + + // Goroutine parts. + GoroutineLabel(ctx *traceContext, ev *tracev2.Event) + GoroutineRange(ctx *traceContext, ev *tracev2.Event) + GoroutineTransition(ctx *traceContext, ev *tracev2.Event) + + // Proc parts. + ProcRange(ctx *traceContext, ev *tracev2.Event) + ProcTransition(ctx *traceContext, ev *tracev2.Event) + + // Finish indicates the end of the trace and finalizes generation. + Finish(ctx *traceContext) +} + +// runGenerator produces a trace into ctx by running the generator over the parsed trace. +func runGenerator(ctx *traceContext, g generator, parsed *parsedTrace, opts *genOpts) { + for i := range parsed.events { + ev := &parsed.events[i] + + switch ev.Kind() { + case tracev2.EventSync: + g.Sync() + case tracev2.EventStackSample: + g.StackSample(ctx, ev) + case tracev2.EventRangeBegin, tracev2.EventRangeActive, tracev2.EventRangeEnd: + r := ev.Range() + switch r.Scope.Kind { + case tracev2.ResourceGoroutine: + g.GoroutineRange(ctx, ev) + case tracev2.ResourceProc: + g.ProcRange(ctx, ev) + case tracev2.ResourceNone: + g.GlobalRange(ctx, ev) + } + case tracev2.EventMetric: + g.GlobalMetric(ctx, ev) + case tracev2.EventLabel: + l := ev.Label() + if l.Resource.Kind == tracev2.ResourceGoroutine { + g.GoroutineLabel(ctx, ev) + } + case tracev2.EventStateTransition: + switch ev.StateTransition().Resource.Kind { + case tracev2.ResourceProc: + g.ProcTransition(ctx, ev) + case tracev2.ResourceGoroutine: + g.GoroutineTransition(ctx, ev) + } + } + } + for i, task := range opts.tasks { + emitTask(ctx, task, i) + if opts.mode&traceviewer.ModeGoroutineOriented != 0 { + for _, region := range task.Regions { + emitRegion(ctx, region) + } + } + } + g.Finish(ctx) +} + +// emitTask emits information about a task into the trace viewer's event stream. +// +// sortIndex sets the order in which this task will appear related to other tasks, +// lowest first. +func emitTask(ctx *traceContext, task *trace.UserTaskSummary, sortIndex int) { + // Collect information about the task. + var startStack, endStack tracev2.Stack + var startG, endG tracev2.GoID + startTime, endTime := ctx.startTime, ctx.endTime + if task.Start != nil { + startStack = task.Start.Stack() + startG = task.Start.Goroutine() + startTime = task.Start.Time() + } + if task.End != nil { + endStack = task.End.Stack() + endG = task.End.Goroutine() + endTime = task.End.Time() + } + arg := struct { + ID uint64 `json:"id"` + StartG uint64 `json:"start_g,omitempty"` + EndG uint64 `json:"end_g,omitempty"` + }{ + ID: uint64(task.ID), + StartG: uint64(startG), + EndG: uint64(endG), + } + + // Emit the task slice and notify the emitter of the task. + ctx.Task(uint64(task.ID), fmt.Sprintf("T%d %s", task.ID, task.Name), sortIndex) + ctx.TaskSlice(traceviewer.SliceEvent{ + Name: task.Name, + Ts: ctx.elapsed(startTime), + Dur: endTime.Sub(startTime), + Resource: uint64(task.ID), + Stack: ctx.Stack(viewerFrames(startStack)), + EndStack: ctx.Stack(viewerFrames(endStack)), + Arg: arg, + }) + // Emit an arrow from the parent to the child. + if task.Parent != nil && task.Start != nil && task.Start.Kind() == tracev2.EventTaskBegin { + ctx.TaskArrow(traceviewer.ArrowEvent{ + Name: "newTask", + Start: ctx.elapsed(task.Start.Time()), + End: ctx.elapsed(task.Start.Time()), + FromResource: uint64(task.Parent.ID), + ToResource: uint64(task.ID), + FromStack: ctx.Stack(viewerFrames(task.Start.Stack())), + }) + } +} + +// emitRegion emits goroutine-based slice events to the UI. The caller +// must be emitting for a goroutine-oriented trace. +// +// TODO(mknyszek): Make regions part of the regular generator loop and +// treat them like ranges so that we can emit regions in traces oriented +// by proc or thread. +func emitRegion(ctx *traceContext, region *trace.UserRegionSummary) { + if region.Name == "" { + return + } + // Collect information about the region. + var startStack, endStack tracev2.Stack + goroutine := tracev2.NoGoroutine + startTime, endTime := ctx.startTime, ctx.endTime + if region.Start != nil { + startStack = region.Start.Stack() + startTime = region.Start.Time() + goroutine = region.Start.Goroutine() + } + if region.End != nil { + endStack = region.End.Stack() + endTime = region.End.Time() + goroutine = region.End.Goroutine() + } + if goroutine == tracev2.NoGoroutine { + return + } + arg := struct { + TaskID uint64 `json:"taskid"` + }{ + TaskID: uint64(region.TaskID), + } + ctx.AsyncSlice(traceviewer.AsyncSliceEvent{ + SliceEvent: traceviewer.SliceEvent{ + Name: region.Name, + Ts: ctx.elapsed(startTime), + Dur: endTime.Sub(startTime), + Resource: uint64(goroutine), + Stack: ctx.Stack(viewerFrames(startStack)), + EndStack: ctx.Stack(viewerFrames(endStack)), + Arg: arg, + }, + Category: "Region", + Scope: fmt.Sprintf("%x", region.TaskID), + TaskColorIndex: uint64(region.TaskID), + }) +} + +// Building blocks for generators. + +// stackSampleGenerator implements a generic handler for stack sample events. +// The provided resource is the resource the stack sample should count against. +type stackSampleGenerator[R resource] struct { + // getResource is a function to extract a resource ID from a stack sample event. + getResource func(*tracev2.Event) R +} + +// StackSample implements a stack sample event handler. It expects ev to be one such event. +func (g *stackSampleGenerator[R]) StackSample(ctx *traceContext, ev *tracev2.Event) { + id := g.getResource(ev) + if id == R(noResource) { + // We have nowhere to put this in the UI. + return + } + ctx.Instant(traceviewer.InstantEvent{ + Name: "CPU profile sample", + Ts: ctx.elapsed(ev.Time()), + Resource: uint64(id), + Stack: ctx.Stack(viewerFrames(ev.Stack())), + }) +} + +// globalRangeGenerator implements a generic handler for EventRange* events that pertain +// to tracev2.ResourceNone (the global scope). +type globalRangeGenerator struct { + ranges map[string]activeRange + seenSync bool +} + +// Sync notifies the generator of an EventSync event. +func (g *globalRangeGenerator) Sync() { + g.seenSync = true +} + +// GlobalRange implements a handler for EventRange* events whose Scope.Kind is ResourceNone. +// It expects ev to be one such event. +func (g *globalRangeGenerator) GlobalRange(ctx *traceContext, ev *tracev2.Event) { + if g.ranges == nil { + g.ranges = make(map[string]activeRange) + } + r := ev.Range() + switch ev.Kind() { + case tracev2.EventRangeBegin: + g.ranges[r.Name] = activeRange{ev.Time(), ev.Stack()} + case tracev2.EventRangeActive: + // If we've seen a Sync event, then Active events are always redundant. + if !g.seenSync { + // Otherwise, they extend back to the start of the trace. + g.ranges[r.Name] = activeRange{ctx.startTime, ev.Stack()} + } + case tracev2.EventRangeEnd: + // Only emit GC events, because we have nowhere to + // put other events. + ar := g.ranges[r.Name] + if strings.Contains(r.Name, "GC") { + ctx.Slice(traceviewer.SliceEvent{ + Name: r.Name, + Ts: ctx.elapsed(ar.time), + Dur: ev.Time().Sub(ar.time), + Resource: trace.GCP, + Stack: ctx.Stack(viewerFrames(ar.stack)), + EndStack: ctx.Stack(viewerFrames(ev.Stack())), + }) + } + delete(g.ranges, r.Name) + } +} + +// Finish flushes any outstanding ranges at the end of the trace. +func (g *globalRangeGenerator) Finish(ctx *traceContext) { + for name, ar := range g.ranges { + if !strings.Contains(name, "GC") { + continue + } + ctx.Slice(traceviewer.SliceEvent{ + Name: name, + Ts: ctx.elapsed(ar.time), + Dur: ctx.endTime.Sub(ar.time), + Resource: trace.GCP, + Stack: ctx.Stack(viewerFrames(ar.stack)), + }) + } +} + +// globalMetricGenerator implements a generic handler for Metric events. +type globalMetricGenerator struct { +} + +// GlobalMetric implements an event handler for EventMetric events. ev must be one such event. +func (g *globalMetricGenerator) GlobalMetric(ctx *traceContext, ev *tracev2.Event) { + m := ev.Metric() + switch m.Name { + case "/memory/classes/heap/objects:bytes": + ctx.HeapAlloc(ctx.elapsed(ev.Time()), m.Value.Uint64()) + case "/gc/heap/goal:bytes": + ctx.HeapGoal(ctx.elapsed(ev.Time()), m.Value.Uint64()) + case "/sched/gomaxprocs:threads": + ctx.Gomaxprocs(m.Value.Uint64()) + } +} + +// procRangeGenerator implements a generic handler for EventRange* events whose Scope.Kind is +// ResourceProc. +type procRangeGenerator struct { + ranges map[tracev2.Range]activeRange + seenSync bool +} + +// Sync notifies the generator of an EventSync event. +func (g *procRangeGenerator) Sync() { + g.seenSync = true +} + +// ProcRange implements a handler for EventRange* events whose Scope.Kind is ResourceProc. +// It expects ev to be one such event. +func (g *procRangeGenerator) ProcRange(ctx *traceContext, ev *tracev2.Event) { + if g.ranges == nil { + g.ranges = make(map[tracev2.Range]activeRange) + } + r := ev.Range() + switch ev.Kind() { + case tracev2.EventRangeBegin: + g.ranges[r] = activeRange{ev.Time(), ev.Stack()} + case tracev2.EventRangeActive: + // If we've seen a Sync event, then Active events are always redundant. + if !g.seenSync { + // Otherwise, they extend back to the start of the trace. + g.ranges[r] = activeRange{ctx.startTime, ev.Stack()} + } + case tracev2.EventRangeEnd: + // Emit proc-based ranges. + ar := g.ranges[r] + ctx.Slice(traceviewer.SliceEvent{ + Name: r.Name, + Ts: ctx.elapsed(ar.time), + Dur: ev.Time().Sub(ar.time), + Resource: uint64(r.Scope.Proc()), + Stack: ctx.Stack(viewerFrames(ar.stack)), + EndStack: ctx.Stack(viewerFrames(ev.Stack())), + }) + delete(g.ranges, r) + } +} + +// Finish flushes any outstanding ranges at the end of the trace. +func (g *procRangeGenerator) Finish(ctx *traceContext) { + for r, ar := range g.ranges { + ctx.Slice(traceviewer.SliceEvent{ + Name: r.Name, + Ts: ctx.elapsed(ar.time), + Dur: ctx.endTime.Sub(ar.time), + Resource: uint64(r.Scope.Proc()), + Stack: ctx.Stack(viewerFrames(ar.stack)), + }) + } +} + +// activeRange represents an active EventRange* range. +type activeRange struct { + time tracev2.Time + stack tracev2.Stack +} + +// completedRange represents a completed EventRange* range. +type completedRange struct { + name string + startTime tracev2.Time + endTime tracev2.Time + startStack tracev2.Stack + endStack tracev2.Stack + arg any +} diff --git a/src/cmd/trace/v2/goroutinegen.go b/src/cmd/trace/v2/goroutinegen.go new file mode 100644 index 0000000000..eb1aea9bfa --- /dev/null +++ b/src/cmd/trace/v2/goroutinegen.go @@ -0,0 +1,164 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + tracev2 "internal/trace/v2" +) + +var _ generator = &goroutineGenerator{} + +type goroutineGenerator struct { + globalRangeGenerator + globalMetricGenerator + stackSampleGenerator[tracev2.GoID] + + gStates map[tracev2.GoID]*gState[tracev2.GoID] + focus tracev2.GoID + filter map[tracev2.GoID]struct{} +} + +func newGoroutineGenerator(ctx *traceContext, focus tracev2.GoID, filter map[tracev2.GoID]struct{}) *goroutineGenerator { + gg := new(goroutineGenerator) + gg.stackSampleGenerator.getResource = func(ev *tracev2.Event) tracev2.GoID { + return ev.Goroutine() + } + gg.gStates = make(map[tracev2.GoID]*gState[tracev2.GoID]) + gg.focus = focus + gg.filter = filter + + // Enable a filter on the emitter. + if filter != nil { + ctx.SetResourceFilter(func(resource uint64) bool { + _, ok := filter[tracev2.GoID(resource)] + return ok + }) + } + return gg +} + +func (g *goroutineGenerator) Sync() { + g.globalRangeGenerator.Sync() +} + +func (g *goroutineGenerator) GoroutineLabel(ctx *traceContext, ev *tracev2.Event) { + l := ev.Label() + g.gStates[l.Resource.Goroutine()].setLabel(l.Label) +} + +func (g *goroutineGenerator) GoroutineRange(ctx *traceContext, ev *tracev2.Event) { + r := ev.Range() + switch ev.Kind() { + case tracev2.EventRangeBegin: + g.gStates[r.Scope.Goroutine()].rangeBegin(ev.Time(), r.Name, ev.Stack()) + case tracev2.EventRangeActive: + g.gStates[r.Scope.Goroutine()].rangeActive(r.Name) + case tracev2.EventRangeEnd: + gs := g.gStates[r.Scope.Goroutine()] + gs.rangeEnd(ev.Time(), r.Name, ev.Stack(), ctx) + } +} + +func (g *goroutineGenerator) GoroutineTransition(ctx *traceContext, ev *tracev2.Event) { + st := ev.StateTransition() + goID := st.Resource.Goroutine() + + // If we haven't seen this goroutine before, create a new + // gState for it. + gs, ok := g.gStates[goID] + if !ok { + gs = newGState[tracev2.GoID](goID) + g.gStates[goID] = gs + } + + // Try to augment the name of the goroutine. + gs.augmentName(st.Stack) + + // Handle the goroutine state transition. + from, to := st.Goroutine() + if from == to { + // Filter out no-op events. + return + } + if from.Executing() && !to.Executing() { + if to == tracev2.GoWaiting { + // Goroutine started blocking. + gs.block(ev.Time(), ev.Stack(), st.Reason, ctx) + } else { + gs.stop(ev.Time(), ev.Stack(), ctx) + } + } + if !from.Executing() && to.Executing() { + start := ev.Time() + if from == tracev2.GoUndetermined { + // Back-date the event to the start of the trace. + start = ctx.startTime + } + gs.start(start, goID, ctx) + } + + if from == tracev2.GoWaiting { + // Goroutine unblocked. + gs.unblock(ev.Time(), ev.Stack(), ev.Goroutine(), ctx) + } + if from == tracev2.GoNotExist && to == tracev2.GoRunnable { + // Goroutine was created. + gs.created(ev.Time(), ev.Goroutine(), ev.Stack()) + } + if from == tracev2.GoSyscall && to != tracev2.GoRunning { + // Exiting blocked syscall. + gs.syscallEnd(ev.Time(), true, ctx) + gs.blockedSyscallEnd(ev.Time(), ev.Stack(), ctx) + } else if from == tracev2.GoSyscall { + // Check if we're exiting a syscall in a non-blocking way. + gs.syscallEnd(ev.Time(), false, ctx) + } + + // Handle syscalls. + if to == tracev2.GoSyscall { + start := ev.Time() + if from == tracev2.GoUndetermined { + // Back-date the event to the start of the trace. + start = ctx.startTime + } + // Write down that we've entered a syscall. Note: we might have no G or P here + // if we're in a cgo callback or this is a transition from GoUndetermined + // (i.e. the G has been blocked in a syscall). + gs.syscallBegin(start, goID, ev.Stack()) + } + + // Note down the goroutine transition. + _, inMarkAssist := gs.activeRanges["GC mark assist"] + ctx.GoroutineTransition(ctx.elapsed(ev.Time()), viewerGState(from, inMarkAssist), viewerGState(to, inMarkAssist)) +} + +func (g *goroutineGenerator) ProcRange(ctx *traceContext, ev *tracev2.Event) { + // TODO(mknyszek): Extend procRangeGenerator to support rendering proc ranges + // that overlap with a goroutine's execution. +} + +func (g *goroutineGenerator) ProcTransition(ctx *traceContext, ev *tracev2.Event) { + // Not needed. All relevant information for goroutines can be derived from goroutine transitions. +} + +func (g *goroutineGenerator) Finish(ctx *traceContext) { + ctx.SetResourceType("G") + + // Finish off global ranges. + g.globalRangeGenerator.Finish(ctx) + + // Finish off all the goroutine slices. + for id, gs := range g.gStates { + gs.finish(ctx) + + // Tell the emitter about the goroutines we want to render. + ctx.Resource(uint64(id), gs.name()) + } + + // Set the goroutine to focus on. + if g.focus != tracev2.NoGoroutine { + ctx.Focus(uint64(g.focus)) + } +} diff --git a/src/cmd/trace/v2/goroutines.go b/src/cmd/trace/v2/goroutines.go new file mode 100644 index 0000000000..44febeba88 --- /dev/null +++ b/src/cmd/trace/v2/goroutines.go @@ -0,0 +1,435 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Goroutine-related profiles. + +package trace + +import ( + "cmp" + "fmt" + "html/template" + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" + "log" + "net/http" + "slices" + "sort" + "strconv" + "strings" + "time" +) + +// GoroutinesHandlerFunc returns a HandlerFunc that serves list of goroutine groups. +func GoroutinesHandlerFunc(summaries map[tracev2.GoID]*trace.GoroutineSummary) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // goroutineGroup describes a group of goroutines grouped by start PC. + type goroutineGroup struct { + ID uint64 // Unique identifier (PC). + Name string // Start function. + N int // Total number of goroutines in this group. + ExecTime time.Duration // Total execution time of all goroutines in this group. + } + // Accumulate groups by PC. + groupsByPC := make(map[uint64]goroutineGroup) + for _, summary := range summaries { + group := groupsByPC[summary.PC] + group.ID = summary.PC + group.Name = summary.Name + group.N++ + group.ExecTime += summary.ExecTime + groupsByPC[summary.PC] = group + } + var groups []goroutineGroup + for pc, group := range groupsByPC { + group.ID = pc + // If goroutine didn't run during the trace (no sampled PC), + // the v.ID and v.Name will be zero value. + if group.ID == 0 && group.Name == "" { + group.Name = "(Inactive, no stack trace sampled)" + } + groups = append(groups, group) + } + slices.SortFunc(groups, func(a, b goroutineGroup) int { + return cmp.Compare(b.ExecTime, a.ExecTime) + }) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + if err := templGoroutines.Execute(w, groups); err != nil { + log.Printf("failed to execute template: %v", err) + return + } + } +} + +var templGoroutines = template.Must(template.New("").Parse(` + + + +

    Goroutines

    +Below is a table of all goroutines in the trace grouped by start location and sorted by the total execution time of the group.
    +
    +Click a start location to view more details about that group.
    +
    + + + + + + +{{range $}} + + + + + +{{end}} +
    Start locationCountTotal execution time
    {{.Name}}{{.N}}{{.ExecTime}}
    + + +`)) + +// GoroutineHandler creates a handler that serves information about +// goroutines in a particular group. +func GoroutineHandler(summaries map[tracev2.GoID]*trace.GoroutineSummary) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + pc, err := strconv.ParseUint(r.FormValue("id"), 10, 64) + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse id parameter '%v': %v", r.FormValue("id"), err), http.StatusInternalServerError) + return + } + + type goroutine struct { + *trace.GoroutineSummary + NonOverlappingStats map[string]time.Duration + HasRangeTime bool + } + + // Collect all the goroutines in the group. + var ( + goroutines []goroutine + name string + totalExecTime, execTime time.Duration + maxTotalTime time.Duration + ) + validNonOverlappingStats := make(map[string]struct{}) + validRangeStats := make(map[string]struct{}) + for _, summary := range summaries { + totalExecTime += summary.ExecTime + + if summary.PC != pc { + continue + } + nonOverlappingStats := summary.NonOverlappingStats() + for name := range nonOverlappingStats { + validNonOverlappingStats[name] = struct{}{} + } + var totalRangeTime time.Duration + for name, dt := range summary.RangeTime { + validRangeStats[name] = struct{}{} + totalRangeTime += dt + } + goroutines = append(goroutines, goroutine{ + GoroutineSummary: summary, + NonOverlappingStats: nonOverlappingStats, + HasRangeTime: totalRangeTime != 0, + }) + name = summary.Name + execTime += summary.ExecTime + if maxTotalTime < summary.TotalTime { + maxTotalTime = summary.TotalTime + } + } + + // Compute the percent of total execution time these goroutines represent. + execTimePercent := "" + if totalExecTime > 0 { + execTimePercent = fmt.Sprintf("%.2f%%", float64(execTime)/float64(totalExecTime)*100) + } + + // Sort. + sortBy := r.FormValue("sortby") + if _, ok := validNonOverlappingStats[sortBy]; ok { + slices.SortFunc(goroutines, func(a, b goroutine) int { + return cmp.Compare(b.NonOverlappingStats[sortBy], a.NonOverlappingStats[sortBy]) + }) + } else { + // Sort by total time by default. + slices.SortFunc(goroutines, func(a, b goroutine) int { + return cmp.Compare(b.TotalTime, a.TotalTime) + }) + } + + // Write down all the non-overlapping stats and sort them. + allNonOverlappingStats := make([]string, 0, len(validNonOverlappingStats)) + for name := range validNonOverlappingStats { + allNonOverlappingStats = append(allNonOverlappingStats, name) + } + slices.SortFunc(allNonOverlappingStats, func(a, b string) int { + if a == b { + return 0 + } + if a == "Execution time" { + return -1 + } + if b == "Execution time" { + return 1 + } + return cmp.Compare(a, b) + }) + + // Write down all the range stats and sort them. + allRangeStats := make([]string, 0, len(validRangeStats)) + for name := range validRangeStats { + allRangeStats = append(allRangeStats, name) + } + sort.Strings(allRangeStats) + + err = templGoroutine.Execute(w, struct { + Name string + PC uint64 + N int + ExecTimePercent string + MaxTotal time.Duration + Goroutines []goroutine + NonOverlappingStats []string + RangeStats []string + }{ + Name: name, + PC: pc, + N: len(goroutines), + ExecTimePercent: execTimePercent, + MaxTotal: maxTotalTime, + Goroutines: goroutines, + NonOverlappingStats: allNonOverlappingStats, + RangeStats: allRangeStats, + }) + if err != nil { + http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) + return + } + } +} + +func stat2Color(statName string) string { + color := "#636363" + if strings.HasPrefix(statName, "Block time") { + color = "#d01c8b" + } + switch statName { + case "Sched wait time": + color = "#2c7bb6" + case "Syscall execution time": + color = "#7b3294" + case "Execution time": + color = "#d7191c" + } + return color +} + +var templGoroutine = template.Must(template.New("").Funcs(template.FuncMap{ + "percent": func(dividend, divisor time.Duration) template.HTML { + if divisor == 0 { + return "" + } + return template.HTML(fmt.Sprintf("(%.1f%%)", float64(dividend)/float64(divisor)*100)) + }, + "headerStyle": func(statName string) template.HTMLAttr { + return template.HTMLAttr(fmt.Sprintf("style=\"background-color: %s;\"", stat2Color(statName))) + }, + "barStyle": func(statName string, dividend, divisor time.Duration) template.HTMLAttr { + width := "0" + if divisor != 0 { + width = fmt.Sprintf("%.2f%%", float64(dividend)/float64(divisor)*100) + } + return template.HTMLAttr(fmt.Sprintf("style=\"width: %s; background-color: %s;\"", width, stat2Color(statName))) + }, +}).Parse(` + +Goroutines: {{.Name}} + + + + +

    Goroutines

    + +Table of contents + + +

    Summary

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Goroutine start location:{{.Name}}
    Count:{{.N}}
    Execution Time:{{.ExecTimePercent}} of total program execution time
    Network wait profile: graph (download)
    Sync block profile: graph (download)
    Syscall profile: graph (download)
    Scheduler wait profile: graph (download)
    + +

    Breakdown

    + +The table below breaks down where each goroutine is spent its time during the +traced period. +All of the columns except total time are non-overlapping. +
    +
    + + + + + + +{{range $.NonOverlappingStats}} + +{{end}} + +{{range .Goroutines}} + + + + + {{$Goroutine := .}} + {{range $.NonOverlappingStats}} + {{$Time := index $Goroutine.NonOverlappingStats .}} + + {{end}} + +{{end}} +
    Goroutine Total {{.}}
    {{.ID}} {{ .TotalTime.String }} +
    + {{$Goroutine := .}} + {{range $.NonOverlappingStats}} + {{$Time := index $Goroutine.NonOverlappingStats .}} + {{if $Time}} +   + {{end}} + {{end}} +
    +
    {{$Time.String}}
    + +

    Special ranges

    + +The table below describes how much of the traced period each goroutine spent in +certain special time ranges. +If a goroutine has spent no time in any special time ranges, it is excluded from +the table. +For example, how much time it spent helping the GC. Note that these times do +overlap with the times from the first table. +In general the goroutine may not be executing in these special time ranges. +For example, it may have blocked while trying to help the GC. +This must be taken into account when interpreting the data. +
    +
    + + + + + +{{range $.RangeStats}} + +{{end}} + +{{range .Goroutines}} + {{if .HasRangeTime}} + + + + {{$Goroutine := .}} + {{range $.RangeStats}} + {{$Time := index $Goroutine.RangeTime .}} + + {{end}} + + {{end}} +{{end}} +
    Goroutine Total {{.}}
    {{.ID}} {{ .TotalTime.String }} {{$Time.String}}
    +`)) diff --git a/src/cmd/trace/v2/gstate.go b/src/cmd/trace/v2/gstate.go new file mode 100644 index 0000000000..aeba7ecbc1 --- /dev/null +++ b/src/cmd/trace/v2/gstate.go @@ -0,0 +1,373 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "fmt" + "internal/trace" + "internal/trace/traceviewer" + "internal/trace/traceviewer/format" + tracev2 "internal/trace/v2" + "strings" +) + +// resource is a generic constraint interface for resource IDs. +type resource interface { + tracev2.GoID | tracev2.ProcID | tracev2.ThreadID +} + +// noResource indicates the lack of a resource. +const noResource = -1 + +// gState represents the trace viewer state of a goroutine in a trace. +// +// The type parameter on this type is the resource which is used to construct +// a timeline of events. e.g. R=ProcID for a proc-oriented view, R=GoID for +// a goroutine-oriented view, etc. +type gState[R resource] struct { + baseName string + named bool // Whether baseName has been set. + label string // EventLabel extension. + isSystemG bool + + executing R // The resource this goroutine is executing on. (Could be itself.) + + // lastStopStack is the stack trace at the point of the last + // call to the stop method. This tends to be a more reliable way + // of picking up stack traces, since the parser doesn't provide + // a stack for every state transition event. + lastStopStack tracev2.Stack + + // activeRanges is the set of all active ranges on the goroutine. + activeRanges map[string]activeRange + + // completedRanges is a list of ranges that completed since before the + // goroutine stopped executing. These are flushed on every stop or block. + completedRanges []completedRange + + // startRunning is the most recent event that caused a goroutine to + // transition to GoRunning. + startRunningTime tracev2.Time + + // startSyscall is the most recent event that caused a goroutine to + // transition to GoSyscall. + syscall struct { + time tracev2.Time + stack tracev2.Stack + active bool + } + + // startBlockReason is the StateTransition.Reason of the most recent + // event that caused a gorotuine to transition to GoWaiting. + startBlockReason string + + // startCause is the event that allowed this goroutine to start running. + // It's used to generate flow events. This is typically something like + // an unblock event or a goroutine creation event. + // + // startCause.resource is the resource on which startCause happened, but is + // listed separately because the cause may have happened on a resource that + // isn't R (or perhaps on some abstract nebulous resource, like trace.NetpollP). + startCause struct { + time tracev2.Time + name string + resource uint64 + stack tracev2.Stack + } +} + +// newGState constructs a new goroutine state for the goroutine +// identified by the provided ID. +func newGState[R resource](goID tracev2.GoID) *gState[R] { + return &gState[R]{ + baseName: fmt.Sprintf("G%d", goID), + executing: R(noResource), + activeRanges: make(map[string]activeRange), + } +} + +// augmentName attempts to use stk to augment the name of the goroutine +// with stack information. This stack must be related to the goroutine +// in some way, but it doesn't really matter which stack. +func (gs *gState[R]) augmentName(stk tracev2.Stack) { + if gs.named { + return + } + if stk == tracev2.NoStack { + return + } + name := lastFunc(stk) + gs.baseName += fmt.Sprintf(" %s", name) + gs.named = true + gs.isSystemG = trace.IsSystemGoroutine(name) +} + +// setLabel adds an additional label to the goroutine's name. +func (gs *gState[R]) setLabel(label string) { + gs.label = label +} + +// name returns a name for the goroutine. +func (gs *gState[R]) name() string { + name := gs.baseName + if gs.label != "" { + name += " (" + gs.label + ")" + } + return name +} + +// setStartCause sets the reason a goroutine will be allowed to start soon. +// For example, via unblocking or exiting a blocked syscall. +func (gs *gState[R]) setStartCause(ts tracev2.Time, name string, resource uint64, stack tracev2.Stack) { + gs.startCause.time = ts + gs.startCause.name = name + gs.startCause.resource = resource + gs.startCause.stack = stack +} + +// created indicates that this goroutine was just created by the provided creator. +func (gs *gState[R]) created(ts tracev2.Time, creator R, stack tracev2.Stack) { + if creator == R(noResource) { + return + } + gs.setStartCause(ts, "go", uint64(creator), stack) +} + +// start indicates that a goroutine has started running on a proc. +func (gs *gState[R]) start(ts tracev2.Time, resource R, ctx *traceContext) { + // Set the time for all the active ranges. + for name := range gs.activeRanges { + gs.activeRanges[name] = activeRange{ts, tracev2.NoStack} + } + + if gs.startCause.name != "" { + // It has a start cause. Emit a flow event. + ctx.Arrow(traceviewer.ArrowEvent{ + Name: gs.startCause.name, + Start: ctx.elapsed(gs.startCause.time), + End: ctx.elapsed(ts), + FromResource: uint64(gs.startCause.resource), + ToResource: uint64(resource), + FromStack: ctx.Stack(viewerFrames(gs.startCause.stack)), + }) + gs.startCause.time = 0 + gs.startCause.name = "" + gs.startCause.resource = 0 + gs.startCause.stack = tracev2.NoStack + } + gs.executing = resource + gs.startRunningTime = ts +} + +// syscallBegin indicates that the goroutine entered a syscall on a proc. +func (gs *gState[R]) syscallBegin(ts tracev2.Time, resource R, stack tracev2.Stack) { + gs.syscall.time = ts + gs.syscall.stack = stack + gs.syscall.active = true + if gs.executing == R(noResource) { + gs.executing = resource + gs.startRunningTime = ts + } +} + +// syscallEnd ends the syscall slice, wherever the syscall is at. This is orthogonal +// to blockedSyscallEnd -- both must be called when a syscall ends and that syscall +// blocked. They're kept separate because syscallEnd indicates the point at which the +// goroutine is no longer executing on the resource (e.g. a proc) whereas blockedSyscallEnd +// is the point at which the goroutine actually exited the syscall regardless of which +// resource that happened on. +func (gs *gState[R]) syscallEnd(ts tracev2.Time, blocked bool, ctx *traceContext) { + if !gs.syscall.active { + return + } + blockString := "no" + if blocked { + blockString = "yes" + } + gs.completedRanges = append(gs.completedRanges, completedRange{ + name: "syscall", + startTime: gs.syscall.time, + endTime: ts, + startStack: gs.syscall.stack, + arg: format.BlockedArg{Blocked: blockString}, + }) + gs.syscall.active = false + gs.syscall.time = 0 + gs.syscall.stack = tracev2.NoStack +} + +// blockedSyscallEnd indicates the point at which the blocked syscall ended. This is distinct +// and orthogonal to syscallEnd; both must be called if the syscall blocked. This sets up an instant +// to emit a flow event from, indicating explicitly that this goroutine was unblocked by the system. +func (gs *gState[R]) blockedSyscallEnd(ts tracev2.Time, stack tracev2.Stack, ctx *traceContext) { + name := "exit blocked syscall" + gs.setStartCause(ts, name, trace.SyscallP, stack) + + // Emit an syscall exit instant event for the "Syscall" lane. + ctx.Instant(traceviewer.InstantEvent{ + Name: name, + Ts: ctx.elapsed(ts), + Resource: trace.SyscallP, + Stack: ctx.Stack(viewerFrames(stack)), + }) +} + +// unblock indicates that the goroutine gs represents has been unblocked. +func (gs *gState[R]) unblock(ts tracev2.Time, stack tracev2.Stack, resource R, ctx *traceContext) { + name := "unblock" + viewerResource := uint64(resource) + if gs.startBlockReason != "" { + name = fmt.Sprintf("%s (%s)", name, gs.startBlockReason) + } + if strings.Contains(gs.startBlockReason, "network") { + // Attribute the network instant to the nebulous "NetpollP" if + // resource isn't a thread, because there's a good chance that + // resource isn't going to be valid in this case. + // + // TODO(mknyszek): Handle this invalidness in a more general way. + if _, ok := any(resource).(tracev2.ThreadID); !ok { + // Emit an unblock instant event for the "Network" lane. + viewerResource = trace.NetpollP + } + ctx.Instant(traceviewer.InstantEvent{ + Name: name, + Ts: ctx.elapsed(ts), + Resource: viewerResource, + Stack: ctx.Stack(viewerFrames(stack)), + }) + } + gs.startBlockReason = "" + if viewerResource != 0 { + gs.setStartCause(ts, name, viewerResource, stack) + } +} + +// block indicates that the goroutine has stopped executing on a proc -- specifically, +// it blocked for some reason. +func (gs *gState[R]) block(ts tracev2.Time, stack tracev2.Stack, reason string, ctx *traceContext) { + gs.startBlockReason = reason + gs.stop(ts, stack, ctx) +} + +// stop indicates that the goroutine has stopped executing on a proc. +func (gs *gState[R]) stop(ts tracev2.Time, stack tracev2.Stack, ctx *traceContext) { + // Emit the execution time slice. + var stk int + if gs.lastStopStack != tracev2.NoStack { + stk = ctx.Stack(viewerFrames(gs.lastStopStack)) + } + // Check invariants. + if gs.startRunningTime == 0 { + panic("silently broken trace or generator invariant (startRunningTime != 0) not held") + } + if gs.executing == R(noResource) { + panic("non-executing goroutine stopped") + } + ctx.Slice(traceviewer.SliceEvent{ + Name: gs.name(), + Ts: ctx.elapsed(gs.startRunningTime), + Dur: ts.Sub(gs.startRunningTime), + Resource: uint64(gs.executing), + Stack: stk, + }) + + // Flush completed ranges. + for _, cr := range gs.completedRanges { + ctx.Slice(traceviewer.SliceEvent{ + Name: cr.name, + Ts: ctx.elapsed(cr.startTime), + Dur: cr.endTime.Sub(cr.startTime), + Resource: uint64(gs.executing), + Stack: ctx.Stack(viewerFrames(cr.startStack)), + EndStack: ctx.Stack(viewerFrames(cr.endStack)), + Arg: cr.arg, + }) + } + gs.completedRanges = gs.completedRanges[:0] + + // Continue in-progress ranges. + for name, r := range gs.activeRanges { + // Check invariant. + if r.time == 0 { + panic("silently broken trace or generator invariant (activeRanges time != 0) not held") + } + ctx.Slice(traceviewer.SliceEvent{ + Name: name, + Ts: ctx.elapsed(r.time), + Dur: ts.Sub(r.time), + Resource: uint64(gs.executing), + Stack: ctx.Stack(viewerFrames(r.stack)), + }) + } + + // Clear the range info. + for name := range gs.activeRanges { + gs.activeRanges[name] = activeRange{0, tracev2.NoStack} + } + + gs.startRunningTime = 0 + gs.lastStopStack = stack + gs.executing = R(noResource) +} + +// finalize writes out any in-progress slices as if the goroutine stopped. +// This must only be used once the trace has been fully processed and no +// further events will be processed. This method may leave the gState in +// an inconsistent state. +func (gs *gState[R]) finish(ctx *traceContext) { + if gs.executing != R(noResource) { + gs.syscallEnd(ctx.endTime, false, ctx) + gs.stop(ctx.endTime, tracev2.NoStack, ctx) + } +} + +// rangeBegin indicates the start of a special range of time. +func (gs *gState[R]) rangeBegin(ts tracev2.Time, name string, stack tracev2.Stack) { + if gs.executing != R(noResource) { + // If we're executing, start the slice from here. + gs.activeRanges[name] = activeRange{ts, stack} + } else { + // If the goroutine isn't executing, there's no place for + // us to create a slice from. Wait until it starts executing. + gs.activeRanges[name] = activeRange{0, stack} + } +} + +// rangeActive indicates that a special range of time has been in progress. +func (gs *gState[R]) rangeActive(name string) { + if gs.executing != R(noResource) { + // If we're executing, and the range is active, then start + // from wherever the goroutine started running from. + gs.activeRanges[name] = activeRange{gs.startRunningTime, tracev2.NoStack} + } else { + // If the goroutine isn't executing, there's no place for + // us to create a slice from. Wait until it starts executing. + gs.activeRanges[name] = activeRange{0, tracev2.NoStack} + } +} + +// rangeEnd indicates the end of a special range of time. +func (gs *gState[R]) rangeEnd(ts tracev2.Time, name string, stack tracev2.Stack, ctx *traceContext) { + if gs.executing != R(noResource) { + r := gs.activeRanges[name] + gs.completedRanges = append(gs.completedRanges, completedRange{ + name: name, + startTime: r.time, + endTime: ts, + startStack: r.stack, + endStack: stack, + }) + } + delete(gs.activeRanges, name) +} + +func lastFunc(s tracev2.Stack) string { + var last tracev2.StackFrame + s.Frames(func(f tracev2.StackFrame) bool { + last = f + return true + }) + return last.Func +} diff --git a/src/cmd/trace/v2/jsontrace.go b/src/cmd/trace/v2/jsontrace.go new file mode 100644 index 0000000000..e4ca613678 --- /dev/null +++ b/src/cmd/trace/v2/jsontrace.go @@ -0,0 +1,229 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "cmp" + "log" + "math" + "net/http" + "slices" + "strconv" + "time" + + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" +) + +func JSONTraceHandler(parsed *parsedTrace) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + opts := defaultGenOpts() + + switch r.FormValue("view") { + case "thread": + opts.mode = traceviewer.ModeThreadOriented + } + if goids := r.FormValue("goid"); goids != "" { + // Render trace focused on a particular goroutine. + + id, err := strconv.ParseUint(goids, 10, 64) + if err != nil { + log.Printf("failed to parse goid parameter %q: %v", goids, err) + return + } + goid := tracev2.GoID(id) + g, ok := parsed.summary.Goroutines[goid] + if !ok { + log.Printf("failed to find goroutine %d", goid) + return + } + opts.mode = traceviewer.ModeGoroutineOriented + if g.StartTime != 0 { + opts.startTime = g.StartTime.Sub(parsed.startTime()) + } else { + opts.startTime = 0 + } + if g.EndTime != 0 { + opts.endTime = g.EndTime.Sub(parsed.startTime()) + } else { // The goroutine didn't end. + opts.endTime = parsed.endTime().Sub(parsed.startTime()) + } + opts.focusGoroutine = goid + opts.goroutines = trace.RelatedGoroutinesV2(parsed.events, goid) + } else if taskids := r.FormValue("focustask"); taskids != "" { + taskid, err := strconv.ParseUint(taskids, 10, 64) + if err != nil { + log.Printf("failed to parse focustask parameter %q: %v", taskids, err) + return + } + task, ok := parsed.summary.Tasks[tracev2.TaskID(taskid)] + if !ok || (task.Start == nil && task.End == nil) { + log.Printf("failed to find task with id %d", taskid) + return + } + opts.setTask(parsed, task) + } else if taskids := r.FormValue("taskid"); taskids != "" { + taskid, err := strconv.ParseUint(taskids, 10, 64) + if err != nil { + log.Printf("failed to parse taskid parameter %q: %v", taskids, err) + return + } + task, ok := parsed.summary.Tasks[tracev2.TaskID(taskid)] + if !ok { + log.Printf("failed to find task with id %d", taskid) + return + } + // This mode is goroutine-oriented. + opts.mode = traceviewer.ModeGoroutineOriented + opts.setTask(parsed, task) + + // Pick the goroutine to orient ourselves around by just + // trying to pick the earliest event in the task that makes + // any sense. Though, we always want the start if that's there. + var firstEv *tracev2.Event + if task.Start != nil { + firstEv = task.Start + } else { + for _, logEv := range task.Logs { + if firstEv == nil || logEv.Time() < firstEv.Time() { + firstEv = logEv + } + } + if task.End != nil && (firstEv == nil || task.End.Time() < firstEv.Time()) { + firstEv = task.End + } + } + if firstEv == nil || firstEv.Goroutine() == tracev2.NoGoroutine { + log.Printf("failed to find task with id %d", taskid) + return + } + + // Set the goroutine filtering options. + goid := firstEv.Goroutine() + opts.focusGoroutine = goid + goroutines := make(map[tracev2.GoID]struct{}) + for _, task := range opts.tasks { + // Find only directly involved goroutines. + for id := range task.Goroutines { + goroutines[id] = struct{}{} + } + } + opts.goroutines = goroutines + } + + // Parse start and end options. Both or none must be present. + start := int64(0) + end := int64(math.MaxInt64) + if startStr, endStr := r.FormValue("start"), r.FormValue("end"); startStr != "" && endStr != "" { + var err error + start, err = strconv.ParseInt(startStr, 10, 64) + if err != nil { + log.Printf("failed to parse start parameter %q: %v", startStr, err) + return + } + + end, err = strconv.ParseInt(endStr, 10, 64) + if err != nil { + log.Printf("failed to parse end parameter %q: %v", endStr, err) + return + } + } + + c := traceviewer.ViewerDataTraceConsumer(w, start, end) + if err := generateTrace(parsed, opts, c); err != nil { + log.Printf("failed to generate trace: %v", err) + } + }) +} + +// traceContext is a wrapper around a traceviewer.Emitter with some additional +// information that's useful to most parts of trace viewer JSON emission. +type traceContext struct { + *traceviewer.Emitter + startTime tracev2.Time + endTime tracev2.Time +} + +// elapsed returns the elapsed time between the trace time and the start time +// of the trace. +func (ctx *traceContext) elapsed(now tracev2.Time) time.Duration { + return now.Sub(ctx.startTime) +} + +type genOpts struct { + mode traceviewer.Mode + startTime time.Duration + endTime time.Duration + + // Used if mode != 0. + focusGoroutine tracev2.GoID + goroutines map[tracev2.GoID]struct{} // Goroutines to be displayed for goroutine-oriented or task-oriented view. goroutines[0] is the main goroutine. + tasks []*trace.UserTaskSummary +} + +// setTask sets a task to focus on. +func (opts *genOpts) setTask(parsed *parsedTrace, task *trace.UserTaskSummary) { + opts.mode |= traceviewer.ModeTaskOriented + if task.Start != nil { + opts.startTime = task.Start.Time().Sub(parsed.startTime()) + } else { // The task started before the trace did. + opts.startTime = 0 + } + if task.End != nil { + opts.endTime = task.End.Time().Sub(parsed.startTime()) + } else { // The task didn't end. + opts.endTime = parsed.endTime().Sub(parsed.startTime()) + } + opts.tasks = task.Descendents() + slices.SortStableFunc(opts.tasks, func(a, b *trace.UserTaskSummary) int { + aStart, bStart := parsed.startTime(), parsed.startTime() + if a.Start != nil { + aStart = a.Start.Time() + } + if b.Start != nil { + bStart = b.Start.Time() + } + if a.Start != b.Start { + return cmp.Compare(aStart, bStart) + } + // Break ties with the end time. + aEnd, bEnd := parsed.endTime(), parsed.endTime() + if a.End != nil { + aEnd = a.End.Time() + } + if b.End != nil { + bEnd = b.End.Time() + } + return cmp.Compare(aEnd, bEnd) + }) +} + +func defaultGenOpts() *genOpts { + return &genOpts{ + startTime: time.Duration(0), + endTime: time.Duration(math.MaxInt64), + } +} + +func generateTrace(parsed *parsedTrace, opts *genOpts, c traceviewer.TraceConsumer) error { + ctx := &traceContext{ + Emitter: traceviewer.NewEmitter(c, opts.startTime, opts.endTime), + startTime: parsed.events[0].Time(), + endTime: parsed.events[len(parsed.events)-1].Time(), + } + defer ctx.Flush() + + var g generator + if opts.mode&traceviewer.ModeGoroutineOriented != 0 { + g = newGoroutineGenerator(ctx, opts.focusGoroutine, opts.goroutines) + } else if opts.mode&traceviewer.ModeThreadOriented != 0 { + g = newThreadGenerator() + } else { + g = newProcGenerator() + } + runGenerator(ctx, g, parsed, opts) + return nil +} diff --git a/src/cmd/trace/v2/jsontrace_test.go b/src/cmd/trace/v2/jsontrace_test.go new file mode 100644 index 0000000000..ac988b7240 --- /dev/null +++ b/src/cmd/trace/v2/jsontrace_test.go @@ -0,0 +1,291 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "bytes" + "encoding/json" + tracev1 "internal/trace" + "io" + "net/http/httptest" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + "testing" + "time" + + "internal/trace/traceviewer/format" + "internal/trace/v2/raw" +) + +func TestJSONTraceHandler(t *testing.T) { + testPaths, err := filepath.Glob("./testdata/*.test") + if err != nil { + t.Fatalf("discovering tests: %v", err) + } + for _, testPath := range testPaths { + t.Run(filepath.Base(testPath), func(t *testing.T) { + parsed := getTestTrace(t, testPath) + data := recordJSONTraceHandlerResponse(t, parsed) + // TODO(mknyszek): Check that there's one at most goroutine per proc at any given time. + checkExecutionTimes(t, data) + checkPlausibleHeapMetrics(t, data) + // TODO(mknyszek): Check for plausible thread and goroutine metrics. + checkMetaNamesEmitted(t, data, "process_name", []string{"STATS", "PROCS"}) + checkMetaNamesEmitted(t, data, "thread_name", []string{"GC", "Network", "Timers", "Syscalls", "Proc 0"}) + checkProcStartStop(t, data) + checkSyscalls(t, data) + checkNetworkUnblock(t, data) + // TODO(mknyszek): Check for flow events. + }) + } +} + +func checkSyscalls(t *testing.T, data format.Data) { + data = filterViewerTrace(data, + filterEventName("syscall"), + filterStackRootFunc("main.blockingSyscall")) + if len(data.Events) <= 1 { + t.Errorf("got %d events, want > 1", len(data.Events)) + } + data = filterViewerTrace(data, filterBlocked("yes")) + if len(data.Events) != 1 { + t.Errorf("got %d events, want 1", len(data.Events)) + } +} + +type eventFilterFn func(*format.Event, *format.Data) bool + +func filterEventName(name string) eventFilterFn { + return func(e *format.Event, _ *format.Data) bool { + return e.Name == name + } +} + +// filterGoRoutineName returns an event filter that returns true if the event's +// goroutine name is equal to name. +func filterGoRoutineName(name string) eventFilterFn { + return func(e *format.Event, _ *format.Data) bool { + return parseGoroutineName(e) == name + } +} + +// parseGoroutineName returns the goroutine name from the event's name field. +// E.g. if e.Name is "G42 main.cpu10", this returns "main.cpu10". +func parseGoroutineName(e *format.Event) string { + parts := strings.SplitN(e.Name, " ", 2) + if len(parts) != 2 || !strings.HasPrefix(parts[0], "G") { + return "" + } + return parts[1] +} + +// filterBlocked returns an event filter that returns true if the event's +// "blocked" argument is equal to blocked. +func filterBlocked(blocked string) eventFilterFn { + return func(e *format.Event, _ *format.Data) bool { + m, ok := e.Arg.(map[string]any) + if !ok { + return false + } + return m["blocked"] == blocked + } +} + +// filterStackRootFunc returns an event filter that returns true if the function +// at the root of the stack trace is named name. +func filterStackRootFunc(name string) eventFilterFn { + return func(e *format.Event, data *format.Data) bool { + frames := stackFrames(data, e.Stack) + rootFrame := frames[len(frames)-1] + return strings.HasPrefix(rootFrame, name+":") + } +} + +// filterViewerTrace returns a copy of data with only the events that pass all +// of the given filters. +func filterViewerTrace(data format.Data, fns ...eventFilterFn) (filtered format.Data) { + filtered = data + filtered.Events = nil + for _, e := range data.Events { + keep := true + for _, fn := range fns { + keep = keep && fn(e, &filtered) + } + if keep { + filtered.Events = append(filtered.Events, e) + } + } + return +} + +func stackFrames(data *format.Data, stackID int) (frames []string) { + for { + frame, ok := data.Frames[strconv.Itoa(stackID)] + if !ok { + return + } + frames = append(frames, frame.Name) + stackID = frame.Parent + } +} + +func checkProcStartStop(t *testing.T, data format.Data) { + procStarted := map[uint64]bool{} + for _, e := range data.Events { + if e.Name == "proc start" { + if procStarted[e.TID] == true { + t.Errorf("proc started twice: %d", e.TID) + } + procStarted[e.TID] = true + } + if e.Name == "proc stop" { + if procStarted[e.TID] == false { + t.Errorf("proc stopped twice: %d", e.TID) + } + procStarted[e.TID] = false + } + } + if got, want := len(procStarted), 8; got != want { + t.Errorf("wrong number of procs started/stopped got=%d want=%d", got, want) + } +} + +func checkNetworkUnblock(t *testing.T, data format.Data) { + count := 0 + var netBlockEv *format.Event + for _, e := range data.Events { + if e.TID == tracev1.NetpollP && e.Name == "unblock (network)" && e.Phase == "I" && e.Scope == "t" { + count++ + netBlockEv = e + } + } + if netBlockEv == nil { + t.Error("failed to find a network unblock") + } + if count == 0 || count > 2 { + t.Errorf("found too many network block events: want 1 or 2, found %d", count) + } + // TODO(mknyszek): Check for the flow of this event to some slice event of a goroutine running. +} + +func checkExecutionTimes(t *testing.T, data format.Data) { + cpu10 := sumExecutionTime(filterViewerTrace(data, filterGoRoutineName("main.cpu10"))) + cpu20 := sumExecutionTime(filterViewerTrace(data, filterGoRoutineName("main.cpu20"))) + if cpu10 <= 0 || cpu20 <= 0 || cpu10 >= cpu20 { + t.Errorf("bad execution times: cpu10=%v, cpu20=%v", cpu10, cpu20) + } +} + +func checkMetaNamesEmitted(t *testing.T, data format.Data, category string, want []string) { + t.Helper() + names := metaEventNameArgs(category, data) + for _, wantName := range want { + if !slices.Contains(names, wantName) { + t.Errorf("%s: names=%v, want %q", category, names, wantName) + } + } +} + +func metaEventNameArgs(category string, data format.Data) (names []string) { + for _, e := range data.Events { + if e.Name == category && e.Phase == "M" { + names = append(names, e.Arg.(map[string]any)["name"].(string)) + } + } + return +} + +func checkPlausibleHeapMetrics(t *testing.T, data format.Data) { + hms := heapMetrics(data) + var nonZeroAllocated, nonZeroNextGC bool + for _, hm := range hms { + if hm.Allocated > 0 { + nonZeroAllocated = true + } + if hm.NextGC > 0 { + nonZeroNextGC = true + } + } + + if !nonZeroAllocated { + t.Errorf("nonZeroAllocated=%v, want true", nonZeroAllocated) + } + if !nonZeroNextGC { + t.Errorf("nonZeroNextGC=%v, want true", nonZeroNextGC) + } +} + +func heapMetrics(data format.Data) (metrics []format.HeapCountersArg) { + for _, e := range data.Events { + if e.Phase == "C" && e.Name == "Heap" { + j, _ := json.Marshal(e.Arg) + var metric format.HeapCountersArg + json.Unmarshal(j, &metric) + metrics = append(metrics, metric) + } + } + return +} + +func recordJSONTraceHandlerResponse(t *testing.T, parsed *parsedTrace) format.Data { + h := JSONTraceHandler(parsed) + recorder := httptest.NewRecorder() + r := httptest.NewRequest("GET", "/jsontrace", nil) + h.ServeHTTP(recorder, r) + + var data format.Data + if err := json.Unmarshal(recorder.Body.Bytes(), &data); err != nil { + t.Fatal(err) + } + return data +} + +func sumExecutionTime(data format.Data) (sum time.Duration) { + for _, e := range data.Events { + sum += time.Duration(e.Dur) * time.Microsecond + } + return +} + +func getTestTrace(t *testing.T, testPath string) *parsedTrace { + t.Helper() + + // First read in the text trace and write it out as bytes. + f, err := os.Open(testPath) + if err != nil { + t.Fatalf("failed to open test %s: %v", testPath, err) + } + r, err := raw.NewTextReader(f) + if err != nil { + t.Fatalf("failed to read test %s: %v", testPath, err) + } + var trace bytes.Buffer + w, err := raw.NewWriter(&trace, r.Version()) + if err != nil { + t.Fatalf("failed to write out test %s: %v", testPath, err) + } + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("failed to read test %s: %v", testPath, err) + } + if err := w.WriteEvent(ev); err != nil { + t.Fatalf("failed to write out test %s: %v", testPath, err) + } + } + + // Parse the test trace. + parsed, err := parseTrace(&trace) + if err != nil { + t.Fatalf("failed to parse trace: %v", err) + } + return parsed +} diff --git a/src/cmd/trace/v2/main.go b/src/cmd/trace/v2/main.go new file mode 100644 index 0000000000..0a60ef04db --- /dev/null +++ b/src/cmd/trace/v2/main.go @@ -0,0 +1,190 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "fmt" + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" + "io" + "log" + "net" + "net/http" + "os" + + "internal/trace/v2/raw" + + "cmd/internal/browser" +) + +// Main is the main function for cmd/trace v2. +func Main(traceFile, httpAddr, pprof string, debug int) error { + tracef, err := os.Open(traceFile) + if err != nil { + return fmt.Errorf("failed to read trace file: %w", err) + } + defer tracef.Close() + + // Debug flags. + switch debug { + case 1: + return debugProcessedEvents(tracef) + case 2: + return debugRawEvents(tracef) + } + + ln, err := net.Listen("tcp", httpAddr) + if err != nil { + return fmt.Errorf("failed to create server socket: %w", err) + } + addr := "http://" + ln.Addr().String() + + log.Print("Preparing trace for viewer...") + parsed, err := parseTrace(tracef) + if err != nil { + return err + } + // N.B. tracef not needed after this point. + // We might double-close, but that's fine; we ignore the error. + tracef.Close() + + log.Print("Splitting trace for viewer...") + ranges, err := splitTrace(parsed) + if err != nil { + return err + } + + log.Printf("Opening browser. Trace viewer is listening on %s", addr) + browser.Open(addr) + + mutatorUtil := func(flags trace.UtilFlags) ([][]trace.MutatorUtil, error) { + return trace.MutatorUtilizationV2(parsed.events, flags), nil + } + + mux := http.NewServeMux() + + // Main endpoint. + mux.Handle("/", traceviewer.MainHandler([]traceviewer.View{ + {Type: traceviewer.ViewProc, Ranges: ranges}, + // N.B. Use the same ranges for threads. It takes a long time to compute + // the split a second time, but the makeup of the events are similar enough + // that this is still a good split. + {Type: traceviewer.ViewThread, Ranges: ranges}, + })) + + // Catapult handlers. + mux.Handle("/trace", traceviewer.TraceHandler()) + mux.Handle("/jsontrace", JSONTraceHandler(parsed)) + mux.Handle("/static/", traceviewer.StaticHandler()) + + // Goroutines handlers. + mux.HandleFunc("/goroutines", GoroutinesHandlerFunc(parsed.summary.Goroutines)) + mux.HandleFunc("/goroutine", GoroutineHandler(parsed.summary.Goroutines)) + + // MMU handler. + mux.HandleFunc("/mmu", traceviewer.MMUHandlerFunc(ranges, mutatorUtil)) + + // Basic pprof endpoints. + mux.HandleFunc("/io", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofIO(), parsed))) + mux.HandleFunc("/block", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofBlock(), parsed))) + mux.HandleFunc("/syscall", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSyscall(), parsed))) + mux.HandleFunc("/sched", traceviewer.SVGProfileHandlerFunc(pprofByGoroutine(computePprofSched(), parsed))) + + // Region-based pprof endpoints. + mux.HandleFunc("/regionio", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofIO(), parsed))) + mux.HandleFunc("/regionblock", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofBlock(), parsed))) + mux.HandleFunc("/regionsyscall", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSyscall(), parsed))) + mux.HandleFunc("/regionsched", traceviewer.SVGProfileHandlerFunc(pprofByRegion(computePprofSched(), parsed))) + + // Region endpoints. + mux.HandleFunc("/userregions", UserRegionsHandlerFunc(parsed)) + mux.HandleFunc("/userregion", UserRegionHandlerFunc(parsed)) + + // Task endpoints. + mux.HandleFunc("/usertasks", UserTasksHandlerFunc(parsed)) + mux.HandleFunc("/usertask", UserTaskHandlerFunc(parsed)) + + err = http.Serve(ln, mux) + return fmt.Errorf("failed to start http server: %w", err) +} + +type parsedTrace struct { + events []tracev2.Event + summary *trace.Summary +} + +func parseTrace(tr io.Reader) (*parsedTrace, error) { + r, err := tracev2.NewReader(tr) + if err != nil { + return nil, fmt.Errorf("failed to create trace reader: %w", err) + } + s := trace.NewSummarizer() + t := new(parsedTrace) + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } else if err != nil { + return nil, fmt.Errorf("failed to read event: %w", err) + } + t.events = append(t.events, ev) + s.Event(&t.events[len(t.events)-1]) + } + t.summary = s.Finalize() + return t, nil +} + +func (t *parsedTrace) startTime() tracev2.Time { + return t.events[0].Time() +} + +func (t *parsedTrace) endTime() tracev2.Time { + return t.events[len(t.events)-1].Time() +} + +// splitTrace splits the trace into a number of ranges, each resulting in approx 100 MiB of +// json output (the trace viewer can hardly handle more). +func splitTrace(parsed *parsedTrace) ([]traceviewer.Range, error) { + // TODO(mknyszek): Split traces by generation by doing a quick first pass over the + // trace to identify all the generation boundaries. + s, c := traceviewer.SplittingTraceConsumer(100 << 20) // 100 MiB + if err := generateTrace(parsed, defaultGenOpts(), c); err != nil { + return nil, err + } + return s.Ranges, nil +} + +func debugProcessedEvents(trace io.Reader) error { + tr, err := tracev2.NewReader(trace) + if err != nil { + return err + } + for { + ev, err := tr.ReadEvent() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + fmt.Println(ev.String()) + } +} + +func debugRawEvents(trace io.Reader) error { + rr, err := raw.NewReader(trace) + if err != nil { + return err + } + for { + ev, err := rr.ReadEvent() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + fmt.Println(ev.String()) + } +} diff --git a/src/cmd/trace/v2/pprof.go b/src/cmd/trace/v2/pprof.go new file mode 100644 index 0000000000..4ec7b3a598 --- /dev/null +++ b/src/cmd/trace/v2/pprof.go @@ -0,0 +1,345 @@ +// Copyright 2014 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. + +// Serving of pprof-like profiles. + +package trace + +import ( + "cmp" + "fmt" + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" + "net/http" + "slices" + "strconv" + "strings" + "time" +) + +func pprofByGoroutine(compute computePprofFunc, t *parsedTrace) traceviewer.ProfileFunc { + return func(r *http.Request) ([]traceviewer.ProfileRecord, error) { + id := r.FormValue("id") + gToIntervals, err := pprofMatchingGoroutines(id, t) + if err != nil { + return nil, err + } + return compute(gToIntervals, t.events) + } +} + +func pprofByRegion(compute computePprofFunc, t *parsedTrace) traceviewer.ProfileFunc { + return func(r *http.Request) ([]traceviewer.ProfileRecord, error) { + filter, err := newRegionFilter(r) + if err != nil { + return nil, err + } + gToIntervals, err := pprofMatchingRegions(filter, t) + if err != nil { + return nil, err + } + return compute(gToIntervals, t.events) + } +} + +// pprofMatchingGoroutines parses the goroutine type id string (i.e. pc) +// and returns the ids of goroutines of the matching type and its interval. +// If the id string is empty, returns nil without an error. +func pprofMatchingGoroutines(id string, t *parsedTrace) (map[tracev2.GoID][]interval, error) { + if id == "" { + return nil, nil + } + pc, err := strconv.ParseUint(id, 10, 64) // id is string + if err != nil { + return nil, fmt.Errorf("invalid goroutine type: %v", id) + } + res := make(map[tracev2.GoID][]interval) + for _, g := range t.summary.Goroutines { + if g.PC != pc { + continue + } + endTime := g.EndTime + if g.EndTime == 0 { + endTime = t.endTime() // Use the trace end time, since the goroutine is still live then. + } + res[g.ID] = []interval{{start: g.StartTime, end: endTime}} + } + if len(res) == 0 && id != "" { + return nil, fmt.Errorf("failed to find matching goroutines for ID: %s", id) + } + return res, nil +} + +// pprofMatchingRegions returns the time intervals of matching regions +// grouped by the goroutine id. If the filter is nil, returns nil without an error. +func pprofMatchingRegions(filter *regionFilter, t *parsedTrace) (map[tracev2.GoID][]interval, error) { + if filter == nil { + return nil, nil + } + + gToIntervals := make(map[tracev2.GoID][]interval) + for _, g := range t.summary.Goroutines { + for _, r := range g.Regions { + if !filter.match(t, r) { + continue + } + gToIntervals[g.ID] = append(gToIntervals[g.ID], regionInterval(t, r)) + } + } + + for g, intervals := range gToIntervals { + // In order to remove nested regions and + // consider only the outermost regions, + // first, we sort based on the start time + // and then scan through to select only the outermost regions. + slices.SortFunc(intervals, func(a, b interval) int { + if c := cmp.Compare(a.start, b.start); c != 0 { + return c + } + return cmp.Compare(a.end, b.end) + }) + var lastTimestamp tracev2.Time + var n int + // Select only the outermost regions. + for _, i := range intervals { + if lastTimestamp <= i.start { + intervals[n] = i // new non-overlapping region starts. + lastTimestamp = i.end + n++ + } + // Otherwise, skip because this region overlaps with a previous region. + } + gToIntervals[g] = intervals[:n] + } + return gToIntervals, nil +} + +type computePprofFunc func(gToIntervals map[tracev2.GoID][]interval, events []tracev2.Event) ([]traceviewer.ProfileRecord, error) + +// computePprofIO returns a computePprofFunc that generates IO pprof-like profile (time spent in +// IO wait, currently only network blocking event). +func computePprofIO() computePprofFunc { + return makeComputePprofFunc(tracev2.GoWaiting, func(reason string) bool { + return reason == "network" + }) +} + +// computePprofBlock returns a computePprofFunc that generates blocking pprof-like profile +// (time spent blocked on synchronization primitives). +func computePprofBlock() computePprofFunc { + return makeComputePprofFunc(tracev2.GoWaiting, func(reason string) bool { + return strings.Contains(reason, "chan") || strings.Contains(reason, "sync") || strings.Contains(reason, "select") + }) +} + +// computePprofSyscall returns a computePprofFunc that generates a syscall pprof-like +// profile (time spent in syscalls). +func computePprofSyscall() computePprofFunc { + return makeComputePprofFunc(tracev2.GoSyscall, func(_ string) bool { + return true + }) +} + +// computePprofSched returns a computePprofFunc that generates a scheduler latency pprof-like profile +// (time between a goroutine become runnable and actually scheduled for execution). +func computePprofSched() computePprofFunc { + return makeComputePprofFunc(tracev2.GoRunnable, func(_ string) bool { + return true + }) +} + +// makeComputePprofFunc returns a computePprofFunc that generates a profile of time goroutines spend +// in a particular state for the specified reasons. +func makeComputePprofFunc(state tracev2.GoState, trackReason func(string) bool) computePprofFunc { + return func(gToIntervals map[tracev2.GoID][]interval, events []tracev2.Event) ([]traceviewer.ProfileRecord, error) { + stacks := newStackMap() + tracking := make(map[tracev2.GoID]*tracev2.Event) + for i := range events { + ev := &events[i] + + // Filter out any non-state-transitions and events without stacks. + if ev.Kind() != tracev2.EventStateTransition { + continue + } + stack := ev.Stack() + if stack == tracev2.NoStack { + continue + } + + // The state transition has to apply to a goroutine. + st := ev.StateTransition() + if st.Resource.Kind != tracev2.ResourceGoroutine { + continue + } + id := st.Resource.Goroutine() + _, new := st.Goroutine() + + // Check if we're tracking this goroutine. + startEv := tracking[id] + if startEv == nil { + // We're not. Start tracking if the new state + // matches what we want and the transition is + // for one of the reasons we care about. + if new == state && trackReason(st.Reason) { + tracking[id] = ev + } + continue + } + // We're tracking this goroutine. + if new == state { + // We're tracking this goroutine, but it's just transitioning + // to the same state (this is a no-ip + continue + } + // The goroutine has transitioned out of the state we care about, + // so remove it from tracking and record the stack. + delete(tracking, id) + + overlapping := pprofOverlappingDuration(gToIntervals, id, interval{startEv.Time(), ev.Time()}) + if overlapping > 0 { + rec := stacks.getOrAdd(startEv.Stack()) + rec.Count++ + rec.Time += overlapping + } + } + return stacks.profile(), nil + } +} + +// pprofOverlappingDuration returns the overlapping duration between +// the time intervals in gToIntervals and the specified event. +// If gToIntervals is nil, this simply returns the event's duration. +func pprofOverlappingDuration(gToIntervals map[tracev2.GoID][]interval, id tracev2.GoID, sample interval) time.Duration { + if gToIntervals == nil { // No filtering. + return sample.duration() + } + intervals := gToIntervals[id] + if len(intervals) == 0 { + return 0 + } + + var overlapping time.Duration + for _, i := range intervals { + if o := i.overlap(sample); o > 0 { + overlapping += o + } + } + return overlapping +} + +// interval represents a time interval in the trace. +type interval struct { + start, end tracev2.Time +} + +func (i interval) duration() time.Duration { + return i.end.Sub(i.start) +} + +func (i1 interval) overlap(i2 interval) time.Duration { + // Assume start1 <= end1 and start2 <= end2 + if i1.end < i2.start || i2.end < i1.start { + return 0 + } + if i1.start < i2.start { // choose the later one + i1.start = i2.start + } + if i1.end > i2.end { // choose the earlier one + i1.end = i2.end + } + return i1.duration() +} + +// pprofMaxStack is the extent of the deduplication we're willing to do. +// +// Because slices aren't comparable and we want to leverage maps for deduplication, +// we have to choose a fixed constant upper bound on the amount of frames we want +// to support. In practice this is fine because there's a maximum depth to these +// stacks anyway. +const pprofMaxStack = 128 + +// stackMap is a map of tracev2.Stack to some value V. +type stackMap struct { + // stacks contains the full list of stacks in the set, however + // it is insufficient for deduplication because tracev2.Stack + // equality is only optimistic. If two tracev2.Stacks are equal, + // then they are guaranteed to be equal in content. If they are + // not equal, then they might still be equal in content. + stacks map[tracev2.Stack]*traceviewer.ProfileRecord + + // pcs is the source-of-truth for deduplication. It is a map of + // the actual PCs in the stack to a tracev2.Stack. + pcs map[[pprofMaxStack]uint64]tracev2.Stack +} + +func newStackMap() *stackMap { + return &stackMap{ + stacks: make(map[tracev2.Stack]*traceviewer.ProfileRecord), + pcs: make(map[[pprofMaxStack]uint64]tracev2.Stack), + } +} + +func (m *stackMap) getOrAdd(stack tracev2.Stack) *traceviewer.ProfileRecord { + // Fast path: check to see if this exact stack is already in the map. + if rec, ok := m.stacks[stack]; ok { + return rec + } + // Slow path: the stack may still be in the map. + + // Grab the stack's PCs as the source-of-truth. + var pcs [pprofMaxStack]uint64 + pcsForStack(stack, &pcs) + + // Check the source-of-truth. + var rec *traceviewer.ProfileRecord + if existing, ok := m.pcs[pcs]; ok { + // In the map. + rec = m.stacks[existing] + delete(m.stacks, existing) + } else { + // Not in the map. + rec = new(traceviewer.ProfileRecord) + } + // Insert regardless of whether we have a match in m.pcs. + // Even if we have a match, we want to keep the newest version + // of that stack, since we're much more likely tos see it again + // as we iterate through the trace linearly. Simultaneously, we + // are likely to never see the old stack again. + m.pcs[pcs] = stack + m.stacks[stack] = rec + return rec +} + +func (m *stackMap) profile() []traceviewer.ProfileRecord { + prof := make([]traceviewer.ProfileRecord, 0, len(m.stacks)) + for stack, record := range m.stacks { + rec := *record + i := 0 + stack.Frames(func(frame tracev2.StackFrame) bool { + rec.Stack = append(rec.Stack, &trace.Frame{ + PC: frame.PC, + Fn: frame.Func, + File: frame.File, + Line: int(frame.Line), + }) + i++ + // Cut this off at pprofMaxStack because that's as far + // as our deduplication goes. + return i < pprofMaxStack + }) + prof = append(prof, rec) + } + return prof +} + +// pcsForStack extracts the first pprofMaxStack PCs from stack into pcs. +func pcsForStack(stack tracev2.Stack, pcs *[pprofMaxStack]uint64) { + i := 0 + stack.Frames(func(frame tracev2.StackFrame) bool { + pcs[i] = frame.PC + i++ + return i < len(pcs) + }) +} diff --git a/src/cmd/trace/v2/procgen.go b/src/cmd/trace/v2/procgen.go new file mode 100644 index 0000000000..30ed568dad --- /dev/null +++ b/src/cmd/trace/v2/procgen.go @@ -0,0 +1,209 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "fmt" + "internal/trace/traceviewer" + "internal/trace/traceviewer/format" + tracev2 "internal/trace/v2" +) + +var _ generator = &procGenerator{} + +type procGenerator struct { + globalRangeGenerator + globalMetricGenerator + procRangeGenerator + stackSampleGenerator[tracev2.ProcID] + + gStates map[tracev2.GoID]*gState[tracev2.ProcID] + inSyscall map[tracev2.ProcID]*gState[tracev2.ProcID] + maxProc tracev2.ProcID +} + +func newProcGenerator() *procGenerator { + pg := new(procGenerator) + pg.stackSampleGenerator.getResource = func(ev *tracev2.Event) tracev2.ProcID { + return ev.Proc() + } + pg.gStates = make(map[tracev2.GoID]*gState[tracev2.ProcID]) + pg.inSyscall = make(map[tracev2.ProcID]*gState[tracev2.ProcID]) + return pg +} + +func (g *procGenerator) Sync() { + g.globalRangeGenerator.Sync() + g.procRangeGenerator.Sync() +} + +func (g *procGenerator) GoroutineLabel(ctx *traceContext, ev *tracev2.Event) { + l := ev.Label() + g.gStates[l.Resource.Goroutine()].setLabel(l.Label) +} + +func (g *procGenerator) GoroutineRange(ctx *traceContext, ev *tracev2.Event) { + r := ev.Range() + switch ev.Kind() { + case tracev2.EventRangeBegin: + g.gStates[r.Scope.Goroutine()].rangeBegin(ev.Time(), r.Name, ev.Stack()) + case tracev2.EventRangeActive: + g.gStates[r.Scope.Goroutine()].rangeActive(r.Name) + case tracev2.EventRangeEnd: + gs := g.gStates[r.Scope.Goroutine()] + gs.rangeEnd(ev.Time(), r.Name, ev.Stack(), ctx) + } +} + +func (g *procGenerator) GoroutineTransition(ctx *traceContext, ev *tracev2.Event) { + st := ev.StateTransition() + goID := st.Resource.Goroutine() + + // If we haven't seen this goroutine before, create a new + // gState for it. + gs, ok := g.gStates[goID] + if !ok { + gs = newGState[tracev2.ProcID](goID) + g.gStates[goID] = gs + } + // If we haven't already named this goroutine, try to name it. + gs.augmentName(st.Stack) + + // Handle the goroutine state transition. + from, to := st.Goroutine() + if from == to { + // Filter out no-op events. + return + } + if from == tracev2.GoRunning && !to.Executing() { + if to == tracev2.GoWaiting { + // Goroutine started blocking. + gs.block(ev.Time(), ev.Stack(), st.Reason, ctx) + } else { + gs.stop(ev.Time(), ev.Stack(), ctx) + } + } + if !from.Executing() && to == tracev2.GoRunning { + start := ev.Time() + if from == tracev2.GoUndetermined { + // Back-date the event to the start of the trace. + start = ctx.startTime + } + gs.start(start, ev.Proc(), ctx) + } + + if from == tracev2.GoWaiting { + // Goroutine was unblocked. + gs.unblock(ev.Time(), ev.Stack(), ev.Proc(), ctx) + } + if from == tracev2.GoNotExist && to == tracev2.GoRunnable { + // Goroutine was created. + gs.created(ev.Time(), ev.Proc(), ev.Stack()) + } + if from == tracev2.GoSyscall && to != tracev2.GoRunning { + // Goroutine exited a blocked syscall. + gs.blockedSyscallEnd(ev.Time(), ev.Stack(), ctx) + } + + // Handle syscalls. + if to == tracev2.GoSyscall && ev.Proc() != tracev2.NoProc { + start := ev.Time() + if from == tracev2.GoUndetermined { + // Back-date the event to the start of the trace. + start = ctx.startTime + } + // Write down that we've entered a syscall. Note: we might have no P here + // if we're in a cgo callback or this is a transition from GoUndetermined + // (i.e. the G has been blocked in a syscall). + gs.syscallBegin(start, ev.Proc(), ev.Stack()) + g.inSyscall[ev.Proc()] = gs + } + // Check if we're exiting a non-blocking syscall. + _, didNotBlock := g.inSyscall[ev.Proc()] + if from == tracev2.GoSyscall && didNotBlock { + gs.syscallEnd(ev.Time(), false, ctx) + delete(g.inSyscall, ev.Proc()) + } + + // Note down the goroutine transition. + _, inMarkAssist := gs.activeRanges["GC mark assist"] + ctx.GoroutineTransition(ctx.elapsed(ev.Time()), viewerGState(from, inMarkAssist), viewerGState(to, inMarkAssist)) +} + +func (g *procGenerator) ProcTransition(ctx *traceContext, ev *tracev2.Event) { + st := ev.StateTransition() + proc := st.Resource.Proc() + + g.maxProc = max(g.maxProc, proc) + viewerEv := traceviewer.InstantEvent{ + Resource: uint64(proc), + Stack: ctx.Stack(viewerFrames(ev.Stack())), + } + + from, to := st.Proc() + if from == to { + // Filter out no-op events. + return + } + if to.Executing() { + start := ev.Time() + if from == tracev2.ProcUndetermined { + start = ctx.startTime + } + viewerEv.Name = "proc start" + viewerEv.Arg = format.ThreadIDArg{ThreadID: uint64(ev.Thread())} + viewerEv.Ts = ctx.elapsed(start) + ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, 1) + } + if from.Executing() { + start := ev.Time() + viewerEv.Name = "proc stop" + viewerEv.Ts = ctx.elapsed(start) + ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, -1) + + // Check if this proc was in a syscall before it stopped. + // This means the syscall blocked. We need to emit it to the + // viewer at this point because we only display the time the + // syscall occupied a P when the viewer is in per-P mode. + // + // TODO(mknyszek): We could do better in a per-M mode because + // all events have to happen on *some* thread, and in v2 traces + // we know what that thread is. + gs, ok := g.inSyscall[proc] + if ok { + // Emit syscall slice for blocked syscall. + gs.syscallEnd(start, true, ctx) + gs.stop(start, ev.Stack(), ctx) + delete(g.inSyscall, proc) + } + } + // TODO(mknyszek): Consider modeling procs differently and have them be + // transition to and from NotExist when GOMAXPROCS changes. We can emit + // events for this to clearly delineate GOMAXPROCS changes. + + if viewerEv.Name != "" { + ctx.Instant(viewerEv) + } +} + +func (g *procGenerator) Finish(ctx *traceContext) { + ctx.SetResourceType("PROCS") + + // Finish off ranges first. It doesn't really matter for the global ranges, + // but the proc ranges need to either be a subset of a goroutine slice or + // their own slice entirely. If the former, it needs to end first. + g.procRangeGenerator.Finish(ctx) + g.globalRangeGenerator.Finish(ctx) + + // Finish off all the goroutine slices. + for _, gs := range g.gStates { + gs.finish(ctx) + } + + // Name all the procs to the emitter. + for i := uint64(0); i <= uint64(g.maxProc); i++ { + ctx.Resource(i, fmt.Sprintf("Proc %v", i)) + } +} diff --git a/src/cmd/trace/v2/regions.go b/src/cmd/trace/v2/regions.go new file mode 100644 index 0000000000..5d04fd2ae5 --- /dev/null +++ b/src/cmd/trace/v2/regions.go @@ -0,0 +1,529 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "cmp" + "fmt" + "html/template" + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" + "net/http" + "net/url" + "slices" + "sort" + "strconv" + "strings" + "time" +) + +// UserTasksHandlerFunc returns a HandlerFunc that reports all regions found in the trace. +func UserRegionsHandlerFunc(t *parsedTrace) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Summarize all the regions. + summary := make(map[regionFingerprint]regionStats) + for _, g := range t.summary.Goroutines { + for _, r := range g.Regions { + id := fingerprintRegion(r) + stats, ok := summary[id] + if !ok { + stats.regionFingerprint = id + } + stats.add(t, r) + summary[id] = stats + } + } + // Sort regions by PC and name. + userRegions := make([]regionStats, 0, len(summary)) + for _, stats := range summary { + userRegions = append(userRegions, stats) + } + slices.SortFunc(userRegions, func(a, b regionStats) int { + if c := cmp.Compare(a.Type, b.Type); c != 0 { + return c + } + return cmp.Compare(a.Frame.PC, b.Frame.PC) + }) + // Emit table. + err := templUserRegionTypes.Execute(w, userRegions) + if err != nil { + http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) + return + } + } +} + +// regionFingerprint is a way to categorize regions that goes just one step beyond the region's Type +// by including the top stack frame. +type regionFingerprint struct { + Frame tracev2.StackFrame + Type string +} + +func fingerprintRegion(r *trace.UserRegionSummary) regionFingerprint { + return regionFingerprint{ + Frame: regionTopStackFrame(r), + Type: r.Name, + } +} + +func regionTopStackFrame(r *trace.UserRegionSummary) tracev2.StackFrame { + var frame tracev2.StackFrame + if r.Start != nil && r.Start.Stack() != tracev2.NoStack { + r.Start.Stack().Frames(func(f tracev2.StackFrame) bool { + frame = f + return false + }) + } + return frame +} + +type regionStats struct { + regionFingerprint + Histogram traceviewer.TimeHistogram +} + +func (s *regionStats) UserRegionURL() func(min, max time.Duration) string { + return func(min, max time.Duration) string { + return fmt.Sprintf("/userregion?type=%s&pc=%x&latmin=%v&latmax=%v", template.URLQueryEscaper(s.Type), s.Frame.PC, template.URLQueryEscaper(min), template.URLQueryEscaper(max)) + } +} + +func (s *regionStats) add(t *parsedTrace, region *trace.UserRegionSummary) { + s.Histogram.Add(regionInterval(t, region).duration()) +} + +var templUserRegionTypes = template.Must(template.New("").Parse(` + +Regions + + +

    Regions

    + +Below is a table containing a summary of all the user-defined regions in the trace. +Regions are grouped by the region type and the point at which the region started. +The rightmost column of the table contains a latency histogram for each region group. +Note that this histogram only counts regions that began and ended within the traced +period. +However, the "Count" column includes all regions, including those that only started +or ended during the traced period. +Regions that were active through the trace period were not recorded, and so are not +accounted for at all. +Click on the links to explore a breakdown of time spent for each region by goroutine +and user-defined task. +
    +
    + + + + + + + +{{range $}} + + + + + +{{end}} +
    Region typeCountDuration distribution (complete tasks)
    {{printf "%q" .Type}}
    {{.Frame.Func}} @ {{printf "0x%x" .Frame.PC}}
    {{.Frame.File}}:{{.Frame.Line}}
    {{.Histogram.Count}}{{.Histogram.ToHTML (.UserRegionURL)}}
    + + +`)) + +// UserRegionHandlerFunc returns a HandlerFunc that presents the details of the selected regions. +func UserRegionHandlerFunc(t *parsedTrace) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Construct the filter from the request. + filter, err := newRegionFilter(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // Collect all the regions with their goroutines. + type region struct { + *trace.UserRegionSummary + Goroutine tracev2.GoID + NonOverlappingStats map[string]time.Duration + HasRangeTime bool + } + var regions []region + var maxTotal time.Duration + validNonOverlappingStats := make(map[string]struct{}) + validRangeStats := make(map[string]struct{}) + for _, g := range t.summary.Goroutines { + for _, r := range g.Regions { + if !filter.match(t, r) { + continue + } + nonOverlappingStats := r.NonOverlappingStats() + for name := range nonOverlappingStats { + validNonOverlappingStats[name] = struct{}{} + } + var totalRangeTime time.Duration + for name, dt := range r.RangeTime { + validRangeStats[name] = struct{}{} + totalRangeTime += dt + } + regions = append(regions, region{ + UserRegionSummary: r, + Goroutine: g.ID, + NonOverlappingStats: nonOverlappingStats, + HasRangeTime: totalRangeTime != 0, + }) + if maxTotal < r.TotalTime { + maxTotal = r.TotalTime + } + } + } + + // Sort. + sortBy := r.FormValue("sortby") + if _, ok := validNonOverlappingStats[sortBy]; ok { + slices.SortFunc(regions, func(a, b region) int { + return cmp.Compare(b.NonOverlappingStats[sortBy], a.NonOverlappingStats[sortBy]) + }) + } else { + // Sort by total time by default. + slices.SortFunc(regions, func(a, b region) int { + return cmp.Compare(b.TotalTime, a.TotalTime) + }) + } + + // Write down all the non-overlapping stats and sort them. + allNonOverlappingStats := make([]string, 0, len(validNonOverlappingStats)) + for name := range validNonOverlappingStats { + allNonOverlappingStats = append(allNonOverlappingStats, name) + } + slices.SortFunc(allNonOverlappingStats, func(a, b string) int { + if a == b { + return 0 + } + if a == "Execution time" { + return -1 + } + if b == "Execution time" { + return 1 + } + return cmp.Compare(a, b) + }) + + // Write down all the range stats and sort them. + allRangeStats := make([]string, 0, len(validRangeStats)) + for name := range validRangeStats { + allRangeStats = append(allRangeStats, name) + } + sort.Strings(allRangeStats) + + err = templUserRegionType.Execute(w, struct { + MaxTotal time.Duration + Regions []region + Name string + Filter *regionFilter + NonOverlappingStats []string + RangeStats []string + }{ + MaxTotal: maxTotal, + Regions: regions, + Name: filter.name, + Filter: filter, + NonOverlappingStats: allNonOverlappingStats, + RangeStats: allRangeStats, + }) + if err != nil { + http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) + return + } + } +} + +var templUserRegionType = template.Must(template.New("").Funcs(template.FuncMap{ + "headerStyle": func(statName string) template.HTMLAttr { + return template.HTMLAttr(fmt.Sprintf("style=\"background-color: %s;\"", stat2Color(statName))) + }, + "barStyle": func(statName string, dividend, divisor time.Duration) template.HTMLAttr { + width := "0" + if divisor != 0 { + width = fmt.Sprintf("%.2f%%", float64(dividend)/float64(divisor)*100) + } + return template.HTMLAttr(fmt.Sprintf("style=\"width: %s; background-color: %s;\"", width, stat2Color(statName))) + }, + "filterParams": func(f *regionFilter) template.URL { + return template.URL(f.params.Encode()) + }, +}).Parse(` + +Regions: {{.Name}} + + + + +

    Regions: {{.Name}}

    + +Table of contents + + +

    Summary

    + +{{ with $p := filterParams .Filter}} + + + + + + + + + + + + + + + + + +
    Network wait profile: graph (download)
    Sync block profile: graph (download)
    Syscall profile: graph (download)
    Scheduler wait profile: graph (download)
    +{{ end }} + +

    Breakdown

    + +The table below breaks down where each goroutine is spent its time during the +traced period. +All of the columns except total time are non-overlapping. +
    +
    + + + + + + + +{{range $.NonOverlappingStats}} + +{{end}} + +{{range .Regions}} + + + + + + {{$Region := .}} + {{range $.NonOverlappingStats}} + {{$Time := index $Region.NonOverlappingStats .}} + + {{end}} + +{{end}} +
    Goroutine Task Total {{.}}
    {{.Goroutine}} {{if .TaskID}}{{.TaskID}}{{end}} {{ .TotalTime.String }} +
    + {{$Region := .}} + {{range $.NonOverlappingStats}} + {{$Time := index $Region.NonOverlappingStats .}} + {{if $Time}} +   + {{end}} + {{end}} +
    +
    {{$Time.String}}
    + +

    Special ranges

    + +The table below describes how much of the traced period each goroutine spent in +certain special time ranges. +If a goroutine has spent no time in any special time ranges, it is excluded from +the table. +For example, how much time it spent helping the GC. Note that these times do +overlap with the times from the first table. +In general the goroutine may not be executing in these special time ranges. +For example, it may have blocked while trying to help the GC. +This must be taken into account when interpreting the data. +
    +
    + + + + + + +{{range $.RangeStats}} + +{{end}} + +{{range .Regions}} + {{if .HasRangeTime}} + + + + + {{$Region := .}} + {{range $.RangeStats}} + {{$Time := index $Region.RangeTime .}} + + {{end}} + + {{end}} +{{end}} +
    Goroutine Task Total {{.}}
    {{.Goroutine}} {{if .TaskID}}{{.TaskID}}{{end}} {{ .TotalTime.String }} {{$Time.String}}
    +`)) + +// regionFilter represents a region filter specified by a user of cmd/trace. +type regionFilter struct { + name string + params url.Values + cond []func(*parsedTrace, *trace.UserRegionSummary) bool +} + +// match returns true if a region, described by its ID and summary, matches +// the filter. +func (f *regionFilter) match(t *parsedTrace, s *trace.UserRegionSummary) bool { + for _, c := range f.cond { + if !c(t, s) { + return false + } + } + return true +} + +// newRegionFilter creates a new region filter from URL query variables. +func newRegionFilter(r *http.Request) (*regionFilter, error) { + if err := r.ParseForm(); err != nil { + return nil, err + } + + var name []string + var conditions []func(*parsedTrace, *trace.UserRegionSummary) bool + filterParams := make(url.Values) + + param := r.Form + if typ, ok := param["type"]; ok && len(typ) > 0 { + name = append(name, fmt.Sprintf("%q", typ[0])) + conditions = append(conditions, func(_ *parsedTrace, r *trace.UserRegionSummary) bool { + return r.Name == typ[0] + }) + filterParams.Add("type", typ[0]) + } + if pc, err := strconv.ParseUint(r.FormValue("pc"), 16, 64); err == nil { + encPC := fmt.Sprintf("0x%x", pc) + name = append(name, "@ "+encPC) + conditions = append(conditions, func(_ *parsedTrace, r *trace.UserRegionSummary) bool { + return regionTopStackFrame(r).PC == pc + }) + filterParams.Add("pc", encPC) + } + + if lat, err := time.ParseDuration(r.FormValue("latmin")); err == nil { + name = append(name, fmt.Sprintf("(latency >= %s)", lat)) + conditions = append(conditions, func(t *parsedTrace, r *trace.UserRegionSummary) bool { + return regionInterval(t, r).duration() >= lat + }) + filterParams.Add("latmin", lat.String()) + } + if lat, err := time.ParseDuration(r.FormValue("latmax")); err == nil { + name = append(name, fmt.Sprintf("(latency <= %s)", lat)) + conditions = append(conditions, func(t *parsedTrace, r *trace.UserRegionSummary) bool { + return regionInterval(t, r).duration() <= lat + }) + filterParams.Add("latmax", lat.String()) + } + + return ®ionFilter{ + name: strings.Join(name, " "), + cond: conditions, + params: filterParams, + }, nil +} + +func regionInterval(t *parsedTrace, s *trace.UserRegionSummary) interval { + var i interval + if s.Start != nil { + i.start = s.Start.Time() + } else { + i.start = t.startTime() + } + if s.End != nil { + i.end = s.End.Time() + } else { + i.end = t.endTime() + } + return i +} diff --git a/src/cmd/trace/v2/tasks.go b/src/cmd/trace/v2/tasks.go new file mode 100644 index 0000000000..fb40811565 --- /dev/null +++ b/src/cmd/trace/v2/tasks.go @@ -0,0 +1,477 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "bytes" + "cmp" + "fmt" + "html/template" + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" + "log" + "net/http" + "slices" + "strings" + "time" +) + +// UserTasksHandlerFunc returns a HandlerFunc that reports all tasks found in the trace. +func UserTasksHandlerFunc(t *parsedTrace) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + tasks := t.summary.Tasks + + // Summarize groups of tasks with the same name. + summary := make(map[string]taskStats) + for _, task := range tasks { + stats, ok := summary[task.Name] + if !ok { + stats.Type = task.Name + } + stats.add(task) + summary[task.Name] = stats + } + + // Sort tasks by type. + userTasks := make([]taskStats, 0, len(summary)) + for _, stats := range summary { + userTasks = append(userTasks, stats) + } + slices.SortFunc(userTasks, func(a, b taskStats) int { + return cmp.Compare(a.Type, b.Type) + }) + + // Emit table. + err := templUserTaskTypes.Execute(w, userTasks) + if err != nil { + http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) + return + } + } +} + +type taskStats struct { + Type string + Count int // Complete + incomplete tasks + Histogram traceviewer.TimeHistogram // Complete tasks only +} + +func (s *taskStats) UserTaskURL(complete bool) func(min, max time.Duration) string { + return func(min, max time.Duration) string { + return fmt.Sprintf("/usertask?type=%s&complete=%v&latmin=%v&latmax=%v", template.URLQueryEscaper(s.Type), template.URLQueryEscaper(complete), template.URLQueryEscaper(min), template.URLQueryEscaper(max)) + } +} + +func (s *taskStats) add(task *trace.UserTaskSummary) { + s.Count++ + if task.Complete() { + s.Histogram.Add(task.End.Time().Sub(task.Start.Time())) + } +} + +var templUserTaskTypes = template.Must(template.New("").Parse(` + +Tasks + + +Search log text:

    + + + + + + +{{range $}} + + + + + +{{end}} +
    Task typeCountDuration distribution (complete tasks)
    {{.Type}}{{.Count}}{{.Histogram.ToHTML (.UserTaskURL true)}}
    + + +`)) + +// UserTaskHandlerFunc returns a HandlerFunc that presents the details of the selected tasks. +func UserTaskHandlerFunc(t *parsedTrace) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + filter, err := newTaskFilter(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + type event struct { + WhenString string + Elapsed time.Duration + Goroutine tracev2.GoID + What string + // TODO: include stack trace of creation time + } + type task struct { + WhenString string + ID tracev2.TaskID + Duration time.Duration + Complete bool + Events []event + Start, End time.Duration // Time since the beginning of the trace + GCTime time.Duration + } + var tasks []task + for _, summary := range t.summary.Tasks { + if !filter.match(t, summary) { + continue + } + + // Collect all the events for the task. + var rawEvents []*tracev2.Event + if summary.Start != nil { + rawEvents = append(rawEvents, summary.Start) + } + if summary.End != nil { + rawEvents = append(rawEvents, summary.End) + } + rawEvents = append(rawEvents, summary.Logs...) + for _, r := range summary.Regions { + if r.Start != nil { + rawEvents = append(rawEvents, r.Start) + } + if r.End != nil { + rawEvents = append(rawEvents, r.End) + } + } + + // Sort them. + slices.SortStableFunc(rawEvents, func(a, b *tracev2.Event) int { + return cmp.Compare(a.Time(), b.Time()) + }) + + // Summarize them. + var events []event + last := t.startTime() + for _, ev := range rawEvents { + what := describeEvent(ev) + if what == "" { + continue + } + sinceStart := ev.Time().Sub(t.startTime()) + events = append(events, event{ + WhenString: fmt.Sprintf("%2.9f", sinceStart.Seconds()), + Elapsed: ev.Time().Sub(last), + What: what, + Goroutine: primaryGoroutine(ev), + }) + last = ev.Time() + } + taskSpan := taskInterval(t, summary) + taskStart := taskSpan.start.Sub(t.startTime()) + + // Produce the task summary. + tasks = append(tasks, task{ + WhenString: fmt.Sprintf("%2.9fs", taskStart.Seconds()), + Duration: taskSpan.duration(), + ID: summary.ID, + Complete: summary.Complete(), + Events: events, + Start: taskStart, + End: taskStart + taskSpan.duration(), + }) + } + // Sort the tasks by duration. + slices.SortFunc(tasks, func(a, b task) int { + return cmp.Compare(a.Duration, b.Duration) + }) + + // Emit table. + err = templUserTaskType.Execute(w, struct { + Name string + Tasks []task + }{ + Name: filter.name, + Tasks: tasks, + }) + if err != nil { + log.Printf("failed to execute template: %v", err) + http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) + return + } + } +} + +var templUserTaskType = template.Must(template.New("userTask").Funcs(template.FuncMap{ + "elapsed": elapsed, + "asMillisecond": asMillisecond, + "trimSpace": strings.TrimSpace, +}).Parse(` + +Tasks: {{.Name}} + + + +

    User Task: {{.Name}}

    + +Search log text:
    + +

    + + + + + + + + + {{range $el := $.Tasks}} + + + + + + + {{range $el.Events}} + + + + + + + {{end}} + {{end}} + + +`)) + +// taskFilter represents a task filter specified by a user of cmd/trace. +type taskFilter struct { + name string + cond []func(*parsedTrace, *trace.UserTaskSummary) bool +} + +// match returns true if a task, described by its ID and summary, matches +// the filter. +func (f *taskFilter) match(t *parsedTrace, task *trace.UserTaskSummary) bool { + if t == nil { + return false + } + for _, c := range f.cond { + if !c(t, task) { + return false + } + } + return true +} + +// newTaskFilter creates a new task filter from URL query variables. +func newTaskFilter(r *http.Request) (*taskFilter, error) { + if err := r.ParseForm(); err != nil { + return nil, err + } + + var name []string + var conditions []func(*parsedTrace, *trace.UserTaskSummary) bool + + param := r.Form + if typ, ok := param["type"]; ok && len(typ) > 0 { + name = append(name, fmt.Sprintf("%q", typ[0])) + conditions = append(conditions, func(_ *parsedTrace, task *trace.UserTaskSummary) bool { + return task.Name == typ[0] + }) + } + if complete := r.FormValue("complete"); complete == "1" { + name = append(name, "complete") + conditions = append(conditions, func(_ *parsedTrace, task *trace.UserTaskSummary) bool { + return task.Complete() + }) + } else if complete == "0" { + name = append(name, "incomplete") + conditions = append(conditions, func(_ *parsedTrace, task *trace.UserTaskSummary) bool { + return !task.Complete() + }) + } + if lat, err := time.ParseDuration(r.FormValue("latmin")); err == nil { + name = append(name, fmt.Sprintf("latency >= %s", lat)) + conditions = append(conditions, func(t *parsedTrace, task *trace.UserTaskSummary) bool { + return task.Complete() && taskInterval(t, task).duration() >= lat + }) + } + if lat, err := time.ParseDuration(r.FormValue("latmax")); err == nil { + name = append(name, fmt.Sprintf("latency <= %s", lat)) + conditions = append(conditions, func(t *parsedTrace, task *trace.UserTaskSummary) bool { + return task.Complete() && taskInterval(t, task).duration() <= lat + }) + } + if text := r.FormValue("logtext"); text != "" { + name = append(name, fmt.Sprintf("log contains %q", text)) + conditions = append(conditions, func(_ *parsedTrace, task *trace.UserTaskSummary) bool { + return taskMatches(task, text) + }) + } + + return &taskFilter{name: strings.Join(name, ","), cond: conditions}, nil +} + +func taskInterval(t *parsedTrace, s *trace.UserTaskSummary) interval { + var i interval + if s.Start != nil { + i.start = s.Start.Time() + } else { + i.start = t.startTime() + } + if s.End != nil { + i.end = s.End.Time() + } else { + i.end = t.endTime() + } + return i +} + +func taskMatches(t *trace.UserTaskSummary, text string) bool { + matches := func(s string) bool { + return strings.Contains(s, text) + } + if matches(t.Name) { + return true + } + for _, r := range t.Regions { + if matches(r.Name) { + return true + } + } + for _, ev := range t.Logs { + log := ev.Log() + if matches(log.Category) { + return true + } + if matches(log.Message) { + return true + } + } + return false +} + +func describeEvent(ev *tracev2.Event) string { + switch ev.Kind() { + case tracev2.EventStateTransition: + st := ev.StateTransition() + if st.Resource.Kind != tracev2.ResourceGoroutine { + return "" + } + old, new := st.Goroutine() + return fmt.Sprintf("%s -> %s", old, new) + case tracev2.EventRegionBegin: + return fmt.Sprintf("region %q begin", ev.Region().Type) + case tracev2.EventRegionEnd: + return fmt.Sprintf("region %q end", ev.Region().Type) + case tracev2.EventTaskBegin: + t := ev.Task() + return fmt.Sprintf("task %q (D %d, parent %d) begin", t.Type, t.ID, t.Parent) + case tracev2.EventTaskEnd: + return "task end" + case tracev2.EventLog: + log := ev.Log() + if log.Category != "" { + return fmt.Sprintf("log %q", log.Message) + } + return fmt.Sprintf("log (category: %s): %q", log.Category, log.Message) + } + return "" +} + +func primaryGoroutine(ev *tracev2.Event) tracev2.GoID { + if ev.Kind() != tracev2.EventStateTransition { + return ev.Goroutine() + } + st := ev.StateTransition() + if st.Resource.Kind != tracev2.ResourceGoroutine { + return tracev2.NoGoroutine + } + return st.Resource.Goroutine() +} + +func elapsed(d time.Duration) string { + b := fmt.Appendf(nil, "%.9f", d.Seconds()) + + // For subsecond durations, blank all zeros before decimal point, + // and all zeros between the decimal point and the first non-zero digit. + if d < time.Second { + dot := bytes.IndexByte(b, '.') + for i := 0; i < dot; i++ { + b[i] = ' ' + } + for i := dot + 1; i < len(b); i++ { + if b[i] == '0' { + b[i] = ' ' + } else { + break + } + } + } + return string(b) +} + +func asMillisecond(d time.Duration) float64 { + return float64(d.Nanoseconds()) / float64(time.Millisecond) +} diff --git a/src/cmd/trace/v2/testdata/generate.go b/src/cmd/trace/v2/testdata/generate.go new file mode 100644 index 0000000000..c0658b2329 --- /dev/null +++ b/src/cmd/trace/v2/testdata/generate.go @@ -0,0 +1,6 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run mktests.go +package testdata diff --git a/src/cmd/trace/v2/testdata/go122.test b/src/cmd/trace/v2/testdata/go122.test new file mode 100644 index 0000000000..36a035d0a2 --- /dev/null +++ b/src/cmd/trace/v2/testdata/go122.test @@ -0,0 +1,4366 @@ +Trace Go1.22 +EventBatch gen=1 m=18446744073709551615 time=420903766321 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=2852347 time=420902165664 size=321 +ProcStart dt=1006 p=6 p_seq=1 +GoStart dt=38 g=14 g_seq=1 +GoStop dt=299059 reason_string=16 stack=52 +GoStart dt=17 g=14 g_seq=2 +GoStop dt=316380 reason_string=16 stack=52 +GoStart dt=10 g=14 g_seq=3 +GoUnblock dt=165829 g=1 g_seq=59 stack=53 +GoDestroy dt=195 +GoStart dt=19 g=1 g_seq=60 +HeapAlloc dt=44 heapalloc_value=23256928 +HeapAlloc dt=19 heapalloc_value=23265088 +GoCreate dt=32 new_g=66 new_stack=54 stack=55 +GoBlock dt=12 reason_string=12 stack=56 +GoStart dt=5 g=66 g_seq=1 +HeapAlloc dt=199 heapalloc_value=23273224 +HeapAlloc dt=13 heapalloc_value=23281032 +GoSyscallBegin dt=19 p_seq=2 stack=57 +GoSyscallEnd dt=30 +GoSyscallBegin dt=10 p_seq=3 stack=58 +GoSyscallEnd dt=17 +GoSyscallBegin dt=295 p_seq=4 stack=59 +GoSyscallEnd dt=16 +GoSyscallBegin dt=6 p_seq=5 stack=60 +GoSyscallEnd dt=14 +HeapAlloc dt=216 heapalloc_value=23289000 +HeapAlloc dt=15 heapalloc_value=23296376 +HeapAlloc dt=19 heapalloc_value=23304328 +GoSyscallBegin dt=16 p_seq=6 stack=61 +GoSyscallEnd dt=15 +GoSyscallBegin dt=7 p_seq=7 stack=62 +GoSyscallEnd dt=14 +GoSyscallBegin dt=12 p_seq=8 stack=63 +ProcStart dt=787812 p=7 p_seq=8 +GoSyscallEndBlocked dt=6 +GoStart dt=1 g=66 g_seq=2 +GoUnblock dt=40 g=1 g_seq=61 stack=66 +GoDestroy dt=191 +GoStart dt=23 g=1 g_seq=62 +GoStop dt=27 reason_string=16 stack=67 +GoStart dt=12 g=1 g_seq=63 +HeapAlloc dt=433 heapalloc_value=23321416 +HeapAlloc dt=23 heapalloc_value=23329608 +HeapAlloc dt=27 heapalloc_value=23337800 +HeapAlloc dt=113 heapalloc_value=23344536 +HeapAlloc dt=447 heapalloc_value=23352728 +HeapAlloc dt=38 heapalloc_value=23360856 +GoSyscallBegin dt=19 p_seq=9 stack=68 +GoSyscallEnd dt=927 +HeapAlloc dt=22 heapalloc_value=23368952 +GoSyscallBegin dt=16 p_seq=10 stack=69 +GoSyscallEnd dt=30 +GoSyscallBegin dt=10 p_seq=11 stack=70 +GoSyscallEnd dt=20 +HeapAlloc dt=287 heapalloc_value=23434488 +GoSyscallBegin dt=45 p_seq=12 stack=71 +GoSyscallEnd dt=234 +GoSyscallBegin dt=11 p_seq=13 stack=71 +GoSyscallEnd dt=109 +GoSyscallBegin dt=77 p_seq=14 stack=72 +GoSyscallEnd dt=54 +GoSyscallBegin dt=22 p_seq=15 stack=73 +GoSyscallEnd dt=41 +GoSyscallBegin dt=24 p_seq=16 stack=74 +GoSyscallEnd dt=173 +GoSyscallBegin dt=11 p_seq=17 stack=75 +GoSyscallEnd dt=68 +HeapAlloc dt=134 heapalloc_value=23442648 +HeapAlloc dt=18 heapalloc_value=23450744 +GoCreate dt=23 new_g=82 new_stack=76 stack=77 +HeapAlloc dt=256 heapalloc_value=23458456 +GoSyscallBegin dt=293 p_seq=18 stack=78 +GoSyscallEnd dt=1156 +GoBlock dt=522 reason_string=7 stack=80 +ProcStop dt=25 +EventBatch gen=1 m=2852346 time=420902051246 size=68 +ProcStart dt=304 p=5 p_seq=1 +ProcStop dt=237 +ProcStart dt=1645 p=5 p_seq=2 +ProcStop dt=30 +ProcStart dt=2505 p=1 p_seq=31 +GoStart dt=199 g=34 g_seq=5 +GoBlock dt=29 reason_string=15 stack=26 +ProcStop dt=25 +ProcStart dt=103771 p=1 p_seq=32 +GoStart dt=185 g=10 g_seq=1 +GoStop dt=304886 reason_string=16 stack=52 +GoStart dt=20 g=10 g_seq=2 +GoStop dt=316414 reason_string=16 stack=52 +GoStart dt=20 g=10 g_seq=3 +GoDestroy dt=159939 +ProcStop dt=47 +EventBatch gen=1 m=2852345 time=420901844115 size=3554 +ProcStart dt=108 p=4 p_seq=1 +ProcStop dt=283 +ProcStart dt=3670 p=0 p_seq=27 +GoStart dt=473 g=1 g_seq=19 +GCMarkAssistEnd dt=34 +HeapAlloc dt=33 heapalloc_value=4117744 +HeapAlloc dt=544 heapalloc_value=4124912 +GCSweepBegin dt=49 stack=38 +GCSweepEnd dt=1131 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=64 heapalloc_value=4133104 +GCSweepBegin dt=44 stack=38 +GCSweepEnd dt=192 swept_value=139264 reclaimed_value=0 +HeapAlloc dt=12 heapalloc_value=4141296 +HeapAlloc dt=29 heapalloc_value=4149488 +HeapAlloc dt=19 heapalloc_value=4157680 +HeapAlloc dt=24 heapalloc_value=4165872 +HeapAlloc dt=23 heapalloc_value=4174064 +GCSweepBegin dt=34 stack=39 +GCSweepEnd dt=33 swept_value=67108864 reclaimed_value=0 +HeapAlloc dt=8 heapalloc_value=4182256 +HeapAlloc dt=117 heapalloc_value=4190448 +HeapAlloc dt=27 heapalloc_value=4198640 +HeapAlloc dt=19 heapalloc_value=4206832 +HeapAlloc dt=23 heapalloc_value=4215024 +HeapAlloc dt=20 heapalloc_value=4223216 +HeapAlloc dt=18 heapalloc_value=4231408 +HeapAlloc dt=70 heapalloc_value=4239600 +HeapAlloc dt=19 heapalloc_value=4247792 +HeapAlloc dt=16 heapalloc_value=4255984 +HeapAlloc dt=16 heapalloc_value=4264176 +HeapAlloc dt=15 heapalloc_value=4272368 +HeapAlloc dt=18 heapalloc_value=4280560 +HeapAlloc dt=15 heapalloc_value=4288752 +HeapAlloc dt=15 heapalloc_value=4296944 +HeapAlloc dt=17 heapalloc_value=4305136 +HeapAlloc dt=18 heapalloc_value=4313328 +HeapAlloc dt=17 heapalloc_value=4321520 +HeapAlloc dt=14 heapalloc_value=4329712 +HeapAlloc dt=15 heapalloc_value=4337904 +HeapAlloc dt=16 heapalloc_value=4346096 +HeapAlloc dt=15 heapalloc_value=4354288 +HeapAlloc dt=17 heapalloc_value=4362480 +HeapAlloc dt=38 heapalloc_value=4370672 +HeapAlloc dt=22 heapalloc_value=4378864 +HeapAlloc dt=23 heapalloc_value=4387056 +HeapAlloc dt=22 heapalloc_value=4395248 +HeapAlloc dt=27 heapalloc_value=4403440 +HeapAlloc dt=27 heapalloc_value=4411632 +HeapAlloc dt=29 heapalloc_value=4419824 +HeapAlloc dt=25 heapalloc_value=4428016 +HeapAlloc dt=25 heapalloc_value=4436208 +HeapAlloc dt=21 heapalloc_value=4444400 +GoBlock dt=45 reason_string=19 stack=21 +ProcStop dt=283 +ProcStart dt=17671 p=1 p_seq=16 +ProcStop dt=21 +ProcStart dt=2566 p=0 p_seq=30 +ProcStop dt=12 +ProcStart dt=16741 p=0 p_seq=31 +GoUnblock dt=27 g=1 g_seq=22 stack=0 +GoStart dt=174 g=1 g_seq=23 +HeapAlloc dt=39 heapalloc_value=5574896 +HeapAlloc dt=20 heapalloc_value=5583088 +HeapAlloc dt=13 heapalloc_value=5591280 +HeapAlloc dt=12 heapalloc_value=5599472 +HeapAlloc dt=12 heapalloc_value=5607664 +HeapAlloc dt=12 heapalloc_value=5615856 +HeapAlloc dt=13 heapalloc_value=5624048 +HeapAlloc dt=41 heapalloc_value=5632240 +HeapAlloc dt=12 heapalloc_value=5640432 +HeapAlloc dt=13 heapalloc_value=5648624 +HeapAlloc dt=11 heapalloc_value=5656816 +HeapAlloc dt=12 heapalloc_value=5665008 +HeapAlloc dt=8 heapalloc_value=5673200 +HeapAlloc dt=39 heapalloc_value=5804272 +HeapAlloc dt=2903 heapalloc_value=5812464 +HeapAlloc dt=14 heapalloc_value=5820656 +HeapAlloc dt=77 heapalloc_value=5828848 +HeapAlloc dt=10 heapalloc_value=5837040 +HeapAlloc dt=8 heapalloc_value=5845232 +HeapAlloc dt=48 heapalloc_value=5853424 +HeapAlloc dt=9 heapalloc_value=5861616 +HeapAlloc dt=8 heapalloc_value=5869808 +HeapAlloc dt=8 heapalloc_value=5878000 +HeapAlloc dt=9 heapalloc_value=5886192 +HeapAlloc dt=8 heapalloc_value=5894384 +HeapAlloc dt=8 heapalloc_value=5902576 +HeapAlloc dt=8 heapalloc_value=5910768 +HeapAlloc dt=8 heapalloc_value=5918960 +HeapAlloc dt=8 heapalloc_value=5927152 +HeapAlloc dt=8 heapalloc_value=5935344 +HeapAlloc dt=8 heapalloc_value=5943536 +HeapAlloc dt=17 heapalloc_value=5951728 +HeapAlloc dt=8 heapalloc_value=5959920 +HeapAlloc dt=6 heapalloc_value=5968112 +HeapAlloc dt=6 heapalloc_value=5976304 +HeapAlloc dt=6 heapalloc_value=5984496 +HeapAlloc dt=7 heapalloc_value=5992688 +HeapAlloc dt=6 heapalloc_value=6000880 +HeapAlloc dt=6 heapalloc_value=6009072 +HeapAlloc dt=7 heapalloc_value=6017264 +HeapAlloc dt=6 heapalloc_value=6025456 +HeapAlloc dt=6 heapalloc_value=6033648 +HeapAlloc dt=6 heapalloc_value=6041840 +HeapAlloc dt=6 heapalloc_value=6050032 +HeapAlloc dt=7 heapalloc_value=6058224 +HeapAlloc dt=44 heapalloc_value=6066416 +HeapAlloc dt=8 heapalloc_value=6074608 +HeapAlloc dt=71 heapalloc_value=6082800 +HeapAlloc dt=8 heapalloc_value=6090992 +HeapAlloc dt=6 heapalloc_value=6099184 +HeapAlloc dt=7 heapalloc_value=6107376 +HeapAlloc dt=6 heapalloc_value=6115568 +HeapAlloc dt=6 heapalloc_value=6123760 +HeapAlloc dt=7 heapalloc_value=6131952 +HeapAlloc dt=6 heapalloc_value=6140144 +HeapAlloc dt=6 heapalloc_value=6148336 +HeapAlloc dt=6 heapalloc_value=6156528 +HeapAlloc dt=7 heapalloc_value=6164720 +HeapAlloc dt=6 heapalloc_value=6172912 +HeapAlloc dt=6 heapalloc_value=6181104 +HeapAlloc dt=6 heapalloc_value=6189296 +HeapAlloc dt=6 heapalloc_value=6197488 +HeapAlloc dt=7 heapalloc_value=6205680 +HeapAlloc dt=6 heapalloc_value=6213872 +HeapAlloc dt=6 heapalloc_value=6222064 +HeapAlloc dt=6 heapalloc_value=6230256 +HeapAlloc dt=6 heapalloc_value=6238448 +HeapAlloc dt=7 heapalloc_value=6246640 +HeapAlloc dt=6 heapalloc_value=6254832 +HeapAlloc dt=6 heapalloc_value=6263024 +HeapAlloc dt=6 heapalloc_value=6271216 +HeapAlloc dt=6 heapalloc_value=6279408 +HeapAlloc dt=6 heapalloc_value=6287600 +HeapAlloc dt=7 heapalloc_value=6295792 +HeapAlloc dt=6 heapalloc_value=6303984 +HeapAlloc dt=7 heapalloc_value=6312176 +HeapAlloc dt=6 heapalloc_value=6320368 +HeapAlloc dt=6 heapalloc_value=6328560 +HeapAlloc dt=6 heapalloc_value=6336752 +HeapAlloc dt=70 heapalloc_value=6344944 +HeapAlloc dt=139 heapalloc_value=6353136 +HeapAlloc dt=8 heapalloc_value=6361328 +HeapAlloc dt=7 heapalloc_value=6369520 +HeapAlloc dt=6 heapalloc_value=6377712 +HeapAlloc dt=6 heapalloc_value=6385904 +HeapAlloc dt=6 heapalloc_value=6394096 +HeapAlloc dt=6 heapalloc_value=6402288 +HeapAlloc dt=7 heapalloc_value=6410480 +HeapAlloc dt=6 heapalloc_value=6418672 +HeapAlloc dt=6 heapalloc_value=6426864 +HeapAlloc dt=6 heapalloc_value=6435056 +HeapAlloc dt=6 heapalloc_value=6443248 +HeapAlloc dt=6 heapalloc_value=6451440 +HeapAlloc dt=7 heapalloc_value=6459632 +HeapAlloc dt=6 heapalloc_value=6467824 +HeapAlloc dt=6 heapalloc_value=6476016 +HeapAlloc dt=6 heapalloc_value=6484208 +HeapAlloc dt=42 heapalloc_value=6492400 +HeapAlloc dt=8 heapalloc_value=6500592 +HeapAlloc dt=6 heapalloc_value=6508784 +HeapAlloc dt=6 heapalloc_value=6516976 +HeapAlloc dt=7 heapalloc_value=6525168 +HeapAlloc dt=6 heapalloc_value=6533360 +HeapAlloc dt=6 heapalloc_value=6541552 +HeapAlloc dt=6 heapalloc_value=6549744 +HeapAlloc dt=6 heapalloc_value=6557936 +HeapAlloc dt=7 heapalloc_value=6566128 +HeapAlloc dt=6 heapalloc_value=6574320 +HeapAlloc dt=6 heapalloc_value=6582512 +HeapAlloc dt=7 heapalloc_value=6590704 +HeapAlloc dt=6 heapalloc_value=6598896 +HeapAlloc dt=66 heapalloc_value=6607088 +HeapAlloc dt=8 heapalloc_value=6615280 +HeapAlloc dt=6 heapalloc_value=6623472 +HeapAlloc dt=6 heapalloc_value=6631664 +HeapAlloc dt=7 heapalloc_value=6639856 +HeapAlloc dt=6 heapalloc_value=6648048 +HeapAlloc dt=6 heapalloc_value=6656240 +HeapAlloc dt=7 heapalloc_value=6664432 +HeapAlloc dt=6 heapalloc_value=6672624 +HeapAlloc dt=6 heapalloc_value=6680816 +HeapAlloc dt=6 heapalloc_value=6689008 +HeapAlloc dt=6 heapalloc_value=6697200 +HeapAlloc dt=7 heapalloc_value=6705392 +HeapAlloc dt=6 heapalloc_value=6713584 +HeapAlloc dt=6 heapalloc_value=6721776 +GoBlock dt=13 reason_string=19 stack=21 +ProcStop dt=220 +ProcStart dt=17605 p=1 p_seq=18 +ProcStop dt=18 +ProcStart dt=8830 p=4 p_seq=2 +ProcStop dt=26 +ProcStart dt=16773 p=0 p_seq=36 +ProcStop dt=12 +ProcStart dt=1445 p=1 p_seq=20 +ProcStop dt=12 +ProcStart dt=16751 p=1 p_seq=21 +GoUnblock dt=16 g=1 g_seq=33 stack=0 +GoStart dt=189 g=1 g_seq=34 +HeapAlloc dt=54 heapalloc_value=9892080 +HeapAlloc dt=21 heapalloc_value=9900272 +HeapAlloc dt=64 heapalloc_value=9908464 +HeapAlloc dt=13 heapalloc_value=9916656 +HeapAlloc dt=13 heapalloc_value=9924848 +HeapAlloc dt=12 heapalloc_value=9933040 +HeapAlloc dt=11 heapalloc_value=9941232 +HeapAlloc dt=13 heapalloc_value=9949424 +HeapAlloc dt=12 heapalloc_value=9957616 +HeapAlloc dt=48 heapalloc_value=9965808 +HeapAlloc dt=338 heapalloc_value=9974000 +HeapAlloc dt=16 heapalloc_value=9982192 +HeapAlloc dt=10 heapalloc_value=9990384 +HeapAlloc dt=8 heapalloc_value=9998576 +HeapAlloc dt=8 heapalloc_value=10006768 +HeapAlloc dt=10 heapalloc_value=10014960 +HeapAlloc dt=8 heapalloc_value=10023152 +HeapAlloc dt=7 heapalloc_value=10031344 +HeapAlloc dt=9 heapalloc_value=10039536 +HeapAlloc dt=6 heapalloc_value=10047728 +HeapAlloc dt=52 heapalloc_value=10055920 +HeapAlloc dt=8 heapalloc_value=10064112 +HeapAlloc dt=7 heapalloc_value=10072304 +HeapAlloc dt=7 heapalloc_value=10080496 +HeapAlloc dt=7 heapalloc_value=10088688 +HeapAlloc dt=7 heapalloc_value=10096880 +HeapAlloc dt=90 heapalloc_value=10105072 +HeapAlloc dt=8 heapalloc_value=10113264 +HeapAlloc dt=8 heapalloc_value=10121456 +HeapAlloc dt=7 heapalloc_value=10129648 +HeapAlloc dt=7 heapalloc_value=10137840 +HeapAlloc dt=7 heapalloc_value=10146032 +HeapAlloc dt=6 heapalloc_value=10154224 +HeapAlloc dt=7 heapalloc_value=10162416 +HeapAlloc dt=7 heapalloc_value=10170608 +HeapAlloc dt=7 heapalloc_value=10178800 +HeapAlloc dt=6 heapalloc_value=10186992 +HeapAlloc dt=7 heapalloc_value=10195184 +HeapAlloc dt=7 heapalloc_value=10203376 +HeapAlloc dt=7 heapalloc_value=10211568 +HeapAlloc dt=6 heapalloc_value=10219760 +HeapAlloc dt=8 heapalloc_value=10227952 +HeapAlloc dt=97 heapalloc_value=10236144 +HeapAlloc dt=9 heapalloc_value=10244336 +HeapAlloc dt=6 heapalloc_value=10252528 +HeapAlloc dt=7 heapalloc_value=10260720 +HeapAlloc dt=7 heapalloc_value=10268912 +HeapAlloc dt=6 heapalloc_value=10277104 +HeapAlloc dt=7 heapalloc_value=10285296 +HeapAlloc dt=7 heapalloc_value=10293488 +HeapAlloc dt=73 heapalloc_value=10301680 +HeapAlloc dt=8 heapalloc_value=10309872 +HeapAlloc dt=6 heapalloc_value=10318064 +HeapAlloc dt=7 heapalloc_value=10326256 +HeapAlloc dt=7 heapalloc_value=10334448 +HeapAlloc dt=6 heapalloc_value=10342640 +HeapAlloc dt=7 heapalloc_value=10350832 +HeapAlloc dt=7 heapalloc_value=10359024 +HeapAlloc dt=7 heapalloc_value=10367216 +HeapAlloc dt=6 heapalloc_value=10375408 +HeapAlloc dt=7 heapalloc_value=10383600 +HeapAlloc dt=7 heapalloc_value=10391792 +HeapAlloc dt=7 heapalloc_value=10399984 +HeapAlloc dt=6 heapalloc_value=10408176 +HeapAlloc dt=7 heapalloc_value=10416368 +HeapAlloc dt=7 heapalloc_value=10424560 +HeapAlloc dt=6 heapalloc_value=10432752 +HeapAlloc dt=7 heapalloc_value=10440944 +HeapAlloc dt=7 heapalloc_value=10449136 +HeapAlloc dt=6 heapalloc_value=10457328 +HeapAlloc dt=7 heapalloc_value=10465520 +HeapAlloc dt=96 heapalloc_value=10473712 +HeapAlloc dt=8 heapalloc_value=10481904 +HeapAlloc dt=6 heapalloc_value=10490096 +HeapAlloc dt=68 heapalloc_value=10498288 +HeapAlloc dt=8 heapalloc_value=10506480 +HeapAlloc dt=7 heapalloc_value=10514672 +HeapAlloc dt=7 heapalloc_value=10522864 +HeapAlloc dt=6 heapalloc_value=10531056 +HeapAlloc dt=7 heapalloc_value=10539248 +HeapAlloc dt=7 heapalloc_value=10547440 +HeapAlloc dt=7 heapalloc_value=10555632 +HeapAlloc dt=6 heapalloc_value=10563824 +HeapAlloc dt=7 heapalloc_value=10572016 +HeapAlloc dt=7 heapalloc_value=10580208 +HeapAlloc dt=6 heapalloc_value=10588400 +HeapAlloc dt=7 heapalloc_value=10596592 +HeapAlloc dt=7 heapalloc_value=10604784 +HeapAlloc dt=7 heapalloc_value=10612976 +HeapAlloc dt=7 heapalloc_value=10621168 +HeapAlloc dt=6 heapalloc_value=10629360 +HeapAlloc dt=7 heapalloc_value=10637552 +HeapAlloc dt=7 heapalloc_value=10645744 +HeapAlloc dt=6 heapalloc_value=10653936 +HeapAlloc dt=7 heapalloc_value=10662128 +HeapAlloc dt=7 heapalloc_value=10670320 +HeapAlloc dt=6 heapalloc_value=10678512 +HeapAlloc dt=7 heapalloc_value=10686704 +HeapAlloc dt=7 heapalloc_value=10694896 +HeapAlloc dt=7 heapalloc_value=10703088 +HeapAlloc dt=7 heapalloc_value=10711280 +HeapAlloc dt=6 heapalloc_value=10719472 +HeapAlloc dt=7 heapalloc_value=10727664 +HeapAlloc dt=7 heapalloc_value=10735856 +HeapAlloc dt=7 heapalloc_value=10744048 +HeapAlloc dt=6 heapalloc_value=10752240 +HeapAlloc dt=78 heapalloc_value=10760432 +HeapAlloc dt=8 heapalloc_value=10768624 +HeapAlloc dt=6 heapalloc_value=10776816 +HeapAlloc dt=7 heapalloc_value=10785008 +HeapAlloc dt=7 heapalloc_value=10793200 +HeapAlloc dt=7 heapalloc_value=10801392 +HeapAlloc dt=6 heapalloc_value=10809584 +HeapAlloc dt=7 heapalloc_value=10817776 +HeapAlloc dt=65 heapalloc_value=10825968 +HeapAlloc dt=9 heapalloc_value=10834160 +HeapAlloc dt=6 heapalloc_value=10842352 +HeapAlloc dt=7 heapalloc_value=10850544 +HeapAlloc dt=6 heapalloc_value=10858736 +HeapAlloc dt=7 heapalloc_value=10866928 +HeapAlloc dt=7 heapalloc_value=10875120 +HeapAlloc dt=7 heapalloc_value=10883312 +HeapAlloc dt=6 heapalloc_value=10891504 +HeapAlloc dt=44 heapalloc_value=10899696 +HeapAlloc dt=7 heapalloc_value=10907888 +GoBlock dt=13 reason_string=19 stack=21 +ProcStop dt=198 +ProcStart dt=17586 p=0 p_seq=38 +ProcStop dt=21 +ProcStart dt=5052 p=1 p_seq=24 +ProcStop dt=13 +ProcStart dt=16760 p=1 p_seq=26 +GoUnblock dt=19 g=1 g_seq=39 stack=0 +GoStart dt=169 g=1 g_seq=40 +HeapAlloc dt=52 heapalloc_value=13250800 +HeapAlloc dt=19 heapalloc_value=13258992 +HeapAlloc dt=9 heapalloc_value=13267184 +HeapAlloc dt=82 heapalloc_value=13275376 +HeapAlloc dt=12 heapalloc_value=13283568 +HeapAlloc dt=9 heapalloc_value=13291760 +HeapAlloc dt=9 heapalloc_value=13299952 +HeapAlloc dt=10 heapalloc_value=13308144 +HeapAlloc dt=10 heapalloc_value=13316336 +HeapAlloc dt=7 heapalloc_value=13324528 +HeapAlloc dt=6 heapalloc_value=13332720 +HeapAlloc dt=6 heapalloc_value=13340912 +HeapAlloc dt=6 heapalloc_value=13349104 +HeapAlloc dt=7 heapalloc_value=13357296 +HeapAlloc dt=6 heapalloc_value=13365488 +HeapAlloc dt=6 heapalloc_value=13373680 +HeapAlloc dt=520 heapalloc_value=13381872 +HeapAlloc dt=15 heapalloc_value=13390064 +HeapAlloc dt=7 heapalloc_value=13398256 +HeapAlloc dt=6 heapalloc_value=13406448 +HeapAlloc dt=8 heapalloc_value=13414640 +HeapAlloc dt=6 heapalloc_value=13422832 +HeapAlloc dt=6 heapalloc_value=13431024 +HeapAlloc dt=7 heapalloc_value=13439216 +HeapAlloc dt=8 heapalloc_value=13447408 +HeapAlloc dt=7 heapalloc_value=13455600 +HeapAlloc dt=6 heapalloc_value=13463792 +HeapAlloc dt=6 heapalloc_value=13471984 +HeapAlloc dt=48 heapalloc_value=13480176 +HeapAlloc dt=7 heapalloc_value=13488368 +HeapAlloc dt=6 heapalloc_value=13496560 +HeapAlloc dt=7 heapalloc_value=13504752 +HeapAlloc dt=9 heapalloc_value=13512944 +HeapAlloc dt=6 heapalloc_value=13521136 +HeapAlloc dt=7 heapalloc_value=13529328 +HeapAlloc dt=6 heapalloc_value=13537520 +HeapAlloc dt=7 heapalloc_value=13545712 +HeapAlloc dt=6 heapalloc_value=13553904 +HeapAlloc dt=7 heapalloc_value=13562096 +HeapAlloc dt=6 heapalloc_value=13570288 +HeapAlloc dt=8 heapalloc_value=13578480 +HeapAlloc dt=6 heapalloc_value=13586672 +HeapAlloc dt=6 heapalloc_value=13594864 +HeapAlloc dt=6 heapalloc_value=13603056 +HeapAlloc dt=6 heapalloc_value=13611248 +HeapAlloc dt=7 heapalloc_value=13619440 +HeapAlloc dt=6 heapalloc_value=13627632 +HeapAlloc dt=6 heapalloc_value=13635824 +HeapAlloc dt=76 heapalloc_value=13644016 +HeapAlloc dt=8 heapalloc_value=13652208 +HeapAlloc dt=6 heapalloc_value=13660400 +HeapAlloc dt=6 heapalloc_value=13668592 +HeapAlloc dt=6 heapalloc_value=13676784 +HeapAlloc dt=7 heapalloc_value=13684976 +HeapAlloc dt=6 heapalloc_value=13693168 +HeapAlloc dt=6 heapalloc_value=13701360 +HeapAlloc dt=8 heapalloc_value=13709552 +HeapAlloc dt=6 heapalloc_value=13717744 +HeapAlloc dt=64 heapalloc_value=13725936 +HeapAlloc dt=7 heapalloc_value=13734128 +HeapAlloc dt=7 heapalloc_value=13742320 +HeapAlloc dt=6 heapalloc_value=13750512 +HeapAlloc dt=6 heapalloc_value=13758704 +HeapAlloc dt=6 heapalloc_value=13766896 +HeapAlloc dt=8 heapalloc_value=13775088 +HeapAlloc dt=7 heapalloc_value=13783280 +HeapAlloc dt=6 heapalloc_value=13791472 +HeapAlloc dt=7 heapalloc_value=13799664 +HeapAlloc dt=6 heapalloc_value=13807856 +HeapAlloc dt=6 heapalloc_value=13816048 +HeapAlloc dt=6 heapalloc_value=13824240 +HeapAlloc dt=6 heapalloc_value=13832432 +HeapAlloc dt=9 heapalloc_value=13840624 +HeapAlloc dt=6 heapalloc_value=13848816 +HeapAlloc dt=6 heapalloc_value=13857008 +HeapAlloc dt=6 heapalloc_value=13865200 +HeapAlloc dt=7 heapalloc_value=13873392 +HeapAlloc dt=7 heapalloc_value=13881584 +HeapAlloc dt=6 heapalloc_value=13889776 +HeapAlloc dt=45 heapalloc_value=13897968 +HeapAlloc dt=75 heapalloc_value=13906160 +HeapAlloc dt=8 heapalloc_value=13914352 +HeapAlloc dt=6 heapalloc_value=13922544 +HeapAlloc dt=6 heapalloc_value=13930736 +HeapAlloc dt=7 heapalloc_value=13938928 +HeapAlloc dt=6 heapalloc_value=13947120 +HeapAlloc dt=6 heapalloc_value=13955312 +HeapAlloc dt=6 heapalloc_value=13963504 +HeapAlloc dt=6 heapalloc_value=13971696 +HeapAlloc dt=7 heapalloc_value=13979888 +HeapAlloc dt=6 heapalloc_value=13988080 +HeapAlloc dt=6 heapalloc_value=13996272 +HeapAlloc dt=6 heapalloc_value=14004464 +HeapAlloc dt=6 heapalloc_value=14012656 +HeapAlloc dt=6 heapalloc_value=14020848 +HeapAlloc dt=7 heapalloc_value=14029040 +HeapAlloc dt=6 heapalloc_value=14037232 +HeapAlloc dt=6 heapalloc_value=14045424 +HeapAlloc dt=7 heapalloc_value=14053616 +HeapAlloc dt=6 heapalloc_value=14061808 +HeapAlloc dt=6 heapalloc_value=14070000 +HeapAlloc dt=6 heapalloc_value=14078192 +HeapAlloc dt=6 heapalloc_value=14086384 +HeapAlloc dt=6 heapalloc_value=14094576 +HeapAlloc dt=9 heapalloc_value=14102768 +HeapAlloc dt=6 heapalloc_value=14110960 +HeapAlloc dt=7 heapalloc_value=14119152 +HeapAlloc dt=6 heapalloc_value=14127344 +HeapAlloc dt=7 heapalloc_value=14135536 +HeapAlloc dt=6 heapalloc_value=14143728 +HeapAlloc dt=6 heapalloc_value=14151920 +HeapAlloc dt=6 heapalloc_value=14160112 +HeapAlloc dt=69 heapalloc_value=14168304 +HeapAlloc dt=8 heapalloc_value=14176496 +HeapAlloc dt=6 heapalloc_value=14184688 +HeapAlloc dt=6 heapalloc_value=14192880 +HeapAlloc dt=7 heapalloc_value=14201072 +HeapAlloc dt=6 heapalloc_value=14209264 +HeapAlloc dt=6 heapalloc_value=14217456 +HeapAlloc dt=16 heapalloc_value=14586096 +HeapAlloc dt=3676 heapalloc_value=14594288 +HeapAlloc dt=11 heapalloc_value=14602480 +HeapAlloc dt=72 heapalloc_value=14610672 +HeapAlloc dt=10 heapalloc_value=14618864 +HeapAlloc dt=7 heapalloc_value=14627056 +HeapAlloc dt=9 heapalloc_value=14635248 +GoBlock dt=13 reason_string=19 stack=21 +ProcStop dt=219 +ProcStart dt=17778 p=2 p_seq=19 +ProcStop dt=25 +ProcStart dt=2221 p=1 p_seq=29 +ProcStop dt=18 +ProcStart dt=16821 p=1 p_seq=30 +GoUnblock dt=23 g=1 g_seq=43 stack=0 +GoStart dt=193 g=1 g_seq=44 +HeapAlloc dt=59 heapalloc_value=15667440 +HeapAlloc dt=26 heapalloc_value=15675632 +HeapAlloc dt=15 heapalloc_value=15683824 +HeapAlloc dt=10 heapalloc_value=15692016 +HeapAlloc dt=9 heapalloc_value=15700208 +HeapAlloc dt=10 heapalloc_value=15708400 +HeapAlloc dt=11 heapalloc_value=15716592 +HeapAlloc dt=9 heapalloc_value=15724784 +HeapAlloc dt=96 heapalloc_value=15732976 +HeapAlloc dt=324 heapalloc_value=15741168 +HeapAlloc dt=17 heapalloc_value=15749360 +HeapAlloc dt=9 heapalloc_value=15757552 +HeapAlloc dt=9 heapalloc_value=15765744 +HeapAlloc dt=7 heapalloc_value=15773936 +HeapAlloc dt=8 heapalloc_value=15782128 +HeapAlloc dt=6 heapalloc_value=15790320 +HeapAlloc dt=6 heapalloc_value=15798512 +HeapAlloc dt=8 heapalloc_value=15806704 +HeapAlloc dt=5 heapalloc_value=15814896 +HeapAlloc dt=7 heapalloc_value=15823088 +HeapAlloc dt=6 heapalloc_value=15831280 +HeapAlloc dt=6 heapalloc_value=15839472 +HeapAlloc dt=6 heapalloc_value=15847664 +HeapAlloc dt=6 heapalloc_value=15855856 +HeapAlloc dt=7 heapalloc_value=15864048 +HeapAlloc dt=10 heapalloc_value=15872240 +HeapAlloc dt=6 heapalloc_value=15880432 +HeapAlloc dt=6 heapalloc_value=15888624 +HeapAlloc dt=6 heapalloc_value=15896816 +HeapAlloc dt=7 heapalloc_value=15905008 +HeapAlloc dt=6 heapalloc_value=15913200 +HeapAlloc dt=6 heapalloc_value=15921392 +HeapAlloc dt=7 heapalloc_value=15929584 +HeapAlloc dt=8 heapalloc_value=15937776 +HeapAlloc dt=48 heapalloc_value=15945968 +HeapAlloc dt=7 heapalloc_value=15954160 +HeapAlloc dt=7 heapalloc_value=15962352 +HeapAlloc dt=6 heapalloc_value=15970544 +HeapAlloc dt=8 heapalloc_value=15978736 +HeapAlloc dt=6 heapalloc_value=15986928 +HeapAlloc dt=7 heapalloc_value=15995120 +HeapAlloc dt=104 heapalloc_value=16003312 +HeapAlloc dt=9 heapalloc_value=16011504 +HeapAlloc dt=8 heapalloc_value=16019696 +HeapAlloc dt=9 heapalloc_value=16027888 +HeapAlloc dt=8 heapalloc_value=16036080 +HeapAlloc dt=7 heapalloc_value=16044272 +HeapAlloc dt=6 heapalloc_value=16052464 +HeapAlloc dt=7 heapalloc_value=16060656 +HeapAlloc dt=6 heapalloc_value=16068848 +HeapAlloc dt=6 heapalloc_value=16077040 +HeapAlloc dt=6 heapalloc_value=16085232 +HeapAlloc dt=7 heapalloc_value=16093424 +HeapAlloc dt=6 heapalloc_value=16101616 +HeapAlloc dt=6 heapalloc_value=16109808 +HeapAlloc dt=6 heapalloc_value=16118000 +HeapAlloc dt=7 heapalloc_value=16126192 +HeapAlloc dt=6 heapalloc_value=16134384 +HeapAlloc dt=6 heapalloc_value=16142576 +HeapAlloc dt=6 heapalloc_value=16150768 +HeapAlloc dt=7 heapalloc_value=16158960 +HeapAlloc dt=6 heapalloc_value=16167152 +HeapAlloc dt=6 heapalloc_value=16175344 +HeapAlloc dt=78 heapalloc_value=16183536 +HeapAlloc dt=7 heapalloc_value=16191728 +HeapAlloc dt=6 heapalloc_value=16199920 +HeapAlloc dt=6 heapalloc_value=16208112 +HeapAlloc dt=7 heapalloc_value=16216304 +HeapAlloc dt=6 heapalloc_value=16224496 +HeapAlloc dt=6 heapalloc_value=16232688 +HeapAlloc dt=6 heapalloc_value=16240880 +HeapAlloc dt=6 heapalloc_value=16249072 +HeapAlloc dt=7 heapalloc_value=16257264 +HeapAlloc dt=73 heapalloc_value=16265456 +HeapAlloc dt=8 heapalloc_value=16273648 +HeapAlloc dt=6 heapalloc_value=16281840 +HeapAlloc dt=6 heapalloc_value=16290032 +HeapAlloc dt=6 heapalloc_value=16298224 +HeapAlloc dt=7 heapalloc_value=16306416 +HeapAlloc dt=6 heapalloc_value=16314608 +HeapAlloc dt=6 heapalloc_value=16322800 +HeapAlloc dt=6 heapalloc_value=16330992 +HeapAlloc dt=7 heapalloc_value=16339184 +HeapAlloc dt=6 heapalloc_value=16347376 +HeapAlloc dt=8 heapalloc_value=16355568 +HeapAlloc dt=44 heapalloc_value=16363760 +HeapAlloc dt=7 heapalloc_value=16371952 +HeapAlloc dt=6 heapalloc_value=16380144 +HeapAlloc dt=6 heapalloc_value=16388336 +HeapAlloc dt=6 heapalloc_value=16396528 +HeapAlloc dt=7 heapalloc_value=16404720 +HeapAlloc dt=6 heapalloc_value=16412912 +HeapAlloc dt=6 heapalloc_value=16421104 +HeapAlloc dt=6 heapalloc_value=16429296 +HeapAlloc dt=7 heapalloc_value=16437488 +HeapAlloc dt=6 heapalloc_value=16445680 +HeapAlloc dt=6 heapalloc_value=16453872 +HeapAlloc dt=6 heapalloc_value=16462064 +HeapAlloc dt=6 heapalloc_value=16470256 +HeapAlloc dt=6 heapalloc_value=16478448 +HeapAlloc dt=7 heapalloc_value=16486640 +HeapAlloc dt=6 heapalloc_value=16494832 +GCBegin dt=18 gc_seq=5 stack=41 +STWBegin dt=46 kind_string=22 stack=42 +GoUnblock dt=209 g=4 g_seq=7 stack=43 +ProcsChange dt=70 procs_value=8 stack=44 +STWEnd dt=24 +GCMarkAssistBegin dt=182 stack=30 +GCMarkAssistEnd dt=3877 +HeapAlloc dt=628 heapalloc_value=16509392 +HeapAlloc dt=22 heapalloc_value=16517584 +HeapAlloc dt=18 heapalloc_value=16525776 +HeapAlloc dt=371 heapalloc_value=16533968 +HeapAlloc dt=14 heapalloc_value=16542160 +HeapAlloc dt=11 heapalloc_value=16550352 +HeapAlloc dt=13 heapalloc_value=16558544 +HeapAlloc dt=13 heapalloc_value=16566736 +HeapAlloc dt=10 heapalloc_value=16574928 +HeapAlloc dt=10 heapalloc_value=16583120 +HeapAlloc dt=8 heapalloc_value=16591312 +HeapAlloc dt=8 heapalloc_value=16599504 +HeapAlloc dt=8 heapalloc_value=16607696 +HeapAlloc dt=7 heapalloc_value=16615888 +HeapAlloc dt=8 heapalloc_value=16624080 +HeapAlloc dt=8 heapalloc_value=16632272 +HeapAlloc dt=9 heapalloc_value=16640464 +HeapAlloc dt=7 heapalloc_value=16648656 +HeapAlloc dt=8 heapalloc_value=16656848 +HeapAlloc dt=9 heapalloc_value=16665040 +HeapAlloc dt=8 heapalloc_value=16673232 +HeapAlloc dt=9 heapalloc_value=16681424 +HeapAlloc dt=8 heapalloc_value=16689616 +GoBlock dt=17 reason_string=19 stack=21 +ProcStop dt=2869 +ProcStart dt=110180 p=4 p_seq=5 +GoStart dt=268 g=15 g_seq=1 +GoStop dt=304685 reason_string=16 stack=52 +GoStart dt=20 g=15 g_seq=2 +GoStop dt=316415 reason_string=16 stack=52 +GoStart dt=23 g=15 g_seq=3 +GoDestroy dt=160136 +ProcStop dt=32 +EventBatch gen=1 m=2852344 time=420901833895 size=3430 +ProcStart dt=383 p=2 p_seq=3 +GoStart dt=284 g=7 g_seq=1 +HeapAlloc dt=35 heapalloc_value=4055040 +GoBlock dt=148 reason_string=15 stack=26 +ProcStop dt=12 +ProcStart dt=791 p=1 p_seq=8 +ProcStop dt=4 +ProcStart dt=817 p=1 p_seq=9 +ProcStop dt=14 +ProcStart dt=796 p=0 p_seq=21 +ProcStop dt=9 +ProcStart dt=393 p=1 p_seq=11 +ProcStop dt=19 +ProcStart dt=324 p=2 p_seq=9 +GoStart dt=339 g=25 g_seq=1 +GoBlock dt=112 reason_string=15 stack=26 +ProcStop dt=4 +ProcStart dt=1331 p=1 p_seq=15 +GoUnblock dt=13 g=9 g_seq=2 stack=0 +GoStart dt=145 g=9 g_seq=3 +GoLabel dt=1 label_string=2 +STWBegin dt=4838 kind_string=23 stack=34 +GoUnblock dt=44 g=1 g_seq=18 stack=35 +HeapAlloc dt=17 heapalloc_value=4112624 +GoStatus dt=15 g=3 m=18446744073709551615 gstatus=4 +GoUnblock dt=5 g=3 g_seq=1 stack=36 +GCEnd dt=4 gc_seq=2 +HeapGoal dt=5 heapgoal_value=8644048 +ProcsChange dt=37 procs_value=8 stack=37 +STWEnd dt=1475 +GoBlock dt=2304 reason_string=15 stack=26 +GoStart dt=12 g=3 g_seq=2 +GoBlock dt=2449 reason_string=14 stack=40 +ProcStop dt=16 +ProcStart dt=67967 p=1 p_seq=19 +GoUnblock dt=21 g=9 g_seq=4 stack=0 +GoStart dt=191 g=9 g_seq=5 +GoLabel dt=1 label_string=2 +GoStop dt=4205 reason_string=16 stack=45 +GoStart dt=189 g=9 g_seq=6 +STWBegin dt=1152 kind_string=23 stack=34 +GoUnblock dt=46 g=1 g_seq=29 stack=35 +HeapAlloc dt=17 heapalloc_value=8626416 +GoUnblock dt=11 g=3 g_seq=3 stack=36 +GCEnd dt=5 gc_seq=4 +HeapGoal dt=5 heapgoal_value=17671632 +ProcsChange dt=43 procs_value=8 stack=37 +STWEnd dt=28 +GoBlock dt=1941 reason_string=15 stack=26 +GoStart dt=12 g=3 g_seq=4 +GoBlock dt=4694 reason_string=14 stack=40 +GoUnblock dt=33 g=1 g_seq=31 stack=0 +GoStart dt=214 g=1 g_seq=32 +HeapAlloc dt=62 heapalloc_value=8646896 +HeapAlloc dt=32 heapalloc_value=8655088 +HeapAlloc dt=18 heapalloc_value=8663280 +HeapAlloc dt=18 heapalloc_value=8671472 +HeapAlloc dt=15 heapalloc_value=8679664 +HeapAlloc dt=18 heapalloc_value=8687856 +HeapAlloc dt=17 heapalloc_value=8696048 +HeapAlloc dt=17 heapalloc_value=8704240 +HeapAlloc dt=19 heapalloc_value=8712432 +HeapAlloc dt=24 heapalloc_value=8720624 +HeapAlloc dt=20 heapalloc_value=8728816 +HeapAlloc dt=31 heapalloc_value=8737008 +HeapAlloc dt=19 heapalloc_value=8745200 +HeapAlloc dt=14 heapalloc_value=8753392 +HeapAlloc dt=14 heapalloc_value=8761584 +HeapAlloc dt=15 heapalloc_value=8769776 +HeapAlloc dt=17 heapalloc_value=8777968 +HeapAlloc dt=16 heapalloc_value=8786160 +HeapAlloc dt=16 heapalloc_value=8794352 +HeapAlloc dt=13 heapalloc_value=8802544 +HeapAlloc dt=14 heapalloc_value=8810736 +HeapAlloc dt=12 heapalloc_value=8818928 +HeapAlloc dt=38 heapalloc_value=9040112 +HeapAlloc dt=3065 heapalloc_value=9048304 +HeapAlloc dt=21 heapalloc_value=9056496 +HeapAlloc dt=16 heapalloc_value=9064688 +HeapAlloc dt=22 heapalloc_value=9072880 +HeapAlloc dt=37 heapalloc_value=9081072 +HeapAlloc dt=28 heapalloc_value=9089264 +HeapAlloc dt=30 heapalloc_value=9097456 +HeapAlloc dt=22 heapalloc_value=9105648 +HeapAlloc dt=36 heapalloc_value=9113840 +HeapAlloc dt=30 heapalloc_value=9122032 +HeapAlloc dt=28 heapalloc_value=9130224 +HeapAlloc dt=26 heapalloc_value=9138416 +HeapAlloc dt=27 heapalloc_value=9146608 +HeapAlloc dt=31 heapalloc_value=9154800 +HeapAlloc dt=37 heapalloc_value=9162992 +HeapAlloc dt=24 heapalloc_value=9171184 +HeapAlloc dt=27 heapalloc_value=9179376 +HeapAlloc dt=26 heapalloc_value=9187568 +HeapAlloc dt=34 heapalloc_value=9195760 +HeapAlloc dt=30 heapalloc_value=9203952 +HeapAlloc dt=30 heapalloc_value=9212144 +HeapAlloc dt=30 heapalloc_value=9220336 +HeapAlloc dt=29 heapalloc_value=9228528 +HeapAlloc dt=28 heapalloc_value=9236720 +HeapAlloc dt=46 heapalloc_value=9244912 +HeapAlloc dt=118 heapalloc_value=9253104 +HeapAlloc dt=31 heapalloc_value=9261296 +HeapAlloc dt=39 heapalloc_value=9269488 +HeapAlloc dt=27 heapalloc_value=9277680 +HeapAlloc dt=32 heapalloc_value=9285872 +HeapAlloc dt=27 heapalloc_value=9294064 +HeapAlloc dt=32 heapalloc_value=9302256 +HeapAlloc dt=33 heapalloc_value=9310448 +HeapAlloc dt=39 heapalloc_value=9318640 +HeapAlloc dt=30 heapalloc_value=9326832 +HeapAlloc dt=33 heapalloc_value=9335024 +HeapAlloc dt=28 heapalloc_value=9343216 +HeapAlloc dt=27 heapalloc_value=9351408 +HeapAlloc dt=27 heapalloc_value=9359600 +HeapAlloc dt=26 heapalloc_value=9367792 +HeapAlloc dt=36 heapalloc_value=9375984 +HeapAlloc dt=20 heapalloc_value=9384176 +HeapAlloc dt=16 heapalloc_value=9392368 +HeapAlloc dt=17 heapalloc_value=9400560 +HeapAlloc dt=22 heapalloc_value=9408752 +HeapAlloc dt=7 heapalloc_value=9416944 +HeapAlloc dt=49 heapalloc_value=9425136 +HeapAlloc dt=7 heapalloc_value=9433328 +HeapAlloc dt=7 heapalloc_value=9441520 +HeapAlloc dt=74 heapalloc_value=9449712 +HeapAlloc dt=8 heapalloc_value=9457904 +HeapAlloc dt=6 heapalloc_value=9466096 +HeapAlloc dt=7 heapalloc_value=9474288 +HeapAlloc dt=6 heapalloc_value=9482480 +HeapAlloc dt=6 heapalloc_value=9490672 +HeapAlloc dt=7 heapalloc_value=9498864 +HeapAlloc dt=6 heapalloc_value=9507056 +HeapAlloc dt=6 heapalloc_value=9515248 +HeapAlloc dt=6 heapalloc_value=9523440 +HeapAlloc dt=6 heapalloc_value=9531632 +HeapAlloc dt=6 heapalloc_value=9539824 +HeapAlloc dt=7 heapalloc_value=9548016 +HeapAlloc dt=7 heapalloc_value=9556208 +HeapAlloc dt=5 heapalloc_value=9564400 +HeapAlloc dt=7 heapalloc_value=9572592 +HeapAlloc dt=6 heapalloc_value=9580784 +HeapAlloc dt=6 heapalloc_value=9588976 +HeapAlloc dt=6 heapalloc_value=9597168 +HeapAlloc dt=6 heapalloc_value=9605360 +HeapAlloc dt=6 heapalloc_value=9613552 +HeapAlloc dt=7 heapalloc_value=9621744 +HeapAlloc dt=6 heapalloc_value=9629936 +HeapAlloc dt=43 heapalloc_value=9638128 +HeapAlloc dt=7 heapalloc_value=9646320 +HeapAlloc dt=7 heapalloc_value=9654512 +HeapAlloc dt=6 heapalloc_value=9662704 +HeapAlloc dt=6 heapalloc_value=9670896 +HeapAlloc dt=6 heapalloc_value=9679088 +HeapAlloc dt=10 heapalloc_value=9687280 +HeapAlloc dt=7 heapalloc_value=9695472 +HeapAlloc dt=8 heapalloc_value=9703664 +HeapAlloc dt=726 heapalloc_value=9711856 +HeapAlloc dt=16 heapalloc_value=9720048 +HeapAlloc dt=7 heapalloc_value=9728240 +HeapAlloc dt=6 heapalloc_value=9736432 +HeapAlloc dt=6 heapalloc_value=9744624 +HeapAlloc dt=6 heapalloc_value=9752816 +HeapAlloc dt=7 heapalloc_value=9761008 +HeapAlloc dt=6 heapalloc_value=9769200 +HeapAlloc dt=63 heapalloc_value=9777392 +HeapAlloc dt=8 heapalloc_value=9785584 +HeapAlloc dt=6 heapalloc_value=9793776 +HeapAlloc dt=7 heapalloc_value=9801968 +HeapAlloc dt=7 heapalloc_value=9810160 +HeapAlloc dt=6 heapalloc_value=9818352 +HeapAlloc dt=6 heapalloc_value=9826544 +HeapAlloc dt=7 heapalloc_value=9834736 +HeapAlloc dt=43 heapalloc_value=9842928 +HeapAlloc dt=7 heapalloc_value=9851120 +HeapAlloc dt=7 heapalloc_value=9859312 +HeapAlloc dt=6 heapalloc_value=9867504 +HeapAlloc dt=6 heapalloc_value=9875696 +HeapAlloc dt=6 heapalloc_value=9883888 +GoBlock dt=13 reason_string=19 stack=21 +ProcStop dt=225 +ProcStart dt=17576 p=0 p_seq=37 +ProcStop dt=18 +ProcStart dt=2169 p=1 p_seq=22 +ProcStop dt=14 +ProcStart dt=16799 p=1 p_seq=23 +GoUnblock dt=15 g=1 g_seq=35 stack=0 +GoStart dt=168 g=1 g_seq=36 +HeapAlloc dt=44 heapalloc_value=10916080 +HeapAlloc dt=18 heapalloc_value=10924272 +HeapAlloc dt=13 heapalloc_value=10932464 +HeapAlloc dt=12 heapalloc_value=10940656 +HeapAlloc dt=11 heapalloc_value=10948848 +HeapAlloc dt=12 heapalloc_value=10957040 +HeapAlloc dt=9 heapalloc_value=10965232 +HeapAlloc dt=11 heapalloc_value=10973424 +HeapAlloc dt=9 heapalloc_value=10981616 +HeapAlloc dt=9 heapalloc_value=10989808 +HeapAlloc dt=6 heapalloc_value=10998000 +HeapAlloc dt=6 heapalloc_value=11006192 +HeapAlloc dt=7 heapalloc_value=11014384 +HeapAlloc dt=303 heapalloc_value=11022576 +HeapAlloc dt=15 heapalloc_value=11030768 +HeapAlloc dt=8 heapalloc_value=11038960 +HeapAlloc dt=6 heapalloc_value=11047152 +HeapAlloc dt=7 heapalloc_value=11055344 +HeapAlloc dt=6 heapalloc_value=11063536 +HeapAlloc dt=6 heapalloc_value=11071728 +HeapAlloc dt=6 heapalloc_value=11079920 +HeapAlloc dt=9 heapalloc_value=11088112 +HeapAlloc dt=6 heapalloc_value=11096304 +HeapAlloc dt=52 heapalloc_value=11104496 +HeapAlloc dt=8 heapalloc_value=11112688 +HeapAlloc dt=7 heapalloc_value=11120880 +HeapAlloc dt=6 heapalloc_value=11129072 +HeapAlloc dt=7 heapalloc_value=11137264 +HeapAlloc dt=396 heapalloc_value=11423984 +HeapAlloc dt=2772 heapalloc_value=11432176 +HeapAlloc dt=23 heapalloc_value=11440368 +HeapAlloc dt=13 heapalloc_value=11448560 +HeapAlloc dt=10 heapalloc_value=11456752 +HeapAlloc dt=9 heapalloc_value=11464944 +HeapAlloc dt=9 heapalloc_value=11473136 +HeapAlloc dt=9 heapalloc_value=11481328 +HeapAlloc dt=9 heapalloc_value=11489520 +HeapAlloc dt=9 heapalloc_value=11497712 +HeapAlloc dt=12 heapalloc_value=11505904 +HeapAlloc dt=9 heapalloc_value=11514096 +HeapAlloc dt=10 heapalloc_value=11522288 +HeapAlloc dt=9 heapalloc_value=11530480 +HeapAlloc dt=10 heapalloc_value=11538672 +HeapAlloc dt=10 heapalloc_value=11546864 +HeapAlloc dt=10 heapalloc_value=11555056 +HeapAlloc dt=9 heapalloc_value=11563248 +HeapAlloc dt=21 heapalloc_value=11571440 +HeapAlloc dt=9 heapalloc_value=11579632 +HeapAlloc dt=6 heapalloc_value=11587824 +HeapAlloc dt=7 heapalloc_value=11596016 +HeapAlloc dt=6 heapalloc_value=11604208 +HeapAlloc dt=6 heapalloc_value=11612400 +HeapAlloc dt=6 heapalloc_value=11620592 +HeapAlloc dt=103 heapalloc_value=11628784 +HeapAlloc dt=9 heapalloc_value=11636976 +HeapAlloc dt=7 heapalloc_value=11645168 +HeapAlloc dt=6 heapalloc_value=11653360 +HeapAlloc dt=7 heapalloc_value=11661552 +HeapAlloc dt=6 heapalloc_value=11669744 +HeapAlloc dt=6 heapalloc_value=11677936 +HeapAlloc dt=6 heapalloc_value=11686128 +HeapAlloc dt=6 heapalloc_value=11694320 +HeapAlloc dt=7 heapalloc_value=11702512 +HeapAlloc dt=6 heapalloc_value=11710704 +HeapAlloc dt=6 heapalloc_value=11718896 +HeapAlloc dt=6 heapalloc_value=11727088 +HeapAlloc dt=6 heapalloc_value=11735280 +HeapAlloc dt=6 heapalloc_value=11743472 +HeapAlloc dt=6 heapalloc_value=11751664 +HeapAlloc dt=6 heapalloc_value=11759856 +HeapAlloc dt=7 heapalloc_value=11768048 +HeapAlloc dt=5 heapalloc_value=11776240 +HeapAlloc dt=7 heapalloc_value=11784432 +HeapAlloc dt=6 heapalloc_value=11792624 +HeapAlloc dt=44 heapalloc_value=11800816 +HeapAlloc dt=82 heapalloc_value=11809008 +HeapAlloc dt=9 heapalloc_value=11817200 +HeapAlloc dt=6 heapalloc_value=11825392 +HeapAlloc dt=6 heapalloc_value=11833584 +HeapAlloc dt=7 heapalloc_value=11841776 +HeapAlloc dt=6 heapalloc_value=11849968 +HeapAlloc dt=6 heapalloc_value=11858160 +HeapAlloc dt=6 heapalloc_value=11866352 +HeapAlloc dt=7 heapalloc_value=11874544 +HeapAlloc dt=6 heapalloc_value=11882736 +HeapAlloc dt=6 heapalloc_value=11890928 +HeapAlloc dt=6 heapalloc_value=11899120 +HeapAlloc dt=6 heapalloc_value=11907312 +HeapAlloc dt=7 heapalloc_value=11915504 +HeapAlloc dt=6 heapalloc_value=11923696 +HeapAlloc dt=6 heapalloc_value=11931888 +HeapAlloc dt=6 heapalloc_value=11940080 +HeapAlloc dt=6 heapalloc_value=11948272 +HeapAlloc dt=6 heapalloc_value=11956464 +HeapAlloc dt=6 heapalloc_value=11964656 +HeapAlloc dt=6 heapalloc_value=11972848 +HeapAlloc dt=7 heapalloc_value=11981040 +HeapAlloc dt=6 heapalloc_value=11989232 +HeapAlloc dt=6 heapalloc_value=11997424 +HeapAlloc dt=6 heapalloc_value=12005616 +HeapAlloc dt=7 heapalloc_value=12013808 +HeapAlloc dt=6 heapalloc_value=12022000 +HeapAlloc dt=6 heapalloc_value=12030192 +HeapAlloc dt=6 heapalloc_value=12038384 +HeapAlloc dt=6 heapalloc_value=12046576 +HeapAlloc dt=6 heapalloc_value=12054768 +HeapAlloc dt=6 heapalloc_value=12062960 +HeapAlloc dt=67 heapalloc_value=12071152 +HeapAlloc dt=8 heapalloc_value=12079344 +HeapAlloc dt=7 heapalloc_value=12087536 +HeapAlloc dt=6 heapalloc_value=12095728 +HeapAlloc dt=6 heapalloc_value=12103920 +HeapAlloc dt=6 heapalloc_value=12112112 +HeapAlloc dt=6 heapalloc_value=12120304 +HeapAlloc dt=6 heapalloc_value=12128496 +HeapAlloc dt=6 heapalloc_value=12136688 +HeapAlloc dt=6 heapalloc_value=12144880 +HeapAlloc dt=59 heapalloc_value=12153072 +HeapAlloc dt=8 heapalloc_value=12161264 +HeapAlloc dt=6 heapalloc_value=12169456 +HeapAlloc dt=6 heapalloc_value=12177648 +HeapAlloc dt=6 heapalloc_value=12185840 +HeapAlloc dt=6 heapalloc_value=12194032 +HeapAlloc dt=7 heapalloc_value=12202224 +HeapAlloc dt=6 heapalloc_value=12210416 +HeapAlloc dt=6 heapalloc_value=12218608 +GoBlock dt=12 reason_string=19 stack=21 +ProcStop dt=223 +ProcStart dt=12071 p=1 p_seq=25 +GoUnblock dt=11 g=1 g_seq=37 stack=0 +GoStart dt=161 g=1 g_seq=38 +HeapAlloc dt=75 heapalloc_value=12226800 +HeapAlloc dt=11 heapalloc_value=12234992 +HeapAlloc dt=6 heapalloc_value=12243184 +HeapAlloc dt=6 heapalloc_value=12251376 +HeapAlloc dt=7 heapalloc_value=12259568 +HeapAlloc dt=6 heapalloc_value=12267760 +HeapAlloc dt=6 heapalloc_value=12275952 +HeapAlloc dt=6 heapalloc_value=12284144 +HeapAlloc dt=6 heapalloc_value=12292336 +HeapAlloc dt=7 heapalloc_value=12300528 +HeapAlloc dt=6 heapalloc_value=12308720 +HeapAlloc dt=6 heapalloc_value=12316912 +HeapAlloc dt=7 heapalloc_value=12325104 +HeapAlloc dt=87 heapalloc_value=12333296 +HeapAlloc dt=25 heapalloc_value=12341488 +HeapAlloc dt=7 heapalloc_value=12349680 +HeapAlloc dt=6 heapalloc_value=12357872 +HeapAlloc dt=7 heapalloc_value=12366064 +HeapAlloc dt=10 heapalloc_value=12374256 +HeapAlloc dt=7 heapalloc_value=12382448 +HeapAlloc dt=9 heapalloc_value=12390640 +HeapAlloc dt=6 heapalloc_value=12398832 +HeapAlloc dt=6 heapalloc_value=12407024 +HeapAlloc dt=7 heapalloc_value=12415216 +HeapAlloc dt=6 heapalloc_value=12423408 +HeapAlloc dt=44 heapalloc_value=12431600 +HeapAlloc dt=7 heapalloc_value=12439792 +HeapAlloc dt=7 heapalloc_value=12447984 +HeapAlloc dt=6 heapalloc_value=12456176 +HeapAlloc dt=6 heapalloc_value=12464368 +HeapAlloc dt=6 heapalloc_value=12472560 +HeapAlloc dt=6 heapalloc_value=12480752 +HeapAlloc dt=7 heapalloc_value=12488944 +HeapAlloc dt=6 heapalloc_value=12497136 +HeapAlloc dt=6 heapalloc_value=12505328 +HeapAlloc dt=6 heapalloc_value=12513520 +HeapAlloc dt=6 heapalloc_value=12521712 +HeapAlloc dt=7 heapalloc_value=12529904 +HeapAlloc dt=6 heapalloc_value=12538096 +HeapAlloc dt=6 heapalloc_value=12546288 +HeapAlloc dt=6 heapalloc_value=12554480 +HeapAlloc dt=6 heapalloc_value=12562672 +HeapAlloc dt=6 heapalloc_value=12570864 +HeapAlloc dt=11 heapalloc_value=12579056 +HeapAlloc dt=6 heapalloc_value=12587248 +HeapAlloc dt=455 heapalloc_value=12595440 +HeapAlloc dt=12 heapalloc_value=12603632 +HeapAlloc dt=7 heapalloc_value=12611824 +HeapAlloc dt=6 heapalloc_value=12620016 +HeapAlloc dt=7 heapalloc_value=12628208 +HeapAlloc dt=6 heapalloc_value=12636400 +HeapAlloc dt=6 heapalloc_value=12644592 +HeapAlloc dt=6 heapalloc_value=12652784 +HeapAlloc dt=7 heapalloc_value=12660976 +HeapAlloc dt=6 heapalloc_value=12669168 +HeapAlloc dt=97 heapalloc_value=12677360 +HeapAlloc dt=8 heapalloc_value=12685552 +HeapAlloc dt=6 heapalloc_value=12693744 +HeapAlloc dt=6 heapalloc_value=12701936 +HeapAlloc dt=6 heapalloc_value=12710128 +HeapAlloc dt=6 heapalloc_value=12718320 +HeapAlloc dt=6 heapalloc_value=12726512 +HeapAlloc dt=7 heapalloc_value=12734704 +HeapAlloc dt=6 heapalloc_value=12742896 +HeapAlloc dt=6 heapalloc_value=12751088 +HeapAlloc dt=6 heapalloc_value=12759280 +HeapAlloc dt=7 heapalloc_value=12767472 +HeapAlloc dt=7 heapalloc_value=12775664 +HeapAlloc dt=6 heapalloc_value=12783856 +HeapAlloc dt=6 heapalloc_value=12792048 +HeapAlloc dt=6 heapalloc_value=12800240 +HeapAlloc dt=7 heapalloc_value=12808432 +HeapAlloc dt=6 heapalloc_value=12816624 +HeapAlloc dt=6 heapalloc_value=12824816 +HeapAlloc dt=6 heapalloc_value=12833008 +HeapAlloc dt=6 heapalloc_value=12841200 +HeapAlloc dt=42 heapalloc_value=12849392 +HeapAlloc dt=79 heapalloc_value=12857584 +HeapAlloc dt=8 heapalloc_value=12865776 +HeapAlloc dt=6 heapalloc_value=12873968 +HeapAlloc dt=6 heapalloc_value=12882160 +HeapAlloc dt=7 heapalloc_value=12890352 +HeapAlloc dt=6 heapalloc_value=12898544 +HeapAlloc dt=6 heapalloc_value=12906736 +HeapAlloc dt=6 heapalloc_value=12914928 +HeapAlloc dt=7 heapalloc_value=12923120 +HeapAlloc dt=6 heapalloc_value=12931312 +HeapAlloc dt=6 heapalloc_value=12939504 +HeapAlloc dt=6 heapalloc_value=12947696 +HeapAlloc dt=6 heapalloc_value=12955888 +HeapAlloc dt=6 heapalloc_value=12964080 +HeapAlloc dt=6 heapalloc_value=12972272 +HeapAlloc dt=6 heapalloc_value=12980464 +HeapAlloc dt=7 heapalloc_value=12988656 +HeapAlloc dt=6 heapalloc_value=12996848 +HeapAlloc dt=6 heapalloc_value=13005040 +HeapAlloc dt=6 heapalloc_value=13013232 +HeapAlloc dt=7 heapalloc_value=13021424 +HeapAlloc dt=6 heapalloc_value=13029616 +HeapAlloc dt=6 heapalloc_value=13037808 +HeapAlloc dt=6 heapalloc_value=13046000 +HeapAlloc dt=6 heapalloc_value=13054192 +HeapAlloc dt=7 heapalloc_value=13062384 +HeapAlloc dt=6 heapalloc_value=13070576 +HeapAlloc dt=6 heapalloc_value=13078768 +HeapAlloc dt=6 heapalloc_value=13086960 +HeapAlloc dt=6 heapalloc_value=13095152 +HeapAlloc dt=7 heapalloc_value=13103344 +HeapAlloc dt=6 heapalloc_value=13111536 +HeapAlloc dt=67 heapalloc_value=13119728 +HeapAlloc dt=8 heapalloc_value=13127920 +HeapAlloc dt=6 heapalloc_value=13136112 +HeapAlloc dt=6 heapalloc_value=13144304 +HeapAlloc dt=7 heapalloc_value=13152496 +HeapAlloc dt=6 heapalloc_value=13160688 +HeapAlloc dt=6 heapalloc_value=13168880 +HeapAlloc dt=6 heapalloc_value=13177072 +HeapAlloc dt=6 heapalloc_value=13185264 +HeapAlloc dt=6 heapalloc_value=13193456 +HeapAlloc dt=105 heapalloc_value=13201648 +HeapAlloc dt=8 heapalloc_value=13209840 +HeapAlloc dt=6 heapalloc_value=13218032 +HeapAlloc dt=6 heapalloc_value=13226224 +HeapAlloc dt=6 heapalloc_value=13234416 +HeapAlloc dt=6 heapalloc_value=13242608 +GoBlock dt=10 reason_string=19 stack=21 +ProcStop dt=13 +ProcStart dt=3484 p=2 p_seq=18 +ProcStop dt=18 +ProcStart dt=5821 p=1 p_seq=27 +ProcStop dt=12 +ProcStart dt=16793 p=1 p_seq=28 +GoUnblock dt=16 g=1 g_seq=41 stack=0 +GoStart dt=193 g=1 g_seq=42 +HeapAlloc dt=36 heapalloc_value=14643440 +HeapAlloc dt=29 heapalloc_value=14651632 +HeapAlloc dt=16 heapalloc_value=14659824 +HeapAlloc dt=20 heapalloc_value=14668016 +HeapAlloc dt=13 heapalloc_value=14676208 +HeapAlloc dt=84 heapalloc_value=14684400 +HeapAlloc dt=17 heapalloc_value=14692592 +HeapAlloc dt=12 heapalloc_value=14700784 +HeapAlloc dt=12 heapalloc_value=14708976 +HeapAlloc dt=12 heapalloc_value=14717168 +HeapAlloc dt=12 heapalloc_value=14725360 +HeapAlloc dt=22 heapalloc_value=14733552 +HeapAlloc dt=12 heapalloc_value=14741744 +HeapAlloc dt=13 heapalloc_value=14749936 +HeapAlloc dt=12 heapalloc_value=14758128 +HeapAlloc dt=11 heapalloc_value=14766320 +HeapAlloc dt=13 heapalloc_value=14774512 +HeapAlloc dt=12 heapalloc_value=14782704 +HeapAlloc dt=12 heapalloc_value=14790896 +HeapAlloc dt=61 heapalloc_value=14799088 +HeapAlloc dt=13 heapalloc_value=14807280 +HeapAlloc dt=7 heapalloc_value=14815472 +HeapAlloc dt=11 heapalloc_value=14823664 +HeapAlloc dt=9 heapalloc_value=14831856 +HeapAlloc dt=11 heapalloc_value=14840048 +HeapAlloc dt=6 heapalloc_value=14848240 +HeapAlloc dt=7 heapalloc_value=14856432 +HeapAlloc dt=9 heapalloc_value=14864624 +HeapAlloc dt=6 heapalloc_value=14872816 +HeapAlloc dt=6 heapalloc_value=14881008 +HeapAlloc dt=46 heapalloc_value=14889200 +HeapAlloc dt=8 heapalloc_value=14897392 +HeapAlloc dt=6 heapalloc_value=14905584 +HeapAlloc dt=7 heapalloc_value=14913776 +HeapAlloc dt=6 heapalloc_value=14921968 +HeapAlloc dt=7 heapalloc_value=14930160 +HeapAlloc dt=7 heapalloc_value=14938352 +HeapAlloc dt=6 heapalloc_value=14946544 +HeapAlloc dt=155 heapalloc_value=14954736 +HeapAlloc dt=9 heapalloc_value=14962928 +HeapAlloc dt=6 heapalloc_value=14971120 +HeapAlloc dt=7 heapalloc_value=14979312 +HeapAlloc dt=6 heapalloc_value=14987504 +HeapAlloc dt=6 heapalloc_value=14995696 +HeapAlloc dt=6 heapalloc_value=15003888 +HeapAlloc dt=6 heapalloc_value=15012080 +HeapAlloc dt=8 heapalloc_value=15020272 +HeapAlloc dt=6 heapalloc_value=15028464 +HeapAlloc dt=7 heapalloc_value=15036656 +HeapAlloc dt=6 heapalloc_value=15044848 +HeapAlloc dt=6 heapalloc_value=15053040 +HeapAlloc dt=6 heapalloc_value=15061232 +HeapAlloc dt=6 heapalloc_value=15069424 +HeapAlloc dt=6 heapalloc_value=15077616 +HeapAlloc dt=8 heapalloc_value=15085808 +HeapAlloc dt=6 heapalloc_value=15094000 +HeapAlloc dt=7 heapalloc_value=15102192 +HeapAlloc dt=6 heapalloc_value=15110384 +HeapAlloc dt=6 heapalloc_value=15118576 +HeapAlloc dt=6 heapalloc_value=15126768 +HeapAlloc dt=68 heapalloc_value=15134960 +HeapAlloc dt=8 heapalloc_value=15143152 +HeapAlloc dt=6 heapalloc_value=15151344 +HeapAlloc dt=6 heapalloc_value=15159536 +HeapAlloc dt=6 heapalloc_value=15167728 +HeapAlloc dt=6 heapalloc_value=15175920 +HeapAlloc dt=6 heapalloc_value=15184112 +HeapAlloc dt=6 heapalloc_value=15192304 +HeapAlloc dt=6 heapalloc_value=15200496 +HeapAlloc dt=6 heapalloc_value=15208688 +HeapAlloc dt=68 heapalloc_value=15216880 +HeapAlloc dt=8 heapalloc_value=15225072 +HeapAlloc dt=7 heapalloc_value=15233264 +HeapAlloc dt=6 heapalloc_value=15241456 +HeapAlloc dt=6 heapalloc_value=15249648 +HeapAlloc dt=7 heapalloc_value=15257840 +HeapAlloc dt=6 heapalloc_value=15266032 +HeapAlloc dt=6 heapalloc_value=15274224 +HeapAlloc dt=8 heapalloc_value=15282416 +HeapAlloc dt=6 heapalloc_value=15290608 +HeapAlloc dt=7 heapalloc_value=15298800 +HeapAlloc dt=43 heapalloc_value=15306992 +HeapAlloc dt=7 heapalloc_value=15315184 +HeapAlloc dt=6 heapalloc_value=15323376 +HeapAlloc dt=7 heapalloc_value=15331568 +HeapAlloc dt=6 heapalloc_value=15339760 +HeapAlloc dt=8 heapalloc_value=15347952 +HeapAlloc dt=6 heapalloc_value=15356144 +HeapAlloc dt=6 heapalloc_value=15364336 +HeapAlloc dt=7 heapalloc_value=15372528 +HeapAlloc dt=6 heapalloc_value=15380720 +HeapAlloc dt=6 heapalloc_value=15388912 +HeapAlloc dt=6 heapalloc_value=15397104 +HeapAlloc dt=7 heapalloc_value=15405296 +HeapAlloc dt=8 heapalloc_value=15413488 +HeapAlloc dt=6 heapalloc_value=15421680 +HeapAlloc dt=6 heapalloc_value=15429872 +HeapAlloc dt=6 heapalloc_value=15438064 +HeapAlloc dt=7 heapalloc_value=15446256 +HeapAlloc dt=7 heapalloc_value=15454448 +HeapAlloc dt=6 heapalloc_value=15462640 +HeapAlloc dt=6 heapalloc_value=15470832 +HeapAlloc dt=470 heapalloc_value=15479024 +HeapAlloc dt=14 heapalloc_value=15487216 +HeapAlloc dt=6 heapalloc_value=15495408 +HeapAlloc dt=7 heapalloc_value=15503600 +HeapAlloc dt=6 heapalloc_value=15511792 +HeapAlloc dt=7 heapalloc_value=15519984 +HeapAlloc dt=6 heapalloc_value=15528176 +HeapAlloc dt=6 heapalloc_value=15536368 +HeapAlloc dt=6 heapalloc_value=15544560 +HeapAlloc dt=5 heapalloc_value=15552752 +HeapAlloc dt=6 heapalloc_value=15560944 +HeapAlloc dt=6 heapalloc_value=15569136 +HeapAlloc dt=6 heapalloc_value=15577328 +HeapAlloc dt=6 heapalloc_value=15585520 +HeapAlloc dt=6 heapalloc_value=15593712 +HeapAlloc dt=6 heapalloc_value=15601904 +HeapAlloc dt=6 heapalloc_value=15610096 +HeapAlloc dt=6 heapalloc_value=15618288 +HeapAlloc dt=6 heapalloc_value=15626480 +HeapAlloc dt=6 heapalloc_value=15634672 +HeapAlloc dt=6 heapalloc_value=15642864 +HeapAlloc dt=6 heapalloc_value=15651056 +HeapAlloc dt=77 heapalloc_value=15659248 +GoBlock dt=13 reason_string=19 stack=21 +ProcStop dt=214 +ProcStart dt=17833 p=2 p_seq=20 +ProcStop dt=18 +ProcStart dt=9948 p=4 p_seq=4 +ProcStop dt=23 +ProcStart dt=5868 p=3 p_seq=6 +ProcStop dt=25 +ProcStart dt=94440 p=3 p_seq=7 +ProcStop dt=17 +ProcStart dt=7801 p=3 p_seq=8 +GoStart dt=172 g=13 g_seq=1 +GoStop dt=306385 reason_string=16 stack=52 +GoStart dt=19 g=13 g_seq=2 +GoStop dt=316412 reason_string=16 stack=52 +GoStart dt=14 g=13 g_seq=3 +GoDestroy dt=158437 +ProcStop dt=31 +EventBatch gen=1 m=2852342 time=420901452973 size=3683 +ProcStart dt=335 p=2 p_seq=1 +GoStart dt=164 g=21 g_seq=1 +HeapAlloc dt=242 heapalloc_value=1654784 +GoSyscallBegin dt=3053 p_seq=2 stack=17 +GoSyscallEnd dt=264 +GoBlock dt=22 reason_string=15 stack=18 +ProcStop dt=21 +ProcStart dt=370120 p=0 p_seq=11 +ProcStop dt=21 +ProcStart dt=7624 p=1 p_seq=5 +ProcStop dt=18 +ProcStart dt=386 p=2 p_seq=4 +GoStart dt=180 g=24 g_seq=1 +GoBlock dt=122 reason_string=15 stack=26 +ProcStop dt=14 +ProcStart dt=378 p=2 p_seq=7 +ProcStop dt=16 +ProcStart dt=1400 p=2 p_seq=8 +GoStart dt=127 g=9 g_seq=1 +GoBlock dt=106 reason_string=15 stack=26 +ProcStop dt=5 +ProcStart dt=482 p=1 p_seq=14 +ProcStop dt=11 +ProcStart dt=2026 p=3 p_seq=2 +HeapAlloc dt=470 heapalloc_value=4079616 +HeapAlloc dt=451 heapalloc_value=4128768 +HeapAlloc dt=21 heapalloc_value=4136960 +GoStart dt=1190 g=4 g_seq=2 +GoBlock dt=29 reason_string=15 stack=32 +ProcStop dt=34 +ProcStart dt=77810 p=3 p_seq=3 +ProcStop dt=32 +ProcStart dt=600 p=3 p_seq=4 +GoUnblock dt=14 g=25 g_seq=6 stack=0 +GoStart dt=184 g=25 g_seq=7 +GoLabel dt=3 label_string=2 +GoBlock dt=145 reason_string=15 stack=26 +ProcStop dt=27 +ProcStart dt=122643 p=3 p_seq=5 +GoStart dt=236 g=4 g_seq=8 +GoBlock dt=24 reason_string=15 stack=32 +GoUnblock dt=25 g=8 g_seq=4 stack=0 +GoStart dt=9 g=8 g_seq=5 +GoLabel dt=1 label_string=4 +GoBlock dt=1341 reason_string=15 stack=26 +GoUnblock dt=4399 g=1 g_seq=45 stack=0 +GoStart dt=12 g=1 g_seq=46 +HeapAlloc dt=416 heapalloc_value=16705232 +HeapAlloc dt=47 heapalloc_value=16721328 +HeapAlloc dt=35 heapalloc_value=16729520 +HeapAlloc dt=24 heapalloc_value=16737712 +HeapAlloc dt=26 heapalloc_value=16745904 +HeapAlloc dt=24 heapalloc_value=16754096 +HeapAlloc dt=13 heapalloc_value=16762288 +HeapAlloc dt=15 heapalloc_value=16770480 +HeapAlloc dt=14 heapalloc_value=16778672 +HeapAlloc dt=14 heapalloc_value=16786864 +HeapAlloc dt=14 heapalloc_value=16795056 +HeapAlloc dt=13 heapalloc_value=16803248 +HeapAlloc dt=12 heapalloc_value=16811440 +HeapAlloc dt=14 heapalloc_value=16819632 +HeapAlloc dt=13 heapalloc_value=16827824 +HeapAlloc dt=13 heapalloc_value=16836016 +HeapAlloc dt=14 heapalloc_value=16844208 +HeapAlloc dt=14 heapalloc_value=16852400 +HeapAlloc dt=13 heapalloc_value=16860592 +HeapAlloc dt=13 heapalloc_value=16868784 +HeapAlloc dt=12 heapalloc_value=16876976 +HeapAlloc dt=19 heapalloc_value=16885168 +HeapAlloc dt=15 heapalloc_value=16893360 +HeapAlloc dt=14 heapalloc_value=16901552 +HeapAlloc dt=14 heapalloc_value=16909744 +HeapAlloc dt=13 heapalloc_value=16917936 +HeapAlloc dt=13 heapalloc_value=16926128 +HeapAlloc dt=12 heapalloc_value=16934320 +HeapAlloc dt=14 heapalloc_value=16942512 +HeapAlloc dt=14 heapalloc_value=16950704 +HeapAlloc dt=12 heapalloc_value=16958896 +HeapAlloc dt=13 heapalloc_value=16967088 +HeapAlloc dt=479 heapalloc_value=16975280 +HeapAlloc dt=207 heapalloc_value=16983472 +HeapAlloc dt=15 heapalloc_value=16991664 +HeapAlloc dt=111 heapalloc_value=16999856 +HeapAlloc dt=14 heapalloc_value=17008048 +HeapAlloc dt=13 heapalloc_value=17016240 +HeapAlloc dt=13 heapalloc_value=17024432 +HeapAlloc dt=13 heapalloc_value=17032624 +HeapAlloc dt=12 heapalloc_value=17040816 +HeapAlloc dt=14 heapalloc_value=17049008 +HeapAlloc dt=13 heapalloc_value=17057200 +HeapAlloc dt=15 heapalloc_value=17065392 +HeapAlloc dt=14 heapalloc_value=17073584 +HeapAlloc dt=15 heapalloc_value=17081776 +HeapAlloc dt=14 heapalloc_value=17089968 +HeapAlloc dt=14 heapalloc_value=17098160 +HeapAlloc dt=14 heapalloc_value=17106352 +HeapAlloc dt=15 heapalloc_value=17114544 +HeapAlloc dt=14 heapalloc_value=17122736 +HeapAlloc dt=19 heapalloc_value=17130928 +HeapAlloc dt=20 heapalloc_value=17139120 +HeapAlloc dt=19 heapalloc_value=17147312 +HeapAlloc dt=14 heapalloc_value=17155504 +HeapAlloc dt=14 heapalloc_value=17163696 +HeapAlloc dt=15 heapalloc_value=17171888 +HeapAlloc dt=14 heapalloc_value=17180080 +HeapAlloc dt=14 heapalloc_value=17188272 +HeapAlloc dt=16 heapalloc_value=17196464 +HeapAlloc dt=147 heapalloc_value=17204656 +HeapAlloc dt=17 heapalloc_value=17212848 +HeapAlloc dt=14 heapalloc_value=17221040 +HeapAlloc dt=15 heapalloc_value=17229232 +HeapAlloc dt=133 heapalloc_value=17237424 +HeapAlloc dt=66 heapalloc_value=17245616 +HeapAlloc dt=17 heapalloc_value=17253808 +HeapAlloc dt=14 heapalloc_value=17262000 +HeapAlloc dt=14 heapalloc_value=17270192 +HeapAlloc dt=15 heapalloc_value=17278384 +HeapAlloc dt=14 heapalloc_value=17286576 +HeapAlloc dt=14 heapalloc_value=17294768 +HeapAlloc dt=17 heapalloc_value=17302960 +HeapAlloc dt=14 heapalloc_value=17311152 +GoStop dt=24 reason_string=16 stack=46 +GoStart dt=859 g=1 g_seq=47 +HeapAlloc dt=19 heapalloc_value=17319344 +HeapAlloc dt=16 heapalloc_value=17327536 +HeapAlloc dt=14 heapalloc_value=17335728 +HeapAlloc dt=14 heapalloc_value=17343920 +HeapAlloc dt=15 heapalloc_value=17352112 +HeapAlloc dt=14 heapalloc_value=17360304 +HeapAlloc dt=14 heapalloc_value=17368496 +HeapAlloc dt=14 heapalloc_value=17376688 +HeapAlloc dt=18 heapalloc_value=17384880 +HeapAlloc dt=17 heapalloc_value=17393072 +HeapAlloc dt=14 heapalloc_value=17401264 +HeapAlloc dt=18 heapalloc_value=17409456 +HeapAlloc dt=14 heapalloc_value=17417648 +HeapAlloc dt=14 heapalloc_value=17425840 +HeapAlloc dt=15 heapalloc_value=17434032 +HeapAlloc dt=12 heapalloc_value=17442224 +HeapAlloc dt=18 heapalloc_value=17450416 +HeapAlloc dt=69 heapalloc_value=17458608 +HeapAlloc dt=15 heapalloc_value=17466800 +HeapAlloc dt=14 heapalloc_value=17474992 +HeapAlloc dt=12 heapalloc_value=17483184 +HeapAlloc dt=14 heapalloc_value=17491376 +HeapAlloc dt=405 heapalloc_value=17499568 +GoStop dt=11 reason_string=16 stack=31 +ProcStop dt=10 +ProcStart dt=1071 p=0 p_seq=41 +GoStart dt=509 g=1 g_seq=48 +HeapAlloc dt=31 heapalloc_value=16800656 +GCSweepBegin dt=40 stack=38 +GCSweepEnd dt=407 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=25 heapalloc_value=16808848 +GCSweepBegin dt=25 stack=38 +GCSweepEnd dt=1029 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=20 heapalloc_value=16817040 +GCSweepBegin dt=33 stack=38 +GCSweepEnd dt=1076 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=13 heapalloc_value=16825232 +GCSweepBegin dt=30 stack=38 +GCSweepEnd dt=1298 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=17 heapalloc_value=16833424 +GCSweepBegin dt=29 stack=38 +GCSweepEnd dt=1140 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=11 heapalloc_value=16841616 +GCSweepBegin dt=32 stack=38 +GCSweepEnd dt=1161 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=14 heapalloc_value=16849808 +GCSweepBegin dt=31 stack=38 +GCSweepEnd dt=763 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=12 heapalloc_value=16858000 +GCSweepBegin dt=29 stack=38 +GCSweepEnd dt=1113 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=9 heapalloc_value=16866192 +GCSweepBegin dt=25 stack=38 +GCSweepEnd dt=1068 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=21 heapalloc_value=16874384 +GCSweepBegin dt=36 stack=38 +GCSweepEnd dt=478 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=7 heapalloc_value=16882576 +GCSweepBegin dt=16 stack=38 +GCSweepEnd dt=32 swept_value=90112 reclaimed_value=0 +HeapAlloc dt=11 heapalloc_value=16890768 +HeapAlloc dt=31 heapalloc_value=16898960 +HeapAlloc dt=24 heapalloc_value=16907152 +HeapAlloc dt=17 heapalloc_value=16915344 +HeapAlloc dt=17 heapalloc_value=16923536 +HeapAlloc dt=23 heapalloc_value=16931728 +HeapAlloc dt=18 heapalloc_value=16939920 +HeapAlloc dt=22 heapalloc_value=16948112 +HeapAlloc dt=17 heapalloc_value=16956304 +HeapAlloc dt=16 heapalloc_value=16964496 +HeapAlloc dt=16 heapalloc_value=16972688 +HeapAlloc dt=106 heapalloc_value=16980880 +HeapAlloc dt=19 heapalloc_value=16989072 +HeapAlloc dt=16 heapalloc_value=16997264 +HeapAlloc dt=13 heapalloc_value=17005456 +HeapAlloc dt=13 heapalloc_value=17013648 +HeapAlloc dt=96 heapalloc_value=17021840 +HeapAlloc dt=16 heapalloc_value=17030032 +GoBlock dt=18 reason_string=19 stack=21 +ProcStop dt=315 +ProcStart dt=17450 p=2 p_seq=23 +ProcStop dt=14 +ProcStart dt=6669 p=0 p_seq=44 +ProcStop dt=11 +ProcStart dt=16752 p=0 p_seq=45 +GoUnblock dt=14 g=1 g_seq=51 stack=0 +GoStart dt=146 g=1 g_seq=52 +HeapAlloc dt=31 heapalloc_value=18529168 +HeapAlloc dt=21 heapalloc_value=18537360 +HeapAlloc dt=13 heapalloc_value=18545552 +HeapAlloc dt=77 heapalloc_value=18553744 +HeapAlloc dt=21 heapalloc_value=18561936 +HeapAlloc dt=15 heapalloc_value=18570128 +HeapAlloc dt=12 heapalloc_value=18578320 +HeapAlloc dt=12 heapalloc_value=18586512 +HeapAlloc dt=12 heapalloc_value=18594704 +HeapAlloc dt=16 heapalloc_value=18602896 +HeapAlloc dt=14 heapalloc_value=18611088 +HeapAlloc dt=13 heapalloc_value=18619280 +HeapAlloc dt=17 heapalloc_value=18627472 +HeapAlloc dt=13 heapalloc_value=18635664 +HeapAlloc dt=14 heapalloc_value=18643856 +HeapAlloc dt=12 heapalloc_value=18652048 +HeapAlloc dt=12 heapalloc_value=18660240 +HeapAlloc dt=12 heapalloc_value=18668432 +HeapAlloc dt=12 heapalloc_value=18676624 +HeapAlloc dt=12 heapalloc_value=18684816 +HeapAlloc dt=93 heapalloc_value=18693008 +HeapAlloc dt=17 heapalloc_value=18701200 +HeapAlloc dt=12 heapalloc_value=18709392 +HeapAlloc dt=13 heapalloc_value=18717584 +HeapAlloc dt=15 heapalloc_value=18725776 +HeapAlloc dt=12 heapalloc_value=18733968 +HeapAlloc dt=13 heapalloc_value=18742160 +HeapAlloc dt=14 heapalloc_value=18750352 +HeapAlloc dt=12 heapalloc_value=18758544 +HeapAlloc dt=54 heapalloc_value=18766736 +HeapAlloc dt=13 heapalloc_value=18774928 +HeapAlloc dt=13 heapalloc_value=18783120 +HeapAlloc dt=12 heapalloc_value=18791312 +HeapAlloc dt=13 heapalloc_value=18799504 +HeapAlloc dt=12 heapalloc_value=18807696 +HeapAlloc dt=13 heapalloc_value=18815888 +HeapAlloc dt=12 heapalloc_value=18824080 +HeapAlloc dt=13 heapalloc_value=18832272 +HeapAlloc dt=12 heapalloc_value=18840464 +HeapAlloc dt=13 heapalloc_value=18848656 +HeapAlloc dt=12 heapalloc_value=18856848 +HeapAlloc dt=13 heapalloc_value=18865040 +HeapAlloc dt=13 heapalloc_value=18873232 +HeapAlloc dt=12 heapalloc_value=18881424 +HeapAlloc dt=14 heapalloc_value=18889616 +HeapAlloc dt=13 heapalloc_value=18897808 +HeapAlloc dt=12 heapalloc_value=18906000 +HeapAlloc dt=13 heapalloc_value=18914192 +HeapAlloc dt=13 heapalloc_value=18922384 +HeapAlloc dt=86 heapalloc_value=18930576 +HeapAlloc dt=15 heapalloc_value=18938768 +HeapAlloc dt=13 heapalloc_value=18946960 +HeapAlloc dt=26 heapalloc_value=18955152 +HeapAlloc dt=19 heapalloc_value=18963344 +HeapAlloc dt=12 heapalloc_value=18971536 +HeapAlloc dt=14 heapalloc_value=18979728 +HeapAlloc dt=14 heapalloc_value=18987920 +HeapAlloc dt=13 heapalloc_value=18996112 +HeapAlloc dt=12 heapalloc_value=19004304 +HeapAlloc dt=64 heapalloc_value=19012496 +HeapAlloc dt=15 heapalloc_value=19020688 +HeapAlloc dt=14 heapalloc_value=19028880 +HeapAlloc dt=14 heapalloc_value=19037072 +HeapAlloc dt=16 heapalloc_value=19045264 +HeapAlloc dt=77 heapalloc_value=19053456 +HeapAlloc dt=16 heapalloc_value=19061648 +HeapAlloc dt=13 heapalloc_value=19069840 +HeapAlloc dt=16 heapalloc_value=19078032 +HeapAlloc dt=12 heapalloc_value=19086224 +HeapAlloc dt=12 heapalloc_value=19094416 +HeapAlloc dt=13 heapalloc_value=19102608 +HeapAlloc dt=14 heapalloc_value=19110800 +HeapAlloc dt=15 heapalloc_value=19118992 +HeapAlloc dt=14 heapalloc_value=19127184 +HeapAlloc dt=13 heapalloc_value=19135376 +HeapAlloc dt=13 heapalloc_value=19143568 +HeapAlloc dt=15 heapalloc_value=19151760 +HeapAlloc dt=18 heapalloc_value=19159952 +HeapAlloc dt=16 heapalloc_value=19168144 +HeapAlloc dt=15 heapalloc_value=19176336 +HeapAlloc dt=113 heapalloc_value=19184528 +HeapAlloc dt=17 heapalloc_value=19192720 +HeapAlloc dt=13 heapalloc_value=19200912 +HeapAlloc dt=18 heapalloc_value=19209104 +HeapAlloc dt=15 heapalloc_value=19217296 +HeapAlloc dt=18 heapalloc_value=19225488 +HeapAlloc dt=15 heapalloc_value=19233680 +HeapAlloc dt=16 heapalloc_value=19241872 +HeapAlloc dt=16 heapalloc_value=19250064 +HeapAlloc dt=15 heapalloc_value=19258256 +HeapAlloc dt=14 heapalloc_value=19266448 +HeapAlloc dt=15 heapalloc_value=19274640 +HeapAlloc dt=13 heapalloc_value=19282832 +HeapAlloc dt=20 heapalloc_value=19291024 +HeapAlloc dt=15 heapalloc_value=19299216 +HeapAlloc dt=16 heapalloc_value=19307408 +HeapAlloc dt=26 heapalloc_value=19315600 +HeapAlloc dt=9 heapalloc_value=19323792 +HeapAlloc dt=6 heapalloc_value=19331984 +HeapAlloc dt=7 heapalloc_value=19340176 +HeapAlloc dt=7 heapalloc_value=19348368 +HeapAlloc dt=8 heapalloc_value=19356560 +HeapAlloc dt=70 heapalloc_value=19364752 +HeapAlloc dt=8 heapalloc_value=19372944 +HeapAlloc dt=7 heapalloc_value=19381136 +HeapAlloc dt=6 heapalloc_value=19389328 +HeapAlloc dt=7 heapalloc_value=19397520 +HeapAlloc dt=8 heapalloc_value=19405712 +HeapAlloc dt=7 heapalloc_value=19413904 +HeapAlloc dt=7 heapalloc_value=19422096 +HeapAlloc dt=8 heapalloc_value=19430288 +HeapAlloc dt=7 heapalloc_value=19438480 +HeapAlloc dt=6 heapalloc_value=19446672 +HeapAlloc dt=7 heapalloc_value=19454864 +HeapAlloc dt=7 heapalloc_value=19463056 +HeapAlloc dt=7 heapalloc_value=19471248 +HeapAlloc dt=6 heapalloc_value=19479440 +HeapAlloc dt=7 heapalloc_value=19487632 +HeapAlloc dt=6 heapalloc_value=19495824 +HeapAlloc dt=7 heapalloc_value=19504016 +HeapAlloc dt=7 heapalloc_value=19512208 +HeapAlloc dt=6 heapalloc_value=19520400 +HeapAlloc dt=8 heapalloc_value=19528592 +HeapAlloc dt=53 heapalloc_value=19536784 +HeapAlloc dt=8 heapalloc_value=19544976 +GoBlock dt=12 reason_string=19 stack=21 +ProcStop dt=196 +ProcStart dt=17347 p=2 p_seq=25 +ProcStop dt=14 +ProcStart dt=2376 p=0 p_seq=48 +ProcStop dt=11 +ProcStart dt=16736 p=0 p_seq=49 +GoUnblock dt=12 g=1 g_seq=55 stack=0 +GoStart dt=137 g=1 g_seq=56 +HeapAlloc dt=24 heapalloc_value=20577168 +HeapAlloc dt=87 heapalloc_value=20585360 +HeapAlloc dt=9 heapalloc_value=20593552 +HeapAlloc dt=6 heapalloc_value=20601744 +HeapAlloc dt=7 heapalloc_value=20609936 +HeapAlloc dt=7 heapalloc_value=20618128 +HeapAlloc dt=6 heapalloc_value=20626320 +HeapAlloc dt=7 heapalloc_value=20634512 +HeapAlloc dt=7 heapalloc_value=20642704 +HeapAlloc dt=6 heapalloc_value=20650896 +HeapAlloc dt=7 heapalloc_value=20659088 +HeapAlloc dt=7 heapalloc_value=20667280 +HeapAlloc dt=238 heapalloc_value=20675472 +HeapAlloc dt=10 heapalloc_value=20683664 +HeapAlloc dt=6 heapalloc_value=20691856 +HeapAlloc dt=7 heapalloc_value=20700048 +HeapAlloc dt=7 heapalloc_value=20708240 +HeapAlloc dt=6 heapalloc_value=20716432 +HeapAlloc dt=7 heapalloc_value=20724624 +HeapAlloc dt=6 heapalloc_value=20732816 +HeapAlloc dt=46 heapalloc_value=20741008 +HeapAlloc dt=8 heapalloc_value=20749200 +HeapAlloc dt=7 heapalloc_value=20757392 +HeapAlloc dt=7 heapalloc_value=20765584 +HeapAlloc dt=7 heapalloc_value=20773776 +HeapAlloc dt=7 heapalloc_value=20781968 +HeapAlloc dt=6 heapalloc_value=20790160 +HeapAlloc dt=7 heapalloc_value=20798352 +HeapAlloc dt=7 heapalloc_value=20806544 +HeapAlloc dt=6 heapalloc_value=20814736 +HeapAlloc dt=7 heapalloc_value=20822928 +HeapAlloc dt=7 heapalloc_value=20831120 +HeapAlloc dt=7 heapalloc_value=20839312 +HeapAlloc dt=7 heapalloc_value=20847504 +HeapAlloc dt=6 heapalloc_value=20855696 +HeapAlloc dt=7 heapalloc_value=20863888 +HeapAlloc dt=6 heapalloc_value=20872080 +HeapAlloc dt=7 heapalloc_value=20880272 +HeapAlloc dt=7 heapalloc_value=20888464 +HeapAlloc dt=6 heapalloc_value=20896656 +HeapAlloc dt=7 heapalloc_value=20904848 +HeapAlloc dt=7 heapalloc_value=20913040 +HeapAlloc dt=6 heapalloc_value=20921232 +HeapAlloc dt=7 heapalloc_value=20929424 +HeapAlloc dt=74 heapalloc_value=20937616 +HeapAlloc dt=8 heapalloc_value=20945808 +HeapAlloc dt=7 heapalloc_value=20954000 +HeapAlloc dt=6 heapalloc_value=20962192 +HeapAlloc dt=7 heapalloc_value=20970384 +HeapAlloc dt=7 heapalloc_value=20978576 +HeapAlloc dt=7 heapalloc_value=20986768 +HeapAlloc dt=6 heapalloc_value=20994960 +HeapAlloc dt=7 heapalloc_value=21003152 +HeapAlloc dt=7 heapalloc_value=21011344 +HeapAlloc dt=7 heapalloc_value=21019536 +HeapAlloc dt=6 heapalloc_value=21027728 +HeapAlloc dt=7 heapalloc_value=21035920 +HeapAlloc dt=6 heapalloc_value=21044112 +HeapAlloc dt=7 heapalloc_value=21052304 +HeapAlloc dt=7 heapalloc_value=21060496 +HeapAlloc dt=6 heapalloc_value=21068688 +HeapAlloc dt=7 heapalloc_value=21076880 +HeapAlloc dt=6 heapalloc_value=21085072 +HeapAlloc dt=7 heapalloc_value=21093264 +HeapAlloc dt=7 heapalloc_value=21101456 +HeapAlloc dt=90 heapalloc_value=21109648 +HeapAlloc dt=8 heapalloc_value=21117840 +HeapAlloc dt=6 heapalloc_value=21126032 +HeapAlloc dt=7 heapalloc_value=21134224 +HeapAlloc dt=7 heapalloc_value=21142416 +HeapAlloc dt=7 heapalloc_value=21150608 +HeapAlloc dt=6 heapalloc_value=21158800 +HeapAlloc dt=44 heapalloc_value=21166992 +HeapAlloc dt=7 heapalloc_value=21175184 +HeapAlloc dt=7 heapalloc_value=21183376 +HeapAlloc dt=7 heapalloc_value=21191568 +HeapAlloc dt=71 heapalloc_value=21199760 +HeapAlloc dt=8 heapalloc_value=21207952 +HeapAlloc dt=7 heapalloc_value=21216144 +HeapAlloc dt=7 heapalloc_value=21224336 +HeapAlloc dt=7 heapalloc_value=21232528 +HeapAlloc dt=6 heapalloc_value=21240720 +HeapAlloc dt=7 heapalloc_value=21248912 +HeapAlloc dt=7 heapalloc_value=21257104 +HeapAlloc dt=6 heapalloc_value=21265296 +HeapAlloc dt=7 heapalloc_value=21273488 +HeapAlloc dt=6 heapalloc_value=21281680 +HeapAlloc dt=7 heapalloc_value=21289872 +HeapAlloc dt=7 heapalloc_value=21298064 +HeapAlloc dt=6 heapalloc_value=21306256 +HeapAlloc dt=7 heapalloc_value=21314448 +HeapAlloc dt=6 heapalloc_value=21322640 +HeapAlloc dt=7 heapalloc_value=21330832 +HeapAlloc dt=7 heapalloc_value=21339024 +HeapAlloc dt=6 heapalloc_value=21347216 +HeapAlloc dt=7 heapalloc_value=21355408 +HeapAlloc dt=6 heapalloc_value=21363600 +HeapAlloc dt=43 heapalloc_value=21371792 +HeapAlloc dt=8 heapalloc_value=21379984 +HeapAlloc dt=7 heapalloc_value=21388176 +HeapAlloc dt=7 heapalloc_value=21396368 +HeapAlloc dt=6 heapalloc_value=21404560 +HeapAlloc dt=7 heapalloc_value=21412752 +HeapAlloc dt=7 heapalloc_value=21420944 +HeapAlloc dt=6 heapalloc_value=21429136 +HeapAlloc dt=7 heapalloc_value=21437328 +HeapAlloc dt=6 heapalloc_value=21445520 +HeapAlloc dt=7 heapalloc_value=21453712 +HeapAlloc dt=68 heapalloc_value=21461904 +HeapAlloc dt=8 heapalloc_value=21470096 +HeapAlloc dt=6 heapalloc_value=21478288 +HeapAlloc dt=7 heapalloc_value=21486480 +HeapAlloc dt=6 heapalloc_value=21494672 +HeapAlloc dt=7 heapalloc_value=21502864 +HeapAlloc dt=7 heapalloc_value=21511056 +HeapAlloc dt=6 heapalloc_value=21519248 +HeapAlloc dt=7 heapalloc_value=21527440 +HeapAlloc dt=6 heapalloc_value=21535632 +HeapAlloc dt=7 heapalloc_value=21543824 +HeapAlloc dt=7 heapalloc_value=21552016 +HeapAlloc dt=6 heapalloc_value=21560208 +HeapAlloc dt=7 heapalloc_value=21568400 +HeapAlloc dt=7 heapalloc_value=21576592 +HeapAlloc dt=7 heapalloc_value=21584784 +HeapAlloc dt=6 heapalloc_value=21592976 +GoBlock dt=11 reason_string=19 stack=21 +ProcStop dt=159 +ProcStart dt=1372 p=0 p_seq=51 +GoUnblock dt=19 g=1 g_seq=57 stack=0 +GoStart dt=211 g=1 g_seq=58 +HeapAlloc dt=39 heapalloc_value=21601168 +HeapAlloc dt=16 heapalloc_value=21609360 +HeapAlloc dt=8 heapalloc_value=21617552 +HeapAlloc dt=6 heapalloc_value=21625744 +HeapAlloc dt=101 heapalloc_value=21633936 +HeapAlloc dt=8 heapalloc_value=21642128 +HeapAlloc dt=7 heapalloc_value=21650320 +HeapAlloc dt=6 heapalloc_value=21658512 +HeapAlloc dt=7 heapalloc_value=21666704 +HeapAlloc dt=6 heapalloc_value=21674896 +HeapAlloc dt=6 heapalloc_value=21683088 +HeapAlloc dt=7 heapalloc_value=21691280 +HeapAlloc dt=6 heapalloc_value=21699472 +HeapAlloc dt=7 heapalloc_value=21707664 +HeapAlloc dt=6 heapalloc_value=21715856 +HeapAlloc dt=102 heapalloc_value=21724048 +HeapAlloc dt=8 heapalloc_value=21732240 +HeapAlloc dt=6 heapalloc_value=21740432 +HeapAlloc dt=7 heapalloc_value=21748624 +HeapAlloc dt=6 heapalloc_value=21756816 +HeapAlloc dt=7 heapalloc_value=21765008 +HeapAlloc dt=6 heapalloc_value=21773200 +HeapAlloc dt=7 heapalloc_value=21781392 +HeapAlloc dt=44 heapalloc_value=21789584 +HeapAlloc dt=7 heapalloc_value=21797776 +HeapAlloc dt=8 heapalloc_value=21805968 +HeapAlloc dt=7 heapalloc_value=21814160 +HeapAlloc dt=6 heapalloc_value=21822352 +HeapAlloc dt=7 heapalloc_value=21830544 +HeapAlloc dt=6 heapalloc_value=21838736 +HeapAlloc dt=7 heapalloc_value=21846928 +HeapAlloc dt=6 heapalloc_value=21855120 +HeapAlloc dt=6 heapalloc_value=21863312 +HeapAlloc dt=7 heapalloc_value=21871504 +HeapAlloc dt=6 heapalloc_value=21879696 +HeapAlloc dt=7 heapalloc_value=21887888 +HeapAlloc dt=6 heapalloc_value=21896080 +HeapAlloc dt=7 heapalloc_value=21904272 +HeapAlloc dt=6 heapalloc_value=21912464 +HeapAlloc dt=7 heapalloc_value=21920656 +HeapAlloc dt=6 heapalloc_value=21928848 +HeapAlloc dt=6 heapalloc_value=21937040 +HeapAlloc dt=7 heapalloc_value=21945232 +HeapAlloc dt=6 heapalloc_value=21953424 +HeapAlloc dt=7 heapalloc_value=21961616 +HeapAlloc dt=6 heapalloc_value=21969808 +HeapAlloc dt=7 heapalloc_value=21978000 +HeapAlloc dt=248 heapalloc_value=21986192 +HeapAlloc dt=18 heapalloc_value=21994384 +HeapAlloc dt=7 heapalloc_value=22002576 +HeapAlloc dt=6 heapalloc_value=22010768 +HeapAlloc dt=7 heapalloc_value=22018960 +HeapAlloc dt=6 heapalloc_value=22027152 +HeapAlloc dt=7 heapalloc_value=22035344 +HeapAlloc dt=6 heapalloc_value=22043536 +HeapAlloc dt=7 heapalloc_value=22051728 +HeapAlloc dt=6 heapalloc_value=22059920 +HeapAlloc dt=7 heapalloc_value=22068112 +HeapAlloc dt=16 heapalloc_value=22657936 +HeapAlloc dt=3547 heapalloc_value=22666128 +HeapAlloc dt=3135 heapalloc_value=22674320 +HeapAlloc dt=11 heapalloc_value=22682512 +HeapAlloc dt=8 heapalloc_value=22690704 +HeapAlloc dt=8 heapalloc_value=22698896 +HeapAlloc dt=8 heapalloc_value=22707088 +HeapAlloc dt=10 heapalloc_value=22715280 +HeapAlloc dt=8 heapalloc_value=22723472 +HeapAlloc dt=8 heapalloc_value=22731664 +HeapAlloc dt=71 heapalloc_value=22739856 +HeapAlloc dt=10 heapalloc_value=22748048 +HeapAlloc dt=8 heapalloc_value=22756240 +HeapAlloc dt=9 heapalloc_value=22764432 +HeapAlloc dt=8 heapalloc_value=22772624 +HeapAlloc dt=8 heapalloc_value=22780816 +HeapAlloc dt=9 heapalloc_value=22789008 +HeapAlloc dt=47 heapalloc_value=22797200 +HeapAlloc dt=9 heapalloc_value=22805392 +HeapAlloc dt=9 heapalloc_value=22813584 +HeapAlloc dt=8 heapalloc_value=22821776 +HeapAlloc dt=9 heapalloc_value=22829968 +HeapAlloc dt=17 heapalloc_value=22838160 +HeapAlloc dt=8 heapalloc_value=22846352 +HeapAlloc dt=6 heapalloc_value=22854544 +HeapAlloc dt=7 heapalloc_value=22862736 +HeapAlloc dt=6 heapalloc_value=22870928 +HeapAlloc dt=6 heapalloc_value=22879120 +HeapAlloc dt=6 heapalloc_value=22887312 +HeapAlloc dt=6 heapalloc_value=22895504 +HeapAlloc dt=7 heapalloc_value=22903696 +HeapAlloc dt=6 heapalloc_value=22911888 +HeapAlloc dt=6 heapalloc_value=22920080 +HeapAlloc dt=6 heapalloc_value=22928272 +HeapAlloc dt=6 heapalloc_value=22936464 +HeapAlloc dt=6 heapalloc_value=22944656 +HeapAlloc dt=7 heapalloc_value=22952848 +HeapAlloc dt=6 heapalloc_value=22961040 +HeapAlloc dt=8 heapalloc_value=22969232 +HeapAlloc dt=6 heapalloc_value=22977424 +HeapAlloc dt=6 heapalloc_value=22985616 +HeapAlloc dt=6 heapalloc_value=22993808 +HeapAlloc dt=43 heapalloc_value=23002000 +HeapAlloc dt=8 heapalloc_value=23010192 +HeapAlloc dt=6 heapalloc_value=23018384 +HeapAlloc dt=7 heapalloc_value=23026576 +HeapAlloc dt=76 heapalloc_value=23034768 +HeapAlloc dt=9 heapalloc_value=23042960 +HeapAlloc dt=6 heapalloc_value=23051152 +HeapAlloc dt=7 heapalloc_value=23059344 +HeapAlloc dt=6 heapalloc_value=23067536 +HeapAlloc dt=6 heapalloc_value=23075728 +HeapAlloc dt=7 heapalloc_value=23083920 +HeapAlloc dt=6 heapalloc_value=23092112 +HeapAlloc dt=6 heapalloc_value=23100304 +HeapAlloc dt=6 heapalloc_value=23108496 +HeapAlloc dt=6 heapalloc_value=23116688 +HeapAlloc dt=6 heapalloc_value=23124880 +HeapAlloc dt=7 heapalloc_value=23133072 +HeapAlloc dt=6 heapalloc_value=23141264 +HeapAlloc dt=8 heapalloc_value=23149456 +HeapAlloc dt=6 heapalloc_value=23157648 +HeapAlloc dt=6 heapalloc_value=23165840 +HeapAlloc dt=7 heapalloc_value=23174032 +HeapAlloc dt=6 heapalloc_value=23182224 +HeapAlloc dt=7 heapalloc_value=23190416 +HeapAlloc dt=6 heapalloc_value=23198608 +HeapAlloc dt=6 heapalloc_value=23206800 +HeapAlloc dt=22 heapalloc_value=23214912 +HeapAlloc dt=22 heapalloc_value=23223008 +HeapAlloc dt=21 heapalloc_value=23224960 +GoCreate dt=50 new_g=10 new_stack=49 stack=50 +GoCreate dt=193 new_g=11 new_stack=49 stack=50 +GoCreate dt=10 new_g=12 new_stack=49 stack=50 +GoCreate dt=5 new_g=13 new_stack=49 stack=50 +HeapAlloc dt=120 heapalloc_value=23232736 +GoCreate dt=9 new_g=14 new_stack=49 stack=50 +GoCreate dt=8 new_g=15 new_stack=49 stack=50 +GoCreate dt=7 new_g=16 new_stack=49 stack=50 +GoCreate dt=8 new_g=50 new_stack=49 stack=50 +GoBlock dt=17 reason_string=10 stack=51 +GoStart dt=7 g=50 g_seq=1 +GoStop dt=306070 reason_string=16 stack=52 +GoStart dt=17 g=50 g_seq=2 +GoStop dt=316463 reason_string=16 stack=52 +GoStart dt=9 g=50 g_seq=3 +GoDestroy dt=158709 +ProcStop dt=33 +ProcStart dt=9387 p=7 p_seq=3 +ProcStop dt=14 +ProcStart dt=63662 p=7 p_seq=4 +ProcStop dt=14 +ProcStart dt=16745 p=7 p_seq=5 +GoUnblock dt=39 g=19 g_seq=2 stack=0 +GoStart dt=155 g=19 g_seq=3 +HeapAlloc dt=297 heapalloc_value=23312520 +GoBlock dt=30 reason_string=12 stack=11 +ProcStop dt=28 +ProcStart dt=706341 p=7 p_seq=6 +ProcStop dt=15 +ProcStart dt=50 p=7 p_seq=7 +ProcStop dt=8 +ProcStart dt=3274 p=6 p_seq=14 +ProcStop dt=13 +ProcStart dt=2696 p=4 p_seq=6 +ProcStop dt=17 +ProcStart dt=416 p=7 p_seq=19 +GoUnblock dt=7 g=1 g_seq=64 stack=0 +GoStart dt=7 g=1 g_seq=65 +GoSyscallBegin dt=33 p_seq=20 stack=81 +GoSyscallEnd dt=43 +GoSyscallBegin dt=134 p_seq=21 stack=82 +GoSyscallEnd dt=38 +GoSyscallBegin dt=10 p_seq=22 stack=83 +GoSyscallEnd dt=40 +GoSyscallBegin dt=7 p_seq=23 stack=84 +GoSyscallEnd dt=26 +GoSyscallBegin dt=10 p_seq=24 stack=85 +GoSyscallEnd dt=31 +GoSyscallBegin dt=39 p_seq=25 stack=86 +GoSyscallEnd dt=61 +GoBlock dt=13 reason_string=7 stack=87 +ProcStop dt=15 +EventBatch gen=1 m=2852341 time=420901453987 size=3492 +ProcStart dt=448 p=3 p_seq=1 +ProcStop dt=26 +ProcStart dt=312314 p=0 p_seq=4 +ProcStop dt=17 +ProcStart dt=16776 p=0 p_seq=5 +GoUnblock dt=31 g=1 g_seq=3 stack=0 +GoStart dt=182 g=1 g_seq=4 +HeapAlloc dt=181 heapalloc_value=1662976 +HeapAlloc dt=25 heapalloc_value=1671168 +HeapAlloc dt=210 heapalloc_value=1679360 +HeapAlloc dt=19 heapalloc_value=1687552 +HeapAlloc dt=15 heapalloc_value=1695744 +HeapAlloc dt=8 heapalloc_value=1703936 +HeapAlloc dt=15 heapalloc_value=1712128 +HeapAlloc dt=7 heapalloc_value=1720320 +HeapAlloc dt=9 heapalloc_value=1728512 +HeapAlloc dt=5 heapalloc_value=1736704 +HeapAlloc dt=8 heapalloc_value=1761280 +HeapAlloc dt=9 heapalloc_value=1769472 +HeapAlloc dt=8 heapalloc_value=1777664 +HeapAlloc dt=6 heapalloc_value=1785856 +HeapAlloc dt=8 heapalloc_value=1794048 +HeapAlloc dt=6 heapalloc_value=1802240 +HeapAlloc dt=6 heapalloc_value=1810432 +HeapAlloc dt=6 heapalloc_value=1818624 +HeapAlloc dt=6 heapalloc_value=1826816 +HeapAlloc dt=5 heapalloc_value=1851392 +HeapAlloc dt=62 heapalloc_value=1859584 +HeapAlloc dt=8 heapalloc_value=1867776 +HeapAlloc dt=6 heapalloc_value=1875968 +HeapAlloc dt=6 heapalloc_value=1884160 +HeapAlloc dt=6 heapalloc_value=1892352 +HeapAlloc dt=6 heapalloc_value=1900544 +HeapAlloc dt=6 heapalloc_value=1908736 +HeapAlloc dt=6 heapalloc_value=1916928 +HeapAlloc dt=75 heapalloc_value=1925120 +HeapAlloc dt=8 heapalloc_value=1933312 +HeapAlloc dt=6 heapalloc_value=1941504 +HeapAlloc dt=7 heapalloc_value=1949696 +HeapAlloc dt=5 heapalloc_value=1957888 +HeapAlloc dt=7 heapalloc_value=1966080 +HeapAlloc dt=7 heapalloc_value=1974272 +HeapAlloc dt=6 heapalloc_value=1982464 +HeapAlloc dt=13 heapalloc_value=2007040 +HeapAlloc dt=12 heapalloc_value=2015232 +HeapAlloc dt=7 heapalloc_value=2023424 +HeapAlloc dt=6 heapalloc_value=2031616 +HeapAlloc dt=6 heapalloc_value=2039808 +HeapAlloc dt=6 heapalloc_value=2048000 +HeapAlloc dt=8 heapalloc_value=2056192 +HeapAlloc dt=6 heapalloc_value=2064384 +HeapAlloc dt=6 heapalloc_value=2072576 +HeapAlloc dt=6 heapalloc_value=2080768 +HeapAlloc dt=6 heapalloc_value=2088960 +HeapAlloc dt=6 heapalloc_value=2097152 +HeapAlloc dt=6 heapalloc_value=2105344 +HeapAlloc dt=6 heapalloc_value=2113536 +HeapAlloc dt=9 heapalloc_value=2121728 +HeapAlloc dt=5 heapalloc_value=2129920 +HeapAlloc dt=67 heapalloc_value=2138112 +HeapAlloc dt=7 heapalloc_value=2146304 +HeapAlloc dt=7 heapalloc_value=2154496 +HeapAlloc dt=5 heapalloc_value=2162688 +HeapAlloc dt=6 heapalloc_value=2170880 +HeapAlloc dt=6 heapalloc_value=2179072 +HeapAlloc dt=79 heapalloc_value=2187264 +HeapAlloc dt=8 heapalloc_value=2195456 +HeapAlloc dt=6 heapalloc_value=2203648 +HeapAlloc dt=6 heapalloc_value=2211840 +HeapAlloc dt=6 heapalloc_value=2220032 +HeapAlloc dt=6 heapalloc_value=2228224 +HeapAlloc dt=6 heapalloc_value=2236416 +HeapAlloc dt=6 heapalloc_value=2244608 +HeapAlloc dt=8 heapalloc_value=2252800 +HeapAlloc dt=6 heapalloc_value=2260992 +HeapAlloc dt=6 heapalloc_value=2269184 +HeapAlloc dt=5 heapalloc_value=2310144 +HeapAlloc dt=19 heapalloc_value=2318336 +HeapAlloc dt=6 heapalloc_value=2326528 +HeapAlloc dt=7 heapalloc_value=2334720 +HeapAlloc dt=6 heapalloc_value=2342912 +HeapAlloc dt=6 heapalloc_value=2351104 +HeapAlloc dt=43 heapalloc_value=2359296 +HeapAlloc dt=8 heapalloc_value=2367488 +HeapAlloc dt=6 heapalloc_value=2375680 +HeapAlloc dt=8 heapalloc_value=2383872 +HeapAlloc dt=6 heapalloc_value=2392064 +HeapAlloc dt=6 heapalloc_value=2400256 +HeapAlloc dt=6 heapalloc_value=2408448 +HeapAlloc dt=6 heapalloc_value=2416640 +HeapAlloc dt=6 heapalloc_value=2424832 +HeapAlloc dt=6 heapalloc_value=2433024 +HeapAlloc dt=90 heapalloc_value=2441216 +HeapAlloc dt=74 heapalloc_value=2449408 +HeapAlloc dt=7 heapalloc_value=2457600 +HeapAlloc dt=7 heapalloc_value=2465792 +HeapAlloc dt=5 heapalloc_value=2473984 +HeapAlloc dt=6 heapalloc_value=2482176 +HeapAlloc dt=6 heapalloc_value=2490368 +HeapAlloc dt=6 heapalloc_value=2498560 +HeapAlloc dt=6 heapalloc_value=2506752 +HeapAlloc dt=8 heapalloc_value=2514944 +HeapAlloc dt=6 heapalloc_value=2523136 +HeapAlloc dt=7 heapalloc_value=2531328 +HeapAlloc dt=6 heapalloc_value=2539520 +HeapAlloc dt=6 heapalloc_value=2547712 +HeapAlloc dt=6 heapalloc_value=2555904 +HeapAlloc dt=6 heapalloc_value=2564096 +HeapAlloc dt=6 heapalloc_value=2572288 +HeapAlloc dt=8 heapalloc_value=2580480 +HeapAlloc dt=6 heapalloc_value=2588672 +HeapAlloc dt=28 heapalloc_value=2596864 +HeapAlloc dt=8 heapalloc_value=2605056 +HeapAlloc dt=5 heapalloc_value=2613248 +HeapAlloc dt=6 heapalloc_value=2621440 +HeapAlloc dt=6 heapalloc_value=2629632 +HeapAlloc dt=7 heapalloc_value=2637824 +HeapAlloc dt=8 heapalloc_value=2646016 +HeapAlloc dt=6 heapalloc_value=2654208 +HeapAlloc dt=13 heapalloc_value=2686976 +HeapAlloc dt=23 heapalloc_value=2695168 +HeapAlloc dt=6 heapalloc_value=2703360 +HeapAlloc dt=75 heapalloc_value=2711552 +HeapAlloc dt=55 heapalloc_value=2719744 +HeapAlloc dt=8 heapalloc_value=2727936 +HeapAlloc dt=6 heapalloc_value=2736128 +HeapAlloc dt=6 heapalloc_value=2744320 +HeapAlloc dt=6 heapalloc_value=2752512 +HeapAlloc dt=6 heapalloc_value=2760704 +HeapAlloc dt=6 heapalloc_value=2768896 +HeapAlloc dt=9 heapalloc_value=2777088 +HeapAlloc dt=5 heapalloc_value=2785280 +HeapAlloc dt=6 heapalloc_value=2793472 +HeapAlloc dt=6 heapalloc_value=2801664 +HeapAlloc dt=6 heapalloc_value=2809856 +HeapAlloc dt=6 heapalloc_value=2818048 +HeapAlloc dt=6 heapalloc_value=2826240 +HeapAlloc dt=6 heapalloc_value=2834432 +GoBlock dt=19 reason_string=19 stack=21 +ProcStop dt=236 +ProcStart dt=17547 p=1 p_seq=2 +ProcStop dt=18 +ProcStart dt=5588 p=0 p_seq=8 +ProcStop dt=13 +ProcStart dt=16789 p=0 p_seq=9 +GoUnblock dt=17 g=1 g_seq=7 stack=0 +GoStart dt=173 g=1 g_seq=8 +HeapAlloc dt=54 heapalloc_value=3915776 +HeapAlloc dt=17 heapalloc_value=3923968 +HeapAlloc dt=6 heapalloc_value=3932160 +HeapAlloc dt=6 heapalloc_value=3940352 +HeapAlloc dt=8 heapalloc_value=3948544 +HeapAlloc dt=10 heapalloc_value=3956736 +HeapAlloc dt=7 heapalloc_value=3964928 +HeapAlloc dt=10 heapalloc_value=4038656 +GCBegin dt=207 gc_seq=1 stack=22 +GoCreate dt=117 new_g=5 new_stack=23 stack=24 +GoSyscallBegin dt=172 p_seq=10 stack=25 +ProcStop dt=2 +ProcStart dt=6567 p=0 p_seq=12 +GoSyscallEndBlocked dt=4 +GoStart dt=1 g=1 g_seq=9 +GoCreate dt=36 new_g=6 new_stack=23 stack=24 +GoSyscallBegin dt=11 p_seq=13 stack=25 +ProcStop dt=1 +ProcStart dt=815 p=0 p_seq=15 +GoSyscallEndBlocked dt=2 +GoStart dt=1 g=1 g_seq=10 +GoCreate dt=23 new_g=7 new_stack=23 stack=24 +GoSyscallBegin dt=4 p_seq=16 stack=25 +ProcStop dt=1 +ProcStart dt=814 p=1 p_seq=6 +GoSyscallEndBlocked dt=2 +GoStart dt=1 g=1 g_seq=11 +GoCreate dt=14 new_g=24 new_stack=23 stack=24 +GoSyscallBegin dt=122 p_seq=7 stack=25 +ProcStop dt=1 +ProcStart dt=519 p=2 p_seq=5 +GoSyscallEndBlocked dt=1 +GoStart dt=1 g=1 g_seq=12 +HeapAlloc dt=19 heapalloc_value=4063232 +GoCreate dt=21 new_g=34 new_stack=23 stack=24 +GoSyscallBegin dt=5 p_seq=6 stack=25 +ProcStop dt=1 +ProcStart dt=924 p=0 p_seq=19 +GoSyscallEndBlocked dt=1 +GoStart dt=1 g=1 g_seq=13 +GoCreate dt=19 new_g=8 new_stack=23 stack=24 +GoSyscallBegin dt=140 p_seq=20 stack=25 +ProcStop dt=2 +ProcStart dt=512 p=0 p_seq=22 +GoSyscallEndBlocked dt=1 +GoStart dt=1 g=1 g_seq=14 +GoCreate dt=14 new_g=9 new_stack=23 stack=24 +GoSyscallBegin dt=3 p_seq=23 stack=25 +ProcStop dt=1 +ProcStart dt=375 p=1 p_seq=12 +GoSyscallEndBlocked dt=2 +GoStart dt=1 g=1 g_seq=15 +HeapAlloc dt=36 heapalloc_value=4071424 +GoCreate dt=13 new_g=25 new_stack=23 stack=24 +GoSyscallBegin dt=115 p_seq=13 stack=25 +ProcStop dt=1 +ProcStart dt=623 p=2 p_seq=10 +GoSyscallEndBlocked dt=1 +GoStart dt=1 g=1 g_seq=16 +STWBegin dt=37 kind_string=22 stack=27 +GoStatus dt=138 g=4 m=18446744073709551615 gstatus=4 +GoUnblock dt=7 g=4 g_seq=1 stack=28 +ProcsChange dt=158 procs_value=8 stack=29 +STWEnd dt=25 +GCMarkAssistBegin dt=1078 stack=30 +GCMarkAssistEnd dt=684 +HeapAlloc dt=15 heapalloc_value=4087808 +HeapAlloc dt=21 heapalloc_value=4096000 +HeapAlloc dt=11 heapalloc_value=4104192 +HeapAlloc dt=9 heapalloc_value=4112384 +HeapAlloc dt=9 heapalloc_value=4120576 +HeapAlloc dt=736 heapalloc_value=4145152 +HeapAlloc dt=27 heapalloc_value=4153344 +HeapAlloc dt=19 heapalloc_value=4161536 +HeapAlloc dt=15 heapalloc_value=4169728 +HeapAlloc dt=19 heapalloc_value=4177920 +HeapAlloc dt=15 heapalloc_value=4186112 +HeapAlloc dt=11 heapalloc_value=4194304 +HeapAlloc dt=16 heapalloc_value=4202496 +HeapAlloc dt=16 heapalloc_value=4210688 +HeapAlloc dt=9 heapalloc_value=4218880 +HeapAlloc dt=9 heapalloc_value=4227072 +HeapAlloc dt=9 heapalloc_value=4235264 +HeapAlloc dt=9 heapalloc_value=4243456 +HeapAlloc dt=10 heapalloc_value=4251648 +HeapAlloc dt=9 heapalloc_value=4259840 +HeapAlloc dt=20 heapalloc_value=4268032 +GoStop dt=11 reason_string=16 stack=31 +GoStart dt=361 g=1 g_seq=17 +HeapAlloc dt=16 heapalloc_value=4276224 +HeapAlloc dt=10 heapalloc_value=4284416 +HeapAlloc dt=9 heapalloc_value=4292608 +HeapAlloc dt=10 heapalloc_value=4300800 +HeapAlloc dt=9 heapalloc_value=4308992 +HeapAlloc dt=10 heapalloc_value=4317184 +HeapAlloc dt=9 heapalloc_value=4325376 +HeapAlloc dt=9 heapalloc_value=4333568 +HeapAlloc dt=11 heapalloc_value=4341760 +HeapAlloc dt=9 heapalloc_value=4349952 +HeapAlloc dt=67 heapalloc_value=4358144 +HeapAlloc dt=10 heapalloc_value=4366336 +HeapAlloc dt=10 heapalloc_value=4374528 +HeapAlloc dt=9 heapalloc_value=4382720 +HeapAlloc dt=9 heapalloc_value=4390912 +HeapAlloc dt=9 heapalloc_value=4399104 +HeapAlloc dt=283 heapalloc_value=4407296 +HeapAlloc dt=15 heapalloc_value=4415488 +HeapAlloc dt=7 heapalloc_value=4423680 +HeapAlloc dt=7 heapalloc_value=4431872 +HeapAlloc dt=7 heapalloc_value=4440064 +HeapAlloc dt=7 heapalloc_value=4448256 +HeapAlloc dt=7 heapalloc_value=4456448 +HeapAlloc dt=7 heapalloc_value=4464640 +HeapAlloc dt=7 heapalloc_value=4472832 +HeapAlloc dt=7 heapalloc_value=4481024 +HeapAlloc dt=7 heapalloc_value=4489216 +HeapAlloc dt=7 heapalloc_value=4497408 +HeapAlloc dt=7 heapalloc_value=4505600 +HeapAlloc dt=6 heapalloc_value=4513792 +HeapAlloc dt=7 heapalloc_value=4521984 +HeapAlloc dt=39 heapalloc_value=4530176 +HeapAlloc dt=8 heapalloc_value=4538368 +HeapAlloc dt=8 heapalloc_value=4546560 +HeapAlloc dt=7 heapalloc_value=4554752 +HeapAlloc dt=7 heapalloc_value=4562944 +HeapAlloc dt=10 heapalloc_value=4571136 +HeapAlloc dt=9 heapalloc_value=4579328 +HeapAlloc dt=72 heapalloc_value=4587520 +HeapAlloc dt=9 heapalloc_value=4595712 +HeapAlloc dt=7 heapalloc_value=4603904 +HeapAlloc dt=7 heapalloc_value=4612096 +HeapAlloc dt=7 heapalloc_value=4620288 +HeapAlloc dt=7 heapalloc_value=4628480 +HeapAlloc dt=7 heapalloc_value=4636672 +HeapAlloc dt=7 heapalloc_value=4644864 +HeapAlloc dt=8 heapalloc_value=4653056 +HeapAlloc dt=7 heapalloc_value=4661248 +HeapAlloc dt=266 heapalloc_value=4669440 +HeapAlloc dt=9 heapalloc_value=4677632 +HeapAlloc dt=50 heapalloc_value=4685824 +HeapAlloc dt=9 heapalloc_value=4694016 +HeapAlloc dt=7 heapalloc_value=4702208 +HeapAlloc dt=8 heapalloc_value=4710400 +HeapAlloc dt=7 heapalloc_value=4718592 +HeapAlloc dt=7 heapalloc_value=4726784 +HeapAlloc dt=7 heapalloc_value=4734976 +HeapAlloc dt=7 heapalloc_value=4743168 +GCMarkAssistBegin dt=9 stack=30 +HeapAlloc dt=40 heapalloc_value=4751360 +GoBlock dt=247 reason_string=10 stack=33 +ProcStop dt=18 +ProcStart dt=5438 p=2 p_seq=11 +ProcStop dt=22 +ProcStart dt=70608 p=2 p_seq=12 +GoUnblock dt=18 g=25 g_seq=4 stack=0 +GoStart dt=190 g=25 g_seq=5 +GoLabel dt=1 label_string=2 +GoBlock dt=594 reason_string=15 stack=26 +ProcStop dt=28 +ProcStart dt=802 p=2 p_seq=13 +ProcStop dt=17 +ProcStart dt=2684 p=2 p_seq=14 +ProcStop dt=29 +ProcStart dt=382 p=2 p_seq=15 +ProcStop dt=51 +ProcStart dt=2622 p=2 p_seq=16 +ProcStop dt=22 +ProcStart dt=66146 p=2 p_seq=17 +ProcStop dt=20 +ProcStart dt=49429 p=0 p_seq=40 +GoUnblock dt=13 g=9 g_seq=7 stack=0 +GoStart dt=174 g=9 g_seq=8 +GoLabel dt=1 label_string=2 +GoBlock dt=1963 reason_string=15 stack=26 +ProcStop dt=4345 +ProcStart dt=16958 p=2 p_seq=22 +ProcStop dt=18 +ProcStart dt=723 p=0 p_seq=42 +ProcStop dt=10 +ProcStart dt=16754 p=0 p_seq=43 +GoUnblock dt=14 g=1 g_seq=49 stack=0 +GoStart dt=152 g=1 g_seq=50 +HeapAlloc dt=32 heapalloc_value=17038224 +HeapAlloc dt=24 heapalloc_value=17046416 +HeapAlloc dt=15 heapalloc_value=17054608 +HeapAlloc dt=16 heapalloc_value=17062800 +HeapAlloc dt=13 heapalloc_value=17070992 +HeapAlloc dt=14 heapalloc_value=17079184 +HeapAlloc dt=17 heapalloc_value=17087376 +HeapAlloc dt=14 heapalloc_value=17095568 +HeapAlloc dt=14 heapalloc_value=17103760 +HeapAlloc dt=13 heapalloc_value=17111952 +HeapAlloc dt=81 heapalloc_value=17120144 +HeapAlloc dt=20 heapalloc_value=17128336 +HeapAlloc dt=14 heapalloc_value=17136528 +HeapAlloc dt=14 heapalloc_value=17144720 +HeapAlloc dt=16 heapalloc_value=17152912 +HeapAlloc dt=15 heapalloc_value=17161104 +HeapAlloc dt=13 heapalloc_value=17169296 +HeapAlloc dt=13 heapalloc_value=17177488 +HeapAlloc dt=16 heapalloc_value=17185680 +HeapAlloc dt=15 heapalloc_value=17193872 +HeapAlloc dt=14 heapalloc_value=17202064 +HeapAlloc dt=13 heapalloc_value=17210256 +HeapAlloc dt=16 heapalloc_value=17218448 +HeapAlloc dt=14 heapalloc_value=17226640 +HeapAlloc dt=14 heapalloc_value=17234832 +HeapAlloc dt=16 heapalloc_value=17243024 +HeapAlloc dt=16 heapalloc_value=17251216 +HeapAlloc dt=15 heapalloc_value=17259408 +HeapAlloc dt=14 heapalloc_value=17267600 +HeapAlloc dt=13 heapalloc_value=17275792 +HeapAlloc dt=45 heapalloc_value=17283984 +HeapAlloc dt=18 heapalloc_value=17292176 +HeapAlloc dt=16 heapalloc_value=17300368 +HeapAlloc dt=16 heapalloc_value=17308560 +HeapAlloc dt=15 heapalloc_value=17316752 +HeapAlloc dt=17 heapalloc_value=17324944 +HeapAlloc dt=15 heapalloc_value=17333136 +HeapAlloc dt=14 heapalloc_value=17341328 +HeapAlloc dt=16 heapalloc_value=17349520 +HeapAlloc dt=14 heapalloc_value=17357712 +HeapAlloc dt=14 heapalloc_value=17365904 +HeapAlloc dt=12 heapalloc_value=17374096 +HeapAlloc dt=14 heapalloc_value=17382288 +HeapAlloc dt=13 heapalloc_value=17390480 +HeapAlloc dt=13 heapalloc_value=17398672 +HeapAlloc dt=13 heapalloc_value=17406864 +HeapAlloc dt=36 heapalloc_value=17873808 +HeapAlloc dt=3813 heapalloc_value=17882000 +HeapAlloc dt=47 heapalloc_value=17890192 +HeapAlloc dt=10 heapalloc_value=17898384 +HeapAlloc dt=10 heapalloc_value=17906576 +HeapAlloc dt=12 heapalloc_value=17914768 +HeapAlloc dt=21 heapalloc_value=17922960 +HeapAlloc dt=17 heapalloc_value=17931152 +HeapAlloc dt=12 heapalloc_value=17939344 +HeapAlloc dt=13 heapalloc_value=17947536 +HeapAlloc dt=24 heapalloc_value=17955728 +HeapAlloc dt=91 heapalloc_value=17963920 +HeapAlloc dt=11 heapalloc_value=17972112 +HeapAlloc dt=9 heapalloc_value=17980304 +HeapAlloc dt=11 heapalloc_value=17988496 +HeapAlloc dt=8 heapalloc_value=17996688 +HeapAlloc dt=9 heapalloc_value=18004880 +HeapAlloc dt=9 heapalloc_value=18013072 +HeapAlloc dt=10 heapalloc_value=18021264 +HeapAlloc dt=9 heapalloc_value=18029456 +HeapAlloc dt=9 heapalloc_value=18037648 +HeapAlloc dt=8 heapalloc_value=18045840 +HeapAlloc dt=11 heapalloc_value=18054032 +HeapAlloc dt=8 heapalloc_value=18062224 +HeapAlloc dt=9 heapalloc_value=18070416 +HeapAlloc dt=9 heapalloc_value=18078608 +HeapAlloc dt=8 heapalloc_value=18086800 +HeapAlloc dt=9 heapalloc_value=18094992 +HeapAlloc dt=9 heapalloc_value=18103184 +HeapAlloc dt=8 heapalloc_value=18111376 +HeapAlloc dt=11 heapalloc_value=18119568 +HeapAlloc dt=9 heapalloc_value=18127760 +HeapAlloc dt=52 heapalloc_value=18135952 +HeapAlloc dt=10 heapalloc_value=18144144 +HeapAlloc dt=12 heapalloc_value=18152336 +HeapAlloc dt=10 heapalloc_value=18160528 +HeapAlloc dt=10 heapalloc_value=18168720 +HeapAlloc dt=25 heapalloc_value=18176912 +HeapAlloc dt=37 heapalloc_value=18185104 +HeapAlloc dt=32 heapalloc_value=18193296 +HeapAlloc dt=27 heapalloc_value=18201488 +HeapAlloc dt=27 heapalloc_value=18209680 +HeapAlloc dt=29 heapalloc_value=18217872 +HeapAlloc dt=28 heapalloc_value=18226064 +HeapAlloc dt=22 heapalloc_value=18234256 +HeapAlloc dt=32 heapalloc_value=18242448 +HeapAlloc dt=30 heapalloc_value=18250640 +HeapAlloc dt=26 heapalloc_value=18258832 +HeapAlloc dt=30 heapalloc_value=18267024 +HeapAlloc dt=33 heapalloc_value=18275216 +HeapAlloc dt=27 heapalloc_value=18283408 +HeapAlloc dt=33 heapalloc_value=18291600 +HeapAlloc dt=25 heapalloc_value=18299792 +HeapAlloc dt=40 heapalloc_value=18307984 +HeapAlloc dt=23 heapalloc_value=18316176 +HeapAlloc dt=32 heapalloc_value=18324368 +HeapAlloc dt=31 heapalloc_value=18332560 +HeapAlloc dt=30 heapalloc_value=18340752 +HeapAlloc dt=25 heapalloc_value=18348944 +HeapAlloc dt=32 heapalloc_value=18357136 +HeapAlloc dt=30 heapalloc_value=18365328 +HeapAlloc dt=32 heapalloc_value=18373520 +HeapAlloc dt=34 heapalloc_value=18381712 +HeapAlloc dt=30 heapalloc_value=18389904 +HeapAlloc dt=31 heapalloc_value=18398096 +HeapAlloc dt=29 heapalloc_value=18406288 +HeapAlloc dt=29 heapalloc_value=18414480 +HeapAlloc dt=29 heapalloc_value=18422672 +HeapAlloc dt=28 heapalloc_value=18430864 +HeapAlloc dt=35 heapalloc_value=18439056 +HeapAlloc dt=31 heapalloc_value=18447248 +HeapAlloc dt=30 heapalloc_value=18455440 +HeapAlloc dt=116 heapalloc_value=18463632 +HeapAlloc dt=21 heapalloc_value=18471824 +HeapAlloc dt=13 heapalloc_value=18480016 +HeapAlloc dt=65 heapalloc_value=18488208 +HeapAlloc dt=21 heapalloc_value=18496400 +HeapAlloc dt=16 heapalloc_value=18504592 +HeapAlloc dt=14 heapalloc_value=18512784 +HeapAlloc dt=13 heapalloc_value=18520976 +GoBlock dt=19 reason_string=19 stack=21 +ProcStop dt=197 +ProcStart dt=17439 p=2 p_seq=24 +ProcStop dt=12 +ProcStart dt=2355 p=0 p_seq=46 +ProcStop dt=8 +ProcStart dt=16730 p=0 p_seq=47 +GoUnblock dt=11 g=1 g_seq=53 stack=0 +GoStart dt=144 g=1 g_seq=54 +HeapAlloc dt=26 heapalloc_value=19553168 +HeapAlloc dt=13 heapalloc_value=19561360 +HeapAlloc dt=8 heapalloc_value=19569552 +HeapAlloc dt=6 heapalloc_value=19577744 +HeapAlloc dt=7 heapalloc_value=19585936 +HeapAlloc dt=7 heapalloc_value=19594128 +HeapAlloc dt=69 heapalloc_value=19602320 +HeapAlloc dt=7 heapalloc_value=19610512 +HeapAlloc dt=7 heapalloc_value=19618704 +HeapAlloc dt=250 heapalloc_value=19626896 +HeapAlloc dt=9 heapalloc_value=19635088 +HeapAlloc dt=7 heapalloc_value=19643280 +HeapAlloc dt=7 heapalloc_value=19651472 +HeapAlloc dt=7 heapalloc_value=19659664 +HeapAlloc dt=7 heapalloc_value=19667856 +HeapAlloc dt=7 heapalloc_value=19676048 +HeapAlloc dt=7 heapalloc_value=19684240 +HeapAlloc dt=6 heapalloc_value=19692432 +HeapAlloc dt=8 heapalloc_value=19700624 +HeapAlloc dt=6 heapalloc_value=19708816 +HeapAlloc dt=7 heapalloc_value=19717008 +HeapAlloc dt=7 heapalloc_value=19725200 +HeapAlloc dt=6 heapalloc_value=19733392 +HeapAlloc dt=7 heapalloc_value=19741584 +HeapAlloc dt=7 heapalloc_value=19749776 +HeapAlloc dt=6 heapalloc_value=19757968 +HeapAlloc dt=7 heapalloc_value=19766160 +HeapAlloc dt=7 heapalloc_value=19774352 +HeapAlloc dt=6 heapalloc_value=19782544 +HeapAlloc dt=7 heapalloc_value=19790736 +HeapAlloc dt=7 heapalloc_value=19798928 +HeapAlloc dt=6 heapalloc_value=19807120 +HeapAlloc dt=48 heapalloc_value=19815312 +HeapAlloc dt=8 heapalloc_value=19823504 +HeapAlloc dt=6 heapalloc_value=19831696 +HeapAlloc dt=7 heapalloc_value=19839888 +HeapAlloc dt=6 heapalloc_value=19848080 +HeapAlloc dt=7 heapalloc_value=19856272 +HeapAlloc dt=7 heapalloc_value=19864464 +HeapAlloc dt=7 heapalloc_value=19872656 +HeapAlloc dt=6 heapalloc_value=19880848 +HeapAlloc dt=70 heapalloc_value=19889040 +HeapAlloc dt=8 heapalloc_value=19897232 +HeapAlloc dt=7 heapalloc_value=19905424 +HeapAlloc dt=8 heapalloc_value=19913616 +HeapAlloc dt=9 heapalloc_value=19921808 +HeapAlloc dt=6 heapalloc_value=19930000 +HeapAlloc dt=7 heapalloc_value=19938192 +HeapAlloc dt=6 heapalloc_value=19946384 +HeapAlloc dt=7 heapalloc_value=19954576 +HeapAlloc dt=7 heapalloc_value=19962768 +HeapAlloc dt=6 heapalloc_value=19970960 +HeapAlloc dt=7 heapalloc_value=19979152 +HeapAlloc dt=7 heapalloc_value=19987344 +HeapAlloc dt=6 heapalloc_value=19995536 +HeapAlloc dt=7 heapalloc_value=20003728 +HeapAlloc dt=6 heapalloc_value=20011920 +HeapAlloc dt=7 heapalloc_value=20020112 +HeapAlloc dt=7 heapalloc_value=20028304 +HeapAlloc dt=7 heapalloc_value=20036496 +HeapAlloc dt=7 heapalloc_value=20044688 +HeapAlloc dt=6 heapalloc_value=20052880 +HeapAlloc dt=177 heapalloc_value=20061072 +HeapAlloc dt=8 heapalloc_value=20069264 +HeapAlloc dt=7 heapalloc_value=20077456 +HeapAlloc dt=7 heapalloc_value=20085648 +HeapAlloc dt=6 heapalloc_value=20093840 +HeapAlloc dt=7 heapalloc_value=20102032 +HeapAlloc dt=7 heapalloc_value=20110224 +HeapAlloc dt=46 heapalloc_value=20118416 +HeapAlloc dt=8 heapalloc_value=20126608 +HeapAlloc dt=6 heapalloc_value=20134800 +HeapAlloc dt=7 heapalloc_value=20142992 +HeapAlloc dt=494 heapalloc_value=20151184 +HeapAlloc dt=13 heapalloc_value=20159376 +HeapAlloc dt=8 heapalloc_value=20167568 +HeapAlloc dt=6 heapalloc_value=20175760 +HeapAlloc dt=93 heapalloc_value=20183952 +HeapAlloc dt=7 heapalloc_value=20192144 +HeapAlloc dt=6 heapalloc_value=20200336 +HeapAlloc dt=7 heapalloc_value=20208528 +HeapAlloc dt=7 heapalloc_value=20216720 +HeapAlloc dt=6 heapalloc_value=20224912 +HeapAlloc dt=46 heapalloc_value=20233104 +HeapAlloc dt=8 heapalloc_value=20241296 +HeapAlloc dt=6 heapalloc_value=20249488 +HeapAlloc dt=7 heapalloc_value=20257680 +HeapAlloc dt=7 heapalloc_value=20265872 +HeapAlloc dt=6 heapalloc_value=20274064 +HeapAlloc dt=7 heapalloc_value=20282256 +HeapAlloc dt=7 heapalloc_value=20290448 +HeapAlloc dt=7 heapalloc_value=20298640 +HeapAlloc dt=6 heapalloc_value=20306832 +HeapAlloc dt=7 heapalloc_value=20315024 +HeapAlloc dt=6 heapalloc_value=20323216 +HeapAlloc dt=7 heapalloc_value=20331408 +HeapAlloc dt=6 heapalloc_value=20339600 +HeapAlloc dt=7 heapalloc_value=20347792 +HeapAlloc dt=7 heapalloc_value=20355984 +HeapAlloc dt=6 heapalloc_value=20364176 +HeapAlloc dt=7 heapalloc_value=20372368 +HeapAlloc dt=7 heapalloc_value=20380560 +HeapAlloc dt=6 heapalloc_value=20388752 +HeapAlloc dt=7 heapalloc_value=20396944 +HeapAlloc dt=7 heapalloc_value=20405136 +HeapAlloc dt=68 heapalloc_value=20413328 +HeapAlloc dt=8 heapalloc_value=20421520 +HeapAlloc dt=6 heapalloc_value=20429712 +HeapAlloc dt=7 heapalloc_value=20437904 +HeapAlloc dt=7 heapalloc_value=20446096 +HeapAlloc dt=6 heapalloc_value=20454288 +HeapAlloc dt=7 heapalloc_value=20462480 +HeapAlloc dt=7 heapalloc_value=20470672 +HeapAlloc dt=7 heapalloc_value=20478864 +HeapAlloc dt=6 heapalloc_value=20487056 +HeapAlloc dt=7 heapalloc_value=20495248 +HeapAlloc dt=7 heapalloc_value=20503440 +HeapAlloc dt=6 heapalloc_value=20511632 +HeapAlloc dt=7 heapalloc_value=20519824 +HeapAlloc dt=7 heapalloc_value=20528016 +HeapAlloc dt=6 heapalloc_value=20536208 +HeapAlloc dt=7 heapalloc_value=20544400 +HeapAlloc dt=7 heapalloc_value=20552592 +HeapAlloc dt=6 heapalloc_value=20560784 +HeapAlloc dt=7 heapalloc_value=20568976 +GoBlock dt=13 reason_string=19 stack=21 +ProcStop dt=190 +ProcStart dt=17383 p=2 p_seq=26 +ProcStop dt=13 +ProcStart dt=1688 p=0 p_seq=50 +ProcStop dt=9 +ProcStart dt=16778 p=7 p_seq=1 +GoStart dt=16 g=12 g_seq=1 +GoStop dt=300490 reason_string=16 stack=52 +GoStart dt=19 g=12 g_seq=2 +GoStop dt=316380 reason_string=16 stack=52 +GoStart dt=17 g=12 g_seq=3 +GoDestroy dt=164357 +ProcStop dt=28 +ProcStart dt=1828 p=7 p_seq=2 +ProcStop dt=17 +ProcStart dt=81856 p=5 p_seq=4 +ProcStop dt=18 +ProcStart dt=688851 p=6 p_seq=10 +ProcStop dt=18 +ProcStart dt=16754 p=6 p_seq=11 +HeapAlloc dt=31 heapalloc_value=23313224 +GoCreate dt=50 new_g=67 new_stack=64 stack=0 +GoStart dt=169 g=67 g_seq=1 +GoSyscallBegin dt=30 p_seq=12 stack=65 +GoSyscallEnd dt=257 +GoDestroy dt=6 +ProcStop dt=8 +ProcStart dt=1281 p=6 p_seq=13 +ProcStop dt=20 +ProcStart dt=3460 p=6 p_seq=15 +GoStart dt=1376 g=82 g_seq=1 +GoSyscallBegin dt=34 p_seq=16 stack=79 +GoSyscallEnd dt=172 +HeapAlloc dt=3741 heapalloc_value=23466520 +HeapAlloc dt=37 heapalloc_value=23474712 +GoSyscallBegin dt=381 p_seq=17 stack=88 +GoSyscallEnd dt=29 +GoSyscallBegin dt=9 p_seq=18 stack=89 +GoSyscallEnd dt=33 +GoSyscallBegin dt=6 p_seq=19 stack=90 +GoSyscallEnd dt=20 +GoSyscallBegin dt=5 p_seq=20 stack=91 +GoSyscallEnd dt=25 +GoBlock dt=19 reason_string=19 stack=92 +ProcStop dt=69 +ProcStart dt=17618 p=7 p_seq=26 +ProcStop dt=33 +ProcStart dt=468 p=7 p_seq=27 +GoUnblock dt=5 g=1 g_seq=66 stack=0 +GoStart dt=5 g=1 g_seq=67 +GoSyscallBegin dt=14 p_seq=28 stack=86 +GoSyscallEnd dt=70 +GoSyscallBegin dt=82 p_seq=29 stack=95 +GoSyscallEnd dt=579 +HeapAlloc dt=276 heapalloc_value=23482840 +EventBatch gen=1 m=2852340 time=420901451500 size=3466 +ProcStart dt=290 p=0 p_seq=1 +GoStart dt=264 g=19 g_seq=1 +GoBlock dt=542 reason_string=12 stack=11 +GoStart dt=20 g=20 g_seq=1 +GoBlock dt=922 reason_string=12 stack=16 +GoStart dt=19 g=22 g_seq=1 +GoDestroy dt=156281 +ProcStop dt=46 +ProcStart dt=66693 p=0 p_seq=2 +ProcStop dt=20 +ProcStart dt=89853 p=0 p_seq=3 +ProcStop dt=20 +ProcStart dt=17575 p=1 p_seq=1 +ProcStop dt=20 +ProcStart dt=2159 p=0 p_seq=6 +ProcStop dt=13 +ProcStart dt=16751 p=0 p_seq=7 +GoUnblock dt=32 g=1 g_seq=5 stack=0 +GoStart dt=182 g=1 g_seq=6 +HeapAlloc dt=62 heapalloc_value=2842624 +HeapAlloc dt=25 heapalloc_value=2850816 +HeapAlloc dt=14 heapalloc_value=2859008 +HeapAlloc dt=11 heapalloc_value=2867200 +HeapAlloc dt=8 heapalloc_value=2875392 +HeapAlloc dt=9 heapalloc_value=2883584 +HeapAlloc dt=9 heapalloc_value=2891776 +HeapAlloc dt=8 heapalloc_value=2899968 +HeapAlloc dt=11 heapalloc_value=2908160 +HeapAlloc dt=102 heapalloc_value=2916352 +HeapAlloc dt=10 heapalloc_value=2924544 +HeapAlloc dt=13 heapalloc_value=2932736 +HeapAlloc dt=7 heapalloc_value=2940928 +HeapAlloc dt=8 heapalloc_value=2949120 +HeapAlloc dt=9 heapalloc_value=2957312 +HeapAlloc dt=8 heapalloc_value=2965504 +HeapAlloc dt=90 heapalloc_value=2973696 +HeapAlloc dt=9 heapalloc_value=2981888 +HeapAlloc dt=7 heapalloc_value=2990080 +HeapAlloc dt=8 heapalloc_value=2998272 +HeapAlloc dt=7 heapalloc_value=3006464 +HeapAlloc dt=6 heapalloc_value=3014656 +HeapAlloc dt=48 heapalloc_value=3022848 +HeapAlloc dt=7 heapalloc_value=3031040 +HeapAlloc dt=9 heapalloc_value=3039232 +HeapAlloc dt=6 heapalloc_value=3047424 +HeapAlloc dt=7 heapalloc_value=3055616 +HeapAlloc dt=8 heapalloc_value=3063808 +HeapAlloc dt=7 heapalloc_value=3072000 +HeapAlloc dt=7 heapalloc_value=3080192 +HeapAlloc dt=6 heapalloc_value=3088384 +HeapAlloc dt=7 heapalloc_value=3096576 +HeapAlloc dt=8 heapalloc_value=3104768 +HeapAlloc dt=8 heapalloc_value=3112960 +HeapAlloc dt=6 heapalloc_value=3121152 +HeapAlloc dt=7 heapalloc_value=3129344 +HeapAlloc dt=6 heapalloc_value=3137536 +HeapAlloc dt=6 heapalloc_value=3145728 +HeapAlloc dt=7 heapalloc_value=3153920 +HeapAlloc dt=6 heapalloc_value=3162112 +HeapAlloc dt=9 heapalloc_value=3170304 +HeapAlloc dt=6 heapalloc_value=3178496 +HeapAlloc dt=6 heapalloc_value=3186688 +HeapAlloc dt=7 heapalloc_value=3194880 +HeapAlloc dt=6 heapalloc_value=3203072 +HeapAlloc dt=7 heapalloc_value=3211264 +HeapAlloc dt=730 heapalloc_value=3260416 +HeapAlloc dt=3100 heapalloc_value=3268608 +HeapAlloc dt=18 heapalloc_value=3276800 +HeapAlloc dt=102 heapalloc_value=3284992 +HeapAlloc dt=9 heapalloc_value=3293184 +HeapAlloc dt=7 heapalloc_value=3301376 +HeapAlloc dt=6 heapalloc_value=3309568 +HeapAlloc dt=7 heapalloc_value=3317760 +HeapAlloc dt=6 heapalloc_value=3325952 +HeapAlloc dt=6 heapalloc_value=3334144 +HeapAlloc dt=7 heapalloc_value=3342336 +HeapAlloc dt=6 heapalloc_value=3350528 +HeapAlloc dt=7 heapalloc_value=3358720 +HeapAlloc dt=6 heapalloc_value=3366912 +HeapAlloc dt=44 heapalloc_value=3375104 +HeapAlloc dt=8 heapalloc_value=3383296 +HeapAlloc dt=6 heapalloc_value=3391488 +HeapAlloc dt=7 heapalloc_value=3399680 +HeapAlloc dt=6 heapalloc_value=3407872 +HeapAlloc dt=7 heapalloc_value=3416064 +HeapAlloc dt=7 heapalloc_value=3424256 +HeapAlloc dt=6 heapalloc_value=3432448 +HeapAlloc dt=6 heapalloc_value=3440640 +HeapAlloc dt=7 heapalloc_value=3448832 +HeapAlloc dt=6 heapalloc_value=3457024 +HeapAlloc dt=7 heapalloc_value=3465216 +HeapAlloc dt=6 heapalloc_value=3473408 +HeapAlloc dt=6 heapalloc_value=3481600 +HeapAlloc dt=7 heapalloc_value=3489792 +HeapAlloc dt=73 heapalloc_value=3497984 +HeapAlloc dt=8 heapalloc_value=3506176 +HeapAlloc dt=7 heapalloc_value=3514368 +HeapAlloc dt=6 heapalloc_value=3522560 +HeapAlloc dt=6 heapalloc_value=3530752 +HeapAlloc dt=7 heapalloc_value=3538944 +HeapAlloc dt=7 heapalloc_value=3547136 +HeapAlloc dt=6 heapalloc_value=3555328 +HeapAlloc dt=7 heapalloc_value=3563520 +HeapAlloc dt=6 heapalloc_value=3571712 +HeapAlloc dt=6 heapalloc_value=3579904 +HeapAlloc dt=47 heapalloc_value=3588096 +HeapAlloc dt=7 heapalloc_value=3596288 +HeapAlloc dt=6 heapalloc_value=3604480 +HeapAlloc dt=7 heapalloc_value=3612672 +HeapAlloc dt=6 heapalloc_value=3620864 +HeapAlloc dt=7 heapalloc_value=3629056 +HeapAlloc dt=6 heapalloc_value=3637248 +HeapAlloc dt=7 heapalloc_value=3645440 +HeapAlloc dt=6 heapalloc_value=3653632 +HeapAlloc dt=6 heapalloc_value=3661824 +HeapAlloc dt=7 heapalloc_value=3670016 +HeapAlloc dt=6 heapalloc_value=3678208 +HeapAlloc dt=6 heapalloc_value=3686400 +HeapAlloc dt=7 heapalloc_value=3694592 +HeapAlloc dt=6 heapalloc_value=3702784 +HeapAlloc dt=7 heapalloc_value=3710976 +HeapAlloc dt=6 heapalloc_value=3719168 +HeapAlloc dt=6 heapalloc_value=3727360 +HeapAlloc dt=7 heapalloc_value=3735552 +HeapAlloc dt=6 heapalloc_value=3743744 +HeapAlloc dt=9 heapalloc_value=3751936 +HeapAlloc dt=72 heapalloc_value=3760128 +HeapAlloc dt=8 heapalloc_value=3768320 +HeapAlloc dt=7 heapalloc_value=3776512 +HeapAlloc dt=6 heapalloc_value=3784704 +HeapAlloc dt=7 heapalloc_value=3792896 +HeapAlloc dt=8 heapalloc_value=3801088 +HeapAlloc dt=64 heapalloc_value=3809280 +HeapAlloc dt=8 heapalloc_value=3817472 +HeapAlloc dt=6 heapalloc_value=3825664 +HeapAlloc dt=7 heapalloc_value=3833856 +HeapAlloc dt=6 heapalloc_value=3842048 +HeapAlloc dt=7 heapalloc_value=3850240 +HeapAlloc dt=6 heapalloc_value=3858432 +HeapAlloc dt=6 heapalloc_value=3866624 +HeapAlloc dt=7 heapalloc_value=3874816 +HeapAlloc dt=7 heapalloc_value=3883008 +HeapAlloc dt=6 heapalloc_value=3891200 +HeapAlloc dt=7 heapalloc_value=3899392 +HeapAlloc dt=6 heapalloc_value=3907584 +GoBlock dt=19 reason_string=19 stack=21 +ProcStop dt=232 +ProcStart dt=17560 p=1 p_seq=3 +ProcStop dt=23 +ProcStart dt=25946 p=0 p_seq=28 +ProcStop dt=14 +ProcStart dt=16839 p=0 p_seq=29 +GoUnblock dt=21 g=1 g_seq=20 stack=0 +GoStart dt=209 g=1 g_seq=21 +HeapAlloc dt=60 heapalloc_value=4452592 +HeapAlloc dt=28 heapalloc_value=4460784 +HeapAlloc dt=29 heapalloc_value=4468976 +HeapAlloc dt=17 heapalloc_value=4477168 +HeapAlloc dt=19 heapalloc_value=4485360 +HeapAlloc dt=26 heapalloc_value=4493552 +HeapAlloc dt=17 heapalloc_value=4501744 +HeapAlloc dt=21 heapalloc_value=4509936 +HeapAlloc dt=22 heapalloc_value=4518128 +HeapAlloc dt=22 heapalloc_value=4526320 +HeapAlloc dt=36 heapalloc_value=4624624 +HeapAlloc dt=174 heapalloc_value=4632816 +HeapAlloc dt=22 heapalloc_value=4641008 +HeapAlloc dt=23 heapalloc_value=4649200 +HeapAlloc dt=144 heapalloc_value=4657392 +HeapAlloc dt=18 heapalloc_value=4665584 +HeapAlloc dt=19 heapalloc_value=4673776 +HeapAlloc dt=59 heapalloc_value=4681968 +HeapAlloc dt=15 heapalloc_value=4690160 +HeapAlloc dt=10 heapalloc_value=4698352 +HeapAlloc dt=20 heapalloc_value=4706544 +HeapAlloc dt=37 heapalloc_value=4714736 +HeapAlloc dt=33 heapalloc_value=4722928 +HeapAlloc dt=32 heapalloc_value=4731120 +HeapAlloc dt=30 heapalloc_value=4739312 +HeapAlloc dt=33 heapalloc_value=4747504 +HeapAlloc dt=28 heapalloc_value=4755696 +HeapAlloc dt=10 heapalloc_value=4763888 +HeapAlloc dt=9 heapalloc_value=4772080 +HeapAlloc dt=10 heapalloc_value=4780272 +HeapAlloc dt=10 heapalloc_value=4788464 +HeapAlloc dt=12 heapalloc_value=4796656 +HeapAlloc dt=11 heapalloc_value=4804848 +HeapAlloc dt=9 heapalloc_value=4813040 +HeapAlloc dt=9 heapalloc_value=4821232 +HeapAlloc dt=9 heapalloc_value=4829424 +HeapAlloc dt=9 heapalloc_value=4837616 +HeapAlloc dt=10 heapalloc_value=4845808 +HeapAlloc dt=10 heapalloc_value=4854000 +HeapAlloc dt=105 heapalloc_value=4862192 +HeapAlloc dt=13 heapalloc_value=4870384 +HeapAlloc dt=10 heapalloc_value=4878576 +HeapAlloc dt=9 heapalloc_value=4886768 +HeapAlloc dt=59 heapalloc_value=4894960 +HeapAlloc dt=11 heapalloc_value=4903152 +HeapAlloc dt=10 heapalloc_value=4911344 +HeapAlloc dt=9 heapalloc_value=4919536 +HeapAlloc dt=11 heapalloc_value=4927728 +HeapAlloc dt=12 heapalloc_value=4935920 +HeapAlloc dt=10 heapalloc_value=4944112 +HeapAlloc dt=9 heapalloc_value=4952304 +HeapAlloc dt=10 heapalloc_value=4960496 +HeapAlloc dt=9 heapalloc_value=4968688 +HeapAlloc dt=10 heapalloc_value=4976880 +HeapAlloc dt=9 heapalloc_value=4985072 +HeapAlloc dt=12 heapalloc_value=4993264 +HeapAlloc dt=9 heapalloc_value=5001456 +HeapAlloc dt=9 heapalloc_value=5009648 +HeapAlloc dt=10 heapalloc_value=5017840 +HeapAlloc dt=9 heapalloc_value=5026032 +HeapAlloc dt=10 heapalloc_value=5034224 +HeapAlloc dt=9 heapalloc_value=5042416 +HeapAlloc dt=10 heapalloc_value=5050608 +HeapAlloc dt=11 heapalloc_value=5058800 +HeapAlloc dt=10 heapalloc_value=5066992 +HeapAlloc dt=14 heapalloc_value=5075184 +HeapAlloc dt=9 heapalloc_value=5083376 +HeapAlloc dt=10 heapalloc_value=5091568 +HeapAlloc dt=9 heapalloc_value=5099760 +HeapAlloc dt=10 heapalloc_value=5107952 +HeapAlloc dt=10 heapalloc_value=5116144 +HeapAlloc dt=21 heapalloc_value=5124336 +HeapAlloc dt=11 heapalloc_value=5132528 +HeapAlloc dt=8 heapalloc_value=5140720 +HeapAlloc dt=7 heapalloc_value=5148912 +HeapAlloc dt=8 heapalloc_value=5157104 +HeapAlloc dt=9 heapalloc_value=5165296 +HeapAlloc dt=10 heapalloc_value=5173488 +HeapAlloc dt=78 heapalloc_value=5181680 +HeapAlloc dt=11 heapalloc_value=5189872 +HeapAlloc dt=7 heapalloc_value=5198064 +HeapAlloc dt=8 heapalloc_value=5206256 +HeapAlloc dt=8 heapalloc_value=5214448 +HeapAlloc dt=7 heapalloc_value=5222640 +HeapAlloc dt=10 heapalloc_value=5230832 +HeapAlloc dt=7 heapalloc_value=5239024 +HeapAlloc dt=8 heapalloc_value=5247216 +HeapAlloc dt=7 heapalloc_value=5255408 +HeapAlloc dt=8 heapalloc_value=5263600 +HeapAlloc dt=7 heapalloc_value=5271792 +HeapAlloc dt=8 heapalloc_value=5279984 +HeapAlloc dt=8 heapalloc_value=5288176 +HeapAlloc dt=96 heapalloc_value=5296368 +HeapAlloc dt=11 heapalloc_value=5304560 +HeapAlloc dt=53 heapalloc_value=5312752 +HeapAlloc dt=9 heapalloc_value=5320944 +HeapAlloc dt=8 heapalloc_value=5329136 +HeapAlloc dt=8 heapalloc_value=5337328 +HeapAlloc dt=7 heapalloc_value=5345520 +HeapAlloc dt=8 heapalloc_value=5353712 +HeapAlloc dt=9 heapalloc_value=5361904 +HeapAlloc dt=8 heapalloc_value=5370096 +HeapAlloc dt=7 heapalloc_value=5378288 +HeapAlloc dt=8 heapalloc_value=5386480 +HeapAlloc dt=7 heapalloc_value=5394672 +HeapAlloc dt=7 heapalloc_value=5402864 +HeapAlloc dt=8 heapalloc_value=5411056 +HeapAlloc dt=8 heapalloc_value=5419248 +HeapAlloc dt=9 heapalloc_value=5427440 +HeapAlloc dt=7 heapalloc_value=5435632 +HeapAlloc dt=8 heapalloc_value=5443824 +HeapAlloc dt=7 heapalloc_value=5452016 +HeapAlloc dt=8 heapalloc_value=5460208 +HeapAlloc dt=7 heapalloc_value=5468400 +HeapAlloc dt=8 heapalloc_value=5476592 +HeapAlloc dt=7 heapalloc_value=5484784 +HeapAlloc dt=10 heapalloc_value=5492976 +HeapAlloc dt=8 heapalloc_value=5501168 +HeapAlloc dt=7 heapalloc_value=5509360 +HeapAlloc dt=8 heapalloc_value=5517552 +HeapAlloc dt=7 heapalloc_value=5525744 +HeapAlloc dt=8 heapalloc_value=5533936 +HeapAlloc dt=8 heapalloc_value=5542128 +HeapAlloc dt=8 heapalloc_value=5550320 +HeapAlloc dt=84 heapalloc_value=5558512 +HeapAlloc dt=10 heapalloc_value=5566704 +GoBlock dt=17 reason_string=19 stack=21 +ProcStop dt=237 +ProcStart dt=17614 p=1 p_seq=17 +ProcStop dt=23 +ProcStart dt=4599 p=0 p_seq=32 +ProcStop dt=14 +ProcStart dt=16769 p=0 p_seq=33 +GoUnblock dt=22 g=1 g_seq=24 stack=0 +GoStart dt=189 g=1 g_seq=25 +HeapAlloc dt=55 heapalloc_value=6729968 +HeapAlloc dt=24 heapalloc_value=6738160 +HeapAlloc dt=12 heapalloc_value=6746352 +HeapAlloc dt=9 heapalloc_value=6754544 +HeapAlloc dt=10 heapalloc_value=6762736 +HeapAlloc dt=11 heapalloc_value=6770928 +HeapAlloc dt=8 heapalloc_value=6779120 +HeapAlloc dt=12 heapalloc_value=6787312 +HeapAlloc dt=8 heapalloc_value=6795504 +HeapAlloc dt=7 heapalloc_value=6803696 +HeapAlloc dt=9 heapalloc_value=6811888 +HeapAlloc dt=6 heapalloc_value=6820080 +HeapAlloc dt=6 heapalloc_value=6828272 +HeapAlloc dt=6 heapalloc_value=6836464 +HeapAlloc dt=6 heapalloc_value=6844656 +HeapAlloc dt=6 heapalloc_value=6852848 +HeapAlloc dt=6 heapalloc_value=6861040 +HeapAlloc dt=595 heapalloc_value=6869232 +HeapAlloc dt=89 heapalloc_value=6877424 +HeapAlloc dt=8 heapalloc_value=6885616 +HeapAlloc dt=7 heapalloc_value=6893808 +HeapAlloc dt=8 heapalloc_value=6902000 +HeapAlloc dt=43 heapalloc_value=6910192 +HeapAlloc dt=7 heapalloc_value=6918384 +HeapAlloc dt=6 heapalloc_value=6926576 +HeapAlloc dt=7 heapalloc_value=6934768 +HeapAlloc dt=6 heapalloc_value=6942960 +HeapAlloc dt=6 heapalloc_value=6951152 +HeapAlloc dt=6 heapalloc_value=6959344 +HeapAlloc dt=6 heapalloc_value=6967536 +HeapAlloc dt=6 heapalloc_value=6975728 +HeapAlloc dt=5 heapalloc_value=6983920 +HeapAlloc dt=6 heapalloc_value=6992112 +HeapAlloc dt=6 heapalloc_value=7000304 +HeapAlloc dt=6 heapalloc_value=7008496 +HeapAlloc dt=6 heapalloc_value=7016688 +HeapAlloc dt=6 heapalloc_value=7024880 +HeapAlloc dt=8 heapalloc_value=7033072 +HeapAlloc dt=5 heapalloc_value=7041264 +HeapAlloc dt=6 heapalloc_value=7049456 +HeapAlloc dt=6 heapalloc_value=7057648 +HeapAlloc dt=6 heapalloc_value=7065840 +HeapAlloc dt=5 heapalloc_value=7074032 +HeapAlloc dt=6 heapalloc_value=7082224 +HeapAlloc dt=6 heapalloc_value=7090416 +HeapAlloc dt=6 heapalloc_value=7098608 +HeapAlloc dt=5 heapalloc_value=7106800 +HeapAlloc dt=43 heapalloc_value=7114992 +HeapAlloc dt=7 heapalloc_value=7123184 +HeapAlloc dt=74 heapalloc_value=7131376 +HeapAlloc dt=8 heapalloc_value=7139568 +HeapAlloc dt=6 heapalloc_value=7147760 +HeapAlloc dt=6 heapalloc_value=7155952 +HeapAlloc dt=5 heapalloc_value=7164144 +HeapAlloc dt=6 heapalloc_value=7172336 +HeapAlloc dt=6 heapalloc_value=7180528 +HeapAlloc dt=6 heapalloc_value=7188720 +HeapAlloc dt=6 heapalloc_value=7196912 +HeapAlloc dt=379 heapalloc_value=7368944 +HeapAlloc dt=4398 heapalloc_value=7377136 +HeapAlloc dt=19 heapalloc_value=7385328 +HeapAlloc dt=14 heapalloc_value=7393520 +HeapAlloc dt=16 heapalloc_value=7401712 +HeapAlloc dt=12 heapalloc_value=7409904 +HeapAlloc dt=11 heapalloc_value=7418096 +HeapAlloc dt=13 heapalloc_value=7426288 +HeapAlloc dt=12 heapalloc_value=7434480 +HeapAlloc dt=12 heapalloc_value=7442672 +HeapAlloc dt=11 heapalloc_value=7450864 +HeapAlloc dt=12 heapalloc_value=7459056 +HeapAlloc dt=13 heapalloc_value=7467248 +HeapAlloc dt=13 heapalloc_value=7475440 +HeapAlloc dt=12 heapalloc_value=7483632 +HeapAlloc dt=13 heapalloc_value=7491824 +HeapAlloc dt=12 heapalloc_value=7500016 +HeapAlloc dt=12 heapalloc_value=7508208 +HeapAlloc dt=11 heapalloc_value=7516400 +HeapAlloc dt=12 heapalloc_value=7524592 +HeapAlloc dt=14 heapalloc_value=7532784 +HeapAlloc dt=12 heapalloc_value=7540976 +HeapAlloc dt=12 heapalloc_value=7549168 +HeapAlloc dt=13 heapalloc_value=7557360 +HeapAlloc dt=96 heapalloc_value=7565552 +HeapAlloc dt=9 heapalloc_value=7573744 +HeapAlloc dt=7 heapalloc_value=7581936 +HeapAlloc dt=6 heapalloc_value=7590128 +HeapAlloc dt=6 heapalloc_value=7598320 +HeapAlloc dt=6 heapalloc_value=7606512 +HeapAlloc dt=6 heapalloc_value=7614704 +HeapAlloc dt=6 heapalloc_value=7622896 +HeapAlloc dt=7 heapalloc_value=7631088 +HeapAlloc dt=6 heapalloc_value=7639280 +HeapAlloc dt=6 heapalloc_value=7647472 +HeapAlloc dt=81 heapalloc_value=7655664 +HeapAlloc dt=8 heapalloc_value=7663856 +HeapAlloc dt=6 heapalloc_value=7672048 +HeapAlloc dt=6 heapalloc_value=7680240 +HeapAlloc dt=6 heapalloc_value=7688432 +HeapAlloc dt=6 heapalloc_value=7696624 +HeapAlloc dt=45 heapalloc_value=7704816 +HeapAlloc dt=8 heapalloc_value=7713008 +HeapAlloc dt=6 heapalloc_value=7721200 +HeapAlloc dt=6 heapalloc_value=7729392 +HeapAlloc dt=6 heapalloc_value=7737584 +HeapAlloc dt=6 heapalloc_value=7745776 +HeapAlloc dt=6 heapalloc_value=7753968 +HeapAlloc dt=6 heapalloc_value=7762160 +HeapAlloc dt=6 heapalloc_value=7770352 +HeapAlloc dt=6 heapalloc_value=7778544 +HeapAlloc dt=6 heapalloc_value=7786736 +HeapAlloc dt=6 heapalloc_value=7794928 +HeapAlloc dt=6 heapalloc_value=7803120 +HeapAlloc dt=6 heapalloc_value=7811312 +HeapAlloc dt=6 heapalloc_value=7819504 +HeapAlloc dt=6 heapalloc_value=7827696 +HeapAlloc dt=6 heapalloc_value=7835888 +HeapAlloc dt=6 heapalloc_value=7844080 +HeapAlloc dt=6 heapalloc_value=7852272 +HeapAlloc dt=6 heapalloc_value=7860464 +HeapAlloc dt=6 heapalloc_value=7868656 +HeapAlloc dt=6 heapalloc_value=7876848 +HeapAlloc dt=6 heapalloc_value=7885040 +HeapAlloc dt=7 heapalloc_value=7893232 +HeapAlloc dt=5 heapalloc_value=7901424 +HeapAlloc dt=7 heapalloc_value=7909616 +HeapAlloc dt=72 heapalloc_value=7917808 +GCBegin dt=27 gc_seq=3 stack=41 +STWBegin dt=46 kind_string=22 stack=42 +GoUnblock dt=163 g=4 g_seq=3 stack=43 +ProcsChange dt=106 procs_value=8 stack=44 +STWEnd dt=26 +GCMarkAssistBegin dt=206 stack=30 +GCMarkAssistEnd dt=1163 +GoBlock dt=20 reason_string=19 stack=21 +GoStart dt=199 g=4 g_seq=4 +GoBlock dt=21 reason_string=15 stack=32 +GoUnblock dt=111 g=8 g_seq=2 stack=0 +GoStart dt=272 g=8 g_seq=3 +GoLabel dt=3 label_string=4 +GoBlock dt=553 reason_string=15 stack=26 +GoUnblock dt=12 g=1 g_seq=26 stack=0 +GoStart dt=9 g=1 g_seq=27 +HeapAlloc dt=172 heapalloc_value=7926000 +HeapAlloc dt=24 heapalloc_value=7934192 +HeapAlloc dt=9 heapalloc_value=7942384 +HeapAlloc dt=9 heapalloc_value=7950576 +HeapAlloc dt=15 heapalloc_value=7958768 +HeapAlloc dt=11 heapalloc_value=7966960 +HeapAlloc dt=12 heapalloc_value=7975152 +HeapAlloc dt=10 heapalloc_value=7983344 +HeapAlloc dt=11 heapalloc_value=7991536 +HeapAlloc dt=8 heapalloc_value=7999728 +HeapAlloc dt=7 heapalloc_value=8007920 +HeapAlloc dt=7 heapalloc_value=8016112 +HeapAlloc dt=9 heapalloc_value=8024304 +HeapAlloc dt=8 heapalloc_value=8032496 +HeapAlloc dt=9 heapalloc_value=8040688 +HeapAlloc dt=8 heapalloc_value=8048880 +HeapAlloc dt=7 heapalloc_value=8057072 +HeapAlloc dt=7 heapalloc_value=8065264 +HeapAlloc dt=8 heapalloc_value=8073456 +HeapAlloc dt=229 heapalloc_value=8081648 +HeapAlloc dt=13 heapalloc_value=8089840 +HeapAlloc dt=7 heapalloc_value=8098032 +HeapAlloc dt=7 heapalloc_value=8106224 +HeapAlloc dt=44 heapalloc_value=8114416 +HeapAlloc dt=9 heapalloc_value=8122608 +HeapAlloc dt=7 heapalloc_value=8130800 +HeapAlloc dt=7 heapalloc_value=8138992 +HeapAlloc dt=263 heapalloc_value=8147184 +HeapAlloc dt=9 heapalloc_value=8155376 +HeapAlloc dt=8 heapalloc_value=8163568 +HeapAlloc dt=7 heapalloc_value=8171760 +HeapAlloc dt=6 heapalloc_value=8179952 +HeapAlloc dt=42 heapalloc_value=8188144 +HeapAlloc dt=7 heapalloc_value=8196336 +HeapAlloc dt=7 heapalloc_value=8204528 +HeapAlloc dt=7 heapalloc_value=8212720 +HeapAlloc dt=8 heapalloc_value=8220912 +HeapAlloc dt=7 heapalloc_value=8229104 +HeapAlloc dt=7 heapalloc_value=8237296 +HeapAlloc dt=7 heapalloc_value=8245488 +HeapAlloc dt=7 heapalloc_value=8253680 +HeapAlloc dt=7 heapalloc_value=8261872 +HeapAlloc dt=6 heapalloc_value=8270064 +HeapAlloc dt=7 heapalloc_value=8278256 +HeapAlloc dt=7 heapalloc_value=8286448 +HeapAlloc dt=7 heapalloc_value=8294640 +HeapAlloc dt=7 heapalloc_value=8302832 +HeapAlloc dt=7 heapalloc_value=8311024 +HeapAlloc dt=50 heapalloc_value=8319216 +HeapAlloc dt=11 heapalloc_value=8327408 +HeapAlloc dt=14 heapalloc_value=8335600 +HeapAlloc dt=14 heapalloc_value=8343792 +HeapAlloc dt=11 heapalloc_value=8351984 +HeapAlloc dt=11 heapalloc_value=8360176 +HeapAlloc dt=13 heapalloc_value=8368368 +HeapAlloc dt=11 heapalloc_value=8376560 +HeapAlloc dt=11 heapalloc_value=8384752 +HeapAlloc dt=12 heapalloc_value=8392944 +HeapAlloc dt=11 heapalloc_value=8401136 +HeapAlloc dt=257 heapalloc_value=8409328 +HeapAlloc dt=19 heapalloc_value=8417520 +HeapAlloc dt=17 heapalloc_value=8425712 +HeapAlloc dt=15 heapalloc_value=8433904 +HeapAlloc dt=14 heapalloc_value=8442096 +HeapAlloc dt=50 heapalloc_value=8450288 +HeapAlloc dt=14 heapalloc_value=8458480 +HeapAlloc dt=14 heapalloc_value=8466672 +HeapAlloc dt=15 heapalloc_value=8474864 +HeapAlloc dt=14 heapalloc_value=8483056 +HeapAlloc dt=12 heapalloc_value=8491248 +HeapAlloc dt=12 heapalloc_value=8499440 +HeapAlloc dt=13 heapalloc_value=8507632 +HeapAlloc dt=14 heapalloc_value=8515824 +HeapAlloc dt=12 heapalloc_value=8524016 +HeapAlloc dt=13 heapalloc_value=8532208 +HeapAlloc dt=13 heapalloc_value=8540400 +HeapAlloc dt=13 heapalloc_value=8548592 +HeapAlloc dt=16 heapalloc_value=8556784 +HeapAlloc dt=14 heapalloc_value=8564976 +HeapAlloc dt=14 heapalloc_value=8573168 +HeapAlloc dt=16 heapalloc_value=8581360 +HeapAlloc dt=14 heapalloc_value=8589552 +HeapAlloc dt=14 heapalloc_value=8597744 +HeapAlloc dt=59 heapalloc_value=8605936 +HeapAlloc dt=15 heapalloc_value=8614128 +HeapAlloc dt=12 heapalloc_value=8622320 +HeapAlloc dt=12 heapalloc_value=8630512 +HeapAlloc dt=11 heapalloc_value=8638704 +HeapAlloc dt=15 heapalloc_value=8646896 +HeapAlloc dt=12 heapalloc_value=8655088 +HeapAlloc dt=11 heapalloc_value=8663280 +HeapAlloc dt=292 heapalloc_value=8671472 +HeapAlloc dt=14 heapalloc_value=8679664 +HeapAlloc dt=12 heapalloc_value=8687856 +HeapAlloc dt=11 heapalloc_value=8696048 +HeapAlloc dt=12 heapalloc_value=8704240 +HeapAlloc dt=44 heapalloc_value=8712432 +HeapAlloc dt=14 heapalloc_value=8720624 +HeapAlloc dt=11 heapalloc_value=8728816 +HeapAlloc dt=14 heapalloc_value=8737008 +HeapAlloc dt=52 heapalloc_value=8745200 +HeapAlloc dt=13 heapalloc_value=8753392 +HeapAlloc dt=13 heapalloc_value=8761584 +HeapAlloc dt=12 heapalloc_value=8769776 +HeapAlloc dt=15 heapalloc_value=8777968 +HeapAlloc dt=12 heapalloc_value=8786160 +HeapAlloc dt=14 heapalloc_value=8794352 +HeapAlloc dt=18 heapalloc_value=8802544 +GoStop dt=25 reason_string=16 stack=46 +GoStart dt=658 g=1 g_seq=28 +HeapAlloc dt=14 heapalloc_value=8810736 +HeapAlloc dt=24 heapalloc_value=8818928 +HeapAlloc dt=12 heapalloc_value=8827120 +HeapAlloc dt=13 heapalloc_value=8835312 +HeapAlloc dt=15 heapalloc_value=8843504 +HeapAlloc dt=12 heapalloc_value=8851696 +HeapAlloc dt=16 heapalloc_value=8859888 +HeapAlloc dt=14 heapalloc_value=8868080 +HeapAlloc dt=14 heapalloc_value=8876272 +HeapAlloc dt=13 heapalloc_value=8884464 +HeapAlloc dt=12 heapalloc_value=8892656 +HeapAlloc dt=13 heapalloc_value=8900848 +HeapAlloc dt=15 heapalloc_value=8909040 +HeapAlloc dt=13 heapalloc_value=8917232 +HeapAlloc dt=15 heapalloc_value=8925424 +HeapAlloc dt=101 heapalloc_value=8933616 +GCMarkAssistBegin dt=13 stack=30 +GoBlock dt=37 reason_string=10 stack=33 +ProcStop dt=16 +ProcStart dt=1559 p=0 p_seq=34 +GoStart dt=433 g=1 g_seq=30 +GCMarkAssistEnd dt=16 +HeapAlloc dt=13 heapalloc_value=8630512 +GCSweepBegin dt=342 stack=47 +GCSweepEnd dt=22 swept_value=131072 reclaimed_value=0 +GCSweepBegin dt=19 stack=38 +GCSweepEnd dt=412 swept_value=827392 reclaimed_value=0 +HeapAlloc dt=32 heapalloc_value=8638704 +GoBlock dt=26 reason_string=19 stack=21 +ProcStop dt=31 +ProcStart dt=4598 p=0 p_seq=35 +ProcStop dt=23 +ProcStart dt=60434 p=0 p_seq=39 +GoStart dt=172 g=4 g_seq=6 +GoBlock dt=37 reason_string=15 stack=32 +ProcStop dt=17 +ProcStart dt=50232 p=2 p_seq=21 +GoUnblock dt=21 g=25 g_seq=8 stack=0 +GoStart dt=277 g=25 g_seq=9 +GoLabel dt=1 label_string=2 +STWBegin dt=10275 kind_string=23 stack=34 +GoUnblock dt=637 g=34 g_seq=4 stack=35 +HeapAlloc dt=30 heapalloc_value=16793488 +GoUnblock dt=20 g=3 g_seq=5 stack=36 +GCEnd dt=7 gc_seq=6 +HeapGoal dt=5 heapgoal_value=34005760 +ProcsChange dt=48 procs_value=8 stack=37 +STWEnd dt=40 +GoBlock dt=1283 reason_string=15 stack=26 +GoStart dt=14 g=3 g_seq=6 +GoBlock dt=10077 reason_string=14 stack=40 +ProcStop dt=21 +ProcStart dt=84537 p=2 p_seq=27 +GoStart dt=249 g=4 g_seq=10 +GoBlock dt=20 reason_string=15 stack=32 +ProcStop dt=102 +ProcStart dt=8641 p=2 p_seq=28 +GoStart dt=201 g=11 g_seq=1 +GoStop dt=305542 reason_string=16 stack=52 +GoStart dt=20 g=11 g_seq=2 +GoStop dt=316424 reason_string=16 stack=52 +GoStart dt=16 g=11 g_seq=3 +GoDestroy dt=159274 +ProcStop dt=45 +EventBatch gen=1 m=2852339 time=420901991582 size=23 +GoUnblock dt=137 g=4 g_seq=5 stack=0 +GoUnblock dt=157581 g=4 g_seq=9 stack=0 +ProcSteal dt=948232 p=6 p_seq=9 m=2852347 +EventBatch gen=1 m=2852338 time=420901450373 size=416 +ProcStatus dt=115 p=1 pstatus=1 +GoStatus dt=4 g=1 m=2852338 gstatus=2 +ProcsChange dt=217 procs_value=8 stack=1 +STWBegin dt=68 kind_string=21 stack=2 +HeapGoal dt=2 heapgoal_value=4194304 +ProcStatus dt=1 p=0 pstatus=2 +ProcStatus dt=4 p=2 pstatus=2 +ProcStatus dt=3 p=3 pstatus=2 +ProcStatus dt=1 p=4 pstatus=2 +ProcStatus dt=1 p=5 pstatus=2 +ProcStatus dt=1 p=6 pstatus=2 +ProcStatus dt=2 p=7 pstatus=2 +ProcsChange dt=65 procs_value=8 stack=3 +STWEnd dt=23 +HeapAlloc dt=190 heapalloc_value=1638400 +GoCreate dt=157 new_g=19 new_stack=4 stack=5 +GoCreate dt=565 new_g=20 new_stack=6 stack=7 +HeapAlloc dt=31 heapalloc_value=1646592 +GoCreate dt=523 new_g=21 new_stack=8 stack=9 +GoCreate dt=702 new_g=22 new_stack=10 stack=12 +GoCreate dt=377 new_g=23 new_stack=13 stack=14 +GoBlock dt=18 reason_string=10 stack=15 +GoStart dt=12 g=23 g_seq=1 +GoStop dt=222604 reason_string=16 stack=19 +GoStart dt=399 g=23 g_seq=2 +GoUnblock dt=89532 g=1 g_seq=1 stack=20 +GoDestroy dt=165 +GoStart dt=14 g=1 g_seq=2 +GoBlock dt=21 reason_string=19 stack=21 +ProcStop dt=257 +ProcStart dt=60623 p=1 p_seq=4 +GoStart dt=5704 g=5 g_seq=1 +HeapAlloc dt=34 heapalloc_value=4046848 +GoBlock dt=461 reason_string=15 stack=26 +ProcStop dt=25 +ProcStart dt=294 p=0 p_seq=14 +GoStart dt=9 g=6 g_seq=1 +GoBlock dt=506 reason_string=15 stack=26 +ProcStop dt=8 +ProcStart dt=230 p=0 p_seq=17 +ProcStop dt=23 +ProcStart dt=1406 p=0 p_seq=18 +GoStart dt=339 g=34 g_seq=1 +GoBlock dt=291 reason_string=15 stack=26 +ProcStop dt=13 +ProcStart dt=465 p=1 p_seq=10 +GoStart dt=173 g=8 g_seq=1 +GoBlock dt=120 reason_string=15 stack=26 +ProcStop dt=12 +ProcStart dt=353 p=0 p_seq=24 +ProcStop dt=17 +ProcStart dt=968 p=0 p_seq=25 +ProcStop dt=6 +ProcStart dt=554 p=0 p_seq=26 +GoUnblock dt=16 g=25 g_seq=2 stack=0 +GoStart dt=334 g=25 g_seq=3 +GoLabel dt=1 label_string=2 +GoBlock dt=2066 reason_string=15 stack=26 +ProcStop dt=24 +ProcStart dt=202889 p=4 p_seq=3 +GoUnblock dt=53 g=34 g_seq=2 stack=0 +HeapAlloc dt=50 heapalloc_value=16498416 +HeapAlloc dt=32 heapalloc_value=16501200 +HeapAlloc dt=4276 heapalloc_value=16697040 +GoStart dt=1376 g=34 g_seq=3 +GoLabel dt=2 label_string=4 +HeapAlloc dt=69 heapalloc_value=16713136 +GoBlock dt=35 reason_string=10 stack=48 +ProcStop dt=50 +ProcStart dt=111183 p=5 p_seq=3 +HeapAlloc dt=56 heapalloc_value=23234784 +HeapAlloc dt=25 heapalloc_value=23242144 +HeapAlloc dt=343 heapalloc_value=23249312 +GoStart dt=1532 g=16 g_seq=1 +GoStop dt=302128 reason_string=16 stack=52 +GoStart dt=13 g=16 g_seq=2 +GoStop dt=316412 reason_string=16 stack=52 +GoStart dt=10 g=16 g_seq=3 +GoDestroy dt=162712 +ProcStop dt=30 +ProcStart dt=798510 p=5 p_seq=5 +ProcStop dt=29 +ProcStart dt=4349 p=6 p_seq=21 +ProcStop dt=41 +ProcStart dt=16784 p=6 p_seq=22 +GoUnblock dt=14 g=82 g_seq=2 stack=0 +GoStart dt=161 g=82 g_seq=3 +GoSyscallBegin dt=27 p_seq=23 stack=93 +GoSyscallEnd dt=663 +GoSyscallBegin dt=102 p_seq=24 stack=94 +GoSyscallEnd dt=258 +GoDestroy dt=9 +ProcStop dt=24 +EventBatch gen=1 m=18446744073709551615 time=420903766597 size=28 +GoStatus dt=69 g=2 m=18446744073709551615 gstatus=4 +GoStatus dt=2 g=18 m=18446744073709551615 gstatus=4 +EventBatch gen=1 m=18446744073709551615 time=420903767161 size=4524 +Stacks +Stack id=20 nframes=3 + pc=4700772 func=24 file=25 line=81 + pc=5073062 func=26 file=25 line=87 + pc=5072984 func=27 file=28 line=105 +Stack id=71 nframes=21 + pc=4746903 func=29 file=30 line=736 + pc=4807597 func=31 file=32 line=181 + pc=4807573 func=33 file=34 line=736 + pc=4807216 func=35 file=34 line=160 + pc=4813553 func=36 file=37 line=29 + pc=4813545 func=38 file=39 line=118 + pc=4735439 func=40 file=41 line=335 + pc=5034447 func=42 file=41 line=354 + pc=5034407 func=43 file=44 line=55 + pc=5039623 func=45 file=46 line=40 + pc=5060942 func=47 file=48 line=373 + pc=4696033 func=49 file=50 line=74 + pc=5026795 func=51 file=50 line=65 + pc=5026766 func=52 file=48 line=373 + pc=5040478 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=21 nframes=3 + pc=4633684 func=64 file=65 line=195 + pc=5073512 func=66 file=28 line=125 + pc=5070323 func=63 file=28 line=32 +Stack id=38 nframes=9 + pc=4578453 func=67 file=68 line=352 + pc=4353540 func=69 file=70 line=521 + pc=4290908 func=71 file=72 line=147 + pc=4288626 func=73 file=74 line=182 + pc=4252996 func=75 file=76 line=948 + pc=4254486 func=77 file=76 line=1149 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=34 nframes=1 + pc=4314212 func=80 file=81 line=1446 +Stack id=13 nframes=1 + pc=5072896 func=27 file=28 line=102 +Stack id=48 nframes=2 + pc=4307927 func=82 file=81 line=807 + pc=4314212 func=80 file=81 line=1446 +Stack id=65 nframes=7 + pc=4747354 func=83 file=30 line=964 + pc=4808839 func=84 file=32 line=209 + pc=4808831 func=33 file=34 line=736 + pc=4808384 func=85 file=34 line=380 + pc=4813744 func=86 file=37 line=46 + pc=4813736 func=87 file=39 line=183 + pc=5072644 func=88 file=28 line=89 +Stack id=63 nframes=3 + pc=4746903 func=29 file=30 line=736 + pc=5072295 func=31 file=32 line=181 + pc=5072241 func=89 file=28 line=90 +Stack id=85 nframes=15 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5052502 func=94 file=95 line=21 + pc=5048264 func=96 file=97 line=245 + pc=5050832 func=98 file=58 line=114 + pc=5049860 func=99 file=58 line=68 + pc=5049861 func=100 file=58 line=64 + pc=4958364 func=101 file=61 line=651 + pc=4956653 func=102 file=61 line=616 + pc=4954291 func=103 file=61 line=517 + pc=4952889 func=104 file=61 line=508 + pc=4951126 func=105 file=61 line=434 + pc=4951127 func=106 file=61 line=401 + pc=5070980 func=63 file=28 line=68 +Stack id=1 nframes=4 + pc=4576875 func=107 file=68 line=255 + pc=4561423 func=108 file=109 line=237 + pc=5069285 func=110 file=111 line=125 + pc=5070075 func=63 file=28 line=20 +Stack id=7 nframes=4 + pc=4567652 func=112 file=109 line=868 + pc=4561732 func=108 file=109 line=258 + pc=5069285 func=110 file=111 line=125 + pc=5070075 func=63 file=28 line=20 +Stack id=88 nframes=8 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5052074 func=113 file=114 line=15 + pc=5048202 func=96 file=97 line=239 + pc=5051452 func=115 file=58 line=156 + pc=5048431 func=116 file=97 line=315 + pc=5071227 func=117 file=28 line=59 +Stack id=16 nframes=3 + pc=4225233 func=118 file=119 line=442 + pc=4568106 func=120 file=109 line=928 + pc=4567719 func=121 file=109 line=871 +Stack id=95 nframes=8 + pc=4746468 func=122 file=30 line=335 + pc=4806160 func=123 file=124 line=24 + pc=4806130 func=125 file=34 line=81 + pc=4803890 func=126 file=127 line=213 + pc=4806308 func=128 file=34 line=104 + pc=4988529 func=129 file=130 line=37 + pc=5026133 func=131 file=48 line=203 + pc=5071131 func=63 file=28 line=74 +Stack id=94 nframes=8 + pc=4746468 func=122 file=30 line=335 + pc=4806160 func=123 file=124 line=24 + pc=4806130 func=125 file=34 line=81 + pc=4803890 func=126 file=127 line=213 + pc=4806308 func=128 file=34 line=104 + pc=4988529 func=129 file=130 line=37 + pc=5026133 func=131 file=48 line=203 + pc=5071317 func=117 file=28 line=66 +Stack id=43 nframes=6 + pc=4637735 func=132 file=133 line=474 + pc=4307012 func=134 file=81 line=684 + pc=4255286 func=77 file=76 line=1292 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=26 nframes=2 + pc=4433005 func=135 file=136 line=402 + pc=4313604 func=80 file=81 line=1310 +Stack id=66 nframes=2 + pc=4221878 func=137 file=119 line=145 + pc=5072533 func=89 file=28 line=94 +Stack id=55 nframes=1 + pc=5070565 func=63 file=28 line=47 +Stack id=42 nframes=4 + pc=4255286 func=77 file=76 line=1292 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=14 nframes=1 + pc=5070300 func=63 file=28 line=28 +Stack id=56 nframes=2 + pc=4225233 func=118 file=119 line=442 + pc=5070586 func=63 file=28 line=48 +Stack id=24 nframes=6 + pc=4313307 func=138 file=81 line=1234 + pc=4306780 func=134 file=81 line=663 + pc=4255286 func=77 file=76 line=1292 + pc=4524282 func=139 file=79 line=246 + pc=5073584 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=45 nframes=1 + pc=0 func=0 file=0 line=0 +Stack id=44 nframes=5 + pc=4307322 func=134 file=81 line=746 + pc=4255286 func=77 file=76 line=1292 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=6 nframes=1 + pc=4567680 func=121 file=109 line=868 +Stack id=11 nframes=3 + pc=4225233 func=118 file=119 line=442 + pc=4568106 func=120 file=109 line=928 + pc=4570874 func=140 file=141 line=54 +Stack id=4 nframes=1 + pc=4570816 func=140 file=141 line=42 +Stack id=22 nframes=4 + pc=4255286 func=77 file=76 line=1292 + pc=4524282 func=139 file=79 line=246 + pc=5073584 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=83 nframes=15 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5047242 func=142 file=143 line=88 + pc=5048249 func=96 file=97 line=244 + pc=5050832 func=98 file=58 line=114 + pc=5049860 func=99 file=58 line=68 + pc=5049861 func=100 file=58 line=64 + pc=4958364 func=101 file=61 line=651 + pc=4956653 func=102 file=61 line=616 + pc=4954291 func=103 file=61 line=517 + pc=4952889 func=104 file=61 line=508 + pc=4951126 func=105 file=61 line=434 + pc=4951127 func=106 file=61 line=401 + pc=5070980 func=63 file=28 line=68 +Stack id=37 nframes=3 + pc=4310566 func=144 file=81 line=1087 + pc=4308676 func=82 file=81 line=927 + pc=4314212 func=80 file=81 line=1446 +Stack id=9 nframes=2 + pc=5069359 func=110 file=111 line=128 + pc=5070075 func=63 file=28 line=20 +Stack id=35 nframes=2 + pc=4308599 func=82 file=81 line=915 + pc=4314212 func=80 file=81 line=1446 +Stack id=72 nframes=20 + pc=4746468 func=122 file=30 line=335 + pc=4806160 func=123 file=124 line=24 + pc=4806130 func=125 file=34 line=81 + pc=4803890 func=126 file=127 line=213 + pc=4806308 func=128 file=34 line=104 + pc=4816631 func=145 file=146 line=315 + pc=5040044 func=147 file=37 line=23 + pc=5040027 func=148 file=44 line=23 + pc=5039886 func=45 file=46 line=53 + pc=5060942 func=47 file=48 line=373 + pc=4696033 func=49 file=50 line=74 + pc=5026795 func=51 file=50 line=65 + pc=5026766 func=52 file=48 line=373 + pc=5040478 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=3 nframes=4 + pc=4442379 func=149 file=136 line=1382 + pc=4561715 func=108 file=109 line=255 + pc=5069285 func=110 file=111 line=125 + pc=5070075 func=63 file=28 line=20 +Stack id=19 nframes=1 + pc=5072969 func=27 file=28 line=104 +Stack id=61 nframes=5 + pc=4746660 func=150 file=30 line=432 + pc=4737158 func=151 file=152 line=106 + pc=4806697 func=153 file=34 line=129 + pc=5072228 func=154 file=146 line=90 + pc=5072241 func=89 file=28 line=90 +Stack id=79 nframes=8 + pc=4748798 func=155 file=30 line=1421 + pc=4743029 func=156 file=157 line=684 + pc=4810951 func=158 file=159 line=17 + pc=4809725 func=160 file=34 line=602 + pc=4992776 func=161 file=162 line=172 + pc=5051421 func=115 file=58 line=152 + pc=5048431 func=116 file=97 line=315 + pc=5071227 func=117 file=28 line=59 +Stack id=91 nframes=8 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5052502 func=94 file=95 line=21 + pc=5048264 func=96 file=97 line=245 + pc=5051452 func=115 file=58 line=156 + pc=5048431 func=116 file=97 line=315 + pc=5071227 func=117 file=28 line=59 +Stack id=62 nframes=5 + pc=4746660 func=150 file=30 line=432 + pc=4737232 func=151 file=152 line=118 + pc=4806697 func=153 file=34 line=129 + pc=5072228 func=154 file=146 line=90 + pc=5072241 func=89 file=28 line=90 +Stack id=75 nframes=9 + pc=4748164 func=163 file=30 line=1213 + pc=5044432 func=164 file=54 line=170 + pc=5040531 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=73 nframes=11 + pc=4750130 func=90 file=30 line=1488 + pc=5046866 func=91 file=32 line=462 + pc=5046876 func=165 file=166 line=28 + pc=5043829 func=164 file=54 line=152 + pc=5040531 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=87 nframes=4 + pc=4807527 func=35 file=34 line=164 + pc=4988612 func=167 file=130 line=55 + pc=5025316 func=168 file=48 line=179 + pc=5071115 func=63 file=28 line=73 +Stack id=84 nframes=15 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5052347 func=94 file=95 line=18 + pc=5048264 func=96 file=97 line=245 + pc=5050832 func=98 file=58 line=114 + pc=5049860 func=99 file=58 line=68 + pc=5049861 func=100 file=58 line=64 + pc=4958364 func=101 file=61 line=651 + pc=4956653 func=102 file=61 line=616 + pc=4954291 func=103 file=61 line=517 + pc=4952889 func=104 file=61 line=508 + pc=4951126 func=105 file=61 line=434 + pc=4951127 func=106 file=61 line=401 + pc=5070980 func=63 file=28 line=68 +Stack id=92 nframes=2 + pc=4633684 func=64 file=65 line=195 + pc=5071262 func=117 file=28 line=63 +Stack id=40 nframes=2 + pc=4352030 func=169 file=136 line=408 + pc=4351996 func=170 file=70 line=317 +Stack id=89 nframes=8 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5047242 func=142 file=143 line=88 + pc=5048249 func=96 file=97 line=244 + pc=5051452 func=115 file=58 line=156 + pc=5048431 func=116 file=97 line=315 + pc=5071227 func=117 file=28 line=59 +Stack id=53 nframes=3 + pc=4700772 func=24 file=25 line=81 + pc=5071718 func=26 file=25 line=87 + pc=5071644 func=171 file=28 line=41 +Stack id=81 nframes=16 + pc=4749883 func=172 file=30 line=1478 + pc=4744812 func=173 file=32 line=313 + pc=4991029 func=174 file=162 line=149 + pc=5041979 func=175 file=54 line=124 + pc=5040762 func=53 file=54 line=70 + pc=5014167 func=55 file=56 line=154 + pc=5050219 func=98 file=58 line=78 + pc=5049860 func=99 file=58 line=68 + pc=5049861 func=100 file=58 line=64 + pc=4958364 func=101 file=61 line=651 + pc=4956653 func=102 file=61 line=616 + pc=4954291 func=103 file=61 line=517 + pc=4952889 func=104 file=61 line=508 + pc=4951126 func=105 file=61 line=434 + pc=4951127 func=106 file=61 line=401 + pc=5070980 func=63 file=28 line=68 +Stack id=23 nframes=1 + pc=4313376 func=80 file=81 line=1276 +Stack id=52 nframes=1 + pc=5071629 func=171 file=28 line=40 +Stack id=28 nframes=6 + pc=4637735 func=132 file=133 line=474 + pc=4307012 func=134 file=81 line=684 + pc=4255286 func=77 file=76 line=1292 + pc=4524282 func=139 file=79 line=246 + pc=5073584 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=12 nframes=1 + pc=5070234 func=63 file=28 line=27 +Stack id=76 nframes=1 + pc=5071200 func=117 file=28 line=58 +Stack id=60 nframes=5 + pc=4746660 func=150 file=30 line=432 + pc=4737232 func=151 file=152 line=118 + pc=4815748 func=176 file=146 line=218 + pc=4817144 func=177 file=178 line=21 + pc=5072014 func=89 file=28 line=82 +Stack id=64 nframes=1 + pc=5072608 func=88 file=28 line=89 +Stack id=47 nframes=11 + pc=4578453 func=67 file=68 line=352 + pc=4353540 func=69 file=70 line=521 + pc=4352604 func=179 file=70 line=389 + pc=4358543 func=180 file=70 line=926 + pc=4290148 func=71 file=72 line=84 + pc=4288626 func=73 file=74 line=182 + pc=4252996 func=75 file=76 line=948 + pc=4254486 func=77 file=76 line=1149 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=68 nframes=19 + pc=4745679 func=181 file=30 line=98 + pc=4814954 func=182 file=157 line=280 + pc=4814931 func=183 file=184 line=15 + pc=4816145 func=185 file=146 line=272 + pc=4814141 func=186 file=39 line=334 + pc=5034884 func=187 file=39 line=314 + pc=5034871 func=188 file=44 line=76 + pc=5039575 func=45 file=46 line=35 + pc=5060942 func=47 file=48 line=373 + pc=4696033 func=49 file=50 line=74 + pc=5026795 func=51 file=50 line=65 + pc=5026766 func=52 file=48 line=373 + pc=5040478 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=77 nframes=1 + pc=5070924 func=63 file=28 line=58 +Stack id=78 nframes=16 + pc=4749256 func=189 file=30 line=1442 + pc=4744549 func=190 file=32 line=298 + pc=4989295 func=174 file=162 line=59 + pc=5041979 func=175 file=54 line=124 + pc=5040762 func=53 file=54 line=70 + pc=5014167 func=55 file=56 line=154 + pc=5050219 func=98 file=58 line=78 + pc=5049860 func=99 file=58 line=68 + pc=5049861 func=100 file=58 line=64 + pc=4958364 func=101 file=61 line=651 + pc=4956653 func=102 file=61 line=616 + pc=4954291 func=103 file=61 line=517 + pc=4952889 func=104 file=61 line=508 + pc=4951126 func=105 file=61 line=434 + pc=4951127 func=106 file=61 line=401 + pc=5070980 func=63 file=28 line=68 +Stack id=54 nframes=1 + pc=5071968 func=89 file=28 line=81 +Stack id=32 nframes=2 + pc=4342189 func=191 file=192 line=425 + pc=4343672 func=193 file=192 line=658 +Stack id=57 nframes=5 + pc=4746660 func=150 file=30 line=432 + pc=4737158 func=151 file=152 line=106 + pc=4815748 func=176 file=146 line=218 + pc=4817109 func=177 file=178 line=21 + pc=5072014 func=89 file=28 line=82 +Stack id=74 nframes=10 + pc=4749032 func=194 file=30 line=1432 + pc=4744421 func=195 file=32 line=290 + pc=5044292 func=164 file=54 line=167 + pc=5040531 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=18 nframes=1 + pc=5069604 func=196 file=111 line=130 +Stack id=5 nframes=4 + pc=4570709 func=197 file=141 line=42 + pc=4561720 func=108 file=109 line=257 + pc=5069285 func=110 file=111 line=125 + pc=5070075 func=63 file=28 line=20 +Stack id=90 nframes=8 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5052347 func=94 file=95 line=18 + pc=5048264 func=96 file=97 line=245 + pc=5051452 func=115 file=58 line=156 + pc=5048431 func=116 file=97 line=315 + pc=5071227 func=117 file=28 line=59 +Stack id=33 nframes=7 + pc=4307927 func=82 file=81 line=807 + pc=4324210 func=198 file=199 line=564 + pc=4255603 func=200 file=76 line=1337 + pc=4253576 func=77 file=76 line=1025 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=70 nframes=19 + pc=4746660 func=150 file=30 line=432 + pc=4737232 func=151 file=152 line=118 + pc=4815748 func=176 file=146 line=218 + pc=4816367 func=185 file=146 line=301 + pc=4814141 func=186 file=39 line=334 + pc=5034884 func=187 file=39 line=314 + pc=5034871 func=188 file=44 line=76 + pc=5039575 func=45 file=46 line=35 + pc=5060942 func=47 file=48 line=373 + pc=4696033 func=49 file=50 line=74 + pc=5026795 func=51 file=50 line=65 + pc=5026766 func=52 file=48 line=373 + pc=5040478 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=8 nframes=1 + pc=5069536 func=196 file=111 line=128 +Stack id=15 nframes=2 + pc=4701031 func=201 file=25 line=116 + pc=5070313 func=63 file=28 line=29 +Stack id=86 nframes=7 + pc=4746903 func=29 file=30 line=736 + pc=4807597 func=31 file=32 line=181 + pc=4807573 func=33 file=34 line=736 + pc=4807216 func=35 file=34 line=160 + pc=4988612 func=167 file=130 line=55 + pc=5025316 func=168 file=48 line=179 + pc=5071115 func=63 file=28 line=73 +Stack id=67 nframes=1 + pc=0 func=0 file=0 line=0 +Stack id=80 nframes=15 + pc=4990958 func=202 file=34 line=683 + pc=4990987 func=174 file=162 line=141 + pc=5041979 func=175 file=54 line=124 + pc=5040762 func=53 file=54 line=70 + pc=5014167 func=55 file=56 line=154 + pc=5050219 func=98 file=58 line=78 + pc=5049860 func=99 file=58 line=68 + pc=5049861 func=100 file=58 line=64 + pc=4958364 func=101 file=61 line=651 + pc=4956653 func=102 file=61 line=616 + pc=4954291 func=103 file=61 line=517 + pc=4952889 func=104 file=61 line=508 + pc=4951126 func=105 file=61 line=434 + pc=4951127 func=106 file=61 line=401 + pc=5070980 func=63 file=28 line=68 +Stack id=93 nframes=7 + pc=4747354 func=83 file=30 line=964 + pc=4808839 func=84 file=32 line=209 + pc=4808831 func=33 file=34 line=736 + pc=4808384 func=85 file=34 line=380 + pc=4988868 func=203 file=130 line=96 + pc=5025764 func=204 file=48 line=191 + pc=5071301 func=117 file=28 line=65 +Stack id=46 nframes=1 + pc=5070323 func=63 file=28 line=32 +Stack id=25 nframes=5 + pc=4306780 func=134 file=81 line=663 + pc=4255286 func=77 file=76 line=1292 + pc=4524282 func=139 file=79 line=246 + pc=5073584 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=58 nframes=5 + pc=4746660 func=150 file=30 line=432 + pc=4737232 func=151 file=152 line=118 + pc=4815748 func=176 file=146 line=218 + pc=4817109 func=177 file=178 line=21 + pc=5072014 func=89 file=28 line=82 +Stack id=39 nframes=9 + pc=4365530 func=205 file=206 line=958 + pc=4291537 func=207 file=72 line=254 + pc=4291127 func=71 file=72 line=170 + pc=4288626 func=73 file=74 line=182 + pc=4252996 func=75 file=76 line=948 + pc=4254486 func=77 file=76 line=1149 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=2 nframes=3 + pc=4561444 func=108 file=109 line=238 + pc=5069285 func=110 file=111 line=125 + pc=5070075 func=63 file=28 line=20 +Stack id=50 nframes=1 + pc=5070361 func=63 file=28 line=38 +Stack id=10 nframes=1 + pc=5072672 func=208 file=28 line=97 +Stack id=41 nframes=4 + pc=4255286 func=77 file=76 line=1292 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=31 nframes=3 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=36 nframes=4 + pc=4637735 func=132 file=133 line=474 + pc=4309597 func=144 file=81 line=965 + pc=4308676 func=82 file=81 line=927 + pc=4314212 func=80 file=81 line=1446 +Stack id=69 nframes=19 + pc=4746660 func=150 file=30 line=432 + pc=4737158 func=151 file=152 line=106 + pc=4815748 func=176 file=146 line=218 + pc=4816367 func=185 file=146 line=301 + pc=4814141 func=186 file=39 line=334 + pc=5034884 func=187 file=39 line=314 + pc=5034871 func=188 file=44 line=76 + pc=5039575 func=45 file=46 line=35 + pc=5060942 func=47 file=48 line=373 + pc=4696033 func=49 file=50 line=74 + pc=5026795 func=51 file=50 line=65 + pc=5026766 func=52 file=48 line=373 + pc=5040478 func=53 file=54 line=57 + pc=5014167 func=55 file=56 line=154 + pc=5051652 func=57 file=58 line=182 + pc=4960356 func=59 file=58 line=172 + pc=4960318 func=60 file=61 line=734 + pc=4961094 func=62 file=61 line=808 + pc=5070695 func=63 file=28 line=53 +Stack id=59 nframes=5 + pc=4746660 func=150 file=30 line=432 + pc=4737158 func=151 file=152 line=106 + pc=4815748 func=176 file=146 line=218 + pc=4817144 func=177 file=178 line=21 + pc=5072014 func=89 file=28 line=82 +Stack id=30 nframes=7 + pc=4578913 func=209 file=68 line=378 + pc=4323996 func=198 file=199 line=536 + pc=4255603 func=200 file=76 line=1337 + pc=4253576 func=77 file=76 line=1025 + pc=4522824 func=78 file=79 line=103 + pc=5073532 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=51 nframes=2 + pc=4701031 func=201 file=25 line=116 + pc=5070481 func=63 file=28 line=43 +Stack id=49 nframes=1 + pc=5071552 func=171 file=28 line=38 +Stack id=29 nframes=5 + pc=4307322 func=134 file=81 line=746 + pc=4255286 func=77 file=76 line=1292 + pc=4524282 func=139 file=79 line=246 + pc=5073584 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=82 nframes=15 + pc=4750130 func=90 file=30 line=1488 + pc=4811799 func=91 file=32 line=462 + pc=4811777 func=92 file=93 line=17 + pc=5052074 func=113 file=114 line=15 + pc=5048202 func=96 file=97 line=239 + pc=5050832 func=98 file=58 line=114 + pc=5049860 func=99 file=58 line=68 + pc=5049861 func=100 file=58 line=64 + pc=4958364 func=101 file=61 line=651 + pc=4956653 func=102 file=61 line=616 + pc=4954291 func=103 file=61 line=517 + pc=4952889 func=104 file=61 line=508 + pc=4951126 func=105 file=61 line=434 + pc=4951127 func=106 file=61 line=401 + pc=5070980 func=63 file=28 line=68 +Stack id=27 nframes=4 + pc=4255286 func=77 file=76 line=1292 + pc=4524282 func=139 file=79 line=246 + pc=5073584 func=66 file=28 line=127 + pc=5070323 func=63 file=28 line=32 +Stack id=17 nframes=7 + pc=4747354 func=83 file=30 line=964 + pc=4808839 func=84 file=32 line=209 + pc=4808831 func=33 file=34 line=736 + pc=4808384 func=85 file=34 line=380 + pc=4813744 func=86 file=37 line=46 + pc=4813736 func=87 file=39 line=183 + pc=5069594 func=196 file=111 line=134 +EventBatch gen=1 m=18446744073709551615 time=420901448919 size=6914 +Strings +String id=1 + data="Not worker" +String id=2 + data="GC (dedicated)" +String id=3 + data="GC (fractional)" +String id=4 + data="GC (idle)" +String id=5 + data="unspecified" +String id=6 + data="forever" +String id=7 + data="network" +String id=8 + data="select" +String id=9 + data="sync.(*Cond).Wait" +String id=10 + data="sync" +String id=11 + data="chan send" +String id=12 + data="chan receive" +String id=13 + data="GC mark assist wait for work" +String id=14 + data="GC background sweeper wait" +String id=15 + data="system goroutine wait" +String id=16 + data="preempted" +String id=17 + data="wait for debug call" +String id=18 + data="wait until GC ends" +String id=19 + data="sleep" +String id=20 + data="runtime.GoSched" +String id=21 + data="start trace" +String id=22 + data="GC sweep termination" +String id=23 + data="GC mark termination" +String id=24 + data="sync.(*WaitGroup).Add" +String id=25 + data="/usr/local/google/home/mknyszek/work/go-1/src/sync/waitgroup.go" +String id=26 + data="sync.(*WaitGroup).Done" +String id=27 + data="main.cpu20" +String id=28 + data="/usr/local/google/home/mknyszek/work/go-1/src/cmd/trace/v2/testdata/testprog/main.go" +String id=29 + data="syscall.read" +String id=30 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/zsyscall_linux_amd64.go" +String id=31 + data="syscall.Read" +String id=32 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/syscall_unix.go" +String id=33 + data="internal/poll.ignoringEINTRIO" +String id=34 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_unix.go" +String id=35 + data="internal/poll.(*FD).Read" +String id=36 + data="os.(*File).read" +String id=37 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_posix.go" +String id=38 + data="os.(*File).Read" +String id=39 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file.go" +String id=40 + data="io.ReadAtLeast" +String id=41 + data="/usr/local/google/home/mknyszek/work/go-1/src/io/io.go" +String id=42 + data="io.ReadFull" +String id=43 + data="net.(*file).readLine" +String id=44 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/parse.go" +String id=45 + data="net.maxListenerBacklog" +String id=46 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/sock_linux.go" +String id=47 + data="net.listenerBacklog.func1" +String id=48 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/net.go" +String id=49 + data="sync.(*Once).doSlow" +String id=50 + data="/usr/local/google/home/mknyszek/work/go-1/src/sync/once.go" +String id=51 + data="sync.(*Once).Do" +String id=52 + data="net.listenerBacklog" +String id=53 + data="net.socket" +String id=54 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/sock_posix.go" +String id=55 + data="net.internetSocket" +String id=56 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/ipsock_posix.go" +String id=57 + data="net.(*sysListener).listenTCPProto" +String id=58 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/tcpsock_posix.go" +String id=59 + data="net.(*sysListener).listenTCP" +String id=60 + data="net.(*ListenConfig).Listen" +String id=61 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/dial.go" +String id=62 + data="net.Listen" +String id=63 + data="main.main" +String id=64 + data="time.Sleep" +String id=65 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/time.go" +String id=66 + data="main.allocHog" +String id=67 + data="runtime.traceLocker.GCSweepSpan" +String id=68 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" +String id=69 + data="runtime.(*sweepLocked).sweep" +String id=70 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgcsweep.go" +String id=71 + data="runtime.(*mcentral).cacheSpan" +String id=72 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mcentral.go" +String id=73 + data="runtime.(*mcache).refill" +String id=74 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mcache.go" +String id=75 + data="runtime.(*mcache).nextFree" +String id=76 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/malloc.go" +String id=77 + data="runtime.mallocgc" +String id=78 + data="runtime.makeslice" +String id=79 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/slice.go" +String id=80 + data="runtime.gcBgMarkWorker" +String id=81 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgc.go" +String id=82 + data="runtime.gcMarkDone" +String id=83 + data="syscall.write" +String id=84 + data="syscall.Write" +String id=85 + data="internal/poll.(*FD).Write" +String id=86 + data="os.(*File).write" +String id=87 + data="os.(*File).Write" +String id=88 + data="main.blockingSyscall.func1" +String id=89 + data="main.blockingSyscall" +String id=90 + data="syscall.setsockopt" +String id=91 + data="syscall.SetsockoptInt" +String id=92 + data="internal/poll.(*FD).SetsockoptInt" +String id=93 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/sockopt.go" +String id=94 + data="net.setKeepAlivePeriod" +String id=95 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/tcpsockopt_unix.go" +String id=96 + data="net.newTCPConn" +String id=97 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/tcpsock.go" +String id=98 + data="net.(*sysDialer).doDialTCPProto" +String id=99 + data="net.(*sysDialer).doDialTCP" +String id=100 + data="net.(*sysDialer).dialTCP" +String id=101 + data="net.(*sysDialer).dialSingle" +String id=102 + data="net.(*sysDialer).dialSerial" +String id=103 + data="net.(*sysDialer).dialParallel" +String id=104 + data="net.(*Dialer).DialContext" +String id=105 + data="net.(*Dialer).Dial" +String id=106 + data="net.Dial" +String id=107 + data="runtime.traceLocker.Gomaxprocs" +String id=108 + data="runtime.StartTrace" +String id=109 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" +String id=110 + data="runtime/trace.Start" +String id=111 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" +String id=112 + data="runtime.(*traceAdvancerState).start" +String id=113 + data="net.setNoDelay" +String id=114 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/tcpsockopt_posix.go" +String id=115 + data="net.(*TCPListener).accept" +String id=116 + data="net.(*TCPListener).Accept" +String id=117 + data="main.main.func2" +String id=118 + data="runtime.chanrecv1" +String id=119 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" +String id=120 + data="runtime.(*wakeableSleep).sleep" +String id=121 + data="runtime.(*traceAdvancerState).start.func1" +String id=122 + data="syscall.Close" +String id=123 + data="internal/poll.(*SysFile).destroy" +String id=124 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_unixjs.go" +String id=125 + data="internal/poll.(*FD).destroy" +String id=126 + data="internal/poll.(*FD).decref" +String id=127 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_mutex.go" +String id=128 + data="internal/poll.(*FD).Close" +String id=129 + data="net.(*netFD).Close" +String id=130 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/fd_posix.go" +String id=131 + data="net.(*conn).Close" +String id=132 + data="runtime.systemstack_switch" +String id=133 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/asm_amd64.s" +String id=134 + data="runtime.gcStart" +String id=135 + data="runtime.gopark" +String id=136 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" +String id=137 + data="runtime.chansend1" +String id=138 + data="runtime.gcBgMarkStartWorkers" +String id=139 + data="runtime.growslice" +String id=140 + data="runtime.traceStartReadCPU.func1" +String id=141 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2cpu.go" +String id=142 + data="net.setKeepAlive" +String id=143 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/sockopt_posix.go" +String id=144 + data="runtime.gcMarkTermination" +String id=145 + data="os.(*file).close" +String id=146 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_unix.go" +String id=147 + data="os.(*File).Close" +String id=148 + data="net.(*file).close" +String id=149 + data="runtime.startTheWorld" +String id=150 + data="syscall.fcntl" +String id=151 + data="syscall.SetNonblock" +String id=152 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/exec_unix.go" +String id=153 + data="internal/poll.(*FD).SetBlocking" +String id=154 + data="os.(*File).Fd" +String id=155 + data="syscall.accept4" +String id=156 + data="syscall.Accept4" +String id=157 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/syscall_linux.go" +String id=158 + data="internal/poll.accept" +String id=159 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/sock_cloexec.go" +String id=160 + data="internal/poll.(*FD).Accept" +String id=161 + data="net.(*netFD).accept" +String id=162 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/fd_unix.go" +String id=163 + data="syscall.Listen" +String id=164 + data="net.(*netFD).listenStream" +String id=165 + data="net.setDefaultListenerSockopts" +String id=166 + data="/usr/local/google/home/mknyszek/work/go-1/src/net/sockopt_linux.go" +String id=167 + data="net.(*netFD).Read" +String id=168 + data="net.(*conn).Read" +String id=169 + data="runtime.goparkunlock" +String id=170 + data="runtime.bgsweep" +String id=171 + data="main.main.func1" +String id=172 + data="syscall.getsockopt" +String id=173 + data="syscall.GetsockoptInt" +String id=174 + data="net.(*netFD).connect" +String id=175 + data="net.(*netFD).dial" +String id=176 + data="os.newFile" +String id=177 + data="os.Pipe" +String id=178 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/pipe2_unix.go" +String id=179 + data="runtime.sweepone" +String id=180 + data="runtime.deductSweepCredit" +String id=181 + data="syscall.openat" +String id=182 + data="syscall.Open" +String id=183 + data="os.open" +String id=184 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_open_unix.go" +String id=185 + data="os.openFileNolog" +String id=186 + data="os.OpenFile" +String id=187 + data="os.Open" +String id=188 + data="net.open" +String id=189 + data="syscall.connect" +String id=190 + data="syscall.Connect" +String id=191 + data="runtime.(*scavengerState).park" +String id=192 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgcscavenge.go" +String id=193 + data="runtime.bgscavenge" +String id=194 + data="syscall.bind" +String id=195 + data="syscall.Bind" +String id=196 + data="runtime/trace.Start.func1" +String id=197 + data="runtime.traceStartReadCPU" +String id=198 + data="runtime.gcAssistAlloc" +String id=199 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mgcmark.go" +String id=200 + data="runtime.deductAssistCredit" +String id=201 + data="sync.(*WaitGroup).Wait" +String id=202 + data="internal/poll.(*FD).WaitWrite" +String id=203 + data="net.(*netFD).Write" +String id=204 + data="net.(*conn).Write" +String id=205 + data="runtime.(*mheap).alloc" +String id=206 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/mheap.go" +String id=207 + data="runtime.(*mcentral).grow" +String id=208 + data="main.cpu10" +String id=209 + data="runtime.traceLocker.GCMarkAssistStart" diff --git a/src/cmd/trace/v2/testdata/mktests.go b/src/cmd/trace/v2/testdata/mktests.go new file mode 100644 index 0000000000..143e8ece35 --- /dev/null +++ b/src/cmd/trace/v2/testdata/mktests.go @@ -0,0 +1,60 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import ( + "bytes" + "fmt" + "internal/trace/v2/raw" + "internal/trace/v2/version" + "io" + "log" + "os" + "os/exec" +) + +func main() { + // Create command. + var trace, stderr bytes.Buffer + cmd := exec.Command("go", "run", "./testprog/main.go") + // TODO(mknyszek): Remove if goexperiment.Exectracer2 becomes the default. + cmd.Env = append(os.Environ(), "GOEXPERIMENT=exectracer2") + cmd.Stdout = &trace + cmd.Stderr = &stderr + + // Run trace program; the trace will appear in stdout. + fmt.Fprintln(os.Stderr, "running trace program...") + if err := cmd.Run(); err != nil { + log.Fatalf("running trace program: %v:\n%s", err, stderr.String()) + } + + // Create file. + f, err := os.Create(fmt.Sprintf("./go1%d.test", version.Current)) + if err != nil { + log.Fatalf("creating output file: %v", err) + } + defer f.Close() + + // Write out the trace. + r, err := raw.NewReader(&trace) + if err != nil { + log.Fatalf("reading trace: %v", err) + } + w, err := raw.NewTextWriter(f, version.Current) + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + log.Fatalf("reading trace: %v", err) + } + if err := w.WriteEvent(ev); err != nil { + log.Fatalf("writing trace: %v", err) + } + } +} diff --git a/src/cmd/trace/v2/testdata/testprog/main.go b/src/cmd/trace/v2/testdata/testprog/main.go new file mode 100644 index 0000000000..fcf4dc156c --- /dev/null +++ b/src/cmd/trace/v2/testdata/testprog/main.go @@ -0,0 +1,129 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "log" + "net" + "os" + "runtime" + "runtime/trace" + "sync" + "syscall" + "time" +) + +func main() { + if err := trace.Start(os.Stdout); err != nil { + log.Fatal(err) + } + + // checkExecutionTimes relies on this. + var wg sync.WaitGroup + wg.Add(2) + go cpu10(&wg) + go cpu20(&wg) + wg.Wait() + + // checkHeapMetrics relies on this. + allocHog(25 * time.Millisecond) + + // checkProcStartStop relies on this. + var wg2 sync.WaitGroup + for i := 0; i < runtime.GOMAXPROCS(0); i++ { + wg2.Add(1) + go func() { + defer wg2.Done() + cpuHog(50 * time.Millisecond) + }() + } + wg2.Wait() + + // checkSyscalls relies on this. + done := make(chan error) + go blockingSyscall(50*time.Millisecond, done) + if err := <-done; err != nil { + log.Fatal(err) + } + + // checkNetworkUnblock relies on this. + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + log.Fatalf("listen failed: %v", err) + } + defer ln.Close() + go func() { + c, err := ln.Accept() + if err != nil { + return + } + time.Sleep(time.Millisecond) + var buf [1]byte + c.Write(buf[:]) + c.Close() + }() + c, err := net.Dial("tcp", ln.Addr().String()) + if err != nil { + log.Fatalf("dial failed: %v", err) + } + var tmp [1]byte + c.Read(tmp[:]) + c.Close() + + trace.Stop() +} + +// blockingSyscall blocks the current goroutine for duration d in a syscall and +// sends a message to done when it is done or if the syscall failed. +func blockingSyscall(d time.Duration, done chan<- error) { + r, w, err := os.Pipe() + if err != nil { + done <- err + return + } + start := time.Now() + msg := []byte("hello") + time.AfterFunc(d, func() { w.Write(msg) }) + _, err = syscall.Read(int(r.Fd()), make([]byte, len(msg))) + if err == nil && time.Since(start) < d { + err = fmt.Errorf("syscall returned too early: want=%s got=%s", d, time.Since(start)) + } + done <- err +} + +func cpu10(wg *sync.WaitGroup) { + defer wg.Done() + cpuHog(10 * time.Millisecond) +} + +func cpu20(wg *sync.WaitGroup) { + defer wg.Done() + cpuHog(20 * time.Millisecond) +} + +func cpuHog(dt time.Duration) { + start := time.Now() + for i := 0; ; i++ { + if i%1000 == 0 && time.Since(start) > dt { + return + } + } +} + +func allocHog(dt time.Duration) { + start := time.Now() + var s [][]byte + for i := 0; ; i++ { + if i%1000 == 0 { + if time.Since(start) > dt { + return + } + // Take a break... this will generate a ton of events otherwise. + time.Sleep(50 * time.Microsecond) + } + s = append(s, make([]byte, 1024)) + } +} diff --git a/src/cmd/trace/v2/threadgen.go b/src/cmd/trace/v2/threadgen.go new file mode 100644 index 0000000000..c2d2071926 --- /dev/null +++ b/src/cmd/trace/v2/threadgen.go @@ -0,0 +1,201 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "fmt" + "internal/trace/traceviewer" + "internal/trace/traceviewer/format" + tracev2 "internal/trace/v2" +) + +var _ generator = &threadGenerator{} + +type threadGenerator struct { + globalRangeGenerator + globalMetricGenerator + stackSampleGenerator[tracev2.ThreadID] + + gStates map[tracev2.GoID]*gState[tracev2.ThreadID] + threads map[tracev2.ThreadID]struct{} +} + +func newThreadGenerator() *threadGenerator { + tg := new(threadGenerator) + tg.stackSampleGenerator.getResource = func(ev *tracev2.Event) tracev2.ThreadID { + return ev.Thread() + } + tg.gStates = make(map[tracev2.GoID]*gState[tracev2.ThreadID]) + tg.threads = make(map[tracev2.ThreadID]struct{}) + return tg +} + +func (g *threadGenerator) Sync() { + g.globalRangeGenerator.Sync() +} + +func (g *threadGenerator) GoroutineLabel(ctx *traceContext, ev *tracev2.Event) { + l := ev.Label() + g.gStates[l.Resource.Goroutine()].setLabel(l.Label) +} + +func (g *threadGenerator) GoroutineRange(ctx *traceContext, ev *tracev2.Event) { + r := ev.Range() + switch ev.Kind() { + case tracev2.EventRangeBegin: + g.gStates[r.Scope.Goroutine()].rangeBegin(ev.Time(), r.Name, ev.Stack()) + case tracev2.EventRangeActive: + g.gStates[r.Scope.Goroutine()].rangeActive(r.Name) + case tracev2.EventRangeEnd: + gs := g.gStates[r.Scope.Goroutine()] + gs.rangeEnd(ev.Time(), r.Name, ev.Stack(), ctx) + } +} + +func (g *threadGenerator) GoroutineTransition(ctx *traceContext, ev *tracev2.Event) { + if ev.Thread() != tracev2.NoThread { + if _, ok := g.threads[ev.Thread()]; !ok { + g.threads[ev.Thread()] = struct{}{} + } + } + + st := ev.StateTransition() + goID := st.Resource.Goroutine() + + // If we haven't seen this goroutine before, create a new + // gState for it. + gs, ok := g.gStates[goID] + if !ok { + gs = newGState[tracev2.ThreadID](goID) + g.gStates[goID] = gs + } + // If we haven't already named this goroutine, try to name it. + gs.augmentName(st.Stack) + + // Handle the goroutine state transition. + from, to := st.Goroutine() + if from == to { + // Filter out no-op events. + return + } + if from.Executing() && !to.Executing() { + if to == tracev2.GoWaiting { + // Goroutine started blocking. + gs.block(ev.Time(), ev.Stack(), st.Reason, ctx) + } else { + gs.stop(ev.Time(), ev.Stack(), ctx) + } + } + if !from.Executing() && to.Executing() { + start := ev.Time() + if from == tracev2.GoUndetermined { + // Back-date the event to the start of the trace. + start = ctx.startTime + } + gs.start(start, ev.Thread(), ctx) + } + + if from == tracev2.GoWaiting { + // Goroutine was unblocked. + gs.unblock(ev.Time(), ev.Stack(), ev.Thread(), ctx) + } + if from == tracev2.GoNotExist && to == tracev2.GoRunnable { + // Goroutine was created. + gs.created(ev.Time(), ev.Thread(), ev.Stack()) + } + if from == tracev2.GoSyscall { + // Exiting syscall. + gs.syscallEnd(ev.Time(), to != tracev2.GoRunning, ctx) + } + + // Handle syscalls. + if to == tracev2.GoSyscall { + start := ev.Time() + if from == tracev2.GoUndetermined { + // Back-date the event to the start of the trace. + start = ctx.startTime + } + // Write down that we've entered a syscall. Note: we might have no P here + // if we're in a cgo callback or this is a transition from GoUndetermined + // (i.e. the G has been blocked in a syscall). + gs.syscallBegin(start, ev.Thread(), ev.Stack()) + } + + // Note down the goroutine transition. + _, inMarkAssist := gs.activeRanges["GC mark assist"] + ctx.GoroutineTransition(ctx.elapsed(ev.Time()), viewerGState(from, inMarkAssist), viewerGState(to, inMarkAssist)) +} + +func (g *threadGenerator) ProcTransition(ctx *traceContext, ev *tracev2.Event) { + if ev.Thread() != tracev2.NoThread { + if _, ok := g.threads[ev.Thread()]; !ok { + g.threads[ev.Thread()] = struct{}{} + } + } + + type procArg struct { + Proc uint64 `json:"proc,omitempty"` + } + st := ev.StateTransition() + viewerEv := traceviewer.InstantEvent{ + Resource: uint64(ev.Thread()), + Stack: ctx.Stack(viewerFrames(ev.Stack())), + Arg: procArg{Proc: uint64(st.Resource.Proc())}, + } + + from, to := st.Proc() + if from == to { + // Filter out no-op events. + return + } + if to.Executing() { + start := ev.Time() + if from == tracev2.ProcUndetermined { + start = ctx.startTime + } + viewerEv.Name = "proc start" + viewerEv.Arg = format.ThreadIDArg{ThreadID: uint64(ev.Thread())} + viewerEv.Ts = ctx.elapsed(start) + // TODO(mknyszek): We don't have a state machine for threads, so approximate + // running threads with running Ps. + ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, 1) + } + if from.Executing() { + start := ev.Time() + viewerEv.Name = "proc stop" + viewerEv.Ts = ctx.elapsed(start) + // TODO(mknyszek): We don't have a state machine for threads, so approximate + // running threads with running Ps. + ctx.IncThreadStateCount(ctx.elapsed(start), traceviewer.ThreadStateRunning, -1) + } + // TODO(mknyszek): Consider modeling procs differently and have them be + // transition to and from NotExist when GOMAXPROCS changes. We can emit + // events for this to clearly delineate GOMAXPROCS changes. + + if viewerEv.Name != "" { + ctx.Instant(viewerEv) + } +} + +func (g *threadGenerator) ProcRange(ctx *traceContext, ev *tracev2.Event) { + // TODO(mknyszek): Extend procRangeGenerator to support rendering proc ranges on threads. +} + +func (g *threadGenerator) Finish(ctx *traceContext) { + ctx.SetResourceType("OS THREADS") + + // Finish off global ranges. + g.globalRangeGenerator.Finish(ctx) + + // Finish off all the goroutine slices. + for _, gs := range g.gStates { + gs.finish(ctx) + } + + // Name all the threads to the emitter. + for id := range g.threads { + ctx.Resource(uint64(id), fmt.Sprintf("Thread %d", id)) + } +} diff --git a/src/cmd/trace/v2/viewer.go b/src/cmd/trace/v2/viewer.go new file mode 100644 index 0000000000..de67fc4e0e --- /dev/null +++ b/src/cmd/trace/v2/viewer.go @@ -0,0 +1,56 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package trace + +import ( + "fmt" + "internal/trace" + "internal/trace/traceviewer" + tracev2 "internal/trace/v2" + "time" +) + +// viewerFrames returns the frames of the stack of ev. The given frame slice is +// used to store the frames to reduce allocations. +func viewerFrames(stk tracev2.Stack) []*trace.Frame { + var frames []*trace.Frame + stk.Frames(func(f tracev2.StackFrame) bool { + frames = append(frames, &trace.Frame{ + PC: f.PC, + Fn: f.Func, + File: f.File, + Line: int(f.Line), + }) + return true + }) + return frames +} + +func viewerGState(state tracev2.GoState, inMarkAssist bool) traceviewer.GState { + switch state { + case tracev2.GoUndetermined: + return traceviewer.GDead + case tracev2.GoNotExist: + return traceviewer.GDead + case tracev2.GoRunnable: + return traceviewer.GRunnable + case tracev2.GoRunning: + return traceviewer.GRunning + case tracev2.GoWaiting: + if inMarkAssist { + return traceviewer.GWaitingGC + } + return traceviewer.GWaiting + case tracev2.GoSyscall: + // N.B. A goroutine in a syscall is considered "executing" (state.Executing() == true). + return traceviewer.GRunning + default: + panic(fmt.Sprintf("unknown GoState: %s", state.String())) + } +} + +func viewerTime(t time.Duration) float64 { + return float64(t) / float64(time.Microsecond) +} diff --git a/src/cmd/vendor/golang.org/x/sys/unix/fcntl.go b/src/cmd/vendor/golang.org/x/sys/unix/fcntl.go index 58c6bfc70f..6200876fb2 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/fcntl.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/fcntl.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 dragonfly || freebsd || linux || netbsd || openbsd +//go:build dragonfly || freebsd || linux || netbsd package unix diff --git a/src/cmd/vendor/golang.org/x/sys/unix/ioctl_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/ioctl_linux.go index 0d12c0851a..dbe680eab8 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/ioctl_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -231,3 +231,8 @@ func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) { func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error { return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value)) } + +// IoctlLoopConfigure configures all loop device parameters in a single step +func IoctlLoopConfigure(fd int, value *LoopConfig) error { + return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value)) +} diff --git a/src/cmd/vendor/golang.org/x/sys/unix/mkerrors.sh b/src/cmd/vendor/golang.org/x/sys/unix/mkerrors.sh index cbe24150a7..6202638bae 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/src/cmd/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -519,6 +519,7 @@ ccflags="$@" $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || + $2 == "LOOP_CONFIGURE" || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ || @@ -560,7 +561,7 @@ ccflags="$@" $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ || $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || $2 ~ /^CLONE_[A-Z_]+/ || - $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ && + $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+|BPF_F_LINK)$/ && $2 ~ /^(BPF|DLT)_/ || $2 ~ /^AUDIT_/ || $2 ~ /^(CLOCK|TIMER)_/ || diff --git a/src/cmd/vendor/golang.org/x/sys/unix/pledge_openbsd.go b/src/cmd/vendor/golang.org/x/sys/unix/pledge_openbsd.go index eb48294b27..6a09af53e6 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/pledge_openbsd.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/pledge_openbsd.go @@ -8,54 +8,31 @@ import ( "errors" "fmt" "strconv" - "syscall" - "unsafe" ) // Pledge implements the pledge syscall. // -// The pledge syscall does not accept execpromises on OpenBSD releases -// before 6.3. -// -// execpromises must be empty when Pledge is called on OpenBSD -// releases predating 6.3, otherwise an error will be returned. +// This changes both the promises and execpromises; use PledgePromises or +// PledgeExecpromises to only change the promises or execpromises +// respectively. // // For more information see pledge(2). func Pledge(promises, execpromises string) error { - maj, min, err := majmin() + if err := pledgeAvailable(); err != nil { + return err + } + + pptr, err := BytePtrFromString(promises) if err != nil { return err } - err = pledgeAvailable(maj, min, execpromises) + exptr, err := BytePtrFromString(execpromises) if err != nil { return err } - pptr, err := syscall.BytePtrFromString(promises) - if err != nil { - return err - } - - // This variable will hold either a nil unsafe.Pointer or - // an unsafe.Pointer to a string (execpromises). - var expr unsafe.Pointer - - // If we're running on OpenBSD > 6.2, pass execpromises to the syscall. - if maj > 6 || (maj == 6 && min > 2) { - exptr, err := syscall.BytePtrFromString(execpromises) - if err != nil { - return err - } - expr = unsafe.Pointer(exptr) - } - - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0) - if e != 0 { - return e - } - - return nil + return pledge(pptr, exptr) } // PledgePromises implements the pledge syscall. @@ -64,30 +41,16 @@ func Pledge(promises, execpromises string) error { // // For more information see pledge(2). func PledgePromises(promises string) error { - maj, min, err := majmin() + if err := pledgeAvailable(); err != nil { + return err + } + + pptr, err := BytePtrFromString(promises) if err != nil { return err } - err = pledgeAvailable(maj, min, "") - if err != nil { - return err - } - - // This variable holds the execpromises and is always nil. - var expr unsafe.Pointer - - pptr, err := syscall.BytePtrFromString(promises) - if err != nil { - return err - } - - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0) - if e != 0 { - return e - } - - return nil + return pledge(pptr, nil) } // PledgeExecpromises implements the pledge syscall. @@ -96,30 +59,16 @@ func PledgePromises(promises string) error { // // For more information see pledge(2). func PledgeExecpromises(execpromises string) error { - maj, min, err := majmin() + if err := pledgeAvailable(); err != nil { + return err + } + + exptr, err := BytePtrFromString(execpromises) if err != nil { return err } - err = pledgeAvailable(maj, min, execpromises) - if err != nil { - return err - } - - // This variable holds the promises and is always nil. - var pptr unsafe.Pointer - - exptr, err := syscall.BytePtrFromString(execpromises) - if err != nil { - return err - } - - _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(pptr), uintptr(unsafe.Pointer(exptr)), 0) - if e != 0 { - return e - } - - return nil + return pledge(nil, exptr) } // majmin returns major and minor version number for an OpenBSD system. @@ -147,16 +96,15 @@ func majmin() (major int, minor int, err error) { // pledgeAvailable checks for availability of the pledge(2) syscall // based on the running OpenBSD version. -func pledgeAvailable(maj, min int, execpromises string) error { - // If OpenBSD <= 5.9, pledge is not available. - if (maj == 5 && min != 9) || maj < 5 { - return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min) +func pledgeAvailable() error { + maj, min, err := majmin() + if err != nil { + return err } - // If OpenBSD <= 6.2 and execpromises is not empty, - // return an error - execpromises is not available before 6.3 - if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" { - return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min) + // Require OpenBSD 6.4 as a minimum. + if maj < 6 || (maj == 6 && min <= 3) { + return fmt.Errorf("cannot call Pledge on OpenBSD %d.%d", maj, min) } return nil diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_aix.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_aix.go index 6d3009d214..67ce6cef2d 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_aix.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_aix.go @@ -106,7 +106,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_bsd.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_bsd.go index 6f328e3a55..a00c3e5450 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -316,7 +316,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_linux.go index 6b8a4ad69c..0f85e29e62 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -61,15 +61,23 @@ func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) ( } //sys fchmodat(dirfd int, path string, mode uint32) (err error) +//sys fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - // Linux fchmodat doesn't support the flags parameter. Mimick glibc's behavior - // and check the flags. Otherwise the mode would be applied to the symlink - // destination which is not what the user expects. - if flags&^AT_SYMLINK_NOFOLLOW != 0 { - return EINVAL - } else if flags&AT_SYMLINK_NOFOLLOW != 0 { - return EOPNOTSUPP +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + // Linux fchmodat doesn't support the flags parameter, but fchmodat2 does. + // Try fchmodat2 if flags are specified. + if flags != 0 { + err := fchmodat2(dirfd, path, mode, flags) + if err == ENOSYS { + // fchmodat2 isn't available. If the flags are known to be valid, + // return EOPNOTSUPP to indicate that fchmodat doesn't support them. + if flags&^(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EINVAL + } else if flags&(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EOPNOTSUPP + } + } + return err } return fchmodat(dirfd, path, mode) } @@ -417,7 +425,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- @@ -1301,7 +1310,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func GetsockoptTpacketStats(fd, level, opt int) (*TpacketStats, error) { diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_openbsd.go index 6f34479b59..b25343c71a 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -137,18 +137,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e } func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { - var _p0 unsafe.Pointer + var bufptr *Statfs_t var bufsize uintptr if len(buf) > 0 { - _p0 = unsafe.Pointer(&buf[0]) + bufptr = &buf[0] bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) } - r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) - n = int(r0) - if e1 != 0 { - err = e1 - } - return + return getfsstat(bufptr, bufsize, flags) } //sysnb getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) @@ -171,6 +166,20 @@ func Getresgid() (rgid, egid, sgid int) { //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL +//sys fcntl(fd int, cmd int, arg int) (n int, err error) +//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) = SYS_FCNTL + +// FcntlInt performs a fcntl syscall on fd with the provided command and argument. +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return fcntl(int(fd), cmd, arg) +} + +// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command. +func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error { + _, err := fcntlPtr(int(fd), cmd, unsafe.Pointer(lk)) + return err +} + //sys ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { @@ -326,4 +335,7 @@ func Uname(uname *Utsname) error { //sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) +//sys getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) //sys utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) +//sys pledge(promises *byte, execpromises *byte) (err error) +//sys unveil(path *byte, flags *byte) (err error) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go index b99cfa1342..21974af064 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -128,7 +128,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { if n > 0 { sl += _Socklen(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- @@ -157,7 +158,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } const ImplementsGetwd = true diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go index d99d05f1bc..b473038c61 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go @@ -1104,7 +1104,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { diff --git a/src/cmd/vendor/golang.org/x/sys/unix/unveil_openbsd.go b/src/cmd/vendor/golang.org/x/sys/unix/unveil_openbsd.go index 168d5ae779..cb7e598cef 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/unveil_openbsd.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/unveil_openbsd.go @@ -4,39 +4,48 @@ package unix -import ( - "syscall" - "unsafe" -) +import "fmt" // Unveil implements the unveil syscall. // For more information see unveil(2). // Note that the special case of blocking further // unveil calls is handled by UnveilBlock. func Unveil(path string, flags string) error { - pathPtr, err := syscall.BytePtrFromString(path) + if err := supportsUnveil(); err != nil { + return err + } + pathPtr, err := BytePtrFromString(path) if err != nil { return err } - flagsPtr, err := syscall.BytePtrFromString(flags) + flagsPtr, err := BytePtrFromString(flags) if err != nil { return err } - _, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(unsafe.Pointer(pathPtr)), uintptr(unsafe.Pointer(flagsPtr)), 0) - if e != 0 { - return e - } - return nil + return unveil(pathPtr, flagsPtr) } // UnveilBlock blocks future unveil calls. // For more information see unveil(2). func UnveilBlock() error { - // Both pointers must be nil. - var pathUnsafe, flagsUnsafe unsafe.Pointer - _, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(pathUnsafe), uintptr(flagsUnsafe), 0) - if e != 0 { - return e + if err := supportsUnveil(); err != nil { + return err } + return unveil(nil, nil) +} + +// supportsUnveil checks for availability of the unveil(2) system call based +// on the running OpenBSD version. +func supportsUnveil() error { + maj, min, err := majmin() + if err != nil { + return err + } + + // unveil is not available before 6.4 + if maj < 6 || (maj == 6 && min <= 3) { + return fmt.Errorf("cannot call Unveil on OpenBSD %d.%d", maj, min) + } + return nil } diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go index 2409c44780..c73cfe2f10 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -480,10 +480,13 @@ const ( BPF_FROM_BE = 0x8 BPF_FROM_LE = 0x0 BPF_FS_MAGIC = 0xcafe4a11 + BPF_F_AFTER = 0x10 BPF_F_ALLOW_MULTI = 0x2 BPF_F_ALLOW_OVERRIDE = 0x1 BPF_F_ANY_ALIGNMENT = 0x2 - BPF_F_KPROBE_MULTI_RETURN = 0x1 + BPF_F_BEFORE = 0x8 + BPF_F_ID = 0x20 + BPF_F_NETFILTER_IP_DEFRAG = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1 BPF_F_REPLACE = 0x4 BPF_F_SLEEPABLE = 0x10 @@ -520,6 +523,7 @@ const ( BPF_MAJOR_VERSION = 0x1 BPF_MAXINSNS = 0x1000 BPF_MEM = 0x60 + BPF_MEMSX = 0x80 BPF_MEMWORDS = 0x10 BPF_MINOR_VERSION = 0x1 BPF_MISC = 0x7 @@ -775,6 +779,8 @@ const ( DEVLINK_GENL_MCGRP_CONFIG_NAME = "config" DEVLINK_GENL_NAME = "devlink" DEVLINK_GENL_VERSION = 0x1 + DEVLINK_PORT_FN_CAP_IPSEC_CRYPTO = 0x4 + DEVLINK_PORT_FN_CAP_IPSEC_PACKET = 0x8 DEVLINK_PORT_FN_CAP_MIGRATABLE = 0x2 DEVLINK_PORT_FN_CAP_ROCE = 0x1 DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 0x14 @@ -1697,6 +1703,7 @@ const ( KEXEC_ON_CRASH = 0x1 KEXEC_PRESERVE_CONTEXT = 0x2 KEXEC_SEGMENT_MAX = 0x10 + KEXEC_UPDATE_ELFCOREHDR = 0x4 KEYCTL_ASSUME_AUTHORITY = 0x10 KEYCTL_CAPABILITIES = 0x1f KEYCTL_CAPS0_BIG_KEY = 0x10 @@ -1794,6 +1801,7 @@ const ( LOCK_SH = 0x1 LOCK_UN = 0x8 LOOP_CLR_FD = 0x4c01 + LOOP_CONFIGURE = 0x4c0a LOOP_CTL_ADD = 0x4c80 LOOP_CTL_GET_FREE = 0x4c82 LOOP_CTL_REMOVE = 0x4c81 @@ -2274,6 +2282,7 @@ const ( PERF_MEM_LVLNUM_PMEM = 0xe PERF_MEM_LVLNUM_RAM = 0xd PERF_MEM_LVLNUM_SHIFT = 0x21 + PERF_MEM_LVLNUM_UNC = 0x8 PERF_MEM_LVL_HIT = 0x2 PERF_MEM_LVL_IO = 0x1000 PERF_MEM_LVL_L1 = 0x8 @@ -3460,6 +3469,7 @@ const ( XDP_PACKET_HEADROOM = 0x100 XDP_PGOFF_RX_RING = 0x0 XDP_PGOFF_TX_RING = 0x80000000 + XDP_PKT_CONTD = 0x1 XDP_RING_NEED_WAKEUP = 0x1 XDP_RX_RING = 0x2 XDP_SHARED_UMEM = 0x1 @@ -3472,6 +3482,7 @@ const ( XDP_UMEM_REG = 0x4 XDP_UMEM_UNALIGNED_CHUNK_FLAG = 0x1 XDP_USE_NEED_WAKEUP = 0x8 + XDP_USE_SG = 0x10 XDP_ZEROCOPY = 0x4 XENFS_SUPER_MAGIC = 0xabba1974 XFS_SUPER_MAGIC = 0x58465342 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index e6467bd23b..27ac4a09e2 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -118,6 +118,7 @@ const ( IXOFF = 0x1000 IXON = 0x400 LASX_CTX_MAGIC = 0x41535801 + LBT_CTX_MAGIC = 0x42540001 LSX_CTX_MAGIC = 0x53580001 MAP_ANON = 0x20 MAP_ANONYMOUS = 0x20 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index c597e31db6..18f2813ed5 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -227,6 +227,9 @@ const ( PPPIOCUNBRIDGECHAN = 0x7434 PPPIOCXFERUNIT = 0x744e PR_SET_PTRACER_ANY = 0xffffffffffffffff + PTRACE_GETFDPIC = 0x21 + PTRACE_GETFDPIC_EXEC = 0x0 + PTRACE_GETFDPIC_INTERP = 0x1 RLIMIT_AS = 0x9 RLIMIT_MEMLOCK = 0x8 RLIMIT_NOFILE = 0x7 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_linux.go index faca7a557b..1488d27128 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -37,6 +37,21 @@ func fchmodat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 9da3ec3772..a1d061597c 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2212,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2228,3 +2269,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s index 3dcacd30d7..41b5617316 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $4 DATA ·libc_munmap_trampoline_addr(SB)/4, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getfsstat_trampoline_addr(SB)/4, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4 DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4 +DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4 +DATA ·libc_unveil_trampoline_addr(SB)/4, $libc_unveil_trampoline<>(SB) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index cc273c4126..5b2a740977 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2212,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2228,3 +2269,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s index 2763620b01..4019a656f6 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index fa34e22e3b..f6eda1344a 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2212,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2228,3 +2269,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s index c922314048..ac4af24f90 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $4 DATA ·libc_munmap_trampoline_addr(SB)/4, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getfsstat_trampoline_addr(SB)/4, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $4 DATA ·libc_utimensat_trampoline_addr(SB)/4, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $4 +DATA ·libc_pledge_trampoline_addr(SB)/4, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $4 +DATA ·libc_unveil_trampoline_addr(SB)/4, $libc_unveil_trampoline<>(SB) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index ab61221477..55df20ae9d 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2212,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2228,3 +2269,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s index a6bc32c922..f77d532121 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index 3882790a52..8c1155cbc0 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2212,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2228,3 +2269,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s index b4e7bceabf..fae140b62c 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 172ba7f62c..7cc80c58d9 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2212,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2228,3 +2269,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s index ca3f766009..9d1e0ff06d 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -213,6 +213,12 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_fcntl(SB) + RET +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_ppoll(SB) RET @@ -801,8 +807,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_getfsstat(SB) + RET +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_utimensat(SB) RET GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_pledge(SB) + RET +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_unveil(SB) + RET +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index fd8e6f62fa..0688737f49 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) @@ -2212,6 +2238,21 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getfsstat(stat *Statfs_t, bufsize uintptr, flags int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_getfsstat_trampoline_addr, uintptr(unsafe.Pointer(stat)), uintptr(bufsize), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_getfsstat_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getfsstat getfsstat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -2228,3 +2269,33 @@ func utimensat(dirfd int, path string, times *[2]Timespec, flags int) (err error var libc_utimensat_trampoline_addr uintptr //go:cgo_import_dynamic libc_utimensat utimensat "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pledge(promises *byte, execpromises *byte) (err error) { + _, _, e1 := syscall_syscall(libc_pledge_trampoline_addr, uintptr(unsafe.Pointer(promises)), uintptr(unsafe.Pointer(execpromises)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pledge_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pledge pledge "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func unveil(path *byte, flags *byte) (err error) { + _, _, e1 := syscall_syscall(libc_unveil_trampoline_addr, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(flags)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_unveil_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_unveil unveil "libc.so" + + diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s index 477a7d5b21..da115f9a4b 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 @@ -668,7 +673,22 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_getfsstat_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getfsstat(SB) +GLOBL ·libc_getfsstat_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getfsstat_trampoline_addr(SB)/8, $libc_getfsstat_trampoline<>(SB) + TEXT libc_utimensat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_utimensat(SB) GLOBL ·libc_utimensat_trampoline_addr(SB), RODATA, $8 DATA ·libc_utimensat_trampoline_addr(SB)/8, $libc_utimensat_trampoline<>(SB) + +TEXT libc_pledge_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pledge(SB) +GLOBL ·libc_pledge_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pledge_trampoline_addr(SB)/8, $libc_pledge_trampoline<>(SB) + +TEXT libc_unveil_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_unveil(SB) +GLOBL ·libc_unveil_trampoline_addr(SB), RODATA, $8 +DATA ·libc_unveil_trampoline_addr(SB)/8, $libc_unveil_trampoline<>(SB) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index 2d181ba7b2..fcf3ecbdde 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -447,4 +447,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index 2b25ab33cf..f56dc2504a 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -369,4 +369,6 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 + SYS_MAP_SHADOW_STACK = 453 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index b43e7c4c21..974bf24676 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -411,4 +411,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index d50622f82e..39a2739e23 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -314,4 +314,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index 7c31dbe4fe..cf9c9d77e1 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -308,4 +308,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 2b01364d7e..10b7362ef4 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -431,4 +431,5 @@ const ( SYS_FUTEX_WAITV = 4449 SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 + SYS_FCHMODAT2 = 4452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 9bb31708e8..cd4d8b4fd3 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -361,4 +361,5 @@ const ( SYS_FUTEX_WAITV = 5449 SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 + SYS_FCHMODAT2 = 5452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index 8413fe6dc0..2c0efca818 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -361,4 +361,5 @@ const ( SYS_FUTEX_WAITV = 5449 SYS_SET_MEMPOLICY_HOME_NODE = 5450 SYS_CACHESTAT = 5451 + SYS_FCHMODAT2 = 5452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index cf76a275ad..a72e31d391 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -431,4 +431,5 @@ const ( SYS_FUTEX_WAITV = 4449 SYS_SET_MEMPOLICY_HOME_NODE = 4450 SYS_CACHESTAT = 4451 + SYS_FCHMODAT2 = 4452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index 7999422733..c7d1e37471 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -438,4 +438,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index 75904d52b9..f4d4838c87 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -410,4 +410,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index f5f681c3de..b64f0e5911 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -410,4 +410,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index 391954743d..95711195a0 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -315,4 +315,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index f33ceef17d..f94e943bc4 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -376,4 +376,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index a2ce59e73f..ba0c2bc515 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -389,4 +389,5 @@ const ( SYS_FUTEX_WAITV = 449 SYS_SET_MEMPOLICY_HOME_NODE = 450 SYS_CACHESTAT = 451 + SYS_FCHMODAT2 = 452 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go index 997bcd55ae..bbf8399ff5 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -2671,6 +2671,7 @@ const ( BPF_PROG_TYPE_LSM = 0x1d BPF_PROG_TYPE_SK_LOOKUP = 0x1e BPF_PROG_TYPE_SYSCALL = 0x1f + BPF_PROG_TYPE_NETFILTER = 0x20 BPF_CGROUP_INET_INGRESS = 0x0 BPF_CGROUP_INET_EGRESS = 0x1 BPF_CGROUP_INET_SOCK_CREATE = 0x2 @@ -2715,6 +2716,11 @@ const ( BPF_PERF_EVENT = 0x29 BPF_TRACE_KPROBE_MULTI = 0x2a BPF_LSM_CGROUP = 0x2b + BPF_STRUCT_OPS = 0x2c + BPF_NETFILTER = 0x2d + BPF_TCX_INGRESS = 0x2e + BPF_TCX_EGRESS = 0x2f + BPF_TRACE_UPROBE_MULTI = 0x30 BPF_LINK_TYPE_UNSPEC = 0x0 BPF_LINK_TYPE_RAW_TRACEPOINT = 0x1 BPF_LINK_TYPE_TRACING = 0x2 @@ -2725,6 +2731,18 @@ const ( BPF_LINK_TYPE_PERF_EVENT = 0x7 BPF_LINK_TYPE_KPROBE_MULTI = 0x8 BPF_LINK_TYPE_STRUCT_OPS = 0x9 + BPF_LINK_TYPE_NETFILTER = 0xa + BPF_LINK_TYPE_TCX = 0xb + BPF_LINK_TYPE_UPROBE_MULTI = 0xc + BPF_PERF_EVENT_UNSPEC = 0x0 + BPF_PERF_EVENT_UPROBE = 0x1 + BPF_PERF_EVENT_URETPROBE = 0x2 + BPF_PERF_EVENT_KPROBE = 0x3 + BPF_PERF_EVENT_KRETPROBE = 0x4 + BPF_PERF_EVENT_TRACEPOINT = 0x5 + BPF_PERF_EVENT_EVENT = 0x6 + BPF_F_KPROBE_MULTI_RETURN = 0x1 + BPF_F_UPROBE_MULTI_RETURN = 0x1 BPF_ANY = 0x0 BPF_NOEXIST = 0x1 BPF_EXIST = 0x2 @@ -2742,6 +2760,8 @@ const ( BPF_F_MMAPABLE = 0x400 BPF_F_PRESERVE_ELEMS = 0x800 BPF_F_INNER_MAP = 0x1000 + BPF_F_LINK = 0x2000 + BPF_F_PATH_FD = 0x4000 BPF_STATS_RUN_TIME = 0x0 BPF_STACK_BUILD_ID_EMPTY = 0x0 BPF_STACK_BUILD_ID_VALID = 0x1 @@ -2762,6 +2782,7 @@ const ( BPF_F_ZERO_CSUM_TX = 0x2 BPF_F_DONT_FRAGMENT = 0x4 BPF_F_SEQ_NUMBER = 0x8 + BPF_F_NO_TUNNEL_KEY = 0x10 BPF_F_TUNINFO_FLAGS = 0x10 BPF_F_INDEX_MASK = 0xffffffff BPF_F_CURRENT_CPU = 0xffffffff @@ -2778,6 +2799,8 @@ const ( BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10 BPF_F_ADJ_ROOM_NO_CSUM_RESET = 0x20 BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 0x40 + BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 0x80 + BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 0x100 BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38 BPF_F_SYSCTL_BASE_NAME = 0x1 @@ -2866,6 +2889,8 @@ const ( BPF_DEVCG_DEV_CHAR = 0x2 BPF_FIB_LOOKUP_DIRECT = 0x1 BPF_FIB_LOOKUP_OUTPUT = 0x2 + BPF_FIB_LOOKUP_SKIP_NEIGH = 0x4 + BPF_FIB_LOOKUP_TBID = 0x8 BPF_FIB_LKUP_RET_SUCCESS = 0x0 BPF_FIB_LKUP_RET_BLACKHOLE = 0x1 BPF_FIB_LKUP_RET_UNREACHABLE = 0x2 @@ -2901,6 +2926,7 @@ const ( BPF_CORE_ENUMVAL_EXISTS = 0xa BPF_CORE_ENUMVAL_VALUE = 0xb BPF_CORE_TYPE_MATCHES = 0xc + BPF_F_TIMER_ABS = 0x1 ) const ( @@ -2979,6 +3005,12 @@ type LoopInfo64 struct { Encrypt_key [32]uint8 Init [2]uint64 } +type LoopConfig struct { + Fd uint32 + Size uint32 + Info LoopInfo64 + _ [8]uint64 +} type TIPCSocketAddr struct { Ref uint32 diff --git a/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go index dcaa40977f..47dc579676 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -155,6 +155,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW //sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW //sys SetDefaultDllDirectories(directoryFlags uint32) (err error) +//sys AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory +//sys RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory //sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW //sys GetVersion() (ver uint32, err error) //sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW @@ -970,7 +972,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, int32, error) { if n > 0 { sl += int32(n) + 1 } - if sa.raw.Path[0] == '@' { + if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) { + // Check sl > 3 so we don't change unnamed socket behavior. sa.raw.Path[0] = 0 // Don't count trailing NUL for abstract address. sl-- diff --git a/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go index db6282e00a..146a1f0196 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -184,6 +184,7 @@ var ( procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") + procAddDllDirectory = modkernel32.NewProc("AddDllDirectory") procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") procCancelIo = modkernel32.NewProc("CancelIo") procCancelIoEx = modkernel32.NewProc("CancelIoEx") @@ -330,6 +331,7 @@ var ( procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory") procReleaseMutex = modkernel32.NewProc("ReleaseMutex") procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW") + procRemoveDllDirectory = modkernel32.NewProc("RemoveDllDirectory") procResetEvent = modkernel32.NewProc("ResetEvent") procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole") procResumeThread = modkernel32.NewProc("ResumeThread") @@ -1605,6 +1607,15 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) { return } +func AddDllDirectory(path *uint16) (cookie uintptr, err error) { + r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + cookie = uintptr(r0) + if cookie == 0 { + err = errnoErr(e1) + } + return +} + func AssignProcessToJobObject(job Handle, process Handle) (err error) { r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0) if r1 == 0 { @@ -2879,6 +2890,14 @@ func RemoveDirectory(path *uint16) (err error) { return } +func RemoveDllDirectory(cookie uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func ResetEvent(event Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0) if r1 == 0 { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go index c7a49776fe..847063bb32 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go @@ -72,7 +72,7 @@ func run(pass *analysis.Pass) (interface{}, error) { } var structuralTypes []types.Type switch typ := typ.(type) { - case *typeparams.TypeParam: + case *types.TypeParam: terms, err := typeparams.StructuralTerms(typ) if err != nil { return // invalid type @@ -163,7 +163,7 @@ func isLocalType(pass *analysis.Pass, typ types.Type) bool { case *types.Named: // names in package foo are local to foo_test too return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test") - case *typeparams.TypeParam: + case *types.TypeParam: return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test") } return false diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go index 2eeb0a330a..6cbbc7e814 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go @@ -255,7 +255,7 @@ func lockPath(tpkg *types.Package, typ types.Type, seen map[types.Type]bool) typ } seen[typ] = true - if tpar, ok := typ.(*typeparams.TypeParam); ok { + if tpar, ok := typ.(*types.TypeParam); ok { terms, err := typeparams.StructuralTerms(tpar) if err != nil { return nil // invalid type diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go index 43996b80a5..7f62ad4c82 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go @@ -66,9 +66,6 @@ func run(pass *analysis.Pass) (interface{}, error) { var errorType = types.Universe.Lookup("error").Type() -// pointerToInterfaceOrError reports whether the type of e is a pointer to an interface or a type implementing error, -// or is the empty interface. - // checkAsTarget reports an error if the second argument to errors.As is invalid. func checkAsTarget(pass *analysis.Pass, e ast.Expr) error { t := pass.TypesInfo.Types[e].Type diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go index b84577fcf8..12507f9967 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go @@ -95,14 +95,14 @@ func (w *tpWalker) isParameterized(typ types.Type) (res bool) { return w.isParameterized(t.Elem()) case *types.Named: - list := typeparams.NamedTypeArgs(t) + list := t.TypeArgs() for i, n := 0, list.Len(); i < n; i++ { if w.isParameterized(list.At(i)) { return true } } - case *typeparams.TypeParam: + case *types.TypeParam: return true default: diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go index c0060753f9..3f01b3b55d 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go @@ -13,6 +13,8 @@ import ( "go/token" "go/types" "os" + + "golang.org/x/tools/internal/analysisinternal" ) // Format returns a string representation of the expression. @@ -150,3 +152,5 @@ func IsFunctionNamed(f *types.Func, pkgPath string, names ...string) bool { } return false } + +var MustExtractDoc = analysisinternal.MustExtractDoc diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/doc.go index dc544df1bf..c95b1c1c98 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/doc.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/doc.go @@ -14,8 +14,12 @@ // in such a way (e.g. with go or defer) that it may outlive the loop // iteration and possibly observe the wrong value of the variable. // +// Note: An iteration variable can only outlive a loop iteration in Go versions <=1.21. +// In Go 1.22 and later, the loop variable lifetimes changed to create a new +// iteration variable per loop iteration. (See go.dev/issue/60078.) +// // In this example, all the deferred functions run after the loop has -// completed, so all observe the final value of v. +// completed, so all observe the final value of v [ 0 { + if tparams := fn.Type.TypeParams; tparams != nil && len(tparams.List) > 0 { pass.Reportf(fn.Pos(), "%s should not have type params", fnName) } @@ -460,7 +459,7 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) { return } - if tparams := typeparams.ForFuncType(fn.Type); tparams != nil && len(tparams.List) > 0 { + if tparams := fn.Type.TypeParams; tparams != nil && len(tparams.List) > 0 { // Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters. // We have currently decided to also warn before compilation/package loading. This can help users in IDEs. // TODO(adonovan): use ReportRangef(tparams). diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go index 7043baa899..f4e73528b4 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go @@ -14,7 +14,6 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" - "golang.org/x/tools/internal/typeparams" ) //go:embed doc.go @@ -92,7 +91,7 @@ func run(pass *analysis.Pass) (interface{}, error) { t := pass.TypesInfo.Types[call.Args[argidx]].Type switch t.Underlying().(type) { - case *types.Pointer, *types.Interface, *typeparams.TypeParam: + case *types.Pointer, *types.Interface, *types.TypeParam: return } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go index 0a40652c1b..1fa0d1f68f 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go @@ -50,7 +50,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/internal/analysisflags" "golang.org/x/tools/internal/facts" - "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/versions" ) // A Config describes a compilation unit to be analyzed. @@ -258,10 +258,11 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), + Instances: make(map[*ast.Ident]types.Instance), Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), } - typeparams.InitInstanceInfo(info) + versions.InitFileVersions(info) pkg, err := tc.Check(cfg.ImportPath, fset, files, info) if err != nil { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go index 9da5692af5..4f2c404562 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go @@ -19,6 +19,8 @@ import ( // that the Requires graph is acyclic; // that analyzer fact types are unique; // that each fact type is a pointer. +// +// Analyzer names need not be unique, though this may be confusing. func Validate(analyzers []*Analyzer) error { // Map each fact type to its sole generating analyzer. factTypes := make(map[reflect.Type]*Analyzer) diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go b/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go index 9fa5aa192c..2c4c4e2328 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/enclosing.go @@ -11,8 +11,6 @@ import ( "go/ast" "go/token" "sort" - - "golang.org/x/tools/internal/typeparams" ) // PathEnclosingInterval returns the node that encloses the source @@ -322,7 +320,7 @@ func childrenOf(n ast.Node) []ast.Node { children = append(children, n.Recv) } children = append(children, n.Name) - if tparams := typeparams.ForFuncType(n.Type); tparams != nil { + if tparams := n.Type.TypeParams; tparams != nil { children = append(children, tparams) } if n.Type.Params != nil { @@ -377,7 +375,7 @@ func childrenOf(n ast.Node) []ast.Node { tok(n.Lbrack, len("[")), tok(n.Rbrack, len("]"))) - case *typeparams.IndexListExpr: + case *ast.IndexListExpr: children = append(children, tok(n.Lbrack, len("[")), tok(n.Rbrack, len("]"))) @@ -588,7 +586,7 @@ func NodeDescription(n ast.Node) string { return "decrement statement" case *ast.IndexExpr: return "index expression" - case *typeparams.IndexListExpr: + case *ast.IndexListExpr: return "index list expression" case *ast.InterfaceType: return "interface type" diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go b/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go index f430b21b9b..58934f7663 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/astutil/rewrite.go @@ -9,8 +9,6 @@ import ( "go/ast" "reflect" "sort" - - "golang.org/x/tools/internal/typeparams" ) // An ApplyFunc is invoked by Apply for each node n, even if n is nil, @@ -252,7 +250,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. a.apply(n, "X", nil, n.X) a.apply(n, "Index", nil, n.Index) - case *typeparams.IndexListExpr: + case *ast.IndexListExpr: a.apply(n, "X", nil, n.X) a.applyList(n, "Indices") @@ -293,7 +291,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. a.apply(n, "Fields", nil, n.Fields) case *ast.FuncType: - if tparams := typeparams.ForFuncType(n); tparams != nil { + if tparams := n.TypeParams; tparams != nil { a.apply(n, "TypeParams", nil, tparams) } a.apply(n, "Params", nil, n.Params) @@ -408,7 +406,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast. case *ast.TypeSpec: a.apply(n, "Doc", nil, n.Doc) a.apply(n, "Name", nil, n.Name) - if tparams := typeparams.ForTypeSpec(n); tparams != nil { + if tparams := n.TypeParams; tparams != nil { a.apply(n, "TypeParams", nil, tparams) } a.apply(n, "Type", nil, n.Type) diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go index 703c813954..2a872f89d4 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go @@ -12,8 +12,6 @@ package inspector import ( "go/ast" "math" - - "golang.org/x/tools/internal/typeparams" ) const ( @@ -171,7 +169,7 @@ func typeOf(n ast.Node) uint64 { return 1 << nIncDecStmt case *ast.IndexExpr: return 1 << nIndexExpr - case *typeparams.IndexListExpr: + case *ast.IndexListExpr: return 1 << nIndexListExpr case *ast.InterfaceType: return 1 << nInterfaceType diff --git a/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go index e742ecc464..11d5c8c3ad 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go +++ b/src/cmd/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -223,7 +223,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { // Reject obviously non-viable cases. switch obj := obj.(type) { case *types.TypeName: - if _, ok := obj.Type().(*typeparams.TypeParam); !ok { + if _, ok := obj.Type().(*types.TypeParam); !ok { // With the exception of type parameters, only package-level type names // have a path. return "", fmt.Errorf("no path for %v", obj) @@ -283,7 +283,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { } } else { if named, _ := T.(*types.Named); named != nil { - if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil { + if r := findTypeParam(obj, named.TypeParams(), path, nil); r != nil { // generic named type return Path(r), nil } @@ -462,7 +462,7 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] } return find(obj, T.Elem(), append(path, opElem), seen) case *types.Signature: - if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil { + if r := findTypeParam(obj, T.TypeParams(), path, seen); r != nil { return r } if r := find(obj, T.Params(), append(path, opParams), seen); r != nil { @@ -505,7 +505,7 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] } } return nil - case *typeparams.TypeParam: + case *types.TypeParam: name := T.Obj() if name == obj { return append(path, opObj) @@ -525,7 +525,7 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName] panic(T) } -func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte { +func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte { for i := 0; i < list.Len(); i++ { tparam := list.At(i) path2 := appendOpArg(path, opTypeParam, i) @@ -562,7 +562,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { } // abstraction of *types.{Named,Signature} type hasTypeParams interface { - TypeParams() *typeparams.TypeParamList + TypeParams() *types.TypeParamList } // abstraction of *types.{Named,TypeParam} type hasObj interface { @@ -664,7 +664,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { t = tparams.At(index) case opConstraint: - tparam, ok := t.(*typeparams.TypeParam) + tparam, ok := t.(*types.TypeParam) if !ok { return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t) } diff --git a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/callee.go b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/callee.go index 90b3ab0e21..90dc541adf 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/callee.go +++ b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/callee.go @@ -22,7 +22,7 @@ func Callee(info *types.Info, call *ast.CallExpr) types.Object { // Look through type instantiation if necessary. isInstance := false switch fun.(type) { - case *ast.IndexExpr, *typeparams.IndexListExpr: + case *ast.IndexExpr, *ast.IndexListExpr: // When extracting the callee from an *IndexExpr, we need to check that // it is a *types.Func and not a *types.Var. // Example: Don't match a slice m within the expression `m[0]()`. diff --git a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go index 7bd2fdb38b..544246dac1 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go +++ b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go @@ -219,7 +219,7 @@ type Hasher struct { // generic types or functions, and instantiated signatures do not have type // parameter lists, we should never encounter a second non-empty type // parameter list when hashing a generic signature. - sigTParams *typeparams.TypeParamList + sigTParams *types.TypeParamList } // MakeHasher returns a new Hasher instance. @@ -297,7 +297,7 @@ func (h Hasher) hashFor(t types.Type) uint32 { // We should never encounter a generic signature while hashing another // generic signature, but defensively set sigTParams only if h.mask is // unset. - tparams := typeparams.ForSignature(t) + tparams := t.TypeParams() if h.sigTParams == nil && tparams.Len() != 0 { h = Hasher{ // There may be something more efficient than discarding the existing @@ -318,7 +318,7 @@ func (h Hasher) hashFor(t types.Type) uint32 { return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) - case *typeparams.Union: + case *types.Union: return h.hashUnion(t) case *types.Interface: @@ -354,14 +354,14 @@ func (h Hasher) hashFor(t types.Type) uint32 { case *types.Named: hash := h.hashPtr(t.Obj()) - targs := typeparams.NamedTypeArgs(t) + targs := t.TypeArgs() for i := 0; i < targs.Len(); i++ { targ := targs.At(i) hash += 2 * h.Hash(targ) } return hash - case *typeparams.TypeParam: + case *types.TypeParam: return h.hashTypeParam(t) case *types.Tuple: @@ -381,7 +381,7 @@ func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { return hash } -func (h Hasher) hashUnion(t *typeparams.Union) uint32 { +func (h Hasher) hashUnion(t *types.Union) uint32 { // Hash type restrictions. terms, err := typeparams.UnionTermSet(t) // if err != nil t has invalid type restrictions. Fall back on a non-zero @@ -392,7 +392,7 @@ func (h Hasher) hashUnion(t *typeparams.Union) uint32 { return h.hashTermSet(terms) } -func (h Hasher) hashTermSet(terms []*typeparams.Term) uint32 { +func (h Hasher) hashTermSet(terms []*types.Term) uint32 { hash := 9157 + 2*uint32(len(terms)) for _, term := range terms { // term order is not significant. @@ -416,7 +416,7 @@ func (h Hasher) hashTermSet(terms []*typeparams.Term) uint32 { // are not identical. // // Otherwise the hash of t depends only on t's pointer identity. -func (h Hasher) hashTypeParam(t *typeparams.TypeParam) uint32 { +func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 { if h.sigTParams != nil { i := t.Index() if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) { @@ -489,7 +489,7 @@ func (h Hasher) shallowHash(t types.Type) uint32 { case *types.Pointer: return 4393139 - case *typeparams.Union: + case *types.Union: return 562448657 case *types.Interface: @@ -504,7 +504,7 @@ func (h Hasher) shallowHash(t types.Type) uint32 { case *types.Named: return h.hashPtr(t.Obj()) - case *typeparams.TypeParam: + case *types.TypeParam: return h.hashPtr(t.Obj()) } panic(fmt.Sprintf("shallowHash: %T: %v", t, t)) diff --git a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go new file mode 100644 index 0000000000..2b29168047 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go @@ -0,0 +1,386 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package analysisinternal provides gopls' internal analyses with a +// number of helper functions that operate on typed syntax trees. +package analysisinternal + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" + "go/types" + "strconv" +) + +func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos { + // Get the end position for the type error. + offset, end := fset.PositionFor(start, false).Offset, start + if offset >= len(src) { + return end + } + if width := bytes.IndexAny(src[offset:], " \n,():;[]+-*"); width > 0 { + end = start + token.Pos(width) + } + return end +} + +func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + under := typ + if n, ok := typ.(*types.Named); ok { + under = n.Underlying() + } + switch u := under.(type) { + case *types.Basic: + switch { + case u.Info()&types.IsNumeric != 0: + return &ast.BasicLit{Kind: token.INT, Value: "0"} + case u.Info()&types.IsBoolean != 0: + return &ast.Ident{Name: "false"} + case u.Info()&types.IsString != 0: + return &ast.BasicLit{Kind: token.STRING, Value: `""`} + default: + panic(fmt.Sprintf("unknown basic type %v", u)) + } + case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array: + return ast.NewIdent("nil") + case *types.Struct: + texpr := TypeExpr(f, pkg, typ) // typ because we want the name here. + if texpr == nil { + return nil + } + return &ast.CompositeLit{ + Type: texpr, + } + } + return nil +} + +// IsZeroValue checks whether the given expression is a 'zero value' (as determined by output of +// analysisinternal.ZeroValue) +func IsZeroValue(expr ast.Expr) bool { + switch e := expr.(type) { + case *ast.BasicLit: + return e.Value == "0" || e.Value == `""` + case *ast.Ident: + return e.Name == "nil" || e.Name == "false" + default: + return false + } +} + +// TypeExpr returns syntax for the specified type. References to +// named types from packages other than pkg are qualified by an appropriate +// package name, as defined by the import environment of file. +func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + switch t := typ.(type) { + case *types.Basic: + switch t.Kind() { + case types.UnsafePointer: + return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")} + default: + return ast.NewIdent(t.Name()) + } + case *types.Pointer: + x := TypeExpr(f, pkg, t.Elem()) + if x == nil { + return nil + } + return &ast.UnaryExpr{ + Op: token.MUL, + X: x, + } + case *types.Array: + elt := TypeExpr(f, pkg, t.Elem()) + if elt == nil { + return nil + } + return &ast.ArrayType{ + Len: &ast.BasicLit{ + Kind: token.INT, + Value: fmt.Sprintf("%d", t.Len()), + }, + Elt: elt, + } + case *types.Slice: + elt := TypeExpr(f, pkg, t.Elem()) + if elt == nil { + return nil + } + return &ast.ArrayType{ + Elt: elt, + } + case *types.Map: + key := TypeExpr(f, pkg, t.Key()) + value := TypeExpr(f, pkg, t.Elem()) + if key == nil || value == nil { + return nil + } + return &ast.MapType{ + Key: key, + Value: value, + } + case *types.Chan: + dir := ast.ChanDir(t.Dir()) + if t.Dir() == types.SendRecv { + dir = ast.SEND | ast.RECV + } + value := TypeExpr(f, pkg, t.Elem()) + if value == nil { + return nil + } + return &ast.ChanType{ + Dir: dir, + Value: value, + } + case *types.Signature: + var params []*ast.Field + for i := 0; i < t.Params().Len(); i++ { + p := TypeExpr(f, pkg, t.Params().At(i).Type()) + if p == nil { + return nil + } + params = append(params, &ast.Field{ + Type: p, + Names: []*ast.Ident{ + { + Name: t.Params().At(i).Name(), + }, + }, + }) + } + var returns []*ast.Field + for i := 0; i < t.Results().Len(); i++ { + r := TypeExpr(f, pkg, t.Results().At(i).Type()) + if r == nil { + return nil + } + returns = append(returns, &ast.Field{ + Type: r, + }) + } + return &ast.FuncType{ + Params: &ast.FieldList{ + List: params, + }, + Results: &ast.FieldList{ + List: returns, + }, + } + case *types.Named: + if t.Obj().Pkg() == nil { + return ast.NewIdent(t.Obj().Name()) + } + if t.Obj().Pkg() == pkg { + return ast.NewIdent(t.Obj().Name()) + } + pkgName := t.Obj().Pkg().Name() + + // If the file already imports the package under another name, use that. + for _, cand := range f.Imports { + if path, _ := strconv.Unquote(cand.Path.Value); path == t.Obj().Pkg().Path() { + if cand.Name != nil && cand.Name.Name != "" { + pkgName = cand.Name.Name + } + } + } + if pkgName == "." { + return ast.NewIdent(t.Obj().Name()) + } + return &ast.SelectorExpr{ + X: ast.NewIdent(pkgName), + Sel: ast.NewIdent(t.Obj().Name()), + } + case *types.Struct: + return ast.NewIdent(t.String()) + case *types.Interface: + return ast.NewIdent(t.String()) + default: + return nil + } +} + +// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable. +// Some examples: +// +// Basic Example: +// z := 1 +// y := z + x +// If x is undeclared, then this function would return `y := z + x`, so that we +// can insert `x := ` on the line before `y := z + x`. +// +// If stmt example: +// if z == 1 { +// } else if z == y {} +// If y is undeclared, then this function would return `if z == 1 {`, because we cannot +// insert a statement between an if and an else if statement. As a result, we need to find +// the top of the if chain to insert `y := ` before. +func StmtToInsertVarBefore(path []ast.Node) ast.Stmt { + enclosingIndex := -1 + for i, p := range path { + if _, ok := p.(ast.Stmt); ok { + enclosingIndex = i + break + } + } + if enclosingIndex == -1 { + return nil + } + enclosingStmt := path[enclosingIndex] + switch enclosingStmt.(type) { + case *ast.IfStmt: + // The enclosingStmt is inside of the if declaration, + // We need to check if we are in an else-if stmt and + // get the base if statement. + return baseIfStmt(path, enclosingIndex) + case *ast.CaseClause: + // Get the enclosing switch stmt if the enclosingStmt is + // inside of the case statement. + for i := enclosingIndex + 1; i < len(path); i++ { + if node, ok := path[i].(*ast.SwitchStmt); ok { + return node + } else if node, ok := path[i].(*ast.TypeSwitchStmt); ok { + return node + } + } + } + if len(path) <= enclosingIndex+1 { + return enclosingStmt.(ast.Stmt) + } + // Check if the enclosing statement is inside another node. + switch expr := path[enclosingIndex+1].(type) { + case *ast.IfStmt: + // Get the base if statement. + return baseIfStmt(path, enclosingIndex+1) + case *ast.ForStmt: + if expr.Init == enclosingStmt || expr.Post == enclosingStmt { + return expr + } + } + return enclosingStmt.(ast.Stmt) +} + +// baseIfStmt walks up the if/else-if chain until we get to +// the top of the current if chain. +func baseIfStmt(path []ast.Node, index int) ast.Stmt { + stmt := path[index] + for i := index + 1; i < len(path); i++ { + if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt { + stmt = node + continue + } + break + } + return stmt.(ast.Stmt) +} + +// WalkASTWithParent walks the AST rooted at n. The semantics are +// similar to ast.Inspect except it does not call f(nil). +func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) { + var ancestors []ast.Node + ast.Inspect(n, func(n ast.Node) (recurse bool) { + if n == nil { + ancestors = ancestors[:len(ancestors)-1] + return false + } + + var parent ast.Node + if len(ancestors) > 0 { + parent = ancestors[len(ancestors)-1] + } + ancestors = append(ancestors, n) + return f(n, parent) + }) +} + +// MatchingIdents finds the names of all identifiers in 'node' that match any of the given types. +// 'pos' represents the position at which the identifiers may be inserted. 'pos' must be within +// the scope of each of identifier we select. Otherwise, we will insert a variable at 'pos' that +// is unrecognized. +func MatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]string { + + // Initialize matches to contain the variable types we are searching for. + matches := make(map[types.Type][]string) + for _, typ := range typs { + if typ == nil { + continue // TODO(adonovan): is this reachable? + } + matches[typ] = nil // create entry + } + + seen := map[types.Object]struct{}{} + ast.Inspect(node, func(n ast.Node) bool { + if n == nil { + return false + } + // Prevent circular definitions. If 'pos' is within an assignment statement, do not + // allow any identifiers in that assignment statement to be selected. Otherwise, + // we could do the following, where 'x' satisfies the type of 'f0': + // + // x := fakeStruct{f0: x} + // + if assign, ok := n.(*ast.AssignStmt); ok && pos > assign.Pos() && pos <= assign.End() { + return false + } + if n.End() > pos { + return n.Pos() <= pos + } + ident, ok := n.(*ast.Ident) + if !ok || ident.Name == "_" { + return true + } + obj := info.Defs[ident] + if obj == nil || obj.Type() == nil { + return true + } + if _, ok := obj.(*types.TypeName); ok { + return true + } + // Prevent duplicates in matches' values. + if _, ok = seen[obj]; ok { + return true + } + seen[obj] = struct{}{} + // Find the scope for the given position. Then, check whether the object + // exists within the scope. + innerScope := pkg.Scope().Innermost(pos) + if innerScope == nil { + return true + } + _, foundObj := innerScope.LookupParent(ident.Name, pos) + if foundObj != obj { + return true + } + // The object must match one of the types that we are searching for. + // TODO(adonovan): opt: use typeutil.Map? + if names, ok := matches[obj.Type()]; ok { + matches[obj.Type()] = append(names, ident.Name) + } else { + // If the object type does not exactly match + // any of the target types, greedily find the first + // target type that the object type can satisfy. + for typ := range matches { + if equivalentTypes(obj.Type(), typ) { + matches[typ] = append(matches[typ], ident.Name) + } + } + } + return true + }) + return matches +} + +func equivalentTypes(want, got types.Type) bool { + if types.Identical(want, got) { + return true + } + // Code segment to help check for untyped equality from (golang/go#32146). + if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 { + if lhs, ok := got.Underlying().(*types.Basic); ok { + return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType + } + } + return types.AssignableTo(want, got) +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/extractdoc.go b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/extractdoc.go similarity index 99% rename from src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/extractdoc.go rename to src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/extractdoc.go index 0e175ca06f..39507723d3 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/extractdoc.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/extractdoc.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. -package analysisutil +package analysisinternal import ( "fmt" diff --git a/src/cmd/vendor/golang.org/x/tools/internal/facts/imports.go b/src/cmd/vendor/golang.org/x/tools/internal/facts/imports.go index f64695ea52..1fe63ca6b5 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/facts/imports.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/facts/imports.go @@ -6,8 +6,6 @@ package facts import ( "go/types" - - "golang.org/x/tools/internal/typeparams" ) // importMap computes the import map for a package by traversing the @@ -55,7 +53,7 @@ func importMap(imports []*types.Package) map[string]*types.Package { // infinite expansions: // type N[T any] struct { F *N[N[T]] } // importMap() is called on such types when Analyzer.RunDespiteErrors is true. - T = typeparams.NamedTypeOrigin(T) + T = T.Origin() if !typs[T] { typs[T] = true addObj(T.Obj()) @@ -63,12 +61,12 @@ func importMap(imports []*types.Package) map[string]*types.Package { for i := 0; i < T.NumMethods(); i++ { addObj(T.Method(i)) } - if tparams := typeparams.ForNamed(T); tparams != nil { + if tparams := T.TypeParams(); tparams != nil { for i := 0; i < tparams.Len(); i++ { addType(tparams.At(i)) } } - if targs := typeparams.NamedTypeArgs(T); targs != nil { + if targs := T.TypeArgs(); targs != nil { for i := 0; i < targs.Len(); i++ { addType(targs.At(i)) } @@ -88,7 +86,7 @@ func importMap(imports []*types.Package) map[string]*types.Package { case *types.Signature: addType(T.Params()) addType(T.Results()) - if tparams := typeparams.ForSignature(T); tparams != nil { + if tparams := T.TypeParams(); tparams != nil { for i := 0; i < tparams.Len(); i++ { addType(tparams.At(i)) } @@ -108,11 +106,11 @@ func importMap(imports []*types.Package) map[string]*types.Package { for i := 0; i < T.NumEmbeddeds(); i++ { addType(T.EmbeddedType(i)) // walk Embedded for implicits } - case *typeparams.Union: + case *types.Union: for i := 0; i < T.Len(); i++ { addType(T.Term(i).Type()) } - case *typeparams.TypeParam: + case *types.TypeParam: if !typs[T] { typs[T] = true addObj(T.Obj()) diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go index d0d0649fe2..cdab988531 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go @@ -42,7 +42,7 @@ func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Ex switch e := n.(type) { case *ast.IndexExpr: return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack - case *IndexListExpr: + case *ast.IndexListExpr: return e.X, e.Lbrack, e.Indices, e.Rbrack } return nil, token.NoPos, nil, token.NoPos @@ -63,7 +63,7 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke Rbrack: rbrack, } default: - return &IndexListExpr{ + return &ast.IndexListExpr{ X: x, Lbrack: lbrack, Indices: indices, @@ -74,7 +74,7 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke // IsTypeParam reports whether t is a type parameter. func IsTypeParam(t types.Type) bool { - _, ok := t.(*TypeParam) + _, ok := t.(*types.TypeParam) return ok } @@ -100,11 +100,11 @@ func OriginMethod(fn *types.Func) *types.Func { // Receiver is a *types.Interface. return fn } - if ForNamed(named).Len() == 0 { + if named.TypeParams().Len() == 0 { // Receiver base has no type parameters, so we can avoid the lookup below. return fn } - orig := NamedTypeOrigin(named) + orig := named.Origin() gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name()) // This is a fix for a gopls crash (#60628) due to a go/types bug (#60634). In: @@ -157,7 +157,7 @@ func OriginMethod(fn *types.Func) *types.Func { // // In this case, GenericAssignableTo reports that instantiations of Container // are assignable to the corresponding instantiation of Interface. -func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { +func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool { // If V and T are not both named, or do not have matching non-empty type // parameter lists, fall back on types.AssignableTo. @@ -167,9 +167,9 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { return types.AssignableTo(V, T) } - vtparams := ForNamed(VN) - ttparams := ForNamed(TN) - if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 { + vtparams := VN.TypeParams() + ttparams := TN.TypeParams() + if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || VN.TypeArgs().Len() != 0 || TN.TypeArgs().Len() != 0 { return types.AssignableTo(V, T) } @@ -182,7 +182,7 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { // Minor optimization: ensure we share a context across the two // instantiations below. if ctxt == nil { - ctxt = NewContext() + ctxt = types.NewContext() } var targs []types.Type @@ -190,12 +190,12 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool { targs = append(targs, vtparams.At(i)) } - vinst, err := Instantiate(ctxt, V, targs, true) + vinst, err := types.Instantiate(ctxt, V, targs, true) if err != nil { panic("type parameters should satisfy their own constraints") } - tinst, err := Instantiate(ctxt, T, targs, true) + tinst, err := types.Instantiate(ctxt, T, targs, true) if err != nil { return false } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go index 71248209ee..7ea8840eab 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go @@ -108,15 +108,15 @@ func CoreType(T types.Type) types.Type { // // _NormalTerms makes no guarantees about the order of terms, except that it // is deterministic. -func _NormalTerms(typ types.Type) ([]*Term, error) { +func _NormalTerms(typ types.Type) ([]*types.Term, error) { switch typ := typ.(type) { - case *TypeParam: + case *types.TypeParam: return StructuralTerms(typ) - case *Union: + case *types.Union: return UnionTermSet(typ) case *types.Interface: return InterfaceTermSet(typ) default: - return []*Term{NewTerm(false, typ)}, nil + return []*types.Term{types.NewTerm(false, typ)}, nil } } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go deleted file mode 100644 index 18212390e1..0000000000 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/enabled_go117.go +++ /dev/null @@ -1,12 +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. - -//go:build !go1.18 -// +build !go1.18 - -package typeparams - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = false diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go deleted file mode 100644 index d67148823c..0000000000 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/enabled_go118.go +++ /dev/null @@ -1,15 +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. - -//go:build go1.18 -// +build go1.18 - -package typeparams - -// Note: this constant is in a separate file as this is the only acceptable -// diff between the <1.18 API of this package and the 1.18 API. - -// Enabled reports whether type parameters are enabled in the current build -// environment. -const Enabled = true diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/normalize.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/normalize.go index 9c631b6512..93c80fdc96 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/normalize.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/normalize.go @@ -60,7 +60,7 @@ var ErrEmptyTypeSet = errors.New("empty type set") // // StructuralTerms makes no guarantees about the order of terms, except that it // is deterministic. -func StructuralTerms(tparam *TypeParam) ([]*Term, error) { +func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) { constraint := tparam.Constraint() if constraint == nil { return nil, fmt.Errorf("%s has nil constraint", tparam) @@ -78,7 +78,7 @@ func StructuralTerms(tparam *TypeParam) ([]*Term, error) { // // See the documentation of StructuralTerms for more information on // normalization. -func InterfaceTermSet(iface *types.Interface) ([]*Term, error) { +func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) { return computeTermSet(iface) } @@ -88,11 +88,11 @@ func InterfaceTermSet(iface *types.Interface) ([]*Term, error) { // // See the documentation of StructuralTerms for more information on // normalization. -func UnionTermSet(union *Union) ([]*Term, error) { +func UnionTermSet(union *types.Union) ([]*types.Term, error) { return computeTermSet(union) } -func computeTermSet(typ types.Type) ([]*Term, error) { +func computeTermSet(typ types.Type) ([]*types.Term, error) { tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0) if err != nil { return nil, err @@ -103,9 +103,9 @@ func computeTermSet(typ types.Type) ([]*Term, error) { if tset.terms.isAll() { return nil, nil } - var terms []*Term + var terms []*types.Term for _, term := range tset.terms { - terms = append(terms, NewTerm(term.tilde, term.typ)) + terms = append(terms, types.NewTerm(term.tilde, term.typ)) } return terms, nil } @@ -162,7 +162,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in tset.terms = allTermlist for i := 0; i < u.NumEmbeddeds(); i++ { embedded := u.EmbeddedType(i) - if _, ok := embedded.Underlying().(*TypeParam); ok { + if _, ok := embedded.Underlying().(*types.TypeParam); ok { return nil, fmt.Errorf("invalid embedded type %T", embedded) } tset2, err := computeTermSetInternal(embedded, seen, depth+1) @@ -171,7 +171,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in } tset.terms = tset.terms.intersect(tset2.terms) } - case *Union: + case *types.Union: // The term set of a union is the union of term sets of its terms. tset.terms = nil for i := 0; i < u.Len(); i++ { @@ -184,7 +184,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in return nil, err } terms = tset2.terms - case *TypeParam, *Union: + case *types.TypeParam, *types.Union: // A stand-alone type parameter or union is not permitted as union // term. return nil, fmt.Errorf("invalid union term %T", t) @@ -199,7 +199,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in return nil, fmt.Errorf("exceeded max term count %d", maxTermCount) } } - case *TypeParam: + case *types.TypeParam: panic("unreachable") default: // For all other types, the term set is just a single non-tilde term diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go deleted file mode 100644 index 7ed86e1711..0000000000 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/typeparams_go117.go +++ /dev/null @@ -1,197 +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. - -//go:build !go1.18 -// +build !go1.18 - -package typeparams - -import ( - "go/ast" - "go/token" - "go/types" -) - -func unsupported() { - panic("type parameters are unsupported at this go version") -} - -// IndexListExpr is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type IndexListExpr struct { - ast.Expr - X ast.Expr // expression - Lbrack token.Pos // position of "[" - Indices []ast.Expr // index expressions - Rbrack token.Pos // position of "]" -} - -// ForTypeSpec returns an empty field list, as type parameters on not supported -// at this Go version. -func ForTypeSpec(*ast.TypeSpec) *ast.FieldList { - return nil -} - -// ForFuncType returns an empty field list, as type parameters are not -// supported at this Go version. -func ForFuncType(*ast.FuncType) *ast.FieldList { - return nil -} - -// TypeParam is a placeholder type, as type parameters are not supported at -// this Go version. Its methods panic on use. -type TypeParam struct{ types.Type } - -func (*TypeParam) Index() int { unsupported(); return 0 } -func (*TypeParam) Constraint() types.Type { unsupported(); return nil } -func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil } - -// TypeParamList is a placeholder for an empty type parameter list. -type TypeParamList struct{} - -func (*TypeParamList) Len() int { return 0 } -func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil } - -// TypeList is a placeholder for an empty type list. -type TypeList struct{} - -func (*TypeList) Len() int { return 0 } -func (*TypeList) At(int) types.Type { unsupported(); return nil } - -// NewTypeParam is unsupported at this Go version, and panics. -func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { - unsupported() - return nil -} - -// SetTypeParamConstraint is unsupported at this Go version, and panics. -func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { - unsupported() -} - -// NewSignatureType calls types.NewSignature, panicking if recvTypeParams or -// typeParams is non-empty. -func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { - if len(recvTypeParams) != 0 || len(typeParams) != 0 { - panic("signatures cannot have type parameters at this Go version") - } - return types.NewSignature(recv, params, results, variadic) -} - -// ForSignature returns an empty slice. -func ForSignature(*types.Signature) *TypeParamList { - return nil -} - -// RecvTypeParams returns a nil slice. -func RecvTypeParams(sig *types.Signature) *TypeParamList { - return nil -} - -// IsComparable returns false, as no interfaces are type-restricted at this Go -// version. -func IsComparable(*types.Interface) bool { - return false -} - -// IsMethodSet returns true, as no interfaces are type-restricted at this Go -// version. -func IsMethodSet(*types.Interface) bool { - return true -} - -// IsImplicit returns false, as no interfaces are implicit at this Go version. -func IsImplicit(*types.Interface) bool { - return false -} - -// MarkImplicit does nothing, because this Go version does not have implicit -// interfaces. -func MarkImplicit(*types.Interface) {} - -// ForNamed returns an empty type parameter list, as type parameters are not -// supported at this Go version. -func ForNamed(*types.Named) *TypeParamList { - return nil -} - -// SetForNamed panics if tparams is non-empty. -func SetForNamed(_ *types.Named, tparams []*TypeParam) { - if len(tparams) > 0 { - unsupported() - } -} - -// NamedTypeArgs returns nil. -func NamedTypeArgs(*types.Named) *TypeList { - return nil -} - -// NamedTypeOrigin is the identity method at this Go version. -func NamedTypeOrigin(named *types.Named) *types.Named { - return named -} - -// Term holds information about a structural type restriction. -type Term struct { - tilde bool - typ types.Type -} - -func (m *Term) Tilde() bool { return m.tilde } -func (m *Term) Type() types.Type { return m.typ } -func (m *Term) String() string { - pre := "" - if m.tilde { - pre = "~" - } - return pre + m.typ.String() -} - -// NewTerm is unsupported at this Go version, and panics. -func NewTerm(tilde bool, typ types.Type) *Term { - return &Term{tilde, typ} -} - -// Union is a placeholder type, as type parameters are not supported at this Go -// version. Its methods panic on use. -type Union struct{ types.Type } - -func (*Union) Len() int { return 0 } -func (*Union) Term(i int) *Term { unsupported(); return nil } - -// NewUnion is unsupported at this Go version, and panics. -func NewUnion(terms []*Term) *Union { - unsupported() - return nil -} - -// InitInstanceInfo is a noop at this Go version. -func InitInstanceInfo(*types.Info) {} - -// Instance is a placeholder type, as type parameters are not supported at this -// Go version. -type Instance struct { - TypeArgs *TypeList - Type types.Type -} - -// GetInstances returns a nil map, as type parameters are not supported at this -// Go version. -func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil } - -// Context is a placeholder type, as type parameters are not supported at -// this Go version. -type Context struct{} - -// NewContext returns a placeholder Context instance. -func NewContext() *Context { - return &Context{} -} - -// Instantiate is unsupported on this Go version, and panics. -func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { - unsupported() - return nil, nil -} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go deleted file mode 100644 index cf301af1db..0000000000 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/typeparams_go118.go +++ /dev/null @@ -1,151 +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. - -//go:build go1.18 -// +build go1.18 - -package typeparams - -import ( - "go/ast" - "go/types" -) - -// IndexListExpr is an alias for ast.IndexListExpr. -type IndexListExpr = ast.IndexListExpr - -// ForTypeSpec returns n.TypeParams. -func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList { - if n == nil { - return nil - } - return n.TypeParams -} - -// ForFuncType returns n.TypeParams. -func ForFuncType(n *ast.FuncType) *ast.FieldList { - if n == nil { - return nil - } - return n.TypeParams -} - -// TypeParam is an alias for types.TypeParam -type TypeParam = types.TypeParam - -// TypeParamList is an alias for types.TypeParamList -type TypeParamList = types.TypeParamList - -// TypeList is an alias for types.TypeList -type TypeList = types.TypeList - -// NewTypeParam calls types.NewTypeParam. -func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam { - return types.NewTypeParam(name, constraint) -} - -// SetTypeParamConstraint calls tparam.SetConstraint(constraint). -func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) { - tparam.SetConstraint(constraint) -} - -// NewSignatureType calls types.NewSignatureType. -func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature { - return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic) -} - -// ForSignature returns sig.TypeParams() -func ForSignature(sig *types.Signature) *TypeParamList { - return sig.TypeParams() -} - -// RecvTypeParams returns sig.RecvTypeParams(). -func RecvTypeParams(sig *types.Signature) *TypeParamList { - return sig.RecvTypeParams() -} - -// IsComparable calls iface.IsComparable(). -func IsComparable(iface *types.Interface) bool { - return iface.IsComparable() -} - -// IsMethodSet calls iface.IsMethodSet(). -func IsMethodSet(iface *types.Interface) bool { - return iface.IsMethodSet() -} - -// IsImplicit calls iface.IsImplicit(). -func IsImplicit(iface *types.Interface) bool { - return iface.IsImplicit() -} - -// MarkImplicit calls iface.MarkImplicit(). -func MarkImplicit(iface *types.Interface) { - iface.MarkImplicit() -} - -// ForNamed extracts the (possibly empty) type parameter object list from -// named. -func ForNamed(named *types.Named) *TypeParamList { - return named.TypeParams() -} - -// SetForNamed sets the type params tparams on n. Each tparam must be of -// dynamic type *types.TypeParam. -func SetForNamed(n *types.Named, tparams []*TypeParam) { - n.SetTypeParams(tparams) -} - -// NamedTypeArgs returns named.TypeArgs(). -func NamedTypeArgs(named *types.Named) *TypeList { - return named.TypeArgs() -} - -// NamedTypeOrigin returns named.Orig(). -func NamedTypeOrigin(named *types.Named) *types.Named { - return named.Origin() -} - -// Term is an alias for types.Term. -type Term = types.Term - -// NewTerm calls types.NewTerm. -func NewTerm(tilde bool, typ types.Type) *Term { - return types.NewTerm(tilde, typ) -} - -// Union is an alias for types.Union -type Union = types.Union - -// NewUnion calls types.NewUnion. -func NewUnion(terms []*Term) *Union { - return types.NewUnion(terms) -} - -// InitInstanceInfo initializes info to record information about type and -// function instances. -func InitInstanceInfo(info *types.Info) { - info.Instances = make(map[*ast.Ident]types.Instance) -} - -// Instance is an alias for types.Instance. -type Instance = types.Instance - -// GetInstances returns info.Instances. -func GetInstances(info *types.Info) map[*ast.Ident]Instance { - return info.Instances -} - -// Context is an alias for types.Context. -type Context = types.Context - -// NewContext calls types.NewContext. -func NewContext() *Context { - return types.NewContext() -} - -// Instantiate calls types.Instantiate. -func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) { - return types.Instantiate(ctxt, typ, targs, validate) -} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/versions/gover.go b/src/cmd/vendor/golang.org/x/tools/internal/versions/gover.go new file mode 100644 index 0000000000..bbabcd22e9 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/versions/gover.go @@ -0,0 +1,172 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is a fork of internal/gover for use by x/tools until +// go1.21 and earlier are no longer supported by x/tools. + +package versions + +import "strings" + +// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]] +// The numbers are the original decimal strings to avoid integer overflows +// and since there is very little actual math. (Probably overflow doesn't matter in practice, +// but at the time this code was written, there was an existing test that used +// go1.99999999999, which does not fit in an int on 32-bit platforms. +// The "big decimal" representation avoids the problem entirely.) +type gover struct { + major string // decimal + minor string // decimal or "" + patch string // decimal or "" + kind string // "", "alpha", "beta", "rc" + pre string // decimal or "" +} + +// compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as toolchain versions. +// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21". +// Malformed versions compare less than well-formed versions and equal to each other. +// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0". +func compare(x, y string) int { + vx := parse(x) + vy := parse(y) + + if c := cmpInt(vx.major, vy.major); c != 0 { + return c + } + if c := cmpInt(vx.minor, vy.minor); c != 0 { + return c + } + if c := cmpInt(vx.patch, vy.patch); c != 0 { + return c + } + if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc + return c + } + if c := cmpInt(vx.pre, vy.pre); c != 0 { + return c + } + return 0 +} + +// lang returns the Go language version. For example, lang("1.2.3") == "1.2". +func lang(x string) string { + v := parse(x) + if v.minor == "" || v.major == "1" && v.minor == "0" { + return v.major + } + return v.major + "." + v.minor +} + +// isValid reports whether the version x is valid. +func isValid(x string) bool { + return parse(x) != gover{} +} + +// parse parses the Go version string x into a version. +// It returns the zero version if x is malformed. +func parse(x string) gover { + var v gover + + // Parse major version. + var ok bool + v.major, x, ok = cutInt(x) + if !ok { + return gover{} + } + if x == "" { + // Interpret "1" as "1.0.0". + v.minor = "0" + v.patch = "0" + return v + } + + // Parse . before minor version. + if x[0] != '.' { + return gover{} + } + + // Parse minor version. + v.minor, x, ok = cutInt(x[1:]) + if !ok { + return gover{} + } + if x == "" { + // Patch missing is same as "0" for older versions. + // Starting in Go 1.21, patch missing is different from explicit .0. + if cmpInt(v.minor, "21") < 0 { + v.patch = "0" + } + return v + } + + // Parse patch if present. + if x[0] == '.' { + v.patch, x, ok = cutInt(x[1:]) + if !ok || x != "" { + // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). + // Allowing them would be a bit confusing because we already have: + // 1.21 < 1.21rc1 + // But a prerelease of a patch would have the opposite effect: + // 1.21.3rc1 < 1.21.3 + // We've never needed them before, so let's not start now. + return gover{} + } + return v + } + + // Parse prerelease. + i := 0 + for i < len(x) && (x[i] < '0' || '9' < x[i]) { + if x[i] < 'a' || 'z' < x[i] { + return gover{} + } + i++ + } + if i == 0 { + return gover{} + } + v.kind, x = x[:i], x[i:] + if x == "" { + return v + } + v.pre, x, ok = cutInt(x) + if !ok || x != "" { + return gover{} + } + + return v +} + +// cutInt scans the leading decimal number at the start of x to an integer +// and returns that value and the rest of the string. +func cutInt(x string) (n, rest string, ok bool) { + i := 0 + for i < len(x) && '0' <= x[i] && x[i] <= '9' { + i++ + } + if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero + return "", "", false + } + return x[:i], x[i:], true +} + +// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. +// (Copied from golang.org/x/mod/semver's compareInt.) +func cmpInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/versions/types.go b/src/cmd/vendor/golang.org/x/tools/internal/versions/types.go new file mode 100644 index 0000000000..562eef21fa --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/versions/types.go @@ -0,0 +1,19 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package versions + +import ( + "go/types" +) + +// GoVersion returns the Go version of the type package. +// It returns zero if no version can be determined. +func GoVersion(pkg *types.Package) string { + // TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25. + if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok { + return pkg.GoVersion() + } + return "" +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/versions/types_go121.go b/src/cmd/vendor/golang.org/x/tools/internal/versions/types_go121.go new file mode 100644 index 0000000000..a7b79207ae --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/versions/types_go121.go @@ -0,0 +1,20 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.22 +// +build !go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersions always reports the a file's Go version as the +// zero version at this Go version. +func FileVersions(info *types.Info, file *ast.File) string { return "" } + +// InitFileVersions is a noop at this Go version. +func InitFileVersions(*types.Info) {} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/versions/types_go122.go b/src/cmd/vendor/golang.org/x/tools/internal/versions/types_go122.go new file mode 100644 index 0000000000..7b9ba89a82 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/versions/types_go122.go @@ -0,0 +1,24 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.22 +// +build go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersions maps a file to the file's semantic Go version. +// The reported version is the zero version if a version cannot be determined. +func FileVersions(info *types.Info, file *ast.File) string { + return info.FileVersions[file] +} + +// InitFileVersions initializes info to record Go versions for Go files. +func InitFileVersions(info *types.Info) { + info.FileVersions = make(map[*ast.File]string) +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/versions/versions.go b/src/cmd/vendor/golang.org/x/tools/internal/versions/versions.go new file mode 100644 index 0000000000..e16f6c33a5 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/versions/versions.go @@ -0,0 +1,52 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package versions + +// Note: If we use build tags to use go/versions when go >=1.22, +// we run into go.dev/issue/53737. Under some operations users would see an +// import of "go/versions" even if they would not compile the file. +// For example, during `go get -u ./...` (go.dev/issue/64490) we do not try to include +// For this reason, this library just a clone of go/versions for the moment. + +// Lang returns the Go language version for version x. +// If x is not a valid version, Lang returns the empty string. +// For example: +// +// Lang("go1.21rc2") = "go1.21" +// Lang("go1.21.2") = "go1.21" +// Lang("go1.21") = "go1.21" +// Lang("go1") = "go1" +// Lang("bad") = "" +// Lang("1.21") = "" +func Lang(x string) string { + v := lang(stripGo(x)) + if v == "" { + return "" + } + return x[:2+len(v)] // "go"+v without allocation +} + +// Compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as Go versions. +// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". +// Invalid versions, including the empty string, compare less than +// valid versions and equal to each other. +// The language version "go1.21" compares less than the +// release candidate and eventual releases "go1.21rc1" and "go1.21.0". +// Custom toolchain suffixes are ignored during comparison: +// "go1.21.0" and "go1.21.0-bigcorp" are equal. +func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) } + +// IsValid reports whether the version x is valid. +func IsValid(x string) bool { return isValid(stripGo(x)) } + +// stripGo converts from a "go1.21" version to a "1.21" version. +// If v does not start with "go", stripGo returns the empty string (a known invalid version). +func stripGo(v string) string { + if len(v) < 2 || v[:2] != "go" { + return "" + } + return v[2:] +} diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 9b2335f20e..d2caf1ffb0 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -17,13 +17,13 @@ github.com/google/pprof/third_party/svgpan # github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab ## explicit; go 1.13 github.com/ianlancetaylor/demangle -# golang.org/x/arch v0.5.1-0.20231011141335-a6bdeed49307 +# golang.org/x/arch v0.6.0 ## explicit; go 1.18 golang.org/x/arch/arm/armasm golang.org/x/arch/arm64/arm64asm golang.org/x/arch/ppc64/ppc64asm golang.org/x/arch/x86/x86asm -# golang.org/x/mod v0.13.1-0.20231025225536-6e58e47c7bd6 +# golang.org/x/mod v0.14.0 ## explicit; go 1.18 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile @@ -34,18 +34,18 @@ golang.org/x/mod/sumdb/dirhash golang.org/x/mod/sumdb/note golang.org/x/mod/sumdb/tlog golang.org/x/mod/zip -# golang.org/x/sync v0.4.1-0.20231011140417-10739b037d36 +# golang.org/x/sync v0.5.0 ## explicit; go 1.18 golang.org/x/sync/semaphore -# golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3 +# golang.org/x/sys v0.15.0 ## explicit; go 1.18 golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.13.1-0.20231011140651-6a610bc55bff +# golang.org/x/term v0.15.0 ## explicit; go 1.18 golang.org/x/term -# golang.org/x/tools v0.14.1-0.20231019165902-71f6a46884ab +# golang.org/x/tools v0.16.2-0.20231218185909-83bceaf2424d ## explicit; go 1.18 golang.org/x/tools/cmd/bisect golang.org/x/tools/cover @@ -92,6 +92,8 @@ golang.org/x/tools/go/ast/inspector golang.org/x/tools/go/cfg golang.org/x/tools/go/types/objectpath golang.org/x/tools/go/types/typeutil +golang.org/x/tools/internal/analysisinternal golang.org/x/tools/internal/bisect golang.org/x/tools/internal/facts golang.org/x/tools/internal/typeparams +golang.org/x/tools/internal/versions diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go index ba5b5ed967..5b2fa3d72f 100644 --- a/src/cmd/vet/doc.go +++ b/src/cmd/vet/doc.go @@ -27,28 +27,38 @@ program correctness. To list the available checks, run "go tool vet help": - asmdecl report mismatches between assembly files and Go declarations - assign check for useless assignments - atomic check for common mistakes using the sync/atomic package - bools check for common mistakes involving boolean operators - buildtag check that +build tags are well-formed and correctly located - cgocall detect some violations of the cgo pointer passing rules - composites check for unkeyed composite literals - copylocks check for locks erroneously passed by value - httpresponse check for mistakes using HTTP responses - loopclosure check references to loop variables from within nested functions - lostcancel check cancel func returned by context.WithCancel is called - nilfunc check for useless comparisons between functions and nil - printf check consistency of Printf format strings and arguments - shift check for shifts that equal or exceed the width of the integer - slog check for incorrect arguments to log/slog functions - stdmethods check signature of methods of well-known interfaces - structtag check that struct field tags conform to reflect.StructTag.Get - tests check for common mistaken usages of tests and examples - unmarshal report passing non-pointer or non-interface values to unmarshal - unreachable check for unreachable code - unsafeptr check for invalid conversions of uintptr to unsafe.Pointer - unusedresult check for unused results of calls to some functions + appends check for missing values after append + asmdecl report mismatches between assembly files and Go declarations + assign check for useless assignments + atomic check for common mistakes using the sync/atomic package + bools check for common mistakes involving boolean operators + buildtag check //go:build and // +build directives + cgocall detect some violations of the cgo pointer passing rules + composites check for unkeyed composite literals + copylocks check for locks erroneously passed by value + defers report common mistakes in defer statements + directive check Go toolchain directives such as //go:debug + errorsas report passing non-pointer or non-error values to errors.As + framepointer report assembly that clobbers the frame pointer before saving it + httpresponse check for mistakes using HTTP responses + ifaceassert detect impossible interface-to-interface type assertions + loopclosure check references to loop variables from within nested functions + lostcancel check cancel func returned by context.WithCancel is called + nilfunc check for useless comparisons between functions and nil + printf check consistency of Printf format strings and arguments + shift check for shifts that equal or exceed the width of the integer + sigchanyzer check for unbuffered channel of os.Signal + slog check for invalid structured logging calls + stdmethods check signature of methods of well-known interfaces + stringintconv check for string(int) conversions + structtag check that struct field tags conform to reflect.StructTag.Get + testinggoroutine report calls to (*testing.T).Fatal from goroutines started by a test + tests check for common mistaken usages of tests and examples + timeformat check for calls of (time.Time).Format or time.Parse with 2006-02-01 + unmarshal report passing non-pointer or non-interface values to unmarshal + unreachable check for unreachable code + unsafeptr check for invalid conversions of uintptr to unsafe.Pointer + unusedresult check for unused results of calls to some functions For details and flags of a particular check, such as printf, run "go tool vet help printf". diff --git a/src/crypto/aes/asm_s390x.s b/src/crypto/aes/asm_s390x.s index efcce3a0d9..a233714fb8 100644 --- a/src/crypto/aes/asm_s390x.s +++ b/src/crypto/aes/asm_s390x.s @@ -127,7 +127,7 @@ crypt: MOVD src_base+56(FP), R6 // src MOVD src_len+64(FP), R7 // len loop: - KMCTR R6, R2, R4 // cipher message with counter (KMCTR) + KMCTR R4, R2, R6 // cipher message with counter (KMCTR) BVS loop // branch back if interrupted RET crash: @@ -180,7 +180,7 @@ TEXT ·kmaGCM(SB),NOSPLIT,$112-120 MVC $8, 24(R8), 104(R1) kma: - KMA R6, R2, R4 // Cipher Message with Authentication + KMA R2, R6, R4 // Cipher Message with Authentication BVS kma MOVD tag+104(FP), R2 diff --git a/src/crypto/internal/boring/Dockerfile b/src/crypto/internal/boring/Dockerfile index 58eb028e8a..8fde5c0018 100644 --- a/src/crypto/internal/boring/Dockerfile +++ b/src/crypto/internal/boring/Dockerfile @@ -13,15 +13,21 @@ WORKDIR /boring ENV LANG=C ENV LANGUAGE= -# Following NIST submission draft dated July 3, 2021. -# This corresponds to boringssl.googlesource.com/boringssl tag fips-20210429. -ENV ClangV=12 +# Following NIST submission draft for In Progress module validation. +# This corresponds to boringssl.googlesource.com/boringssl tag fips-20220613. RUN apt-get update && \ - apt-get install --no-install-recommends -y cmake xz-utils wget unzip ca-certificates clang-$ClangV python + apt-get install --no-install-recommends -y cmake xz-utils wget unzip ca-certificates python lsb-release software-properties-common gnupg + +# Install Clang. +ENV ClangV=14 +RUN \ + wget https://apt.llvm.org/llvm.sh && \ + chmod +x llvm.sh && \ + ./llvm.sh $ClangV # Download, validate, unpack, build, and install Ninja. -ENV NinjaV=1.10.2 -ENV NinjaH=ce35865411f0490368a8fc383f29071de6690cbadc27704734978221f25e2bed +ENV NinjaV=1.10.1 +ENV NinjaH=a6b6f7ac360d4aabd54e299cc1d8fa7b234cd81b9401693da21221c62569a23e RUN \ wget https://github.com/ninja-build/ninja/archive/refs/tags/v$NinjaV.tar.gz && \ echo "$NinjaH v$NinjaV.tar.gz" >sha && sha256sum -c sha && \ @@ -33,9 +39,9 @@ RUN \ # Download, validate, unpack, and install Go. ARG GOARCH -ENV GoV=1.16.5 -ENV GoHamd64=b12c23023b68de22f74c0524f10b753e7b08b1504cb7e417eccebdd3fae49061 -ENV GoHarm64=d5446b46ef6f36fdffa852f73dfbbe78c1ddf010b99fa4964944b9ae8b4d6799 +ENV GoV=1.18.1 +ENV GoHamd64=b3b815f47ababac13810fc6021eb73d65478e0b2db4b09d348eefad9581a2334 +ENV GoHarm64=56a91851c97fb4697077abbca38860f735c32b38993ff79b088dac46e4735633 RUN \ eval GoH=\${GoH$GOARCH} && \ wget https://golang.org/dl/go$GoV.linux-$GOARCH.tar.gz && \ @@ -45,8 +51,8 @@ RUN \ ln -s /usr/local/go/bin/go /usr/local/bin/ # Download, validate, and unpack BoringCrypto. -ENV BoringV=853ca1ea1168dff08011e5d42d94609cc0ca2e27 -ENV BoringH=a4d069ccef6f3c7bc0c68de82b91414f05cb817494cd1ab483dcf3368883c7c2 +ENV BoringV=0c6f40132b828e92ba365c6b7680e32820c63fa7 +ENV BoringH=62f733289f2d677c2723f556aa58034c438f3a7bbca6c12b156538a88e38da8a RUN \ wget https://commondatastorage.googleapis.com/chromium-boringssl-fips/boringssl-$BoringV.tar.xz && \ echo "$BoringH boringssl-$BoringV.tar.xz" >sha && sha256sum -c sha && \ diff --git a/src/crypto/internal/boring/LICENSE b/src/crypto/internal/boring/LICENSE index 38990bdb77..05b0963f5e 100644 --- a/src/crypto/internal/boring/LICENSE +++ b/src/crypto/internal/boring/LICENSE @@ -6,7 +6,7 @@ When building with GOEXPERIMENT=boringcrypto, the following applies. The goboringcrypto_linux_amd64.syso object file is built from BoringSSL source code by build/build.sh and is covered by the BoringSSL license reproduced below and also at -https://boringssl.googlesource.com/boringssl/+/fips-20190808/LICENSE. +https://boringssl.googlesource.com/boringssl/+/fips-20220613/LICENSE. BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL licensing. Files that are completely new have a Google copyright and an ISC diff --git a/src/crypto/internal/boring/README.md b/src/crypto/internal/boring/README.md index ec02786d96..62106cdc70 100644 --- a/src/crypto/internal/boring/README.md +++ b/src/crypto/internal/boring/README.md @@ -27,13 +27,14 @@ syso/goboringcrypto_linux_arm64.syso is built with: GOARCH=arm64 ./build.sh -Both run on an x86 Debian Linux system using Docker. +Both run using Docker. + For the arm64 build to run on an x86 system, you need apt-get install qemu-user-static qemu-binfmt-support to allow the x86 kernel to run arm64 binaries via QEMU. +For the amd64 build to run on an Apple Silicon macOS, you need Rosetta 2. + See build.sh for more details about the build. - - diff --git a/src/crypto/internal/boring/aes.go b/src/crypto/internal/boring/aes.go index 8819f576f4..d18ed5cdc5 100644 --- a/src/crypto/internal/boring/aes.go +++ b/src/crypto/internal/boring/aes.go @@ -228,26 +228,41 @@ func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { if tagSize != gcmTagSize { return cipher.NewGCMWithTagSize(&noGCM{c}, tagSize) } - return c.newGCM(false) + return c.newGCM(0) } +const ( + VersionTLS12 = 0x0303 + VersionTLS13 = 0x0304 +) + func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { - return c.(*aesCipher).newGCM(true) + return c.(*aesCipher).newGCM(VersionTLS12) } -func (c *aesCipher) newGCM(tls bool) (cipher.AEAD, error) { +func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) { + return c.(*aesCipher).newGCM(VersionTLS13) +} + +func (c *aesCipher) newGCM(tlsVersion uint16) (cipher.AEAD, error) { var aead *C.GO_EVP_AEAD switch len(c.key) * 8 { case 128: - if tls { + switch tlsVersion { + case VersionTLS12: aead = C._goboringcrypto_EVP_aead_aes_128_gcm_tls12() - } else { + case VersionTLS13: + aead = C._goboringcrypto_EVP_aead_aes_128_gcm_tls13() + default: aead = C._goboringcrypto_EVP_aead_aes_128_gcm() } case 256: - if tls { + switch tlsVersion { + case VersionTLS12: aead = C._goboringcrypto_EVP_aead_aes_256_gcm_tls12() - } else { + case VersionTLS13: + aead = C._goboringcrypto_EVP_aead_aes_256_gcm_tls13() + default: aead = C._goboringcrypto_EVP_aead_aes_256_gcm() } default: diff --git a/src/crypto/internal/boring/build-goboring.sh b/src/crypto/internal/boring/build-goboring.sh index 4938b5eac3..c43fad24e8 100755 --- a/src/crypto/internal/boring/build-goboring.sh +++ b/src/crypto/internal/boring/build-goboring.sh @@ -122,7 +122,7 @@ awk -f boringx.awk goboringcrypto.h # writes goboringcrypto.x awk -f boringh.awk goboringcrypto.h # writes goboringcrypto[01].h ls -l ../boringssl/include -clang++ -std=c++11 -fPIC -I../boringssl/include -O2 -o a.out goboringcrypto.cc +clang++ -fPIC -I../boringssl/include -O2 -o a.out goboringcrypto.cc ./a.out || exit 2 # clang implements u128 % u128 -> u128 by calling __umodti3, diff --git a/src/crypto/internal/boring/build.sh b/src/crypto/internal/boring/build.sh index ec960d729d..e2026018a3 100755 --- a/src/crypto/internal/boring/build.sh +++ b/src/crypto/internal/boring/build.sh @@ -22,6 +22,12 @@ platform="" buildargs="" case "$GOARCH" in amd64) + if ! docker run --rm -t amd64/ubuntu:focal uname -m >/dev/null 2>&1; then + echo "# Docker cannot run amd64 binaries." + exit 1 + fi + platform="--platform linux/amd64" + buildargs="--build-arg ubuntu=amd64/ubuntu" ;; arm64) if ! docker run --rm -t arm64v8/ubuntu:focal uname -m >/dev/null 2>&1; then diff --git a/src/crypto/internal/boring/goboringcrypto.h b/src/crypto/internal/boring/goboringcrypto.h index 2b11049728..3663a1b1c3 100644 --- a/src/crypto/internal/boring/goboringcrypto.h +++ b/src/crypto/internal/boring/goboringcrypto.h @@ -125,7 +125,9 @@ void _goboringcrypto_EVP_AEAD_CTX_cleanup(GO_EVP_AEAD_CTX*); int _goboringcrypto_EVP_AEAD_CTX_seal(const GO_EVP_AEAD_CTX*, uint8_t*, size_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t); int _goboringcrypto_EVP_AEAD_CTX_open(const GO_EVP_AEAD_CTX*, uint8_t*, size_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t); const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_128_gcm_tls12(void); +const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_128_gcm_tls13(void); const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_256_gcm_tls12(void); +const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_256_gcm_tls13(void); enum go_evp_aead_direction_t { go_evp_aead_open = 0, go_evp_aead_seal = 1 diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go index 361dec9672..02bc468a0d 100644 --- a/src/crypto/internal/boring/notboring.go +++ b/src/crypto/internal/boring/notboring.go @@ -50,6 +50,7 @@ func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("boringcrypto: no func NewAESCipher(key []byte) (cipher.Block, error) { panic("boringcrypto: not available") } func NewGCMTLS(cipher.Block) (cipher.AEAD, error) { panic("boringcrypto: not available") } +func NewGCMTLS13(cipher.Block) (cipher.AEAD, error) { panic("boringcrypto: not available") } type PublicKeyECDSA struct{ _ int } type PrivateKeyECDSA struct{ _ int } diff --git a/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso b/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso index 6cea789355..b99e7f5766 100644 Binary files a/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso and b/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso differ diff --git a/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso b/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso index 9659aa1a5e..143a47a0aa 100644 Binary files a/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso and b/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso differ diff --git a/src/crypto/tls/boring.go b/src/crypto/tls/boring.go index 1827f76458..aad96b1c74 100644 --- a/src/crypto/tls/boring.go +++ b/src/crypto/tls/boring.go @@ -6,9 +6,10 @@ package tls -import ( - "crypto/internal/boring/fipstls" -) +import "crypto/internal/boring/fipstls" + +// The FIPS-only policies enforced here currently match BoringSSL's +// ssl_policy_fips_202205. // needFIPS returns fipstls.Required(); it avoids a new import in common.go. func needFIPS() bool { @@ -17,19 +18,19 @@ func needFIPS() bool { // fipsMinVersion replaces c.minVersion in FIPS-only mode. func fipsMinVersion(c *Config) uint16 { - // FIPS requires TLS 1.2. + // FIPS requires TLS 1.2 or TLS 1.3. return VersionTLS12 } // fipsMaxVersion replaces c.maxVersion in FIPS-only mode. func fipsMaxVersion(c *Config) uint16 { - // FIPS requires TLS 1.2. - return VersionTLS12 + // FIPS requires TLS 1.2 or TLS 1.3. + return VersionTLS13 } // default defaultFIPSCurvePreferences is the FIPS-allowed curves, // in preference order (most preferable first). -var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521} +var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384} // fipsCurvePreferences replaces c.curvePreferences in FIPS-only mode. func fipsCurvePreferences(c *Config) []CurveID { @@ -54,8 +55,6 @@ var defaultCipherSuitesFIPS = []uint16{ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, } // fipsCipherSuites replaces c.cipherSuites in FIPS-only mode. @@ -75,8 +74,14 @@ func fipsCipherSuites(c *Config) []uint16 { return list } +// defaultCipherSuitesTLS13FIPS are the FIPS-allowed cipher suites for TLS 1.3. +var defaultCipherSuitesTLS13FIPS = []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, +} + // fipsSupportedSignatureAlgorithms currently are a subset of -// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1. +// defaultSupportedSignatureAlgorithms without Ed25519, SHA-1, and P-521. var fipsSupportedSignatureAlgorithms = []SignatureScheme{ PSSWithSHA256, PSSWithSHA384, @@ -86,7 +91,6 @@ var fipsSupportedSignatureAlgorithms = []SignatureScheme{ PKCS1WithSHA384, ECDSAWithP384AndSHA384, PKCS1WithSHA512, - ECDSAWithP521AndSHA512, } // supportedSignatureAlgorithms returns the supported signature algorithms. diff --git a/src/crypto/tls/boring_test.go b/src/crypto/tls/boring_test.go index 085ff5713e..a192a657b4 100644 --- a/src/crypto/tls/boring_test.go +++ b/src/crypto/tls/boring_test.go @@ -25,6 +25,31 @@ import ( "time" ) +func allCipherSuitesIncludingTLS13() []uint16 { + s := allCipherSuites() + for _, suite := range cipherSuitesTLS13 { + s = append(s, suite.id) + } + return s +} + +func isTLS13CipherSuite(id uint16) bool { + for _, suite := range cipherSuitesTLS13 { + if id == suite.id { + return true + } + } + return false +} + +func generateKeyShare(group CurveID) keyShare { + key, err := generateECDHEKey(rand.Reader, group) + if err != nil { + panic(err) + } + return keyShare{group: group, data: key.PublicKey().Bytes()} +} + func TestBoringServerProtocolVersion(t *testing.T) { test := func(name string, v uint16, msg string) { t.Run(name, func(t *testing.T) { @@ -33,8 +58,11 @@ func TestBoringServerProtocolVersion(t *testing.T) { clientHello := &clientHelloMsg{ vers: v, random: make([]byte, 32), - cipherSuites: allCipherSuites(), + cipherSuites: allCipherSuitesIncludingTLS13(), compressionMethods: []uint8{compressionNone}, + supportedCurves: defaultCurvePreferences, + keyShares: []keyShare{generateKeyShare(CurveP256)}, + supportedPoints: []uint8{pointFormatUncompressed}, supportedVersions: []uint16{v}, } testClientHelloFailure(t, serverConfig, clientHello, msg) @@ -48,25 +76,25 @@ func TestBoringServerProtocolVersion(t *testing.T) { fipstls.Force() defer fipstls.Abandon() - test("VersionSSL30", VersionSSL30, "client offered only unsupported versions") - test("VersionTLS10", VersionTLS10, "client offered only unsupported versions") - test("VersionTLS11", VersionTLS11, "client offered only unsupported versions") - test("VersionTLS12", VersionTLS12, "") - test("VersionTLS13", VersionTLS13, "client offered only unsupported versions") + test("VersionSSL30/fipstls", VersionSSL30, "client offered only unsupported versions") + test("VersionTLS10/fipstls", VersionTLS10, "client offered only unsupported versions") + test("VersionTLS11/fipstls", VersionTLS11, "client offered only unsupported versions") + test("VersionTLS12/fipstls", VersionTLS12, "") + test("VersionTLS13/fipstls", VersionTLS13, "") } func isBoringVersion(v uint16) bool { - return v == VersionTLS12 + return v == VersionTLS12 || v == VersionTLS13 } func isBoringCipherSuite(id uint16) bool { switch id { - case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + case TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384: + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: return true } return false @@ -74,7 +102,7 @@ func isBoringCipherSuite(id uint16) bool { func isBoringCurve(id CurveID) bool { switch id { - case CurveP256, CurveP384, CurveP521: + case CurveP256, CurveP384: return true } return false @@ -86,7 +114,7 @@ func isECDSA(id uint16) bool { return suite.flags&suiteECSign == suiteECSign } } - panic(fmt.Sprintf("unknown cipher suite %#x", id)) + return false // TLS 1.3 cipher suites are not tied to the signature algorithm. } func isBoringSignatureScheme(alg SignatureScheme) bool { @@ -98,7 +126,6 @@ func isBoringSignatureScheme(alg SignatureScheme) bool { PKCS1WithSHA384, ECDSAWithP384AndSHA384, PKCS1WithSHA512, - ECDSAWithP521AndSHA512, PSSWithSHA256, PSSWithSHA384, PSSWithSHA512: @@ -109,10 +136,9 @@ func isBoringSignatureScheme(alg SignatureScheme) bool { func TestBoringServerCipherSuites(t *testing.T) { serverConfig := testConfig.Clone() - serverConfig.CipherSuites = allCipherSuites() serverConfig.Certificates = make([]Certificate, 1) - for _, id := range allCipherSuites() { + for _, id := range allCipherSuitesIncludingTLS13() { if isECDSA(id) { serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey @@ -121,14 +147,19 @@ func TestBoringServerCipherSuites(t *testing.T) { serverConfig.Certificates[0].PrivateKey = testRSAPrivateKey } serverConfig.BuildNameToCertificate() - t.Run(fmt.Sprintf("suite=%#x", id), func(t *testing.T) { + t.Run(fmt.Sprintf("suite=%s", CipherSuiteName(id)), func(t *testing.T) { clientHello := &clientHelloMsg{ vers: VersionTLS12, random: make([]byte, 32), cipherSuites: []uint16{id}, compressionMethods: []uint8{compressionNone}, supportedCurves: defaultCurvePreferences, + keyShares: []keyShare{generateKeyShare(CurveP256)}, supportedPoints: []uint8{pointFormatUncompressed}, + supportedVersions: []uint16{VersionTLS12}, + } + if isTLS13CipherSuite(id) { + clientHello.supportedVersions = []uint16{VersionTLS13} } testClientHello(t, serverConfig, clientHello) @@ -160,7 +191,9 @@ func TestBoringServerCurves(t *testing.T) { cipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, compressionMethods: []uint8{compressionNone}, supportedCurves: []CurveID{curveid}, + keyShares: []keyShare{generateKeyShare(curveid)}, supportedPoints: []uint8{pointFormatUncompressed}, + supportedVersions: []uint16{VersionTLS12}, } testClientHello(t, serverConfig, clientHello) @@ -279,7 +312,7 @@ func TestBoringClientHello(t *testing.T) { } if !isBoringVersion(hello.vers) { - t.Errorf("client vers=%#x, want %#x (TLS 1.2)", hello.vers, VersionTLS12) + t.Errorf("client vers=%#x", hello.vers) } for _, v := range hello.supportedVersions { if !isBoringVersion(v) { diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index eaeb7e04e6..636689beb4 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -17,7 +17,6 @@ import ( "fmt" "hash" "internal/cpu" - "internal/godebug" "runtime" "golang.org/x/crypto/chacha20poly1305" @@ -53,11 +52,6 @@ var ( // and might not match those returned by this function. func CipherSuites() []*CipherSuite { return []*CipherSuite{ - {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false}, - {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false}, - {TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, - {TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, - {TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false}, {TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false}, {TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false}, @@ -86,7 +80,11 @@ func InsecureCipherSuites() []*CipherSuite { return []*CipherSuite{ {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true}, + {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, true}, + {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, true}, {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, + {TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, true}, + {TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, true}, {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, {TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true}, @@ -323,25 +321,21 @@ var cipherSuitesPreferenceOrderNoAES = []uint16{ TLS_RSA_WITH_RC4_128_SHA, } -// disabledCipherSuites are not used unless explicitly listed in -// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder. -var disabledCipherSuites = []uint16{ +// disabledCipherSuites are not used unless explicitly listed in Config.CipherSuites. +var disabledCipherSuites = map[uint16]bool{ // CBC_SHA256 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: true, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: true, + TLS_RSA_WITH_AES_128_CBC_SHA256: true, // RC4 - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_RC4_128_SHA, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: true, + TLS_ECDHE_RSA_WITH_RC4_128_SHA: true, + TLS_RSA_WITH_RC4_128_SHA: true, } -var ( - defaultCipherSuitesLen int - defaultCipherSuites []uint16 -) - // rsaKexCiphers contains the ciphers which use RSA based key exchange, -// which we disable by default. +// which we also disable by default unless a GODEBUG is set. var rsaKexCiphers = map[uint16]bool{ TLS_RSA_WITH_RC4_128_SHA: true, TLS_RSA_WITH_3DES_EDE_CBC_SHA: true, @@ -352,17 +346,21 @@ var rsaKexCiphers = map[uint16]bool{ TLS_RSA_WITH_AES_256_GCM_SHA384: true, } -var rsaKEXgodebug = godebug.New("tlsrsakex") +var defaultCipherSuites []uint16 +var defaultCipherSuitesWithRSAKex []uint16 func init() { - rsaKexEnabled := rsaKEXgodebug.Value() == "1" - for _, c := range cipherSuitesPreferenceOrder[:len(cipherSuitesPreferenceOrder)-len(disabledCipherSuites)] { - if !rsaKexEnabled && rsaKexCiphers[c] { + defaultCipherSuites = make([]uint16, 0, len(cipherSuitesPreferenceOrder)) + defaultCipherSuitesWithRSAKex = make([]uint16, 0, len(cipherSuitesPreferenceOrder)) + for _, c := range cipherSuitesPreferenceOrder { + if disabledCipherSuites[c] { continue } - defaultCipherSuites = append(defaultCipherSuites, c) + if !rsaKexCiphers[c] { + defaultCipherSuites = append(defaultCipherSuites, c) + } + defaultCipherSuitesWithRSAKex = append(defaultCipherSuitesWithRSAKex, c) } - defaultCipherSuitesLen = len(defaultCipherSuites) } // defaultCipherSuitesTLS13 is also the preference order, since there are no @@ -558,7 +556,13 @@ func aeadAESGCMTLS13(key, nonceMask []byte) aead { if err != nil { panic(err) } - aead, err := cipher.NewGCM(aes) + var aead cipher.AEAD + if boring.Enabled { + aead, err = boring.NewGCMTLS13(aes) + } else { + boring.Unreachable() + aead, err = cipher.NewGCM(aes) + } if err != nil { panic(err) } diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index 6058824ded..849e8b0a20 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -304,11 +304,13 @@ type ConnectionState struct { // ExportKeyingMaterial returns length bytes of exported key material in a new // slice as defined in RFC 5705. If context is nil, it is not used as part of // the seed. If the connection was set to allow renegotiation via -// Config.Renegotiation, this function will return an error. +// Config.Renegotiation, or if the connections supports neither TLS 1.3 nor +// Extended Master Secret, this function will return an error. // -// There are conditions in which the returned values might not be unique to a -// connection. See the Security Considerations sections of RFC 5705 and RFC 7627, -// and https://mitls.org/pages/attacks/3SHAKE#channelbindings. +// Exporting key material without Extended Master Secret or TLS 1.3 was disabled +// in Go 1.22 due to security issues (see the Security Considerations sections +// of RFC 5705 and RFC 7627), but can be re-enabled with the GODEBUG setting +// tlsunsafeekm=1. func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { return cs.ekm(label, context, length) } @@ -1006,6 +1008,8 @@ func (c *Config) time() time.Time { return t() } +var tlsrsakex = godebug.New("tlsrsakex") + func (c *Config) cipherSuites() []uint16 { if needFIPS() { return fipsCipherSuites(c) @@ -1013,6 +1017,9 @@ func (c *Config) cipherSuites() []uint16 { if c.CipherSuites != nil { return c.CipherSuites } + if tlsrsakex.Value() == "1" { + return defaultCipherSuitesWithRSAKex + } return defaultCipherSuites } @@ -1028,7 +1035,7 @@ var supportedVersions = []uint16{ const roleClient = true const roleServer = false -var tls10godebug = godebug.New("tls10server") +var tls10server = godebug.New("tls10server") func (c *Config) supportedVersions(isClient bool) []uint16 { versions := make([]uint16, 0, len(supportedVersions)) @@ -1037,9 +1044,7 @@ func (c *Config) supportedVersions(isClient bool) []uint16 { continue } if (c == nil || c.MinVersion == 0) && v < VersionTLS12 { - if !isClient && tls10godebug.Value() == "1" { - tls10godebug.IncNonDefault() - } else { + if isClient || tls10server.Value() != "1" { continue } } diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index 647e5b85b6..0e4669866e 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -15,6 +15,7 @@ import ( "errors" "fmt" "hash" + "internal/godebug" "io" "net" "sync" @@ -1599,6 +1600,8 @@ func (c *Conn) ConnectionState() ConnectionState { return c.connectionStateLocked() } +var tlsunsafeekm = godebug.New("tlsunsafeekm") + func (c *Conn) connectionStateLocked() ConnectionState { var state ConnectionState state.HandshakeComplete = c.isHandshakeComplete.Load() @@ -1620,7 +1623,15 @@ func (c *Conn) connectionStateLocked() ConnectionState { } } if c.config.Renegotiation != RenegotiateNever { - state.ekm = noExportedKeyingMaterial + state.ekm = noEKMBecauseRenegotiation + } else if c.vers != VersionTLS13 && !c.extMasterSecret { + state.ekm = func(label string, context []byte, length int) ([]byte, error) { + if tlsunsafeekm.Value() == "1" { + tlsunsafeekm.IncNonDefault() + return c.ekm(label, context, length) + } + return noEKMBecauseNoEMS(label, context, length) + } } else { state.ekm = c.ekm } diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index 4649f36dea..89004c2898 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -139,7 +139,9 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) { if len(hello.supportedVersions) == 1 { hello.cipherSuites = nil } - if hasAESGCMHardwareSupport { + if needFIPS() { + hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13FIPS...) + } else if hasAESGCMHardwareSupport { hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...) } else { hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...) @@ -526,6 +528,10 @@ func (hs *clientHandshakeState) pickCipherSuite() error { return errors.New("tls: server chose an unconfigured cipher suite") } + if hs.c.config.CipherSuites == nil && rsaKexCiphers[hs.suite.id] { + tlsrsakex.IncNonDefault() + } + hs.c.cipherSuite = hs.suite.id return nil } diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go index 2f59f6888c..a84cede1b0 100644 --- a/src/crypto/tls/handshake_client_tls13.go +++ b/src/crypto/tls/handshake_client_tls13.go @@ -41,10 +41,6 @@ type clientHandshakeStateTLS13 struct { func (hs *clientHandshakeStateTLS13) handshake() error { c := hs.c - if needFIPS() { - return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") - } - // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, // sections 4.1.2 and 4.1.3. if c.handshakes > 0 { diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 996b23b1f5..8129e9c616 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -168,6 +168,10 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { c.in.version = c.vers c.out.version = c.vers + if c.config.MinVersion == 0 && c.vers < VersionTLS12 { + tls10server.IncNonDefault() + } + return clientHello, nil } @@ -366,6 +370,10 @@ func (hs *serverHandshakeState) pickCipherSuite() error { } c.cipherSuite = hs.suite.id + if c.config.CipherSuites == nil && rsaKexCiphers[hs.suite.id] { + tlsrsakex.IncNonDefault() + } + for _, id := range hs.clientHello.cipherSuites { if id == TLS_FALLBACK_SCSV { // The client is doing a fallback connection. See RFC 7507. diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go index 15db760716..c0a86a4984 100644 --- a/src/crypto/tls/handshake_server_test.go +++ b/src/crypto/tls/handshake_server_test.go @@ -27,6 +27,7 @@ import ( ) func testClientHello(t *testing.T, serverConfig *Config, m handshakeMessage) { + t.Helper() testClientHelloFailure(t, serverConfig, m, "") } @@ -52,23 +53,32 @@ func testClientHelloFailure(t *testing.T, serverConfig *Config, m handshakeMessa ctx := context.Background() conn := Server(s, serverConfig) ch, err := conn.readClientHello(ctx) - hs := serverHandshakeState{ - c: conn, - ctx: ctx, - clientHello: ch, - } - if err == nil { + if err == nil && conn.vers == VersionTLS13 { + hs := serverHandshakeStateTLS13{ + c: conn, + ctx: ctx, + clientHello: ch, + } err = hs.processClientHello() - } - if err == nil { - err = hs.pickCipherSuite() + } else if err == nil { + hs := serverHandshakeState{ + c: conn, + ctx: ctx, + clientHello: ch, + } + err = hs.processClientHello() + if err == nil { + err = hs.pickCipherSuite() + } } s.Close() if len(expectedSubStr) == 0 { if err != nil && err != io.EOF { + t.Helper() t.Errorf("Got error: %s; expected to succeed", err) } } else if err == nil || !strings.Contains(err.Error(), expectedSubStr) { + t.Helper() t.Errorf("Got error: %v; expected to match substring '%s'", err, expectedSubStr) } } diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 07b1a3851e..b68ff9db4c 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -45,10 +45,6 @@ type serverHandshakeStateTLS13 struct { func (hs *serverHandshakeStateTLS13) handshake() error { c := hs.c - if needFIPS() { - return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") - } - // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. if err := hs.processClientHello(); err != nil { return err @@ -163,6 +159,9 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) { preferenceList = defaultCipherSuitesTLS13NoAES } + if needFIPS() { + preferenceList = defaultCipherSuitesTLS13FIPS + } for _, suiteID := range preferenceList { hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID) if hs.suite != nil { @@ -240,8 +239,15 @@ GroupSelection: c.clientProtocol = selectedProto if c.quic != nil { + // RFC 9001 Section 4.2: Clients MUST NOT offer TLS versions older than 1.3. + for _, v := range hs.clientHello.supportedVersions { + if v < VersionTLS13 { + c.sendAlert(alertProtocolVersion) + return errors.New("tls: client offered TLS version older than TLS 1.3") + } + } + // RFC 9001 Section 8.2. if hs.clientHello.quicTransportParameters == nil { - // RFC 9001 Section 8.2. c.sendAlert(alertMissingExtension) return errors.New("tls: client did not send a quic_transport_parameters extension") } diff --git a/src/crypto/tls/notboring.go b/src/crypto/tls/notboring.go index 7d85b39c59..edccb44d87 100644 --- a/src/crypto/tls/notboring.go +++ b/src/crypto/tls/notboring.go @@ -18,3 +18,5 @@ func fipsCurvePreferences(c *Config) []CurveID { panic("fipsCurvePreferences") } func fipsCipherSuites(c *Config) []uint16 { panic("fipsCipherSuites") } var fipsSupportedSignatureAlgorithms []SignatureScheme + +var defaultCipherSuitesTLS13FIPS []uint16 diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go index 20bac96e86..a7fa3370e6 100644 --- a/src/crypto/tls/prf.go +++ b/src/crypto/tls/prf.go @@ -252,13 +252,20 @@ func (h *finishedHash) discardHandshakeBuffer() { h.buffer = nil } -// noExportedKeyingMaterial is used as a value of +// noEKMBecauseRenegotiation is used as a value of // ConnectionState.ekm when renegotiation is enabled and thus // we wish to fail all key-material export requests. -func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) { +func noEKMBecauseRenegotiation(label string, context []byte, length int) ([]byte, error) { return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled") } +// noEKMBecauseNoEMS is used as a value of ConnectionState.ekm when Extended +// Master Secret is not negotiated and thus we wish to fail all key-material +// export requests. +func noEKMBecauseNoEMS(label string, context []byte, length int) ([]byte, error) { + return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated; override with GODEBUG=tlsunsafeekm=1") +} + // ekmFromMasterSecret generates exported keying material as defined in RFC 5705. func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) { return func(label string, context []byte, length int) ([]byte, error) { diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index 5b09e53524..42a0272f00 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -1490,24 +1490,21 @@ func TestCipherSuites(t *testing.T) { if len(cipherSuitesPreferenceOrderNoAES) != len(cipherSuitesPreferenceOrder) { t.Errorf("cipherSuitesPreferenceOrderNoAES is not the same size as cipherSuitesPreferenceOrder") } + if len(defaultCipherSuites) >= len(defaultCipherSuitesWithRSAKex) { + t.Errorf("defaultCipherSuitesWithRSAKex should be longer than defaultCipherSuites") + } - // Check that disabled suites are at the end of the preference lists, and - // that they are marked insecure. - for i, id := range disabledCipherSuites { - offset := len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites) - if cipherSuitesPreferenceOrder[offset+i] != id { - t.Errorf("disabledCipherSuites[%d]: not at the end of cipherSuitesPreferenceOrder", i) - } - if cipherSuitesPreferenceOrderNoAES[offset+i] != id { - t.Errorf("disabledCipherSuites[%d]: not at the end of cipherSuitesPreferenceOrderNoAES", i) - } - c := CipherSuiteByID(id) - if c == nil { - t.Errorf("%#04x: no CipherSuite entry", id) - continue - } - if !c.Insecure { - t.Errorf("%#04x: disabled by default but not marked insecure", id) + // Check that disabled suites are marked insecure. + for _, badSuites := range []map[uint16]bool{disabledCipherSuites, rsaKexCiphers} { + for id := range badSuites { + c := CipherSuiteByID(id) + if c == nil { + t.Errorf("%#04x: no CipherSuite entry", id) + continue + } + if !c.Insecure { + t.Errorf("%#04x: disabled by default but not marked insecure", id) + } } } diff --git a/src/crypto/x509/boring.go b/src/crypto/x509/boring.go index 095b58c315..e6237e96bb 100644 --- a/src/crypto/x509/boring.go +++ b/src/crypto/x509/boring.go @@ -22,7 +22,7 @@ func boringAllowCert(c *Certificate) bool { } // The key must be RSA 2048, RSA 3072, RSA 4096, - // or ECDSA P-256, P-384, P-521. + // or ECDSA P-256 or P-384. switch k := c.PublicKey.(type) { default: return false @@ -31,7 +31,7 @@ func boringAllowCert(c *Certificate) bool { return false } case *ecdsa.PublicKey: - if k.Curve != elliptic.P256() && k.Curve != elliptic.P384() && k.Curve != elliptic.P521() { + if k.Curve != elliptic.P256() && k.Curve != elliptic.P384() { return false } } diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go index 7bc74462de..861d2b3890 100644 --- a/src/crypto/x509/verify_test.go +++ b/src/crypto/x509/verify_test.go @@ -16,9 +16,11 @@ import ( "fmt" "internal/testenv" "math/big" + "os/exec" "reflect" "runtime" "sort" + "strconv" "strings" "testing" "time" @@ -1867,17 +1869,40 @@ func TestSystemRootsErrorUnwrap(t *testing.T) { } } +func macosMajorVersion(t *testing.T) (int, error) { + cmd := testenv.Command(t, "sw_vers", "-productVersion") + out, err := cmd.Output() + if err != nil { + if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { + return 0, fmt.Errorf("%v: %v\n%s", cmd, err, ee.Stderr) + } + return 0, fmt.Errorf("%v: %v", cmd, err) + } + before, _, ok := strings.Cut(string(out), ".") + major, err := strconv.Atoi(before) + if !ok || err != nil { + return 0, fmt.Errorf("%v: unexpected output: %q", cmd, out) + } + + return major, nil +} + func TestIssue51759(t *testing.T) { if runtime.GOOS != "darwin" { t.Skip("only affects darwin") } - builder := testenv.Builder() - if builder == "" { - t.Skip("only run this test on the builders, as we have no reasonable way to gate tests on macOS versions elsewhere") - } - if builder == "darwin-amd64-10_14" || builder == "darwin-amd64-10_15" { + + testenv.MustHaveExecPath(t, "sw_vers") + if vers, err := macosMajorVersion(t); err != nil { + if builder := testenv.Builder(); builder != "" { + t.Fatalf("unable to determine macOS version: %s", err) + } else { + t.Skip("unable to determine macOS version") + } + } else if vers < 11 { t.Skip("behavior only enforced in macOS 11 and after") } + // badCertData contains a cert that we parse as valid // but that macOS SecCertificateCreateWithData rejects. const badCertData = "0\x82\x01U0\x82\x01\a\xa0\x03\x02\x01\x02\x02\x01\x020\x05\x06\x03+ep0R1P0N\x06\x03U\x04\x03\x13Gderpkey8dc58100b2493614ee1692831a461f3f4dd3f9b3b088e244f887f81b4906ac260\x1e\x17\r220112235755Z\x17\r220313235755Z0R1P0N\x06\x03U\x04\x03\x13Gderpkey8dc58100b2493614ee1692831a461f3f4dd3f9b3b088e244f887f81b4906ac260*0\x05\x06\x03+ep\x03!\x00bA\xd8e\xadW\xcb\xefZ\x89\xb5\"\x1eR\x9d\xba\x0e:\x1042Q@\u007f\xbd\xfb{ks\x04\xd1£\x020\x000\x05\x06\x03+ep\x03A\x00[\xa7\x06y\x86(\x94\x97\x9eLwA\x00\x01x\xaa\xbc\xbd Ê]\n(΅!ف0\xf5\x9a%I\x19<\xffo\xf1\xeaaf@\xb1\xa7\xaf\xfd\xe9R\xc7\x0f\x8d&\xd5\xfc\x0f;Ϙ\x82\x84a\xbc\r" diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index fa2785aace..f33283b559 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -1101,6 +1101,8 @@ func isIA5String(s string) error { return nil } +var usePoliciesField = godebug.New("x509usepolicies") + func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKeyId []byte, subjectKeyId []byte) (ret []pkix.Extension, err error) { ret = make([]pkix.Extension, 10 /* maximum number of elements. */) n := 0 @@ -1186,7 +1188,8 @@ func buildCertExtensions(template *Certificate, subjectIsEmpty bool, authorityKe n++ } - if (len(template.PolicyIdentifiers) > 0 || len(template.Policies) > 0) && + usePolicies := usePoliciesField.Value() == "1" + if ((!usePolicies && len(template.PolicyIdentifiers) > 0) || (usePolicies && len(template.Policies) > 0)) && !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) { ret[n], err = marshalCertificatePolicies(template.Policies, template.PolicyIdentifiers) if err != nil { @@ -1378,30 +1381,25 @@ func marshalCertificatePolicies(policies []OID, policyIdentifiers []asn1.ObjectI b := cryptobyte.NewBuilder(make([]byte, 0, 128)) b.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { - // added is used to track OIDs which are duplicated in both Policies and PolicyIdentifiers - // so they can be skipped. Note that this explicitly doesn't check for duplicate OIDs in - // Policies or in PolicyIdentifiers themselves, as this would be considered breaking behavior. - added := map[string]bool{} - for _, v := range policies { - child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { - child.AddASN1(cryptobyte_asn1.OBJECT_IDENTIFIER, func(child *cryptobyte.Builder) { - oidStr := v.String() - added[oidStr] = true - if len(v.der) == 0 { - child.SetError(errors.New("invalid policy object identifier")) - return - } - child.AddBytes(v.der) + if usePoliciesField.Value() == "1" { + usePoliciesField.IncNonDefault() + for _, v := range policies { + child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { + child.AddASN1(cryptobyte_asn1.OBJECT_IDENTIFIER, func(child *cryptobyte.Builder) { + if len(v.der) == 0 { + child.SetError(errors.New("invalid policy object identifier")) + return + } + child.AddBytes(v.der) + }) + }) + } + } else { + for _, v := range policyIdentifiers { + child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { + child.AddASN1ObjectIdentifier(v) }) - }) - } - for _, v := range policyIdentifiers { - if added[v.String()] { - continue } - child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) { - child.AddASN1ObjectIdentifier(v) - }) } }) @@ -1546,8 +1544,8 @@ var emptyASN1Subject = []byte{0x30, 0} // - PermittedEmailAddresses // - PermittedIPRanges // - PermittedURIDomains -// - PolicyIdentifiers -// - Policies +// - PolicyIdentifiers (see note below) +// - Policies (see note below) // - SerialNumber // - SignatureAlgorithm // - Subject @@ -1572,8 +1570,12 @@ var emptyASN1Subject = []byte{0x30, 0} // If SubjectKeyId from template is empty and the template is a CA, SubjectKeyId // will be generated from the hash of the public key. // -// If both PolicyIdentifiers and Policies are populated, any OID which appears -// in both slices will only be added to the certificate policies extension once. +// The PolicyIdentifier and Policies fields are both used to marshal certificate +// policy OIDs. By default, only the PolicyIdentifier is marshaled, but if the +// GODEBUG setting "x509usepolicies" has the value "1", the Policies field will +// be marshalled instead of the PolicyIdentifier field. The Policies field can +// be used to marshal policy OIDs which have components that are larger than 31 +// bits. func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv any) ([]byte, error) { key, ok := priv.(crypto.Signer) if !ok { diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index f32c390900..ead0453f66 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -3928,25 +3928,14 @@ func TestCertificateOIDPolicies(t *testing.T) { NotBefore: time.Unix(1000, 0), NotAfter: time.Unix(100000, 0), PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, - Policies: []OID{ - mustNewOIDFromInts(t, []uint64{1, 2, 3}), - mustNewOIDFromInts(t, []uint64{1, 2, 3, 4, 5}), - mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxInt32}), - mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxUint32, math.MaxUint64}), - }, } var expectPolicyIdentifiers = []asn1.ObjectIdentifier{ []int{1, 2, 3}, - []int{1, 2, 3, 4, 5}, - []int{1, 2, 3, math.MaxInt32}, } var expectPolicies = []OID{ mustNewOIDFromInts(t, []uint64{1, 2, 3}), - mustNewOIDFromInts(t, []uint64{1, 2, 3, 4, 5}), - mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxInt32}), - mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxUint32, math.MaxUint64}), } certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey) @@ -3968,18 +3957,45 @@ func TestCertificateOIDPolicies(t *testing.T) { } } -func TestInvalidPolicyOID(t *testing.T) { +func TestCertificatePoliciesGODEBUG(t *testing.T) { template := Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "Cert"}, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour), + NotBefore: time.Unix(1000, 0), + NotAfter: time.Unix(100000, 0), PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, - Policies: []OID{OID{}}, + Policies: []OID{mustNewOIDFromInts(t, []uint64{1, 2, math.MaxUint32 + 1})}, } - _, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey) - expected := "invalid policy object identifier" - if err.Error() != expected { - t.Fatalf("CreateCertificate() unexpected error: %v, want: %v", err, expected) + + expectPolicies := []OID{mustNewOIDFromInts(t, []uint64{1, 2, 3})} + certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey) + if err != nil { + t.Fatalf("CreateCertificate() unexpected error: %v", err) + } + + cert, err := ParseCertificate(certDER) + if err != nil { + t.Fatalf("ParseCertificate() unexpected error: %v", err) + } + + if !slices.EqualFunc(cert.Policies, expectPolicies, OID.Equal) { + t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies) + } + + t.Setenv("GODEBUG", "x509usepolicies=1") + expectPolicies = []OID{mustNewOIDFromInts(t, []uint64{1, 2, math.MaxUint32 + 1})} + + certDER, err = CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey) + if err != nil { + t.Fatalf("CreateCertificate() unexpected error: %v", err) + } + + cert, err = ParseCertificate(certDER) + if err != nil { + t.Fatalf("ParseCertificate() unexpected error: %v", err) + } + + if !slices.EqualFunc(cert.Policies, expectPolicies, OID.Equal) { + t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies) } } diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go index d1a8981e5e..cca5d15e07 100644 --- a/src/database/sql/convert.go +++ b/src/database/sql/convert.go @@ -203,7 +203,6 @@ func driverArgsConnLocked(ci driver.Conn, ds *driverStmt, args []any) ([]driver. } return nvargs, nil - } // convertAssign is the same as convertAssignRows, but without the optional diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index 44b202192a..4f1197dc6e 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -1979,9 +1979,9 @@ type Conn struct { // Once done, all operations fail with ErrConnDone. done atomic.Bool - // releaseConn is a cache of c.closemuRUnlockCondReleaseConn + releaseConnOnce sync.Once + // releaseConnCache is a cache of c.closemuRUnlockCondReleaseConn // to save allocations in a call to grabConn. - releaseConnOnce sync.Once releaseConnCache releaseConn } diff --git a/src/debug/elf/elf.go b/src/debug/elf/elf.go index 534cfa9127..e78367750f 100644 --- a/src/debug/elf/elf.go +++ b/src/debug/elf/elf.go @@ -13,7 +13,7 @@ * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ * "System V ABI" (http://www.sco.com/developers/gabi/latest/ch4.eheader.html) * "ELF for the ARM® 64-bit Architecture (AArch64)" (ARM IHI 0056B) - * "RISC-V ELF psABI specification" (https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc) + * "RISC-V ELF psABI specification" (https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc) * llvm/BinaryFormat/ELF.h - ELF constants and structures * * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index fcbe76b195..7228447c21 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -1656,6 +1656,14 @@ func (f *File) DynValue(tag DynTag) ([]uint64, error) { return nil, err } + dynSize := 8 + if f.Class == ELFCLASS64 { + dynSize = 16 + } + if len(d)%dynSize != 0 { + return nil, errors.New("length of dynamic section is not a multiple of dynamic entry size") + } + // Parse the .dynamic section as a string of bytes. var vals []uint64 for len(d) > 0 { diff --git a/src/errors/wrap.go b/src/errors/wrap.go index e1cc466eea..88ee0a9281 100644 --- a/src/errors/wrap.go +++ b/src/errors/wrap.go @@ -27,8 +27,8 @@ func Unwrap(err error) error { // Is reports whether any error in err's tree matches target. // // The tree consists of err itself, followed by the errors obtained by repeatedly -// calling [Unwrap]. When err wraps multiple errors, Is examines err followed by a -// depth-first traversal of its children. +// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple +// errors, Is examines err followed by a depth-first traversal of its children. // // An error is considered to match a target if it is equal to that target or if // it implements a method Is(error) bool such that Is(target) returns true. @@ -81,8 +81,8 @@ func is(err, target error, targetComparable bool) bool { // target to that error value and returns true. Otherwise, it returns false. // // The tree consists of err itself, followed by the errors obtained by repeatedly -// calling [Unwrap]. When err wraps multiple errors, As examines err followed by a -// depth-first traversal of its children. +// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple +// errors, As examines err followed by a depth-first traversal of its children. // // An error matches target if the error's concrete value is assignable to the value // pointed to by target, or if the error has a method As(interface{}) bool such that diff --git a/src/go.mod b/src/go.mod index 003d6a96a9..c18ae7760f 100644 --- a/src/go.mod +++ b/src/go.mod @@ -3,11 +3,11 @@ module std go 1.22 require ( - golang.org/x/crypto v0.14.1-0.20231011220222-8779cbd1c995 - golang.org/x/net v0.17.1-0.20231025180729-4c7a5b64f145 + golang.org/x/crypto v0.16.1-0.20231129163542-152cdb1503eb + golang.org/x/net v0.19.0 ) require ( - golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3 // indirect - golang.org/x/text v0.13.1-0.20231011215848-6c97a165dd66 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index 22519d7feb..7c3519882a 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.14.1-0.20231011220222-8779cbd1c995 h1:fwu25/DhiXr/++jtzz2gHfC/FL/FimAGVxczaK4pjR8= -golang.org/x/crypto v0.14.1-0.20231011220222-8779cbd1c995/go.mod h1:aSyBXtGhRzSMdne9jbl3+PPMVS0IgOWF6Llc+HB5uUU= -golang.org/x/net v0.17.1-0.20231025180729-4c7a5b64f145 h1:b50rfQxeg+jm4ud87SAQt4escws56zdE+/rT1oKU4oU= -golang.org/x/net v0.17.1-0.20231025180729-4c7a5b64f145/go.mod h1:ctp6Rq0w6YZwLQMXSCn4tot1uuoj9E9vKYJV26guPn4= -golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3 h1:G9se7UpoI67yWrFY0IIFGf6H3nwLLUZFDBCyOJwWeSc= -golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.13.1-0.20231011215848-6c97a165dd66 h1:9Dyvhe9/DDm3upTpNNwowKgum0eUka4TBRU6uHiINKo= -golang.org/x/text v0.13.1-0.20231011215848-6c97a165dd66/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/crypto v0.16.1-0.20231129163542-152cdb1503eb h1:1ceSY7sk6sJuiDREHpfyrqDnDljsLfEP2GuTClhBBfI= +golang.org/x/crypto v0.16.1-0.20231129163542-152cdb1503eb/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 3d54fc7aac..7ce8d346b4 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -45,20 +45,26 @@ var depsRules = ` internal/cpu, internal/goarch, internal/godebugs, internal/goexperiment, internal/goos, internal/goversion, internal/nettrace, internal/platform, + internal/trace/traceviewer/format, log/internal, unicode/utf8, unicode/utf16, unicode, unsafe; # These packages depend only on internal/goarch and unsafe. internal/goarch, unsafe - < internal/abi; + < internal/abi, internal/chacha8rand; unsafe < maps; # RUNTIME is the core runtime group of packages, all of them very light-weight. - internal/abi, internal/cpu, internal/goarch, - internal/coverage/rtcov, internal/godebugs, internal/goexperiment, - internal/goos, unsafe + internal/abi, + internal/chacha8rand, + internal/coverage/rtcov, + internal/cpu, + internal/goarch, + internal/godebugs, + internal/goexperiment, + internal/goos < internal/bytealg < internal/itoa < internal/unsafeheader @@ -77,6 +83,9 @@ var depsRules = ` < internal/oserror, math/bits < RUNTIME; + internal/race + < iter; + # slices depends on unsafe for overlapping check, cmp for comparison # semantics, and math/bits for # calculating bitlength of numbers. unsafe, cmp, math/bits @@ -633,6 +642,17 @@ var depsRules = ` FMT, container/heap, math/rand, internal/trace/v2 < internal/trace; + # cmd/trace dependencies. + FMT, + embed, + encoding/json, + html/template, + internal/profile, + internal/trace, + internal/trace/traceviewer/format, + net/http + < internal/trace/traceviewer; + # Coverage. FMT, crypto/md5, encoding/binary, regexp, sort, text/tabwriter, unsafe, internal/coverage, internal/coverage/uleb128 diff --git a/src/go/internal/gcimporter/ureader.go b/src/go/internal/gcimporter/ureader.go index ac85a415b1..5397a2796f 100644 --- a/src/go/internal/gcimporter/ureader.go +++ b/src/go/internal/gcimporter/ureader.go @@ -118,7 +118,7 @@ type readerDict struct { // 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 + // derived 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 diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index a28960523e..17808b366f 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -881,6 +881,13 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok // Type parameters are the only parameter list closed by ']'. tparams := closing == token.RBRACK + pos0 := p.pos + if name0 != nil { + pos0 = name0.Pos() + } else if typ0 != nil { + pos0 = typ0.Pos() + } + // Note: The code below matches the corresponding code in the syntax // parser closely. Changes must be reflected in either parser. // For the code to match, we use the local []field list that @@ -923,8 +930,8 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok } // distribute parameter types (len(list) > 0) - if named == 0 && !tparams { - // all unnamed and we're not in a type parameter list => found names are type names + if named == 0 { + // all unnamed => found names are type names for i := 0; i < len(list); i++ { par := &list[i] if typ := par.name; typ != nil { @@ -932,6 +939,23 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok par.name = nil } } + if tparams { + // This is the same error handling as below, adjusted for type parameters only. + // See comment below for details. (go.dev/issue/64534) + var errPos token.Pos + var msg string + if named == typed /* same as typed == 0 */ { + errPos = p.pos // position error at closing ] + msg = "missing type constraint" + } else { + errPos = pos0 // position at opening [ or first name + msg = "missing type parameter name" + if len(list) == 1 { + msg += " or invalid array length" + } + } + p.error(errPos, msg) + } } else if named != len(list) { // some named or we're in a type parameter list => all must be named var errPos token.Pos // left-most error position (or invalid) diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go index e72c03a3d4..43b3416b27 100644 --- a/src/go/parser/parser_test.go +++ b/src/go/parser/parser_test.go @@ -573,7 +573,7 @@ type x int // comment var parseDepthTests = []struct { name string format string - // multiplier is used when a single statement may result in more than one + // parseMultiplier is used when a single statement may result in more than one // change in the depth level, for instance "1+(..." produces a BinaryExpr // followed by a UnaryExpr, which increments the depth twice. The test // case comment explains which nodes are triggering the multiple depth diff --git a/src/go/parser/testdata/issue64534.src b/src/go/parser/testdata/issue64534.src new file mode 100644 index 0000000000..006cc934a1 --- /dev/null +++ b/src/go/parser/testdata/issue64534.src @@ -0,0 +1,10 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test case for go.dev/issue/64534. +// Parser should not panic during object resolution. + +package main + +func _[A /* ERROR "missing type parameter name" */ $(B](){}} diff --git a/src/go/types/api.go b/src/go/types/api.go index f729e33dec..796fe055e6 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -263,6 +263,15 @@ type Info struct { // scope, the function scopes are embedded in the file scope of the file // containing the function declaration. // + // The Scope of a function contains the declarations of any + // type parameters, parameters, and named results, plus any + // local declarations in the body block. + // It is coextensive with the complete extent of the + // function's syntax ([*ast.FuncDecl] or [*ast.FuncLit]). + // The Scopes mapping does not contain an entry for the + // function body ([*ast.BlockStmt]); the function's scope is + // associated with the [*ast.FuncType]. + // // The following node types may appear in Scopes: // // *ast.File diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index 594b92bb23..0dc5f35dff 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -27,7 +27,7 @@ import ( var nopos token.Pos func mustParse(fset *token.FileSet, src string) *ast.File { - f, err := parser.ParseFile(fset, pkgName(src), src, 0) + f, err := parser.ParseFile(fset, pkgName(src), src, parser.ParseComments) if err != nil { panic(err) // so we don't need to pass *testing.T } @@ -1896,12 +1896,12 @@ const Pi = 3.1415 type T struct{} var Y, _ = lib.X, X -func F(){ +func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ { const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/ type /*t=undef*/ t /*t=typename:14*/ *t print(Y) /*Y=var:10*/ x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y - var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F + var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F var a []int for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x } @@ -1920,6 +1920,10 @@ func F(){ println(int) default /*int=var:31*/ : } + + _ = param1 + _ = res1 + return } /*main=undef*/ ` @@ -1981,8 +1985,29 @@ func F(){ _, gotObj := inner.LookupParent(id.Name, id.Pos()) if gotObj != wantObj { - t.Errorf("%s: got %v, want %v", - fset.Position(id.Pos()), gotObj, wantObj) + // Print the scope tree of mainScope in case of error. + var printScopeTree func(indent string, s *Scope) + printScopeTree = func(indent string, s *Scope) { + t.Logf("%sscope %s %v-%v = %v", + indent, + ScopeComment(s), + s.Pos(), + s.End(), + s.Names()) + for i := range s.NumChildren() { + printScopeTree(indent+" ", s.Child(i)) + } + } + printScopeTree("", mainScope) + + t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]", + fset.Position(id.Pos()), + ScopeComment(inner), + id.Name, + id.Pos(), + gotObj, + wantObj, + ObjectScopePos(wantObj)) continue } } diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index f9f0e8f20a..3ea45699b1 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -243,8 +243,15 @@ func (check *Checker) assignVar(lhs, rhs ast.Expr, x *operand) { } if x == nil { + var target *target + // avoid calling ExprString if not needed + if T != nil { + if _, ok := under(T).(*Signature); ok { + target = newTarget(T, ExprString(lhs)) + } + } x = new(operand) - check.expr(T, x, rhs) + check.expr(target, x, rhs) } context := "assignment" @@ -368,7 +375,11 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []ast.Expr, returnStmt ast.S if l == r && !isCall { var x operand for i, lhs := range lhs { - check.expr(lhs.typ, &x, orig_rhs[i]) + desc := lhs.name + if returnStmt != nil && desc == "" { + desc = "result variable" + } + check.expr(newTarget(lhs.typ, desc), &x, orig_rhs[i]) check.initVar(lhs, &x, context) } return diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 72b4c5370d..901573661b 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -798,7 +798,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // unsafe.Slice(ptr *T, len IntegerType) []T check.verifyVersionf(call.Fun, go1_17, "unsafe.Slice") - ptr, _ := under(x.typ).(*Pointer) // TODO(gri) should this be coreType rather than under? + ptr, _ := coreType(x.typ).(*Pointer) if ptr == nil { check.errorf(x, InvalidUnsafeSlice, invalidArg+"%s is not a pointer", x) return @@ -819,7 +819,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // unsafe.SliceData(slice []T) *T check.verifyVersionf(call.Fun, go1_20, "unsafe.SliceData") - slice, _ := under(x.typ).(*Slice) // TODO(gri) should this be coreType rather than under? + slice, _ := coreType(x.typ).(*Slice) if slice == nil { check.errorf(x, InvalidUnsafeSliceData, invalidArg+"%s is not a slice", x) return @@ -953,7 +953,7 @@ func hasVarSize(t Type, seen map[*Named]bool) (varSized bool) { } // applyTypeFunc applies f to x. If x is a type parameter, -// the result is a type parameter constrained by an new +// the result is a type parameter constrained by a new // interface bound. The type bounds for that interface // are computed by applying f to each of the type bounds // of x. If any of these applications of f return nil, diff --git a/src/go/types/call.go b/src/go/types/call.go index 12f547ea38..c7de3bdb9f 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -18,8 +18,8 @@ import ( // funcInst type-checks a function instantiation. // The incoming x must be a generic function. // If ix != nil, it provides some or all of the type arguments (ix.Indices). -// If target type tsig != nil, the signature may be used to infer missing type -// arguments of x, if any. At least one of tsig or inst must be provided. +// If target != nil, it may be used to infer missing type arguments of x, if any. +// At least one of T or ix must be provided. // // There are two modes of operation: // @@ -34,8 +34,8 @@ import ( // // If an error (other than a version error) occurs in any case, it is reported // and x.mode is set to invalid. -func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *typeparams.IndexExpr, infer bool) ([]Type, []ast.Expr) { - assert(tsig != nil || ix != nil) +func (check *Checker) funcInst(T *target, pos token.Pos, x *operand, ix *typeparams.IndexExpr, infer bool) ([]Type, []ast.Expr) { + assert(T != nil || ix != nil) var instErrPos positioner if ix != nil { @@ -89,7 +89,8 @@ func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *t // var args []*operand var params []*Var - if tsig != nil && sig.tparams != nil { + var reverse bool + if T != nil && sig.tparams != nil { if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) { if ix != nil { check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment") @@ -102,16 +103,17 @@ func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *t // The type of the argument operand is tsig, which is the type of the LHS in an assignment // or the result type in a return statement. Create a pseudo-expression for that operand // that makes sense when reported in error messages from infer, below. - expr := ast.NewIdent("variable in assignment") + expr := ast.NewIdent(T.desc) expr.NamePos = x.Pos() // correct position - args = []*operand{{mode: value, expr: expr, typ: tsig}} + args = []*operand{{mode: value, expr: expr, typ: T.sig}} + reverse = true } // Rename type parameters to avoid problems with recursive instantiations. // Note that NewTuple(params...) below is (*Tuple)(nil) if len(params) == 0, as desired. tparams, params2 := check.renameTParams(pos, sig.TypeParams().list(), NewTuple(params...)) - targs = check.infer(atPos(pos), tparams, targs, params2.(*Tuple), args) + targs = check.infer(atPos(pos), tparams, targs, params2.(*Tuple), args, reverse) if targs == nil { // error was already reported x.mode = invalid @@ -610,7 +612,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type // infer missing type arguments of callee and function arguments if len(tparams) > 0 { - targs = check.infer(call, tparams, targs, sigParams, args) + targs = check.infer(call, tparams, targs, sigParams, args, false) if targs == nil { // TODO(gri) If infer inferred the first targs[:n], consider instantiating // the call signature for better error messages/gopls behavior. diff --git a/src/go/types/check.go b/src/go/types/check.go index 4a5f0731df..85fd0744d2 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -14,6 +14,7 @@ import ( "go/token" "internal/godebug" . "internal/types/errors" + "strings" ) // nopos indicates an unknown position @@ -618,7 +619,12 @@ func instantiatedIdent(expr ast.Expr) *ast.Ident { case *ast.SelectorExpr: return x.Sel } - panic("instantiated ident not found") + + // extra debugging of #63933 + var buf strings.Builder + buf.WriteString("instantiated ident not found; please report: ") + ast.Fprint(&buf, token.NewFileSet(), expr, ast.NotNilFilter) + panic(buf.String()) } func (check *Checker) recordDef(id *ast.Ident, obj Object) { diff --git a/src/go/types/decl.go b/src/go/types/decl.go index c8716bd74f..9f8c44ab50 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -518,7 +518,7 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) { if lhs == nil || len(lhs) == 1 { assert(lhs == nil || lhs[0] == obj) var x operand - check.expr(obj.typ, &x, init) + check.expr(newTarget(obj.typ, obj.name), &x, init) check.initVar(obj, &x, "variable declaration") return } @@ -638,8 +638,9 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList // Declare type parameters up-front, with empty interface as type bound. // The scope of type parameters starts at the beginning of the type parameter // list (so we can have mutually recursive parameterized interfaces). + scopePos := list.Pos() for _, f := range list.List { - tparams = check.declareTypeParams(tparams, f.Names) + tparams = check.declareTypeParams(tparams, f.Names, scopePos) } // Set the type parameters before collecting the type constraints because @@ -708,7 +709,7 @@ func (check *Checker) bound(x ast.Expr) Type { return check.typ(x) } -func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam { +func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident, scopePos token.Pos) []*TypeParam { // Use Typ[Invalid] for the type constraint to ensure that a type // is present even if the actual constraint has not been assigned // yet. @@ -717,8 +718,8 @@ func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident // are not properly set yet. for _, name := range names { tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil) - tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect - check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position + tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect + check.declare(check.scope, name, tname, scopePos) tparams = append(tparams, tpar) } @@ -835,6 +836,11 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { check.funcType(sig, fdecl.Recv, fdecl.Type) obj.color_ = saved + // Set the scope's extent to the complete "func (...) { ... }" + // so that Scope.Innermost works correctly. + sig.scope.pos = fdecl.Pos() + sig.scope.end = fdecl.End() + if fdecl.Type.TypeParams.NumFields() > 0 && fdecl.Body == nil { check.softErrorf(fdecl.Name, BadDecl, "generic function is missing function body") } diff --git a/src/go/types/errorcalls_test.go b/src/go/types/errorcalls_test.go index d76c06da30..0238909282 100644 --- a/src/go/types/errorcalls_test.go +++ b/src/go/types/errorcalls_test.go @@ -1,6 +1,6 @@ // 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 ast. +// license that can be found in the LICENSE file. package types_test diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go index 4e995afd7e..dd9bd7f426 100644 --- a/src/go/types/eval_test.go +++ b/src/go/types/eval_test.go @@ -139,7 +139,7 @@ func TestEvalPos(t *testing.T) { /* c => , struct{c int} */ _ = c } - _ = func(a, b, c int) /* c => , string */ { + _ = func(a, b, c int /* c => , string */) /* c => , int */ { /* c => , int */ } _ = c diff --git a/src/go/types/expr.go b/src/go/types/expr.go index b018939730..8651ddad93 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -941,18 +941,32 @@ const ( statement ) -// TODO(gri) In rawExpr below, consider using T instead of hint and -// some sort of "operation mode" instead of allowGeneric. -// May be clearer and less error-prone. +// target represent the (signature) type and description of the LHS +// variable of an assignment, or of a function result variable. +type target struct { + sig *Signature + desc string +} + +// newTarget creates a new target for the given type and description. +// The result is nil if typ is not a signature. +func newTarget(typ Type, desc string) *target { + if typ != nil { + if sig, _ := under(typ).(*Signature); sig != nil { + return &target{sig, desc} + } + } + return nil +} // rawExpr typechecks expression e and initializes x with the expression // value or type. If an error occurred, x.mode is set to invalid. -// If a non-nil target type T is given and e is a generic function -// or function call, T is used to infer the type arguments for e. +// If a non-nil target T is given and e is a generic function, +// T is used to infer the type arguments for e. // 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(T Type, x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind { +func (check *Checker) rawExpr(T *target, x *operand, e ast.Expr, hint Type, allowGeneric bool) exprKind { if check.conf._Trace { check.trace(e.Pos(), "-- expr %s", e) check.indent++ @@ -974,9 +988,9 @@ func (check *Checker) rawExpr(T Type, x *operand, e ast.Expr, hint Type, allowGe } // If x is a generic type, or a generic function whose type arguments cannot be inferred -// from a non-nil target type T, nonGeneric reports an error and invalidates x.mode and x.typ. +// from a non-nil target T, nonGeneric reports an error and invalidates x.mode and x.typ. // Otherwise it leaves x alone. -func (check *Checker) nonGeneric(T Type, x *operand) { +func (check *Checker) nonGeneric(T *target, x *operand) { if x.mode == invalid || x.mode == novalue { return } @@ -989,10 +1003,8 @@ func (check *Checker) nonGeneric(T Type, x *operand) { case *Signature: if t.tparams != nil { if enableReverseTypeInference && T != nil { - if tsig, _ := under(T).(*Signature); tsig != nil { - check.funcInst(tsig, x.Pos(), x, nil, true) - return - } + check.funcInst(T, x.Pos(), x, nil, true) + return } what = "function" } @@ -1007,7 +1019,7 @@ func (check *Checker) nonGeneric(T Type, x *operand) { // exprInternal contains the core of type checking of expressions. // Must only be called by rawExpr. // (See rawExpr for an explanation of the parameters.) -func (check *Checker) exprInternal(T Type, x *operand, e ast.Expr, hint Type) exprKind { +func (check *Checker) exprInternal(T *target, x *operand, e ast.Expr, hint Type) exprKind { // make sure x has a valid state in case of bailout // (was go.dev/issue/5770) x.mode = invalid @@ -1059,6 +1071,10 @@ func (check *Checker) exprInternal(T Type, x *operand, e ast.Expr, hint Type) ex case *ast.FuncLit: if sig, ok := check.typ(e.Type).(*Signature); ok { + // Set the Scope's extent to the complete "func (...) {...}" + // so that Scope.Innermost works correctly. + sig.scope.pos = e.Pos() + sig.scope.end = e.End() if !check.conf.IgnoreFuncBodies && e.Body != nil { // Anonymous functions are considered part of the // init expression/func declaration which contains @@ -1311,11 +1327,10 @@ func (check *Checker) exprInternal(T Type, x *operand, e ast.Expr, hint Type) ex case *ast.IndexExpr, *ast.IndexListExpr: ix := typeparams.UnpackIndexExpr(e) if check.indexExpr(x, ix) { - var tsig *Signature - if enableReverseTypeInference && T != nil { - tsig, _ = under(T).(*Signature) + if !enableReverseTypeInference { + T = nil } - check.funcInst(tsig, e.Pos(), x, ix, true) + check.funcInst(T, e.Pos(), x, ix, true) } if x.mode == invalid { goto Error @@ -1490,11 +1505,11 @@ func (check *Checker) typeAssertion(e ast.Expr, x *operand, T Type, typeSwitch b } // expr typechecks expression e and initializes x with the expression value. -// If a non-nil target type T is given and e is a generic function -// or function call, T is used to infer the type arguments for e. +// If a non-nil target T is given and e is a generic function or +// a function call, T is used to infer the type arguments for e. // The result must be a single value. // If an error occurred, x.mode is set to invalid. -func (check *Checker) expr(T Type, x *operand, e ast.Expr) { +func (check *Checker) expr(T *target, x *operand, e ast.Expr) { check.rawExpr(T, x, e, nil, false) check.exclude(x, 1< want: + qual = "too many" + default: + return true } - return true + + msg := check.sprintf("%s type arguments for type %s: have %d, want %d", qual, name, got, want) + if check != nil { + check.error(atPos(pos), WrongTypeArgCount, msg) + return false + } + + panic(fmt.Sprintf("%v: %s", pos, msg)) } func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type, ctxt *Context) (int, error) { diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index c38459e488..b4c8218bc4 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -1008,8 +1008,81 @@ type S struct{ A } } got := S.String() - const want = "type p.S struct{p.A /* = []int */}" + const want = "type p.S struct{p.A}" if got != want { t.Fatalf("got %q; want %q", got, want) } } + +func TestIssue59831(t *testing.T) { + // Package a exports a type S with an unexported method m; + // the tests check the error messages when m is not found. + const asrc = `package a; type S struct{}; func (S) m() {}` + apkg := mustTypecheck(asrc, nil, nil) + + // Package b exports a type S with an exported method m; + // the tests check the error messages when M is not found. + const bsrc = `package b; type S struct{}; func (S) M() {}` + bpkg := mustTypecheck(bsrc, nil, nil) + + tests := []struct { + imported *Package + src, err string + }{ + // tests importing a (or nothing) + {apkg, `package a1; import "a"; var _ interface { M() } = a.S{}`, + "a.S does not implement interface{M()} (missing method M) have m() want M()"}, + + {apkg, `package a2; import "a"; var _ interface { m() } = a.S{}`, + "a.S does not implement interface{m()} (unexported method m)"}, // test for issue + + {nil, `package a3; type S struct{}; func (S) m(); var _ interface { M() } = S{}`, + "S does not implement interface{M()} (missing method M) have m() want M()"}, + + {nil, `package a4; type S struct{}; func (S) m(); var _ interface { m() } = S{}`, + ""}, // no error expected + + {nil, `package a5; type S struct{}; func (S) m(); var _ interface { n() } = S{}`, + "S does not implement interface{n()} (missing method n)"}, + + // tests importing b (or nothing) + {bpkg, `package b1; import "b"; var _ interface { m() } = b.S{}`, + "b.S does not implement interface{m()} (missing method m) have M() want m()"}, + + {bpkg, `package b2; import "b"; var _ interface { M() } = b.S{}`, + ""}, // no error expected + + {nil, `package b3; type S struct{}; func (S) M(); var _ interface { M() } = S{}`, + ""}, // no error expected + + {nil, `package b4; type S struct{}; func (S) M(); var _ interface { m() } = S{}`, + "S does not implement interface{m()} (missing method m) have M() want m()"}, + + {nil, `package b5; type S struct{}; func (S) M(); var _ interface { n() } = S{}`, + "S does not implement interface{n()} (missing method n)"}, + } + + for _, test := range tests { + // typecheck test source + conf := Config{Importer: importHelper{pkg: test.imported}} + pkg, err := typecheck(test.src, &conf, nil) + if err == nil { + if test.err != "" { + t.Errorf("package %s: got no error, want %q", pkg.Name(), test.err) + } + continue + } + if test.err == "" { + t.Errorf("package %s: got %q, want not error", pkg.Name(), err.Error()) + } + + // flatten reported error message + errmsg := strings.ReplaceAll(err.Error(), "\n", " ") + errmsg = strings.ReplaceAll(errmsg, "\t", "") + + // verify error message + if !strings.Contains(errmsg, test.err) { + t.Errorf("package %s: got %q, want %q", pkg.Name(), errmsg, test.err) + } + } +} diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index 2857ba358c..7723c43565 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -98,7 +98,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o // and missingMethod (the latter doesn't care about struct fields). // // If foldCase is true, method names are considered equal if they are equal -// with case folding. +// with case folding, irrespective of which package they are in. // // The resulting object may not be fully type-checked. func lookupFieldOrMethodImpl(T Type, addressable bool, pkg *Package, name string, foldCase bool) (obj Object, index []int, indirect bool) { @@ -345,6 +345,7 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y ok = iota notFound wrongName + unexported wrongSig ambigSel ptrRecv @@ -390,6 +391,11 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y f, _ = obj.(*Func) if f != nil { state = wrongName + if f.name == m.name { + // If the names are equal, f must be unexported + // (otherwise the package wouldn't matter). + state = unexported + } } } break @@ -438,8 +444,9 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y } case wrongName: fs, ms := check.funcString(f, false), check.funcString(m, false) - *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s", - m.Name(), fs, ms) + *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s", m.Name(), fs, ms) + case unexported: + *cause = check.sprintf("(unexported method %s)", m.Name()) case wrongSig: fs, ms := check.funcString(f, false), check.funcString(m, false) if fs == ms { @@ -447,8 +454,18 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y // Add package information to disambiguate (go.dev/issue/54258). fs, ms = check.funcString(f, true), check.funcString(m, true) } - *cause = check.sprintf("(wrong type for method %s)\n\t\thave %s\n\t\twant %s", - m.Name(), fs, ms) + if fs == ms { + // We still have "want Foo, have Foo". + // This is most likely due to different type parameters with + // the same name appearing in the instantiated signatures + // (go.dev/issue/61685). + // Rather than reporting this misleading error cause, for now + // just point out that the method signature is incorrect. + // TODO(gri) should find a good way to report the root cause + *cause = check.sprintf("(wrong type for method %s)", m.Name()) + break + } + *cause = check.sprintf("(wrong type for method %s)\n\t\thave %s\n\t\twant %s", m.Name(), fs, ms) case ambigSel: *cause = check.sprintf("(ambiguous selector %s.%s)", V, m.Name()) case ptrRecv: @@ -574,11 +591,12 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int { } // lookupMethod returns the index of and method with matching package and name, or (-1, nil). -// If foldCase is true, method names are considered equal if they are equal with case folding. +// If foldCase is true, method names are considered equal if they are equal with case folding +// and their packages are ignored (e.g., pkg1.m, pkg1.M, pkg2.m, and pkg2.M are all equal). func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) { if name != "_" { for i, m := range methods { - if (m.name == name || foldCase && strings.EqualFold(m.name, name)) && m.sameId(pkg, m.name) { + if m.sameId(pkg, name) || foldCase && strings.EqualFold(m.name, name) { return i, m } } diff --git a/src/go/types/signature.go b/src/go/types/signature.go index ed9fcfe58e..cad42cb942 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -7,6 +7,7 @@ package types import ( "fmt" "go/ast" + "go/token" . "internal/types/errors" ) @@ -115,7 +116,10 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast // - the receiver specification acts as local declaration for its type parameters, which may be blank _, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true) if len(rparams) > 0 { - tparams := check.declareTypeParams(nil, rparams) + // The scope of the type parameter T in "func (r T[T]) f()" + // starts after f, not at "r"; see #52038. + scopePos := ftyp.Params.Pos() + tparams := check.declareTypeParams(nil, rparams, scopePos) sig.rparams = bindTParams(tparams) // Blank identifiers don't get declared, so naive type-checking of the // receiver type expression would fail in Checker.collectParams below, @@ -176,13 +180,18 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast } } - // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their - // declarations and then squash that scope into the parent scope (and report any redeclarations at - // that time). + // Use a temporary scope for all parameter declarations and then + // squash that scope into the parent scope (and report any + // redeclarations at that time). + // + // TODO(adonovan): now that each declaration has the correct + // scopePos, there should be no need for scope squashing. + // Audit to ensure all lookups honor scopePos and simplify. scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)") - recvList, _ := check.collectParams(scope, recvPar, false) - params, variadic := check.collectParams(scope, ftyp.Params, true) - results, _ := check.collectParams(scope, ftyp.Results, false) + scopePos := ftyp.End() // all parameters' scopes start after the signature + recvList, _ := check.collectParams(scope, recvPar, false, scopePos) + params, variadic := check.collectParams(scope, ftyp.Params, true, scopePos) + results, _ := check.collectParams(scope, ftyp.Results, false, scopePos) scope.squash(func(obj, alt Object) { check.errorf(obj, DuplicateDecl, "%s redeclared in this block", obj.Name()) check.reportAltDecl(alt) @@ -262,7 +271,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast // collectParams declares the parameters of list in scope and returns the corresponding // variable list. -func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) { +func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool, scopePos token.Pos) (params []*Var, variadic bool) { if list == nil { return } @@ -290,7 +299,7 @@ func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicO // ok to continue } par := NewParam(name.Pos(), check.pkg, name.Name, typ) - check.declare(scope, name, par, scope.pos) + check.declare(scope, name, par, scopePos) params = append(params, par) } named = true diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go index f90f9388c2..a89cd858db 100644 --- a/src/go/types/stdlib_test.go +++ b/src/go/types/stdlib_test.go @@ -312,6 +312,7 @@ func TestStdFixed(t *testing.T) { testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"), "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore + "bug398.go", // go/types doesn't check for anonymous interface cycles (go.dev/issue/56103) "issue6889.go", // gc-specific test "issue11362.go", // canonical import path check "issue16369.go", // go/types handles this correctly - not an issue diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 7a3bcf029b..35c485827d 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -24,10 +24,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body check.trace(body.Pos(), "-- %s: %s", name, sig) } - // set function scope extent - sig.scope.pos = body.Pos() - sig.scope.end = body.End() - // save/restore current environment and set up function environment // (and use 0 indentation at function start) defer func(env environment, indent int) { @@ -856,7 +852,9 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) { var key, val Type if x.mode != invalid { // Ranging over a type parameter is permitted if it has a core type. - k, v, cause, isFunc, ok := rangeKeyVal(x.typ) + k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool { + return check.allowVersion(check.pkg, x.expr, v) + }) switch { case !ok && cause != "": check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s: %s", &x, cause) @@ -959,10 +957,11 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) { } // rangeKeyVal returns the key and value type produced by a range clause -// over an expression of type typ. If the range clause is not permitted, -// rangeKeyVal returns ok = false. When ok = false, rangeKeyVal may also -// return a reason in cause. -func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) { +// over an expression of type typ. +// If allowVersion != nil, it is used to check the required language version. +// If the range clause is not permitted, rangeKeyVal returns ok = false. +// When ok = false, rangeKeyVal may also return a reason in cause. +func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, cause string, isFunc, ok bool) { bad := func(cause string) (Type, Type, string, bool, bool) { return Typ[Invalid], Typ[Invalid], cause, false, false } @@ -980,6 +979,9 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) { return Typ[Int], universeRune, "", false, true // use 'rune' name } if isInteger(typ) { + if allowVersion != nil && !allowVersion(go1_22) { + return bad("requires go1.22 or later") + } return orig, nil, "", false, true } case *Array: @@ -994,6 +996,7 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) { } return typ.elem, nil, "", false, true case *Signature: + // TODO(gri) when this becomes enabled permanently, add version check if !buildcfg.Experiment.RangeFunc { break } diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index d5623d3d86..23bddb2673 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -334,8 +334,6 @@ func (w *typeWriter) typ(typ Type) { if w.ctxt != nil { // TODO(gri) do we need to print the alias type name, too? w.typ(Unalias(t.obj.typ)) - } else { - w.string(fmt.Sprintf(" /* = %s */", Unalias(t.obj.typ))) } default: diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 2835958d98..c887b5115a 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -454,7 +454,7 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *TypeName) // errors. check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst) - if check.validateTArgLen(ix.Pos(), inst.TypeParams().Len(), inst.TypeArgs().Len()) { + if check.validateTArgLen(ix.Pos(), inst.obj.name, inst.TypeParams().Len(), inst.TypeArgs().Len()) { if i, err := check.verify(ix.Pos(), inst.TypeParams().list(), inst.TypeArgs().list(), check.context()); err != nil { // best position for error reporting pos := ix.Pos() diff --git a/src/go/types/util_test.go b/src/go/types/util_test.go index 205237211f..70d376f0bb 100644 --- a/src/go/types/util_test.go +++ b/src/go/types/util_test.go @@ -9,6 +9,11 @@ package types -import "go/token" +import ( + "go/token" +) func CmpPos(p, q token.Pos) int { return cmpPos(p, q) } + +func ScopeComment(s *Scope) string { return s.comment } +func ObjectScopePos(obj Object) token.Pos { return obj.scopePos() } diff --git a/src/go/types/version.go b/src/go/types/version.go index cfbab0f2a8..f2466edc1f 100644 --- a/src/go/types/version.go +++ b/src/go/types/version.go @@ -44,6 +44,7 @@ var ( go1_18 = asGoVersion("go1.18") go1_20 = asGoVersion("go1.20") go1_21 = asGoVersion("go1.21") + go1_22 = asGoVersion("go1.22") // current (deployed) Go version go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version)) diff --git a/src/go/version/version.go b/src/go/version/version.go index 20c9cbc477..466c8091ea 100644 --- a/src/go/version/version.go +++ b/src/go/version/version.go @@ -7,7 +7,10 @@ // [Go versions]: https://go.dev/doc/toolchain#version package version // import "go/version" -import "internal/gover" +import ( + "internal/gover" + "strings" +) // stripGo converts from a "go1.21" version to a "1.21" version. // If v does not start with "go", stripGo returns the empty string (a known invalid version). @@ -33,7 +36,11 @@ func Lang(x string) string { if v == "" { return "" } - return x[:2+len(v)] // "go"+v without allocation + if strings.HasPrefix(x[2:], v) { + return x[:2+len(v)] // "go"+v without allocation + } else { + return "go" + v + } } // Compare returns -1, 0, or +1 depending on whether diff --git a/src/go/version/version_test.go b/src/go/version/version_test.go index 62aabad3a1..7c12e7ffd9 100644 --- a/src/go/version/version_test.go +++ b/src/go/version/version_test.go @@ -48,6 +48,7 @@ var langTests = []testCase1[string, string]{ {"go1.2.3", "go1.2"}, {"go1.2", "go1.2"}, {"go1", "go1"}, + {"go222", "go222.0"}, {"go1.999testmod", "go1.999"}, } diff --git a/src/hash/fnv/fnv.go b/src/hash/fnv/fnv.go index a3a944a05e..29439e2c1d 100644 --- a/src/hash/fnv/fnv.go +++ b/src/hash/fnv/fnv.go @@ -241,7 +241,6 @@ func (s *sum64) MarshalBinary() ([]byte, error) { b = append(b, magic64...) b = appendUint64(b, uint64(*s)) return b, nil - } func (s *sum64a) MarshalBinary() ([]byte, error) { diff --git a/src/hash/maphash/maphash_runtime.go b/src/hash/maphash/maphash_runtime.go index 98097ff9c3..b831df2cf4 100644 --- a/src/hash/maphash/maphash_runtime.go +++ b/src/hash/maphash/maphash_runtime.go @@ -10,8 +10,8 @@ import ( "unsafe" ) -//go:linkname runtime_fastrand64 runtime.fastrand64 -func runtime_fastrand64() uint64 +//go:linkname runtime_rand runtime.rand +func runtime_rand() uint64 //go:linkname runtime_memhash runtime.memhash //go:noescape @@ -39,5 +39,5 @@ func rthashString(s string, state uint64) uint64 { } func randUint64() uint64 { - return runtime_fastrand64() + return runtime_rand() } diff --git a/src/internal/abi/abi_loong64.go b/src/internal/abi/abi_loong64.go new file mode 100644 index 0000000000..c2306ae8d8 --- /dev/null +++ b/src/internal/abi/abi_loong64.go @@ -0,0 +1,19 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.regabiargs + +package abi + +const ( + // See abi_generic.go. + + // R4 - R19 + IntArgRegs = 16 + + // F0 - F15 + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/src/internal/abi/map.go b/src/internal/abi/map.go index e5b0a0bb6f..ad054e7d77 100644 --- a/src/internal/abi/map.go +++ b/src/internal/abi/map.go @@ -12,3 +12,6 @@ const ( MapMaxKeyBytes = 128 // Must fit in a uint8. MapMaxElemBytes = 128 // Must fit in a uint8. ) + +// ZeroValSize is the size in bytes of runtime.zeroVal. +const ZeroValSize = 1024 diff --git a/src/internal/abi/symtab.go b/src/internal/abi/symtab.go index bf6ea82f1c..ce1b650155 100644 --- a/src/internal/abi/symtab.go +++ b/src/internal/abi/symtab.go @@ -44,6 +44,7 @@ const ( FuncID_asmcgocall FuncID_asyncPreempt FuncID_cgocallback + FuncID_corostart FuncID_debugCallV2 FuncID_gcBgMarkWorker FuncID_goexit diff --git a/src/internal/bisect/bisect.go b/src/internal/bisect/bisect.go index bf67ceb9d7..3e5a6849f7 100644 --- a/src/internal/bisect/bisect.go +++ b/src/internal/bisect/bisect.go @@ -482,7 +482,6 @@ func (m *Matcher) stack(w Writer) bool { } } return m.ShouldEnable(h) - } // Writer is the same interface as io.Writer. diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go index b97b9c1b53..8b97a653d7 100644 --- a/src/internal/buildcfg/cfg.go +++ b/src/internal/buildcfg/cfg.go @@ -69,22 +69,61 @@ func goamd64() int { return int(defaultGOAMD64[len("v")] - '0') } -func goarm() int { +type goarmFeatures struct { + Version int + SoftFloat bool +} + +func (g goarmFeatures) String() string { + armStr := strconv.Itoa(g.Version) + if g.SoftFloat { + armStr += ",softfloat" + } else { + armStr += ",hardfloat" + } + return armStr +} + +func goarm() (g goarmFeatures) { + const ( + softFloatOpt = ",softfloat" + hardFloatOpt = ",hardfloat" + ) def := defaultGOARM if GOOS == "android" && GOARCH == "arm" { // Android arm devices always support GOARM=7. def = "7" } - switch v := envOr("GOARM", def); v { - case "5": - return 5 - case "6": - return 6 - case "7": - return 7 + v := envOr("GOARM", def) + + floatSpecified := false + if strings.HasSuffix(v, softFloatOpt) { + g.SoftFloat = true + floatSpecified = true + v = v[:len(v)-len(softFloatOpt)] } - Error = fmt.Errorf("invalid GOARM: must be 5, 6, 7") - return int(def[0] - '0') + if strings.HasSuffix(v, hardFloatOpt) { + floatSpecified = true + v = v[:len(v)-len(hardFloatOpt)] + } + + switch v { + case "5": + g.Version = 5 + case "6": + g.Version = 6 + case "7": + g.Version = 7 + default: + Error = fmt.Errorf("invalid GOARM: must start with 5, 6, or 7, and may optionally end in either %q or %q", hardFloatOpt, softFloatOpt) + g.Version = int(def[0] - '0') + } + + // 5 defaults to softfloat. 6 and 7 default to hardfloat. + if !floatSpecified && g.Version == 5 { + g.SoftFloat = true + } + return } func gomips() string { @@ -182,7 +221,7 @@ func GOGOARCH() (name, value string) { case "amd64": return "GOAMD64", fmt.Sprintf("v%d", GOAMD64) case "arm": - return "GOARM", strconv.Itoa(GOARM) + return "GOARM", GOARM.String() case "mips", "mipsle": return "GOMIPS", GOMIPS case "mips64", "mips64le": @@ -207,7 +246,7 @@ func gogoarchTags() []string { return list case "arm": var list []string - for i := 5; i <= GOARM; i++ { + for i := 5; i <= GOARM.Version; i++ { list = append(list, fmt.Sprintf("%s.%d", GOARCH, i)) } return list diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index c6a6c84f3c..a45cfaf862 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -65,6 +65,8 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { case "amd64", "arm64", "ppc64le", "ppc64", "riscv64": regabiAlwaysOn = true regabiSupported = true + case "loong64": + regabiSupported = true } baseline := goexperiment.Flags{ @@ -72,6 +74,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { RegabiArgs: regabiSupported, CoverageRedesign: true, AllocHeaders: true, + ExecTracer2: true, } // Start with the statically enabled set of experiments. @@ -130,7 +133,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { flags.RegabiWrappers = true flags.RegabiArgs = true } - // regabi is only supported on amd64, arm64, riscv64, ppc64 and ppc64le. + // regabi is only supported on amd64, arm64, loong64, riscv64, ppc64 and ppc64le. if !regabiSupported { flags.RegabiWrappers = false flags.RegabiArgs = false diff --git a/src/internal/bytealg/compare_loong64.s b/src/internal/bytealg/compare_loong64.s index c89c5a9256..311449ab18 100644 --- a/src/internal/bytealg/compare_loong64.s +++ b/src/internal/bytealg/compare_loong64.s @@ -5,83 +5,102 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·Compare(SB),NOSPLIT,$0-56 - MOVV a_base+0(FP), R6 - MOVV b_base+24(FP), R7 - MOVV a_len+8(FP), R4 - MOVV b_len+32(FP), R5 +TEXT ·Compare(SB),NOSPLIT,$0-56 +#ifndef GOEXPERIMENT_regabiargs + MOVV a_base+0(FP), R4 + MOVV a_len+8(FP), R5 + MOVV b_base+24(FP), R6 + MOVV b_len+32(FP), R7 MOVV $ret+48(FP), R13 +#else + // R4 = a_base + // R5 = a_len + // R6 = a_cap (unused) + // R7 = b_base (want in R6) + // R8 = b_len (want in R7) + // R9 = b_cap (unused) + MOVV R7, R6 + MOVV R8, R7 +#endif JMP cmpbody<>(SB) -TEXT runtime·cmpstring(SB),NOSPLIT,$0-40 - MOVV a_base+0(FP), R6 - MOVV b_base+16(FP), R7 - MOVV a_len+8(FP), R4 - MOVV b_len+24(FP), R5 +TEXT runtime·cmpstring(SB),NOSPLIT,$0-40 +#ifndef GOEXPERIMENT_regabiargs + MOVV a_base+0(FP), R4 + MOVV b_base+16(FP), R6 + MOVV a_len+8(FP), R5 + MOVV b_len+24(FP), R7 MOVV $ret+32(FP), R13 +#endif + // R4 = a_base + // R5 = a_len + // R6 = b_base + // R7 = b_len JMP cmpbody<>(SB) // On entry: -// R4 length of a -// R5 length of b -// R6 points to the start of a -// R7 points to the start of b +// R5 length of a +// R7 length of b +// R4 points to the start of a +// R6 points to the start of b // R13 points to the return value (-1/0/1) TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0 - BEQ R6, R7, samebytes // same start of a and b + BEQ R4, R6, samebytes // same start of a and b - SGTU R4, R5, R9 + SGTU R5, R7, R9 BNE R0, R9, r2_lt_r1 - MOVV R4, R14 + MOVV R5, R14 JMP entry r2_lt_r1: - MOVV R5, R14 // R14 is min(R4, R5) + MOVV R7, R14 // R14 is min(R4, R5) entry: - ADDV R6, R14, R12 // R6 start of a, R14 end of a - BEQ R6, R12, samebytes // length is 0 + ADDV R4, R14, R12 // R6 start of a, R14 end of a + BEQ R4, R12, samebytes // length is 0 SRLV $4, R14 // R14 is number of chunks BEQ R0, R14, byte_loop // make sure both a and b are aligned. - OR R6, R7, R15 + OR R4, R6, R15 AND $7, R15 BNE R0, R15, byte_loop PCALIGN $16 chunk16_loop: BEQ R0, R14, byte_loop - MOVV (R6), R8 - MOVV (R7), R9 + MOVV (R4), R8 + MOVV (R6), R9 BNE R8, R9, byte_loop - MOVV 8(R6), R16 - MOVV 8(R7), R17 + MOVV 8(R4), R16 + MOVV 8(R6), R17 + ADDV $16, R4 ADDV $16, R6 - ADDV $16, R7 SUBVU $1, R14 BEQ R16, R17, chunk16_loop + SUBV $8, R4 SUBV $8, R6 - SUBV $8, R7 byte_loop: - BEQ R6, R12, samebytes - MOVBU (R6), R8 + BEQ R4, R12, samebytes + MOVBU (R4), R8 + ADDVU $1, R4 + MOVBU (R6), R9 ADDVU $1, R6 - MOVBU (R7), R9 - ADDVU $1, R7 BEQ R8, R9, byte_loop byte_cmp: - SGTU R8, R9, R12 // R12 = 1 if (R8 > R9) - BNE R0, R12, ret - MOVV $-1, R12 + SGTU R8, R9, R4 // R12 = 1 if (R8 > R9) + BNE R0, R4, ret + MOVV $-1, R4 JMP ret samebytes: - SGTU R4, R5, R8 - SGTU R5, R4, R9 - SUBV R9, R8, R12 + SGTU R5, R7, R8 + SGTU R7, R5, R9 + SUBV R9, R8, R4 ret: - MOVV R12, (R13) +#ifndef GOEXPERIMENT_regabiargs + MOVV R4, (R13) +#endif RET diff --git a/src/internal/bytealg/count_riscv64.s b/src/internal/bytealg/count_riscv64.s index d123cbd7c6..3f255cd263 100644 --- a/src/internal/bytealg/count_riscv64.s +++ b/src/internal/bytealg/count_riscv64.s @@ -14,6 +14,7 @@ TEXT ·Count(SB),NOSPLIT,$0-40 MOV ZERO, X14 // count ADD X10, X11 // end + PCALIGN $16 loop: BEQ X10, X11, done MOVBU (X10), X15 @@ -34,6 +35,7 @@ TEXT ·CountString(SB),NOSPLIT,$0-32 MOV ZERO, X14 // count ADD X10, X11 // end + PCALIGN $16 loop: BEQ X10, X11, done MOVBU (X10), X15 diff --git a/src/internal/bytealg/equal_loong64.s b/src/internal/bytealg/equal_loong64.s index ba2a5578c3..a3ad5c1b35 100644 --- a/src/internal/bytealg/equal_loong64.s +++ b/src/internal/bytealg/equal_loong64.s @@ -8,17 +8,21 @@ #define REGCTXT R29 // memequal(a, b unsafe.Pointer, size uintptr) bool -TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 +TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 +#ifndef GOEXPERIMENT_regabiargs MOVV a+0(FP), R4 MOVV b+8(FP), R5 - BEQ R4, R5, eq MOVV size+16(FP), R6 +#endif + BEQ R4, R5, eq ADDV R4, R6, R7 PCALIGN $16 loop: BNE R4, R7, test MOVV $1, R4 +#ifndef GOEXPERIMENT_regabiargs MOVB R4, ret+24(FP) +#endif RET test: MOVBU (R4), R9 @@ -27,17 +31,24 @@ test: ADDV $1, R5 BEQ R9, R10, loop + MOVB R0, R4 +#ifndef GOEXPERIMENT_regabiargs MOVB R0, ret+24(FP) +#endif RET eq: MOVV $1, R4 +#ifndef GOEXPERIMENT_regabiargs MOVB R4, ret+24(FP) +#endif RET // memequal_varlen(a, b unsafe.Pointer) bool -TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 +TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 +#ifndef GOEXPERIMENT_regabiargs MOVV a+0(FP), R4 MOVV b+8(FP), R5 +#endif BEQ R4, R5, eq MOVV 8(REGCTXT), R6 // compiler stores size at offset 8 in the closure MOVV R4, 8(R3) @@ -45,9 +56,13 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT,$40-17 MOVV R6, 24(R3) JAL runtime·memequal(SB) MOVBU 32(R3), R4 +#ifndef GOEXPERIMENT_regabiargs MOVB R4, ret+16(FP) +#endif RET eq: MOVV $1, R4 +#ifndef GOEXPERIMENT_regabiargs MOVB R4, ret+16(FP) +#endif RET diff --git a/src/internal/bytealg/indexbyte_loong64.s b/src/internal/bytealg/indexbyte_loong64.s index 604970549f..03e0660973 100644 --- a/src/internal/bytealg/indexbyte_loong64.s +++ b/src/internal/bytealg/indexbyte_loong64.s @@ -5,11 +5,18 @@ #include "go_asm.h" #include "textflag.h" -TEXT ·IndexByte(SB),NOSPLIT,$0-40 +TEXT ·IndexByte(SB),NOSPLIT,$0-40 +#ifndef GOEXPERIMENT_regabiargs MOVV b_base+0(FP), R4 MOVV b_len+8(FP), R5 - MOVBU c+24(FP), R6 // byte to find - MOVV R4, R7 // store base for later + MOVBU c+24(FP), R7 // byte to find +#endif + // R4 = b_base + // R5 = b_len + // R6 = b_cap (unused) + // R7 = byte to find + AND $0xff, R7 + MOVV R4, R6 // store base for later ADDV R4, R5 // end ADDV $-1, R4 @@ -18,21 +25,30 @@ loop: ADDV $1, R4 BEQ R4, R5, notfound MOVBU (R4), R8 - BNE R6, R8, loop + BNE R7, R8, loop - SUBV R7, R4 // remove base + SUBV R6, R4 // remove base +#ifndef GOEXPERIMENT_regabiargs MOVV R4, ret+32(FP) +#endif RET notfound: MOVV $-1, R4 +#ifndef GOEXPERIMENT_regabiargs MOVV R4, ret+32(FP) +#endif RET -TEXT ·IndexByteString(SB),NOSPLIT,$0-32 +TEXT ·IndexByteString(SB),NOSPLIT,$0-32 +#ifndef GOEXPERIMENT_regabiargs MOVV s_base+0(FP), R4 MOVV s_len+8(FP), R5 MOVBU c+16(FP), R6 // byte to find +#endif + // R4 = s_base + // R5 = s_len + // R6 = byte to find MOVV R4, R7 // store base for later ADDV R4, R5 // end ADDV $-1, R4 @@ -45,10 +61,14 @@ loop: BNE R6, R8, loop SUBV R7, R4 // remove base +#ifndef GOEXPERIMENT_regabiargs MOVV R4, ret+24(FP) +#endif RET notfound: MOVV $-1, R4 +#ifndef GOEXPERIMENT_regabiargs MOVV R4, ret+24(FP) +#endif RET diff --git a/src/internal/chacha8rand/chacha8.go b/src/internal/chacha8rand/chacha8.go new file mode 100644 index 0000000000..ce55c07d05 --- /dev/null +++ b/src/internal/chacha8rand/chacha8.go @@ -0,0 +1,197 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package chacha8rand implements a pseudorandom generator +// based on ChaCha8. It is used by both runtime and math/rand/v2 +// and must have no dependencies. +package chacha8rand + +const ( + ctrInc = 4 // increment counter by 4 between block calls + ctrMax = 16 // reseed when counter reaches 16 + chunk = 32 // each chunk produced by block is 32 uint64s + reseed = 4 // reseed with 4 words +) + +// block is the chacha8rand block function. +func block(seed *[4]uint64, blocks *[32]uint64, counter uint32) + +// A State holds the state for a single random generator. +// It must be used from one goroutine at a time. +// If used by multiple goroutines at a time, the goroutines +// may see the same random values, but the code will not +// crash or cause out-of-bounds memory accesses. +type State struct { + buf [32]uint64 + seed [4]uint64 + i uint32 + n uint32 + c uint32 +} + +// Next returns the next random value, along with a boolean +// indicating whether one was available. +// If one is not available, the caller should call Refill +// and then repeat the call to Next. +// +// Next is //go:nosplit to allow its use in the runtime +// with per-m data without holding the per-m lock. +//go:nosplit +func (s *State) Next() (uint64, bool) { + i := s.i + if i >= s.n { + return 0, false + } + s.i = i + 1 + return s.buf[i&31], true // i&31 eliminates bounds check +} + +// Init seeds the State with the given seed value. +func (s *State) Init(seed [32]byte) { + s.Init64([4]uint64{ + leUint64(seed[0*8:]), + leUint64(seed[1*8:]), + leUint64(seed[2*8:]), + leUint64(seed[3*8:]), + }) +} + +// Init64 seeds the state with the given seed value. +func (s *State) Init64(seed [4]uint64) { + s.seed = seed + block(&s.seed, &s.buf, 0) + s.c = 0 + s.i = 0 + s.n = chunk +} + +// Refill refills the state with more random values. +// After a call to Refill, an immediate call to Next will succeed +// (unless multiple goroutines are incorrectly sharing a state). +func (s *State) Refill() { + s.c += ctrInc + if s.c == ctrMax { + // Reseed with generated uint64s for forward secrecy. + // Normally this is done immediately after computing a block, + // but we do it immediately before computing the next block, + // to allow a much smaller serialized state (just the seed plus offset). + // This gives a delayed benefit for the forward secrecy + // (you can reconstruct the recent past given a memory dump), + // which we deem acceptable in exchange for the reduced size. + s.seed[0] = s.buf[len(s.buf)-reseed+0] + s.seed[1] = s.buf[len(s.buf)-reseed+1] + s.seed[2] = s.buf[len(s.buf)-reseed+2] + s.seed[3] = s.buf[len(s.buf)-reseed+3] + s.c = 0 + } + block(&s.seed, &s.buf, s.c) + s.i = 0 + s.n = uint32(len(s.buf)) + if s.c == ctrMax-ctrInc { + s.n = uint32(len(s.buf)) - reseed + } +} + +// Reseed reseeds the state with new random values. +// After a call to Reseed, any previously returned random values +// have been erased from the memory of the state and cannot be +// recovered. +func (s *State) Reseed() { + var seed [4]uint64 + for i := range seed { + for { + x, ok := s.Next() + if ok { + seed[i] = x + break + } + s.Refill() + } + } + s.Init64(seed) +} + +// Marshal marshals the state into a byte slice. +// Marshal and Unmarshal are functions, not methods, +// so that they will not be linked into the runtime +// when it uses the State struct, since the runtime +// does not need these. +func Marshal(s *State) []byte { + data := make([]byte, 6*8) + copy(data, "chacha8:") + used := (s.c/ctrInc)*chunk + s.i + bePutUint64(data[1*8:], uint64(used)) + for i, seed := range s.seed { + lePutUint64(data[(2+i)*8:], seed) + } + return data +} + +type errUnmarshalChaCha8 struct{} + +func (*errUnmarshalChaCha8) Error() string { + return "invalid ChaCha8 encoding" +} + +// Unmarshal unmarshals the state from a byte slice. +func Unmarshal(s *State, data []byte) error { + if len(data) != 6*8 || string(data[:8]) != "chacha8:" { + return new(errUnmarshalChaCha8) + } + used := beUint64(data[1*8:]) + if used > (ctrMax/ctrInc)*chunk-reseed { + return new(errUnmarshalChaCha8) + } + for i := range s.seed { + s.seed[i] = leUint64(data[(2+i)*8:]) + } + s.c = ctrInc * (uint32(used) / chunk) + block(&s.seed, &s.buf, s.c) + s.i = uint32(used) % chunk + s.n = chunk + if s.c == ctrMax-ctrInc { + s.n = chunk - reseed + } + return nil +} + +// binary.bigEndian.Uint64, copied to avoid dependency +func beUint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 +} + +// binary.bigEndian.PutUint64, copied to avoid dependency +func bePutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v >> 56) + b[1] = byte(v >> 48) + b[2] = byte(v >> 40) + b[3] = byte(v >> 32) + b[4] = byte(v >> 24) + b[5] = byte(v >> 16) + b[6] = byte(v >> 8) + b[7] = byte(v) +} + +// binary.littleEndian.Uint64, copied to avoid dependency +func leUint64(b []byte) uint64 { + _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +// binary.littleEndian.PutUint64, copied to avoid dependency +func lePutUint64(b []byte, v uint64) { + _ = b[7] // early bounds check to guarantee safety of writes below + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) +} diff --git a/src/internal/chacha8rand/chacha8_amd64.s b/src/internal/chacha8rand/chacha8_amd64.s new file mode 100644 index 0000000000..b56deb3b0b --- /dev/null +++ b/src/internal/chacha8rand/chacha8_amd64.s @@ -0,0 +1,174 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// ChaCha8 is ChaCha with 8 rounds. +// See https://cr.yp.to/chacha/chacha-20080128.pdf. +// See chacha8_generic.go for additional details. + +// ROL rotates the uint32s in register R left by N bits, using temporary T. +#define ROL(N, R, T) \ + MOVO R, T; PSLLL $(N), T; PSRLL $(32-(N)), R; PXOR T, R + +// ROL16 rotates the uint32s in register R left by 16, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL16(R, T) PSHUFB ·rol16<>(SB), R +#else +#define ROL16(R, T) ROL(16, R, T) +#endif + +// ROL8 rotates the uint32s in register R left by 8, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL8(R, T) PSHUFB ·rol8<>(SB), R +#else +#define ROL8(R, T) ROL(8, R, T) +#endif + +// QR is the ChaCha quarter-round on A, B, C, and D. T is an available temporary. +#define QR(A, B, C, D, T) \ + PADDD B, A; PXOR A, D; ROL16(D, T); \ + PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $12, T; PSRLL $20, B; PXOR T, B; \ + PADDD B, A; PXOR A, D; ROL8(D, T); \ + PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $7, T; PSRLL $25, B; PXOR T, B + +// REPLREG replicates the register R into 4 uint32s in XR. +#define REPLREG(R, XR) \ + MOVQ R, XR; \ + PSHUFD $0, XR, XR + +// REPL replicates the uint32 constant val into 4 uint32s in XR. It smashes DX. +#define REPL(val, XR) \ + MOVL $val, DX; \ + REPLREG(DX, XR) + +// SEED copies the off'th uint32 of the seed into the register XR, +// replicating it into all four stripes of the register. +#define SEED(off, reg, XR) \ + MOVL (4*off)(AX), reg; \ + REPLREG(reg, XR) \ + +// block runs 4 ChaCha8 block transformations in the four stripes of the X registers. + +// func block(seed *[8]uint32, blocks *[16][4]uint32, counter uint32) +TEXT ·block(SB), NOSPLIT, $16 + // seed in AX + // blocks in BX + // counter in CX + + // Load initial constants into top row. + REPL(0x61707865, X0) + REPL(0x3320646e, X1) + REPL(0x79622d32, X2) + REPL(0x6b206574, X3) + + // Load counter into bottom left cell. + // Each stripe gets a different counter: 0, 1, 2, 3. + // (PINSRD is not available in GOAMD64_v1, + // so just do it in memory on all systems. + // This is not on the critical path.) + MOVL CX, 0(SP) + INCL CX + MOVL CX, 4(SP) + INCL CX + MOVL CX, 8(SP) + INCL CX + MOVL CX, 12(SP) + MOVOU 0(SP), X12 + + // Load seed words into next two rows and into DI, SI, R8..R13 + SEED(0, DI, X4) + SEED(1, SI, X5) + SEED(2, R8, X6) + SEED(3, R9, X7) + SEED(4, R10, X8) + SEED(5, R11, X9) + SEED(6, R12, X10) + SEED(7, R13, X11) + + // Zeros for remaining two matrix entries. + // We have just enough XMM registers to hold the state, + // without one for the temporary, so we flush and restore + // some values to and from memory to provide a temporary. + // The initial temporary is X15, so zero its memory instead + // of X15 itself. + MOVL $0, DX + MOVQ DX, X13 + MOVQ DX, X14 + MOVOU X14, (15*16)(BX) + + // 4 iterations. Each iteration is 8 quarter-rounds. + MOVL $4, DX +loop: + QR(X0, X4, X8, X12, X15) + MOVOU X4, (4*16)(BX) // save X4 + QR(X1, X5, X9, X13, X15) + MOVOU (15*16)(BX), X15 // reload X15; temp now X4 + QR(X2, X6, X10, X14, X4) + QR(X3, X7, X11, X15, X4) + + QR(X0, X5, X10, X15, X4) + MOVOU X15, (15*16)(BX) // save X15 + QR(X1, X6, X11, X12, X4) + MOVOU (4*16)(BX), X4 // reload X4; temp now X15 + QR(X2, X7, X8, X13, X15) + QR(X3, X4, X9, X14, X15) + + DECL DX + JNZ loop + + // Store interlaced blocks back to output buffer, + // adding original seed along the way. + + // First the top and bottom rows. + MOVOU X0, (0*16)(BX) + MOVOU X1, (1*16)(BX) + MOVOU X2, (2*16)(BX) + MOVOU X3, (3*16)(BX) + MOVOU X12, (12*16)(BX) + MOVOU X13, (13*16)(BX) + MOVOU X14, (14*16)(BX) + // X15 has already been stored. + + // Now we have X0-X3, X12-X15 available for temporaries. + // Add seed rows back to output. We left seed in DI, SI, R8..R13 above. + REPLREG(DI, X0) + REPLREG(SI, X1) + REPLREG(R8, X2) + REPLREG(R9, X3) + REPLREG(R10, X12) + REPLREG(R11, X13) + REPLREG(R12, X14) + REPLREG(R13, X15) + PADDD X0, X4 + PADDD X1, X5 + PADDD X2, X6 + PADDD X3, X7 + PADDD X12, X8 + PADDD X13, X9 + PADDD X14, X10 + PADDD X15, X11 + MOVOU X4, (4*16)(BX) + MOVOU X5, (5*16)(BX) + MOVOU X6, (6*16)(BX) + MOVOU X7, (7*16)(BX) + MOVOU X8, (8*16)(BX) + MOVOU X9, (9*16)(BX) + MOVOU X10, (10*16)(BX) + MOVOU X11, (11*16)(BX) + + MOVL $0, AX + MOVQ AX, X15 // must be 0 on return + + RET + +// rotate left 16 indexes for PSHUFB +GLOBL ·rol16<>(SB), NOPTR|RODATA, $16 +DATA ·rol16<>+0(SB)/8, $0x0504070601000302 +DATA ·rol16<>+8(SB)/8, $0x0D0C0F0E09080B0A + +// rotate left 8 indexes for PSHUFB +GLOBL ·rol8<>(SB), NOPTR|RODATA, $16 +DATA ·rol8<>+0(SB)/8, $0x0605040702010003 +DATA ·rol8<>+8(SB)/8, $0x0E0D0C0F0A09080B diff --git a/src/internal/chacha8rand/chacha8_arm64.s b/src/internal/chacha8rand/chacha8_arm64.s new file mode 100644 index 0000000000..18e34dd148 --- /dev/null +++ b/src/internal/chacha8rand/chacha8_arm64.s @@ -0,0 +1,104 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// QR is the ChaCha quarter-round on A, B, C, and D. +// V30 is used as a temporary, and V31 is assumed to +// hold the index table for rotate left 8. +#define QR(A, B, C, D) \ + VADD A.S4, B.S4, A.S4; VEOR D.B16, A.B16, D.B16; VREV32 D.H8, D.H8; \ + VADD C.S4, D.S4, C.S4; VEOR B.B16, C.B16, V30.B16; VSHL $12, V30.S4, B.S4; VSRI $20, V30.S4, B.S4 \ + VADD A.S4, B.S4, A.S4; VEOR D.B16, A.B16, D.B16; VTBL V31.B16, [D.B16], D.B16; \ + VADD C.S4, D.S4, C.S4; VEOR B.B16, C.B16, V30.B16; VSHL $7, V30.S4, B.S4; VSRI $25, V30.S4, B.S4 + +// block runs 4 ChaCha8 block transformations in the four stripes of the V registers. + +// func block(seed *[8]uint32, blocks *[4][16]uint32, counter uint32) +TEXT ·block(SB), NOSPLIT, $16 + // seed in R0 + // blocks in R1 + // counter in R2 + + // Load initial constants into top row. + MOVD $·chachaConst(SB), R10 + VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4] + + // Load increment and rotate 8 constants into V30, V31. + MOVD $·chachaIncRot(SB), R11 + VLD1 (R11), [V30.S4, V31.S4] + + VLD4R.P 16(R0), [V4.S4, V5.S4, V6.S4, V7.S4] + VLD4R.P 16(R0), [V8.S4, V9.S4, V10.S4, V11.S4] + + // store counter to memory to replicate its uint32 halfs back out + MOVW R2, 0(RSP) + VLD1R 0(RSP), [V12.S4] + + // Add 0, 1, 2, 3 to counter stripes. + VADD V30.S4, V12.S4, V12.S4 + + // Zeros for remaining two matrix entries. + VEOR V13.B16, V13.B16, V13.B16 + VEOR V14.B16, V14.B16, V14.B16 + VEOR V15.B16, V15.B16, V15.B16 + + // Save seed state for adding back later. + VMOV V4.B16, V20.B16 + VMOV V5.B16, V21.B16 + VMOV V6.B16, V22.B16 + VMOV V7.B16, V23.B16 + VMOV V8.B16, V24.B16 + VMOV V9.B16, V25.B16 + VMOV V10.B16, V26.B16 + VMOV V11.B16, V27.B16 + + // 4 iterations. Each iteration is 8 quarter-rounds. + MOVD $4, R0 +loop: + QR(V0, V4, V8, V12) + QR(V1, V5, V9, V13) + QR(V2, V6, V10, V14) + QR(V3, V7, V11, V15) + + QR(V0, V5, V10, V15) + QR(V1, V6, V11, V12) + QR(V2, V7, V8, V13) + QR(V3, V4, V9, V14) + + SUB $1, R0 + CBNZ R0, loop + + // Add seed back. + VADD V4.S4, V20.S4, V4.S4 + VADD V5.S4, V21.S4, V5.S4 + VADD V6.S4, V22.S4, V6.S4 + VADD V7.S4, V23.S4, V7.S4 + VADD V8.S4, V24.S4, V8.S4 + VADD V9.S4, V25.S4, V9.S4 + VADD V10.S4, V26.S4, V10.S4 + VADD V11.S4, V27.S4, V11.S4 + + // Store interlaced blocks back to output buffer. + VST1.P [ V0.B16, V1.B16, V2.B16, V3.B16], 64(R1) + VST1.P [ V4.B16, V5.B16, V6.B16, V7.B16], 64(R1) + VST1.P [ V8.B16, V9.B16, V10.B16, V11.B16], 64(R1) + VST1.P [V12.B16, V13.B16, V14.B16, V15.B16], 64(R1) + RET + +GLOBL ·chachaConst(SB), NOPTR|RODATA, $32 +DATA ·chachaConst+0x00(SB)/4, $0x61707865 +DATA ·chachaConst+0x04(SB)/4, $0x3320646e +DATA ·chachaConst+0x08(SB)/4, $0x79622d32 +DATA ·chachaConst+0x0c(SB)/4, $0x6b206574 + +GLOBL ·chachaIncRot(SB), NOPTR|RODATA, $32 +DATA ·chachaIncRot+0x00(SB)/4, $0x00000000 +DATA ·chachaIncRot+0x04(SB)/4, $0x00000001 +DATA ·chachaIncRot+0x08(SB)/4, $0x00000002 +DATA ·chachaIncRot+0x0c(SB)/4, $0x00000003 +DATA ·chachaIncRot+0x10(SB)/4, $0x02010003 +DATA ·chachaIncRot+0x14(SB)/4, $0x06050407 +DATA ·chachaIncRot+0x18(SB)/4, $0x0A09080B +DATA ·chachaIncRot+0x1c(SB)/4, $0x0E0D0C0F diff --git a/src/internal/chacha8rand/chacha8_generic.go b/src/internal/chacha8rand/chacha8_generic.go new file mode 100644 index 0000000000..2a0f5cd1d6 --- /dev/null +++ b/src/internal/chacha8rand/chacha8_generic.go @@ -0,0 +1,235 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// ChaCha8 is ChaCha with 8 rounds. +// See https://cr.yp.to/chacha/chacha-20080128.pdf. +// +// ChaCha8 operates on a 4x4 matrix of uint32 values, initially set to: +// +// const1 const2 const3 const4 +// seed seed seed seed +// seed seed seed seed +// counter64 0 0 +// +// We use the same constants as ChaCha20 does, a random seed, +// and a counter. Running ChaCha8 on this input produces +// a 4x4 matrix of pseudo-random values with as much entropy +// as the seed. +// +// Given SIMD registers that can hold N uint32s, it is possible +// to run N ChaCha8 block transformations in parallel by filling +// the first register with the N copies of const1, the second +// with N copies of const2, and so on, and then running the operations. +// +// Each iteration of ChaCha8Rand operates over 32 bytes of input and +// produces 992 bytes of RNG output, plus 32 bytes of input for the next +// iteration. +// +// The 32 bytes of input are used as a ChaCha8 key, with a zero nonce, to +// produce 1024 bytes of output (16 blocks, with counters 0 to 15). +// First, for each block, the values 0x61707865, 0x3320646e, 0x79622d32, +// 0x6b206574 are subtracted from the 32-bit little-endian words at +// position 0, 1, 2, and 3 respectively, and an increasing counter +// starting at zero is subtracted from each word at position 12. Then, +// this stream is permuted such that for each sequence of four blocks, +// first we output the first four bytes of each block, then the next four +// bytes of each block, and so on. Finally, the last 32 bytes of output +// are used as the input of the next iteration, and the remaining 992 +// bytes are the RNG output. +// +// See https://c2sp.org/chacha8rand for additional details. +// +// Normal ChaCha20 implementations for encryption use this same +// parallelism but then have to deinterlace the results so that +// it appears the blocks were generated separately. For the purposes +// of generating random numbers, the interlacing is fine. +// We are simply locked in to preserving the 4-way interlacing +// in any future optimizations. +package chacha8rand + +import ( + "internal/goarch" + "unsafe" +) + +// setup sets up 4 ChaCha8 blocks in b32 with the counter and seed. +// Note that b32 is [16][4]uint32 not [4][16]uint32: the blocks are interlaced +// the same way they would be in a 4-way SIMD implementations. +func setup(seed *[4]uint64, b32 *[16][4]uint32, counter uint32) { + // Convert to uint64 to do half as many stores to memory. + b := (*[16][2]uint64)(unsafe.Pointer(b32)) + + // Constants; same as in ChaCha20: "expand 32-byte k" + b[0][0] = 0x61707865_61707865 + b[0][1] = 0x61707865_61707865 + + b[1][0] = 0x3320646e_3320646e + b[1][1] = 0x3320646e_3320646e + + b[2][0] = 0x79622d32_79622d32 + b[2][1] = 0x79622d32_79622d32 + + b[3][0] = 0x6b206574_6b206574 + b[3][1] = 0x6b206574_6b206574 + + // Seed values. + var x64 uint64 + var x uint32 + + x = uint32(seed[0]) + x64 = uint64(x)<<32 | uint64(x) + b[4][0] = x64 + b[4][1] = x64 + + x = uint32(seed[0] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[5][0] = x64 + b[5][1] = x64 + + x = uint32(seed[1]) + x64 = uint64(x)<<32 | uint64(x) + b[6][0] = x64 + b[6][1] = x64 + + x = uint32(seed[1] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[7][0] = x64 + b[7][1] = x64 + + x = uint32(seed[2]) + x64 = uint64(x)<<32 | uint64(x) + b[8][0] = x64 + b[8][1] = x64 + + x = uint32(seed[2] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[9][0] = x64 + b[9][1] = x64 + + x = uint32(seed[3]) + x64 = uint64(x)<<32 | uint64(x) + b[10][0] = x64 + b[10][1] = x64 + + x = uint32(seed[3] >> 32) + x64 = uint64(x)<<32 | uint64(x) + b[11][0] = x64 + b[11][1] = x64 + + // Counters. + if goarch.BigEndian { + b[12][0] = uint64(counter+0)<<32 | uint64(counter+1) + b[12][1] = uint64(counter+2)<<32 | uint64(counter+3) + } else { + b[12][0] = uint64(counter+0) | uint64(counter+1)<<32 + b[12][1] = uint64(counter+2) | uint64(counter+3)<<32 + } + + // Zeros. + b[13][0] = 0 + b[13][1] = 0 + b[14][0] = 0 + b[14][1] = 0 + + b[15][0] = 0 + b[15][1] = 0 +} + +func _() { + // block and block_generic must have same type + x := block + x = block_generic + _ = x +} + +// block_generic is the non-assembly block implementation, +// for use on systems without special assembly. +// Even on such systems, it is quite fast: on GOOS=386, +// ChaCha8 using this code generates random values faster than PCG-DXSM. +func block_generic(seed *[4]uint64, buf *[32]uint64, counter uint32) { + b := (*[16][4]uint32)(unsafe.Pointer(buf)) + + setup(seed, b, counter) + + for i := range b[0] { + // Load block i from b[*][i] into local variables. + b0 := b[0][i] + b1 := b[1][i] + b2 := b[2][i] + b3 := b[3][i] + b4 := b[4][i] + b5 := b[5][i] + b6 := b[6][i] + b7 := b[7][i] + b8 := b[8][i] + b9 := b[9][i] + b10 := b[10][i] + b11 := b[11][i] + b12 := b[12][i] + b13 := b[13][i] + b14 := b[14][i] + b15 := b[15][i] + + // 4 iterations of eight quarter-rounds each is 8 rounds + for round := 0; round < 4; round++ { + b0, b4, b8, b12 = qr(b0, b4, b8, b12) + b1, b5, b9, b13 = qr(b1, b5, b9, b13) + b2, b6, b10, b14 = qr(b2, b6, b10, b14) + b3, b7, b11, b15 = qr(b3, b7, b11, b15) + + b0, b5, b10, b15 = qr(b0, b5, b10, b15) + b1, b6, b11, b12 = qr(b1, b6, b11, b12) + b2, b7, b8, b13 = qr(b2, b7, b8, b13) + b3, b4, b9, b14 = qr(b3, b4, b9, b14) + } + + // Store block i back into b[*][i]. + // Add b4..b11 back to the original key material, + // like in ChaCha20, to avoid trivial invertibility. + // There is no entropy in b0..b3 and b12..b15 + // so we can skip the additions and save some time. + b[0][i] = b0 + b[1][i] = b1 + b[2][i] = b2 + b[3][i] = b3 + b[4][i] += b4 + b[5][i] += b5 + b[6][i] += b6 + b[7][i] += b7 + b[8][i] += b8 + b[9][i] += b9 + b[10][i] += b10 + b[11][i] += b11 + b[12][i] = b12 + b[13][i] = b13 + b[14][i] = b14 + b[15][i] = b15 + } + + if goarch.BigEndian { + // On a big-endian system, reading the uint32 pairs as uint64s + // will word-swap them compared to little-endian, so we word-swap + // them here first to make the next swap get the right answer. + for i, x := range buf { + buf[i] = x>>32 | x<<32 + } + } +} + +// qr is the (inlinable) ChaCha8 quarter round. +func qr(a, b, c, d uint32) (_a, _b, _c, _d uint32) { + a += b + d ^= a + d = d<<16 | d>>16 + c += d + b ^= c + b = b<<12 | b>>20 + a += b + d ^= a + d = d<<8 | d>>24 + c += d + b ^= c + b = b<<7 | b>>25 + return a, b, c, d +} diff --git a/src/internal/chacha8rand/chacha8_stub.s b/src/internal/chacha8rand/chacha8_stub.s new file mode 100644 index 0000000000..09be558fcb --- /dev/null +++ b/src/internal/chacha8rand/chacha8_stub.s @@ -0,0 +1,12 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !amd64 && !arm64 + +#include "textflag.h" + +// func block(counter uint64, seed *[8]uint32, blocks *[16][4]uint32) +TEXT ·block(SB), NOSPLIT, $0 + JMP ·block_generic(SB) + diff --git a/src/internal/chacha8rand/export_test.go b/src/internal/chacha8rand/export_test.go new file mode 100644 index 0000000000..728aded682 --- /dev/null +++ b/src/internal/chacha8rand/export_test.go @@ -0,0 +1,12 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package chacha8rand + +var Block = block +var Block_generic = block_generic + +func Seed(s *State) [4]uint64 { + return s.seed +} diff --git a/src/internal/chacha8rand/rand_test.go b/src/internal/chacha8rand/rand_test.go new file mode 100644 index 0000000000..2975013bfa --- /dev/null +++ b/src/internal/chacha8rand/rand_test.go @@ -0,0 +1,202 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package chacha8rand_test + +import ( + "bytes" + "encoding/binary" + "fmt" + . "internal/chacha8rand" + "slices" + "testing" +) + +func TestOutput(t *testing.T) { + var s State + s.Init(seed) + for i := range output { + for { + x, ok := s.Next() + if ok { + if x != output[i] { + t.Errorf("#%d: have %#x want %#x", i, x, output[i]) + } + break + } + s.Refill() + } + } +} + +func TestMarshal(t *testing.T) { + var s State + s.Init(seed) + for i := range output { + for { + b := Marshal(&s) + s = State{} + err := Unmarshal(&s, b) + if err != nil { + t.Fatalf("#%d: Unmarshal: %v", i, err) + } + x, ok := s.Next() + if ok { + if x != output[i] { + t.Fatalf("#%d: have %#x want %#x", i, x, output[i]) + } + break + } + s.Refill() + } + } +} + +func TestReseed(t *testing.T) { + var s State + s.Init(seed) + old := Seed(&s) + s.Reseed() + if Seed(&s) == old { + t.Errorf("Reseed did not change seed") + } +} + +func BenchmarkBlock(b *testing.B) { + var seed [4]uint64 + var blocks [32]uint64 + + for i := 0; i < b.N; i++ { + Block(&seed, &blocks, 0) + } + b.SetBytes(32 * 8) +} + +func TestBlockGeneric(t *testing.T) { + var b1, b2 [32]uint64 + s := seed // byte seed + seed := [4]uint64{ + binary.LittleEndian.Uint64(s[0*8:]), + binary.LittleEndian.Uint64(s[1*8:]), + binary.LittleEndian.Uint64(s[2*8:]), + binary.LittleEndian.Uint64(s[3*8:]), + } + + Block(&seed, &b1, 4) + Block_generic(&seed, &b2, 4) + if !slices.Equal(b1[:], b2[:]) { + var out bytes.Buffer + fmt.Fprintf(&out, "%-18s %-18s\n", "block", "block_generic") + for i := range b1 { + suffix := "" + if b1[i] != b2[i] { + suffix = " mismatch!" + } + fmt.Fprintf(&out, "%#016x %#016x%s\n", b1[i], b2[i], suffix) + } + t.Errorf("block and block_generic disagree:\n%s", out.String()) + } +} + +// Golden output test to make sure algorithm never changes, +// so that its use in math/rand/v2 stays stable. +// See https://c2sp.org/chacha8rand. + +var seed = [32]byte([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ123456")) + +var output = []uint64{ + 0xb773b6063d4616a5, 0x1160af22a66abc3c, 0x8c2599d9418d287c, 0x7ee07e037edc5cd6, + 0xcfaa9ee02d1c16ad, 0x0e090eef8febea79, 0x3c82d271128b5b3e, 0x9c5addc11252a34f, + 0xdf79bb617d6ceea6, 0x36d553591f9d736a, 0xeef0d14e181ee01f, 0x089bfc760ae58436, + 0xd9e52b59cc2ad268, 0xeb2fb4444b1b8aba, 0x4f95c8a692c46661, 0xc3c6323217cae62c, + 0x91ebb4367f4e2e7e, 0x784cf2c6a0ec9bc6, 0x5c34ec5c34eabe20, 0x4f0a8f515570daa8, + 0xfc35dcb4113d6bf2, 0x5b0da44c645554bc, 0x6d963da3db21d9e1, 0xeeaefc3150e500f3, + 0x2d37923dda3750a5, 0x380d7a626d4bc8b0, 0xeeaf68ede3d7ee49, 0xf4356695883b717c, + 0x846a9021392495a4, 0x8e8510549630a61b, 0x18dc02545dbae493, 0x0f8f9ff0a65a3d43, + 0xccf065f7190ff080, 0xfd76d1aa39673330, 0x95d232936cba6433, 0x6c7456d1070cbd17, + 0x462acfdaff8c6562, 0x5bafab866d34fc6a, 0x0c862f78030a2988, 0xd39a83e407c3163d, + 0xc00a2b7b45f22ebf, 0x564307c62466b1a9, 0x257e0424b0c072d4, 0x6fb55e99496c28fe, + 0xae9873a88f5cd4e0, 0x4657362ac60d3773, 0x1c83f91ecdf23e8e, 0x6fdc0792c15387c0, + 0x36dad2a30dfd2b5c, 0xa4b593290595bdb7, 0x4de18934e4cc02c5, 0xcdc0d604f015e3a7, + 0xfba0dbf69ad80321, 0x60e8bea3d139de87, 0xd18a4d851ef48756, 0x6366447c2215f34a, + 0x05682e97d3d007ee, 0x4c0e8978c6d54ab2, 0xcf1e9f6a6712edc2, 0x061439414c80cfd3, + 0xd1a8b6e2745c0ead, 0x31a7918d45c410e8, 0xabcc61ad90216eec, 0x4040d92d2032a71a, + 0x3cd2f66ffb40cd68, 0xdcd051c07295857a, 0xeab55cbcd9ab527e, 0x18471dce781bdaac, + 0xf7f08cd144dc7252, 0x5804e0b13d7f40d1, 0x5cb1a446e4b2d35b, 0xe6d4a728d2138a06, + 0x05223e40ca60dad8, 0x2d61ec3206ac6a68, 0xab692356874c17b8, 0xc30954417676de1c, + 0x4f1ace3732225624, 0xfba9510813988338, 0x997f200f52752e11, 0x1116aaafe86221fa, + 0x07ce3b5cb2a13519, 0x2956bc72bc458314, 0x4188b7926140eb78, 0x56ca6dbfd4adea4d, + 0x7fe3c22349340ce5, 0x35c08f9c37675f8a, 0x11e1c7fbef5ed521, 0x98adc8464ec1bc75, + 0xd163b2c73d1203f8, 0x8c761ee043a2f3f3, 0x24b99d6accecd7b7, 0x793e31aa112f0370, + 0x8e87dc2a19285139, 0x4247ae04f7096e25, 0x514f3122926fe20f, 0xdc6fb3f045d2a7e9, + 0x15cb30cecdd18eba, 0xcbc7fdecf6900274, 0x3fb5c696dc8ba021, 0xd1664417c8d274e6, + 0x05f7e445ea457278, 0xf920bbca1b9db657, 0x0c1950b4da22cb99, 0xf875baf1af09e292, + 0xbed3d7b84250f838, 0xf198e8080fd74160, 0xc9eda51d9b7ea703, 0xf709ef55439bf8f6, + 0xd20c74feebf116fc, 0x305668eb146d7546, 0x829af3ec10d89787, 0x15b8f9697b551dbc, + 0xfc823c6c8e64b8c9, 0x345585e8183b40bc, 0x674b4171d6581368, 0x1234d81cd670e9f7, + 0x0e505210d8a55e19, 0xe8258d69eeeca0dc, 0x05d4c452e8baf67e, 0xe8dbe30116a45599, + 0x1cf08ce1b1176f00, 0xccf7d0a4b81ecb49, 0x303fea136b2c430e, 0x861d6c139c06c871, + 0x5f41df72e05e0487, 0x25bd7e1e1ae26b1d, 0xbe9f4004d662a41d, 0x65bf58d483188546, + 0xd1b27cff69db13cc, 0x01a6663372c1bb36, 0x578dd7577b727f4d, 0x19c78f066c083cf6, + 0xdbe014d4f9c391bb, 0x97fbb2dd1d13ffb3, 0x31c91e0af9ef8d4f, 0x094dfc98402a43ba, + 0x069bd61bea37b752, 0x5b72d762e8d986ca, 0x72ee31865904bc85, 0xd1f5fdc5cd36c33e, + 0xba9b4980a8947cad, 0xece8f05eac49ab43, 0x65fe1184abae38e7, 0x2d7cb9dea5d31452, + 0xcc71489476e467e3, 0x4c03a258a578c68c, 0x00efdf9ecb0fd8fc, 0x9924cad471e2666d, + 0x87f8668318f765e9, 0xcb4dc57c1b55f5d8, 0xd373835a86604859, 0xe526568b5540e482, + 0x1f39040f08586fec, 0xb764f3f00293f8e6, 0x049443a2f6bd50a8, 0x76fec88697d3941a, + 0x3efb70d039bae7a2, 0xe2f4611368eca8a8, 0x7c007a96e01d2425, 0xbbcce5768e69c5bf, + 0x784fb4985c42aac3, 0xf72b5091aa223874, 0x3630333fb1e62e07, 0x8e7319ebdebbb8de, + 0x2a3982bca959fa00, 0xb2b98b9f964ba9b3, 0xf7e31014adb71951, 0xebd0fca3703acc82, + 0xec654e2a2fe6419a, 0xb326132d55a52e2c, 0x2248c57f44502978, 0x32710c2f342daf16, + 0x0517b47b5acb2bec, 0x4c7a718fca270937, 0xd69142bed0bcc541, 0xe40ebcb8ff52ce88, + 0x3e44a2dbc9f828d4, 0xc74c2f4f8f873f58, 0x3dbf648eb799e45b, 0x33f22475ee0e86f8, + 0x1eb4f9ee16d47f65, 0x40f8d2b8712744e3, 0xb886b4da3cb14572, 0x2086326fbdd6f64d, + 0xcc3de5907dd882b9, 0xa2e8b49a5ee909df, 0xdbfb8e7823964c10, 0x70dd6089ef0df8d5, + 0x30141663cdd9c99f, 0x04b805325c240365, 0x7483d80314ac12d6, 0x2b271cb91aa7f5f9, + 0x97e2245362abddf0, 0x5a84f614232a9fab, 0xf71125fcda4b7fa2, 0x1ca5a61d74b27267, + 0x38cc6a9b3adbcb45, 0xdde1bb85dc653e39, 0xe9d0c8fa64f89fd4, 0x02c5fb1ecd2b4188, + 0xf2bd137bca5756e5, 0xadefe25d121be155, 0x56cd1c3c5d893a8e, 0x4c50d337beb65bb9, + 0x918c5151675cf567, 0xaba649ffcfb56a1e, 0x20c74ab26a2247cd, 0x71166bac853c08da, + 0xb07befe2e584fc5d, 0xda45ff2a588dbf32, 0xdb98b03c4d75095e, 0x60285ae1aaa65a4c, + 0xf93b686a263140b8, 0xde469752ee1c180e, 0xcec232dc04129aae, 0xeb916baa1835ea04, + 0xd49c21c8b64388ff, 0x72a82d9658864888, 0x003348ef7eac66a8, 0x7f6f67e655b209eb, + 0x532ffb0b7a941b25, 0xd940ade6128deede, 0xdf24f2a1af89fe23, 0x95aa3b4988195ae0, + 0x3da649404f94be4a, 0x692dad132c3f7e27, 0x40aee76ecaaa9eb8, 0x1294a01e09655024, + 0x6df797abdba4e4f5, 0xea2fb6024c1d7032, 0x5f4e0492295489fc, 0x57972914ea22e06a, + 0x9a8137d133aad473, 0xa2e6dd6ae7cdf2f3, 0x9f42644f18086647, 0x16d03301c170bd3e, + 0x908c416fa546656d, 0xe081503be22e123e, 0x077cf09116c4cc72, 0xcbd25cd264b7f229, + 0x3db2f468ec594031, 0x46c00e734c9badd5, 0xd0ec0ac72075d861, 0x3037cb3cf80b7630, + 0x574c3d7b3a2721c6, 0xae99906a0076824b, 0xb175a5418b532e70, 0xd8b3e251ee231ddd, + 0xb433eec25dca1966, 0x530f30dc5cff9a93, 0x9ff03d98b53cd335, 0xafc4225076558cdf, + 0xef81d3a28284402a, 0x110bdbf51c110a28, 0x9ae1b255d027e8f6, 0x7de3e0aa24688332, + 0xe483c3ecd2067ee2, 0xf829328b276137e6, 0xa413ccad57562cad, 0xe6118e8b496acb1f, + 0x8288dca6da5ec01f, 0xa53777dc88c17255, 0x8a00f1e0d5716eda, 0x618e6f47b7a720a8, + 0x9e3907b0c692a841, 0x978b42ca963f34f3, 0x75e4b0cd98a7d7ef, 0xde4dbd6e0b5f4752, + 0x0252e4153f34493f, 0x50f0e7d803734ef9, 0x237766a38ed167ee, 0x4124414001ee39a0, + 0xd08df643e535bb21, 0x34f575b5a9a80b74, 0x2c343af87297f755, 0xcd8b6d99d821f7cb, + 0xe376fd7256fc48ae, 0xe1b06e7334352885, 0xfa87b26f86c169eb, 0x36c1604665a971de, + 0xdba147c2239c8e80, 0x6b208e69fc7f0e24, 0x8795395b6f2b60c3, 0x05dabee9194907f4, + 0xb98175142f5ed902, 0x5e1701e2021ddc81, 0x0875aba2755eed08, 0x778d83289251de95, + 0x3bfbe46a039ecb31, 0xb24704fce4cbd7f9, 0x6985ffe9a7c91e3d, 0xc8efb13df249dabb, + 0xb1037e64b0f4c9f6, 0x55f69fd197d6b7c3, 0x672589d71d68a90c, 0xbebdb8224f50a77e, + 0x3f589f80007374a7, 0xd307f4635954182a, 0xcff5850c10d4fd90, 0xc6da02dfb6408e15, + 0x93daeef1e2b1a485, 0x65d833208aeea625, 0xe2b13fa13ed3b5fa, 0x67053538130fb68e, + 0xc1042f6598218fa9, 0xee5badca749b8a2e, 0x6d22a3f947dae37d, 0xb62c6d1657f4dbaf, + 0x6e007de69704c20b, 0x1af2b913fc3841d8, 0xdc0e47348e2e8e22, 0x9b1ddef1cf958b22, + 0x632ed6b0233066b8, 0xddd02d3311bed8f2, 0xf147cfe1834656e9, 0x399aaa49d511597a, + 0x6b14886979ec0309, 0x64fc4ac36b5afb97, 0xb82f78e07f7cf081, 0x10925c9a323d0e1b, + 0xf451c79ee13c63f6, 0x7c2fc180317876c7, 0x35a12bd9eecb7d22, 0x335654a539621f90, + 0xcc32a3f35db581f0, 0xc60748a80b2369cb, 0x7c4dd3b08591156b, 0xac1ced4b6de22291, + 0xa32cfa2df134def5, 0x627108918dea2a53, 0x0555b1608fcb4ff4, 0x143ee7ac43aaa33c, + 0xdae90ce7cf4fc218, 0x4d68fc2582bcf4b5, 0x37094e1849135d71, 0xf7857e09f3d49fd8, + 0x007538c503768be7, 0xedf648ba2f6be601, 0xaa347664dd72513e, 0xbe63893c6ef23b86, + 0x130b85710605af97, 0xdd765c6b1ef6ab56, 0xf3249a629a97dc6b, 0x2a114f9020fab8e5, + 0x5a69e027cfc6ad08, 0x3c4ccb36f1a5e050, 0x2e9e7d596834f0a5, 0x2430be6858fce789, + 0xe90b862f2466e597, 0x895e2884f159a9ec, 0x26ab8fa4902fcb57, 0xa6efff5c54e1fa50, + 0x333ac4e5811a8255, 0xa58d515f02498611, 0xfe5a09dcb25c6ef4, 0x03898988ab5f5818, + 0x289ff6242af6c617, 0x3d9dd59fd381ea23, 0x52d7d93d8a8aae51, 0xc76a123d511f786f, + 0xf68901edaf00c46c, 0x8c630871b590de80, 0x05209c308991e091, 0x1f809f99b4788177, + 0x11170c2eb6c19fd8, 0x44433c779062ba58, 0xc0acb51af1874c45, 0x9f2e134284809fa1, + 0xedb523bd15c619fa, 0x02d97fd53ecc23c0, 0xacaf05a34462374c, 0xddd9c6d34bffa11f, +} diff --git a/src/internal/coverage/pkid.go b/src/internal/coverage/pkid.go index 8ddd44d6bb..372a9cb19f 100644 --- a/src/internal/coverage/pkid.go +++ b/src/internal/coverage/pkid.go @@ -49,6 +49,7 @@ var rtPkgs = [...]string{ "internal/goarch", "runtime/internal/atomic", "internal/goos", + "internal/chacha8rand", "runtime/internal/sys", "internal/abi", "runtime/internal/math", diff --git a/src/internal/cpu/cpu_s390x.s b/src/internal/cpu/cpu_s390x.s index c55a4c725d..4ffbbde38d 100644 --- a/src/internal/cpu/cpu_s390x.s +++ b/src/internal/cpu/cpu_s390x.s @@ -30,14 +30,14 @@ TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16 TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16 MOVD $0, R0 // set function code to 0 (KMCTR-Query) MOVD $ret+0(FP), R1 // address of 16-byte return value - KMCTR R6, R2, R4 // cipher message with counter (KMCTR) + KMCTR R2, R4, R4 // cipher message with counter (KMCTR) RET // func kmaQuery() queryResult TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16 MOVD $0, R0 // set function code to 0 (KMA-Query) MOVD $ret+0(FP), R1 // address of 16-byte return value - KMA R6, R2, R4 // cipher message with authentication (KMA) + KMA R2, R6, R4 // cipher message with authentication (KMA) RET // func kimdQuery() queryResult diff --git a/src/internal/godebug/godebug_test.go b/src/internal/godebug/godebug_test.go index ed8e93d453..1ed0a365ab 100644 --- a/src/internal/godebug/godebug_test.go +++ b/src/internal/godebug/godebug_test.go @@ -7,6 +7,7 @@ package godebug_test import ( "fmt" . "internal/godebug" + "internal/race" "internal/testenv" "os" "os/exec" @@ -70,6 +71,36 @@ func TestMetrics(t *testing.T) { } } +// TestPanicNilRace checks for a race in the runtime caused by use of runtime +// atomics (not visible to usual race detection) to install the counter for +// non-default panic(nil) semantics. For #64649. +func TestPanicNilRace(t *testing.T) { + if !race.Enabled { + t.Skip("Skipping test intended for use with -race.") + } + if os.Getenv("GODEBUG") != "panicnil=1" { + cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestPanicNilRace$", "-test.v", "-test.parallel=2", "-test.count=1")) + cmd.Env = append(cmd.Env, "GODEBUG=panicnil=1") + out, err := cmd.CombinedOutput() + t.Logf("output:\n%s", out) + + if err != nil { + t.Errorf("Was not expecting a crash") + } + return + } + + test := func(t *testing.T) { + t.Parallel() + defer func() { + recover() + }() + panic(nil) + } + t.Run("One", test) + t.Run("Two", test) +} + func TestCmdBisect(t *testing.T) { testenv.MustHaveGoBuild(t) out, err := exec.Command("go", "run", "cmd/vendor/golang.org/x/tools/cmd/bisect", "GODEBUG=buggy=1#PATTERN", os.Args[0], "-test.run=^TestBisectTestCase$").CombinedOutput() diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go index 92a0d089ca..a0a0672966 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go @@ -48,8 +48,10 @@ var All = []Info{ {Name: "tls10server", Package: "crypto/tls", Changed: 22, Old: "1"}, {Name: "tlsmaxrsasize", Package: "crypto/tls"}, {Name: "tlsrsakex", Package: "crypto/tls", Changed: 22, Old: "1"}, + {Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"}, {Name: "x509sha1", Package: "crypto/x509"}, {Name: "x509usefallbackroots", Package: "crypto/x509"}, + {Name: "x509usepolicies", Package: "crypto/x509"}, {Name: "zipinsecurepath", Package: "archive/zip"}, } diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go index 4589903550..82c66e2195 100644 --- a/src/internal/platform/supported.go +++ b/src/internal/platform/supported.go @@ -206,7 +206,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool { case "plugin": switch platform { - case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le", + case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le", "android/amd64", "android/386", "darwin/amd64", "darwin/arm64", "freebsd/amd64": diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go index 9df39edced..2095a6aa29 100644 --- a/src/internal/poll/fd_windows.go +++ b/src/internal/poll/fd_windows.go @@ -1037,8 +1037,7 @@ func (fd *FD) Fchmod(mode uint32) error { var du windows.FILE_BASIC_INFO du.FileAttributes = attrs - l := uint32(unsafe.Sizeof(d)) - return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, uintptr(unsafe.Pointer(&du)), l) + return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, unsafe.Pointer(&du), uint32(unsafe.Sizeof(du))) } // Fchdir wraps syscall.Fchdir. diff --git a/src/internal/profile/encode.go b/src/internal/profile/encode.go index 72d6fe2fa7..94d04bf094 100644 --- a/src/internal/profile/encode.go +++ b/src/internal/profile/encode.go @@ -207,9 +207,6 @@ var profileDecoder = []decoder{ // suffix X) and populates the corresponding exported fields. // The unexported fields are cleared up to facilitate testing. func (p *Profile) postDecode() error { - if p.Empty() { - return nil - } var err error mappings := make(map[uint64]*Mapping) diff --git a/src/internal/profile/profile.go b/src/internal/profile/profile.go index c779bb2b11..02d1bed3de 100644 --- a/src/internal/profile/profile.go +++ b/src/internal/profile/profile.go @@ -141,10 +141,14 @@ func Parse(r io.Reader) (*Profile, error) { } orig = data } - if p, err = parseUncompressed(orig); err != nil { - if p, err = parseLegacy(orig); err != nil { - return nil, fmt.Errorf("parsing profile: %v", err) - } + + var lErr error + p, pErr := parseUncompressed(orig) + if pErr != nil { + p, lErr = parseLegacy(orig) + } + if pErr != nil && lErr != nil { + return nil, fmt.Errorf("parsing profile: not a valid proto profile (%w) or legacy profile (%w)", pErr, lErr) } if err := p.CheckValid(); err != nil { @@ -155,6 +159,7 @@ func Parse(r io.Reader) (*Profile, error) { var errUnrecognized = fmt.Errorf("unrecognized profile format") var errMalformed = fmt.Errorf("malformed profile format") +var ErrNoData = fmt.Errorf("empty input file") func parseLegacy(data []byte) (*Profile, error) { parsers := []func([]byte) (*Profile, error){ @@ -180,6 +185,10 @@ func parseLegacy(data []byte) (*Profile, error) { } func parseUncompressed(data []byte) (*Profile, error) { + if len(data) == 0 { + return nil, ErrNoData + } + p := &Profile{} if err := unmarshal(data, p); err != nil { return nil, err diff --git a/src/internal/profile/profile_test.go b/src/internal/profile/profile_test.go index e1963f3351..84158b6233 100644 --- a/src/internal/profile/profile_test.go +++ b/src/internal/profile/profile_test.go @@ -5,24 +5,9 @@ package profile import ( - "bytes" "testing" ) -func TestEmptyProfile(t *testing.T) { - var buf bytes.Buffer - p, err := Parse(&buf) - if err != nil { - t.Error("Want no error, got", err) - } - if p == nil { - t.Fatal("Want a valid profile, got ") - } - if !p.Empty() { - t.Errorf("Profile should be empty, got %#v", p) - } -} - func TestParseContention(t *testing.T) { tests := []struct { name string diff --git a/src/internal/syscall/unix/at_fstatat.go b/src/internal/syscall/unix/at_fstatat.go index 8f25fe9f64..25de336a80 100644 --- a/src/internal/syscall/unix/at_fstatat.go +++ b/src/internal/syscall/unix/at_fstatat.go @@ -24,5 +24,4 @@ func Fstatat(dirfd int, path string, stat *syscall.Stat_t, flags int) error { } return nil - } diff --git a/src/internal/syscall/unix/pidfd_linux.go b/src/internal/syscall/unix/pidfd_linux.go new file mode 100644 index 0000000000..02cfaa062c --- /dev/null +++ b/src/internal/syscall/unix/pidfd_linux.go @@ -0,0 +1,15 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import "syscall" + +func PidFDSendSignal(pidfd uintptr, s syscall.Signal) error { + _, _, errno := syscall.Syscall(pidfdSendSignalTrap, pidfd, uintptr(s), 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/internal/syscall/unix/sysnum_linux_386.go b/src/internal/syscall/unix/sysnum_linux_386.go index 2bda08ccf1..9f750a1c03 100644 --- a/src/internal/syscall/unix/sysnum_linux_386.go +++ b/src/internal/syscall/unix/sysnum_linux_386.go @@ -5,6 +5,7 @@ package unix const ( - getrandomTrap uintptr = 355 - copyFileRangeTrap uintptr = 377 + getrandomTrap uintptr = 355 + copyFileRangeTrap uintptr = 377 + pidfdSendSignalTrap uintptr = 424 ) diff --git a/src/internal/syscall/unix/sysnum_linux_amd64.go b/src/internal/syscall/unix/sysnum_linux_amd64.go index ae5239ebfb..706898d41e 100644 --- a/src/internal/syscall/unix/sysnum_linux_amd64.go +++ b/src/internal/syscall/unix/sysnum_linux_amd64.go @@ -5,6 +5,7 @@ package unix const ( - getrandomTrap uintptr = 318 - copyFileRangeTrap uintptr = 326 + getrandomTrap uintptr = 318 + copyFileRangeTrap uintptr = 326 + pidfdSendSignalTrap uintptr = 424 ) diff --git a/src/internal/syscall/unix/sysnum_linux_arm.go b/src/internal/syscall/unix/sysnum_linux_arm.go index acaec05879..c00644b552 100644 --- a/src/internal/syscall/unix/sysnum_linux_arm.go +++ b/src/internal/syscall/unix/sysnum_linux_arm.go @@ -5,6 +5,7 @@ package unix const ( - getrandomTrap uintptr = 384 - copyFileRangeTrap uintptr = 391 + getrandomTrap uintptr = 384 + copyFileRangeTrap uintptr = 391 + pidfdSendSignalTrap uintptr = 424 ) diff --git a/src/internal/syscall/unix/sysnum_linux_generic.go b/src/internal/syscall/unix/sysnum_linux_generic.go index 8c132c6bf5..bf25428e7e 100644 --- a/src/internal/syscall/unix/sysnum_linux_generic.go +++ b/src/internal/syscall/unix/sysnum_linux_generic.go @@ -11,6 +11,7 @@ package unix // means only arm64 loong64 and riscv64 use the standard numbers. const ( - getrandomTrap uintptr = 278 - copyFileRangeTrap uintptr = 285 + getrandomTrap uintptr = 278 + copyFileRangeTrap uintptr = 285 + pidfdSendSignalTrap uintptr = 424 ) diff --git a/src/internal/syscall/unix/sysnum_linux_mips64x.go b/src/internal/syscall/unix/sysnum_linux_mips64x.go index bca526d2b9..6a9e238ce3 100644 --- a/src/internal/syscall/unix/sysnum_linux_mips64x.go +++ b/src/internal/syscall/unix/sysnum_linux_mips64x.go @@ -7,6 +7,7 @@ package unix const ( - getrandomTrap uintptr = 5313 - copyFileRangeTrap uintptr = 5320 + getrandomTrap uintptr = 5313 + copyFileRangeTrap uintptr = 5320 + pidfdSendSignalTrap uintptr = 5424 ) diff --git a/src/internal/syscall/unix/sysnum_linux_mipsx.go b/src/internal/syscall/unix/sysnum_linux_mipsx.go index c86195e496..22d38f148e 100644 --- a/src/internal/syscall/unix/sysnum_linux_mipsx.go +++ b/src/internal/syscall/unix/sysnum_linux_mipsx.go @@ -7,6 +7,7 @@ package unix const ( - getrandomTrap uintptr = 4353 - copyFileRangeTrap uintptr = 4360 + getrandomTrap uintptr = 4353 + copyFileRangeTrap uintptr = 4360 + pidfdSendSignalTrap uintptr = 4424 ) diff --git a/src/internal/syscall/unix/sysnum_linux_ppc64x.go b/src/internal/syscall/unix/sysnum_linux_ppc64x.go index a4dcf2bc9d..945ec28c2a 100644 --- a/src/internal/syscall/unix/sysnum_linux_ppc64x.go +++ b/src/internal/syscall/unix/sysnum_linux_ppc64x.go @@ -7,6 +7,7 @@ package unix const ( - getrandomTrap uintptr = 359 - copyFileRangeTrap uintptr = 379 + getrandomTrap uintptr = 359 + copyFileRangeTrap uintptr = 379 + pidfdSendSignalTrap uintptr = 424 ) diff --git a/src/internal/syscall/unix/sysnum_linux_s390x.go b/src/internal/syscall/unix/sysnum_linux_s390x.go index bf2c01e4e1..2c74343820 100644 --- a/src/internal/syscall/unix/sysnum_linux_s390x.go +++ b/src/internal/syscall/unix/sysnum_linux_s390x.go @@ -5,6 +5,7 @@ package unix const ( - getrandomTrap uintptr = 349 - copyFileRangeTrap uintptr = 375 + getrandomTrap uintptr = 349 + copyFileRangeTrap uintptr = 375 + pidfdSendSignalTrap uintptr = 424 ) diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go index 5854ca60b5..d10e30cb68 100644 --- a/src/internal/syscall/windows/syscall_windows.go +++ b/src/internal/syscall/windows/syscall_windows.go @@ -129,11 +129,22 @@ type SecurityAttributes struct { } type FILE_BASIC_INFO struct { - CreationTime syscall.Filetime - LastAccessTime syscall.Filetime - LastWriteTime syscall.Filetime - ChangedTime syscall.Filetime + CreationTime int64 + LastAccessTime int64 + LastWriteTime int64 + ChangedTime int64 FileAttributes uint32 + + // Pad out to 8-byte alignment. + // + // Without this padding, TestChmod fails due to an argument validation error + // in SetFileInformationByHandle on windows/386. + // + // https://learn.microsoft.com/en-us/cpp/build/reference/zp-struct-member-alignment?view=msvc-170 + // says that “The C/C++ headers in the Windows SDK assume the platform's + // default alignment is used.” What we see here is padding rather than + // alignment, but maybe it is related. + _ uint32 } const ( @@ -150,7 +161,7 @@ const ( //sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW //sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW -//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle +//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf unsafe.Pointer, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle //sys VirtualQuery(address uintptr, buffer *MemoryBasicInformation, length uintptr) (err error) = kernel32.VirtualQuery //sys GetTempPath2(buflen uint32, buf *uint16) (n uint32, err error) = GetTempPath2W diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go index 5a587ad4f1..931f157cf1 100644 --- a/src/internal/syscall/windows/zsyscall_windows.go +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -342,7 +342,7 @@ func RtlVirtualUnwind(handlerType uint32, baseAddress uintptr, pc uintptr, entry return } -func SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) { +func SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf unsafe.Pointer, bufsize uint32) (err error) { r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(handle), uintptr(fileInformationClass), uintptr(buf), uintptr(bufsize), 0, 0) if r1 == 0 { err = errnoErr(e1) diff --git a/src/internal/trace/gc.go b/src/internal/trace/gc.go index e6a23835d6..ca91969cfb 100644 --- a/src/internal/trace/gc.go +++ b/src/internal/trace/gc.go @@ -7,7 +7,6 @@ package trace import ( "container/heap" tracev2 "internal/trace/v2" - "io" "math" "sort" "strings" @@ -212,13 +211,7 @@ func MutatorUtilization(events []*Event, flags UtilFlags) [][]MutatorUtil { // // If the UtilPerProc flag is not given, this always returns a single // utilization function. Otherwise, it returns one function per P. -func MutatorUtilizationV2(trace io.Reader, flags UtilFlags) ([][]MutatorUtil, error) { - // Create a reader. - r, err := tracev2.NewReader(trace) - if err != nil { - return nil, err - } - +func MutatorUtilizationV2(events []tracev2.Event, flags UtilFlags) [][]MutatorUtil { // Set up a bunch of analysis state. type perP struct { // gc > 0 indicates that GC is active on this P. @@ -255,16 +248,9 @@ func MutatorUtilizationV2(trace io.Reader, flags UtilFlags) ([][]MutatorUtil, er } // Iterate through the trace, tracking mutator utilization. - var lastEv tracev2.Event - for { - // Read a single event. - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - return nil, err - } + var lastEv *tracev2.Event + for i := range events { + ev := &events[i] lastEv = ev // Process the event. @@ -451,8 +437,8 @@ func MutatorUtilizationV2(trace io.Reader, flags UtilFlags) ([][]MutatorUtil, er } // No events in the stream. - if lastEv.Kind() == tracev2.EventBad { - return nil, nil + if lastEv == nil { + return nil } // Add final 0 utilization event to any remaining series. This @@ -463,7 +449,7 @@ func MutatorUtilizationV2(trace io.Reader, flags UtilFlags) ([][]MutatorUtil, er for i := range ps { out[ps[i].series] = addUtil(out[ps[i].series], mu) } - return out, nil + return out } func addUtil(util []MutatorUtil, mu MutatorUtil) []MutatorUtil { diff --git a/src/internal/trace/gc_test.go b/src/internal/trace/gc_test.go index 2bdcfef006..4bbf1604f5 100644 --- a/src/internal/trace/gc_test.go +++ b/src/internal/trace/gc_test.go @@ -6,7 +6,10 @@ package trace import ( "bytes" + "internal/trace/v2" + tracev2 "internal/trace/v2" "internal/trace/v2/testtrace" + "io" "math" "os" "testing" @@ -133,12 +136,23 @@ func TestMMUTrace(t *testing.T) { if err != nil { t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } - // Pass the trace through MutatorUtilizationV2. - mu, err := MutatorUtilizationV2(r, UtilSTW|UtilBackground|UtilAssist) + var events []tracev2.Event + tr, err := trace.NewReader(r) if err != nil { - t.Fatalf("failed to compute mutator utilization or parse trace: %v", err) + t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } - check(t, mu) + for { + ev, err := tr.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) + } + events = append(events, ev) + } + // Pass the trace through MutatorUtilizationV2 and check it. + check(t, MutatorUtilizationV2(events, UtilSTW|UtilBackground|UtilAssist)) }) } diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go index 67fa60b8fb..3bbfbebab4 100644 --- a/src/internal/trace/parser.go +++ b/src/internal/trace/parser.go @@ -136,17 +136,23 @@ type rawEvent struct { sargs []string } -// readTrace does wire-format parsing and verification. -// It does not care about specific event types and argument meaning. -func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]string, err error) { +func ReadVersion(r io.Reader) (ver int, off int, err error) { // Read and validate trace header. var buf [16]byte - off, err := io.ReadFull(r, buf[:]) + off, err = io.ReadFull(r, buf[:]) if err != nil { err = fmt.Errorf("failed to read header: read %v, err %v", off, err) return } ver, err = parseHeader(buf[:]) + return +} + +// readTrace does wire-format parsing and verification. +// It does not care about specific event types and argument meaning. +func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]string, err error) { + var off int + ver, off, err = ReadVersion(r) if err != nil { return } @@ -161,6 +167,7 @@ func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]stri } // Read events. + var buf [16]byte strings = make(map[uint64]string) for { // Read event type and number of arguments (1 byte). diff --git a/src/internal/trace/goroutinesv2.go b/src/internal/trace/summary.go similarity index 69% rename from src/internal/trace/goroutinesv2.go rename to src/internal/trace/summary.go index 6b13f20425..9003385fc7 100644 --- a/src/internal/trace/goroutinesv2.go +++ b/src/internal/trace/summary.go @@ -6,11 +6,16 @@ package trace import ( tracev2 "internal/trace/v2" - "io" "sort" "time" ) +// Summary is the analysis result produced by the summarizer. +type Summary struct { + Goroutines map[tracev2.GoID]*GoroutineSummary + Tasks map[tracev2.TaskID]*UserTaskSummary +} + // GoroutineSummary contains statistics and execution details of a single goroutine. // (For v2 traces.) type GoroutineSummary struct { @@ -35,6 +40,45 @@ type GoroutineSummary struct { *goroutineSummary } +// UserTaskSummary represents a task in the trace. +type UserTaskSummary struct { + ID tracev2.TaskID + Name string + Parent *UserTaskSummary // nil if the parent is unknown. + Children []*UserTaskSummary + + // Task begin event. An EventTaskBegin event or nil. + Start *tracev2.Event + + // End end event. Normally EventTaskEnd event or nil. + End *tracev2.Event + + // Logs is a list of tracev2.EventLog events associated with the task. + Logs []*tracev2.Event + + // List of regions in the task, sorted based on the start time. + Regions []*UserRegionSummary + + // Goroutines is the set of goroutines associated with this task. + Goroutines map[tracev2.GoID]*GoroutineSummary +} + +// Complete returns true if we have complete information about the task +// from the trace: both a start and an end. +func (s *UserTaskSummary) Complete() bool { + return s.Start != nil && s.End != nil +} + +// Descendents returns a slice consisting of itself (always the first task returned), +// and the transitive closure of all of its children. +func (s *UserTaskSummary) Descendents() []*UserTaskSummary { + descendents := []*UserTaskSummary{s} + for _, child := range s.Children { + descendents = append(descendents, child.Descendents()...) + } + return descendents +} + // UserRegionSummary represents a region and goroutine execution stats // while the region was active. (For v2 traces.) type UserRegionSummary struct { @@ -58,13 +102,51 @@ type UserRegionSummary struct { // GoroutineExecStats contains statistics about a goroutine's execution // during a period of time. type GoroutineExecStats struct { + // These stats are all non-overlapping. ExecTime time.Duration SchedWaitTime time.Duration BlockTimeByReason map[string]time.Duration SyscallTime time.Duration SyscallBlockTime time.Duration - RangeTime map[string]time.Duration - TotalTime time.Duration + + // TotalTime is the duration of the goroutine's presence in the trace. + // Necessarily overlaps with other stats. + TotalTime time.Duration + + // Total time the goroutine spent in certain ranges; may overlap + // with other stats. + RangeTime map[string]time.Duration +} + +func (s GoroutineExecStats) NonOverlappingStats() map[string]time.Duration { + stats := map[string]time.Duration{ + "Execution time": s.ExecTime, + "Sched wait time": s.SchedWaitTime, + "Syscall execution time": s.SyscallTime, + "Block time (syscall)": s.SyscallBlockTime, + "Unknown time": s.UnknownTime(), + } + for reason, dt := range s.BlockTimeByReason { + stats["Block time ("+reason+")"] += dt + } + // N.B. Don't include RangeTime or TotalTime; they overlap with these other + // stats. + return stats +} + +// UnknownTime returns whatever isn't accounted for in TotalTime. +func (s GoroutineExecStats) UnknownTime() time.Duration { + sum := s.ExecTime + s.SchedWaitTime + s.SyscallTime + + s.SyscallBlockTime + for _, dt := range s.BlockTimeByReason { + sum += dt + } + // N.B. Don't include range time. Ranges overlap with + // other stats, whereas these stats are non-overlapping. + if sum < s.TotalTime { + return s.TotalTime - sum + } + return 0 } // sub returns the stats v-s. @@ -171,39 +253,14 @@ type goroutineSummary struct { activeRegions []*UserRegionSummary // stack of active regions } -// SummarizeGoroutines generates statistics for all goroutines in the trace. -func SummarizeGoroutines(trace io.Reader) (map[tracev2.GoID]*GoroutineSummary, error) { - // Create the analysis state. - b := goroutineStatsBuilder{ - gs: make(map[tracev2.GoID]*GoroutineSummary), - syscallingP: make(map[tracev2.ProcID]tracev2.GoID), - syscallingG: make(map[tracev2.GoID]tracev2.ProcID), - rangesP: make(map[rangeP]tracev2.GoID), - } - - // Process the trace. - r, err := tracev2.NewReader(trace) - if err != nil { - return nil, err - } - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - b.event(ev) - } - return b.finalize(), nil -} - -// goroutineStatsBuilder constructs per-goroutine time statistics for v2 traces. -type goroutineStatsBuilder struct { +// Summarizer constructs per-goroutine time statistics for v2 traces. +type Summarizer struct { // gs contains the map of goroutine summaries we're building up to return to the caller. gs map[tracev2.GoID]*GoroutineSummary + // tasks contains the map of task summaries we're building up to return to the caller. + tasks map[tracev2.TaskID]*UserTaskSummary + // syscallingP and syscallingG represent a binding between a P and G in a syscall. // Used to correctly identify and clean up after syscalls (blocking or otherwise). syscallingP map[tracev2.ProcID]tracev2.GoID @@ -219,22 +276,33 @@ type goroutineStatsBuilder struct { syncTs tracev2.Time // timestamp of the last sync event processed (or the first timestamp in the trace). } +// NewSummarizer creates a new struct to build goroutine stats from a trace. +func NewSummarizer() *Summarizer { + return &Summarizer{ + gs: make(map[tracev2.GoID]*GoroutineSummary), + tasks: make(map[tracev2.TaskID]*UserTaskSummary), + syscallingP: make(map[tracev2.ProcID]tracev2.GoID), + syscallingG: make(map[tracev2.GoID]tracev2.ProcID), + rangesP: make(map[rangeP]tracev2.GoID), + } +} + type rangeP struct { id tracev2.ProcID name string } -// event feeds a single event into the stats builder. -func (b *goroutineStatsBuilder) event(ev tracev2.Event) { - if b.syncTs == 0 { - b.syncTs = ev.Time() +// Event feeds a single event into the stats summarizer. +func (s *Summarizer) Event(ev *tracev2.Event) { + if s.syncTs == 0 { + s.syncTs = ev.Time() } - b.lastTs = ev.Time() + s.lastTs = ev.Time() switch ev.Kind() { // Record sync time for the RangeActive events. case tracev2.EventSync: - b.syncTs = ev.Time() + s.syncTs = ev.Time() // Handle state transitions. case tracev2.EventStateTransition: @@ -250,14 +318,14 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { } // Handle transition out. - g := b.gs[id] + g := s.gs[id] switch old { case tracev2.GoUndetermined, tracev2.GoNotExist: g = &GoroutineSummary{ID: id, goroutineSummary: &goroutineSummary{}} // If we're coming out of GoUndetermined, then the creation time is the // time of the last sync. if old == tracev2.GoUndetermined { - g.CreationTime = b.syncTs + g.CreationTime = s.syncTs } else { g.CreationTime = ev.Time() } @@ -276,14 +344,12 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { // // N.B. ev.Goroutine() will always be NoGoroutine for the // Undetermined case, so this is will simply not fire. - if creatorG := b.gs[ev.Goroutine()]; creatorG != nil && len(creatorG.activeRegions) > 0 { + if creatorG := s.gs[ev.Goroutine()]; creatorG != nil && len(creatorG.activeRegions) > 0 { regions := creatorG.activeRegions s := regions[len(regions)-1] - if s.TaskID != tracev2.NoTask { - g.activeRegions = []*UserRegionSummary{{TaskID: s.TaskID, Start: &ev}} - } + g.activeRegions = []*UserRegionSummary{{TaskID: s.TaskID, Start: ev}} } - b.gs[g.ID] = g + s.gs[g.ID] = g case tracev2.GoRunning: // Record execution time as we transition out of running g.ExecTime += ev.Time().Sub(g.lastStartTime) @@ -313,15 +379,17 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { g.lastSyscallBlockTime = 0 // Clear the syscall map. - delete(b.syscallingP, b.syscallingG[id]) - delete(b.syscallingG, id) + delete(s.syscallingP, s.syscallingG[id]) + delete(s.syscallingG, id) } } - // The goroutine hasn't been identified yet. Take any stack we - // can get and identify it by the bottom-most frame of that stack. + // The goroutine hasn't been identified yet. Take the transition stack + // and identify the goroutine by the bottom-most frame of that stack. + // This bottom-most frame will be identical for all transitions on this + // goroutine, because it represents its immutable start point. if g.PC == 0 { - stk := ev.Stack() + stk := st.Stack if stk != tracev2.NoStack { var frame tracev2.StackFrame var ok bool @@ -356,10 +424,10 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { // "Forever" is like goroutine death. fallthrough case tracev2.GoNotExist: - g.finalize(ev.Time(), &ev) + g.finalize(ev.Time(), ev) case tracev2.GoSyscall: - b.syscallingP[ev.Proc()] = id - b.syscallingG[id] = ev.Proc() + s.syscallingP[ev.Proc()] = id + s.syscallingG[id] = ev.Proc() g.lastSyscallTime = ev.Time() } @@ -369,10 +437,10 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { id := st.Resource.Proc() old, new := st.Proc() if old != new && new == tracev2.ProcIdle { - if goid, ok := b.syscallingP[id]; ok { - g := b.gs[goid] + if goid, ok := s.syscallingP[id]; ok { + g := s.gs[goid] g.lastSyscallBlockTime = ev.Time() - delete(b.syscallingP, id) + delete(s.syscallingP, id) } } } @@ -388,14 +456,14 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { // goroutine blocked often in mark assist will have both high mark assist // and high block times. Those interested in a deeper view can look at the // trace viewer. - g = b.gs[r.Scope.Goroutine()] + g = s.gs[r.Scope.Goroutine()] case tracev2.ResourceProc: // N.B. These ranges are not actually bound to the goroutine, they're // bound to the P. But if we happen to be on the P the whole time, let's // try to attribute it to the goroutine. (e.g. GC sweeps are here.) - g = b.gs[ev.Goroutine()] + g = s.gs[ev.Goroutine()] if g != nil { - b.rangesP[rangeP{id: r.Scope.Proc(), name: r.Name}] = ev.Goroutine() + s.rangesP[rangeP{id: r.Scope.Proc(), name: r.Name}] = ev.Goroutine() } } if g == nil { @@ -403,9 +471,9 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { } if ev.Kind() == tracev2.EventRangeActive { if ts := g.lastRangeTime[r.Name]; ts != 0 { - g.RangeTime[r.Name] += b.syncTs.Sub(ts) + g.RangeTime[r.Name] += s.syncTs.Sub(ts) } - g.lastRangeTime[r.Name] = b.syncTs + g.lastRangeTime[r.Name] = s.syncTs } else { g.lastRangeTime[r.Name] = ev.Time() } @@ -414,16 +482,16 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { var g *GoroutineSummary switch r.Scope.Kind { case tracev2.ResourceGoroutine: - g = b.gs[r.Scope.Goroutine()] + g = s.gs[r.Scope.Goroutine()] case tracev2.ResourceProc: rp := rangeP{id: r.Scope.Proc(), name: r.Name} - if goid, ok := b.rangesP[rp]; ok { + if goid, ok := s.rangesP[rp]; ok { if goid == ev.Goroutine() { // As the comment in the RangeBegin case states, this is only OK // if we finish on the same goroutine we started on. - g = b.gs[goid] + g = s.gs[goid] } - delete(b.rangesP, rp) + delete(s.rangesP, rp) } } if g == nil { @@ -438,16 +506,21 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { // Handle user-defined regions. case tracev2.EventRegionBegin: - g := b.gs[ev.Goroutine()] + g := s.gs[ev.Goroutine()] r := ev.Region() - g.activeRegions = append(g.activeRegions, &UserRegionSummary{ + region := &UserRegionSummary{ Name: r.Type, TaskID: r.Task, - Start: &ev, + Start: ev, GoroutineExecStats: g.snapshotStat(ev.Time()), - }) + } + g.activeRegions = append(g.activeRegions, region) + // Associate the region and current goroutine to the task. + task := s.getOrAddTask(r.Task) + task.Regions = append(task.Regions, region) + task.Goroutines[g.ID] = g case tracev2.EventRegionEnd: - g := b.gs[ev.Goroutine()] + g := s.gs[ev.Goroutine()] r := ev.Region() var sd *UserRegionSummary if regionStk := g.activeRegions; len(regionStk) > 0 { @@ -456,21 +529,63 @@ func (b *goroutineStatsBuilder) event(ev tracev2.Event) { sd = regionStk[n-1] regionStk = regionStk[:n-1] g.activeRegions = regionStk + // N.B. No need to add the region to a task; the EventRegionBegin already handled it. } else { // This is an "end" without a start. Just fabricate the region now. sd = &UserRegionSummary{Name: r.Type, TaskID: r.Task} + // Associate the region and current goroutine to the task. + task := s.getOrAddTask(r.Task) + task.Goroutines[g.ID] = g + task.Regions = append(task.Regions, sd) } sd.GoroutineExecStats = g.snapshotStat(ev.Time()).sub(sd.GoroutineExecStats) - sd.End = &ev + sd.End = ev g.Regions = append(g.Regions, sd) + + // Handle tasks and logs. + case tracev2.EventTaskBegin, tracev2.EventTaskEnd: + // Initialize the task. + t := ev.Task() + task := s.getOrAddTask(t.ID) + task.Name = t.Type + task.Goroutines[ev.Goroutine()] = s.gs[ev.Goroutine()] + if ev.Kind() == tracev2.EventTaskBegin { + task.Start = ev + } else { + task.End = ev + } + // Initialize the parent, if one exists and it hasn't been done yet. + // We need to avoid doing it twice, otherwise we could appear twice + // in the parent's Children list. + if t.Parent != tracev2.NoTask && task.Parent == nil { + parent := s.getOrAddTask(t.Parent) + task.Parent = parent + parent.Children = append(parent.Children, task) + } + case tracev2.EventLog: + log := ev.Log() + // Just add the log to the task. We'll create the task if it + // doesn't exist (it's just been mentioned now). + task := s.getOrAddTask(log.Task) + task.Goroutines[ev.Goroutine()] = s.gs[ev.Goroutine()] + task.Logs = append(task.Logs, ev) } } -// finalize indicates to the builder that we're done processing the trace. +func (s *Summarizer) getOrAddTask(id tracev2.TaskID) *UserTaskSummary { + task := s.tasks[id] + if task == nil { + task = &UserTaskSummary{ID: id, Goroutines: make(map[tracev2.GoID]*GoroutineSummary)} + s.tasks[id] = task + } + return task +} + +// Finalize indicates to the summarizer that we're done processing the trace. // It cleans up any remaining state and returns the full summary. -func (b *goroutineStatsBuilder) finalize() map[tracev2.GoID]*GoroutineSummary { - for _, g := range b.gs { - g.finalize(b.lastTs, nil) +func (s *Summarizer) Finalize() *Summary { + for _, g := range s.gs { + g.finalize(s.lastTs, nil) // Sort based on region start time. sort.Slice(g.Regions, func(i, j int) bool { @@ -486,17 +601,16 @@ func (b *goroutineStatsBuilder) finalize() map[tracev2.GoID]*GoroutineSummary { }) g.goroutineSummary = nil } - return b.gs + return &Summary{ + Goroutines: s.gs, + Tasks: s.tasks, + } } // RelatedGoroutinesV2 finds a set of goroutines related to goroutine goid for v2 traces. // The association is based on whether they have synchronized with each other in the Go // scheduler (one has unblocked another). -func RelatedGoroutinesV2(trace io.Reader, goid tracev2.GoID) (map[tracev2.GoID]struct{}, error) { - r, err := tracev2.NewReader(trace) - if err != nil { - return nil, err - } +func RelatedGoroutinesV2(events []tracev2.Event, goid tracev2.GoID) map[tracev2.GoID]struct{} { // Process all the events, looking for transitions of goroutines // out of GoWaiting. If there was an active goroutine when this // happened, then we know that active goroutine unblocked another. @@ -506,14 +620,7 @@ func RelatedGoroutinesV2(trace io.Reader, goid tracev2.GoID) (map[tracev2.GoID]s operand tracev2.GoID } var unblockEdges []unblockEdge - for { - ev, err := r.ReadEvent() - if err == io.EOF { - break - } - if err != nil { - return nil, err - } + for _, ev := range events { if ev.Goroutine() == tracev2.NoGoroutine { continue } @@ -551,5 +658,5 @@ func RelatedGoroutinesV2(trace io.Reader, goid tracev2.GoID) (map[tracev2.GoID]s } gmap = gmap1 } - return gmap, nil + return gmap } diff --git a/src/internal/trace/goroutinesv2_test.go b/src/internal/trace/summary_test.go similarity index 53% rename from src/internal/trace/goroutinesv2_test.go rename to src/internal/trace/summary_test.go index 99ec8dd8b0..862218bf10 100644 --- a/src/internal/trace/goroutinesv2_test.go +++ b/src/internal/trace/summary_test.go @@ -7,18 +7,19 @@ package trace import ( tracev2 "internal/trace/v2" "internal/trace/v2/testtrace" + "io" "testing" ) func TestSummarizeGoroutinesTrace(t *testing.T) { - summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-gc-stress.test") + summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-gc-stress.test").Goroutines var ( hasSchedWaitTime bool hasSyncBlockTime bool hasGCMarkAssistTime bool ) for _, summary := range summaries { - basicSummaryChecks(t, summary) + basicGoroutineSummaryChecks(t, summary) hasSchedWaitTime = hasSchedWaitTime || summary.SchedWaitTime > 0 if dt, ok := summary.BlockTimeByReason["sync"]; ok && dt > 0 { hasSyncBlockTime = true @@ -39,7 +40,7 @@ func TestSummarizeGoroutinesTrace(t *testing.T) { } func TestSummarizeGoroutinesRegionsTrace(t *testing.T) { - summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-annotations.test") + summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-annotations.test").Goroutines type region struct { startKind tracev2.EventKind endKind tracev2.EventKind @@ -57,7 +58,7 @@ func TestSummarizeGoroutinesRegionsTrace(t *testing.T) { "post-existing region": {tracev2.EventRegionBegin, tracev2.EventBad}, } for _, summary := range summaries { - basicSummaryChecks(t, summary) + basicGoroutineSummaryChecks(t, summary) for _, region := range summary.Regions { want, ok := wantRegions[region.Name] if !ok { @@ -72,7 +73,166 @@ func TestSummarizeGoroutinesRegionsTrace(t *testing.T) { } } -func basicSummaryChecks(t *testing.T, summary *GoroutineSummary) { +func TestSummarizeTasksTrace(t *testing.T) { + summaries := summarizeTraceTest(t, "v2/testdata/tests/go122-annotations-stress.test").Tasks + type task struct { + name string + parent *tracev2.TaskID + children []tracev2.TaskID + logs []tracev2.Log + goroutines []tracev2.GoID + } + parent := func(id tracev2.TaskID) *tracev2.TaskID { + p := new(tracev2.TaskID) + *p = id + return p + } + wantTasks := map[tracev2.TaskID]task{ + tracev2.BackgroundTask: { + // The background task (0) is never any task's parent. + logs: []tracev2.Log{ + {Task: tracev2.BackgroundTask, Category: "log", Message: "before do"}, + {Task: tracev2.BackgroundTask, Category: "log", Message: "before do"}, + }, + goroutines: []tracev2.GoID{1}, + }, + 1: { + // This started before tracing started and has no parents. + // Task 2 is technically a child, but we lost that information. + children: []tracev2.TaskID{3, 7, 16}, + logs: []tracev2.Log{ + {Task: 1, Category: "log", Message: "before do"}, + {Task: 1, Category: "log", Message: "before do"}, + }, + goroutines: []tracev2.GoID{1}, + }, + 2: { + // This started before tracing started and its parent is technically (1), but that information was lost. + children: []tracev2.TaskID{8, 17}, + logs: []tracev2.Log{ + {Task: 2, Category: "log", Message: "before do"}, + {Task: 2, Category: "log", Message: "before do"}, + }, + goroutines: []tracev2.GoID{1}, + }, + 3: { + parent: parent(1), + children: []tracev2.TaskID{10, 19}, + logs: []tracev2.Log{ + {Task: 3, Category: "log", Message: "before do"}, + {Task: 3, Category: "log", Message: "before do"}, + }, + goroutines: []tracev2.GoID{1}, + }, + 4: { + // Explicitly, no parent. + children: []tracev2.TaskID{12, 21}, + logs: []tracev2.Log{ + {Task: 4, Category: "log", Message: "before do"}, + {Task: 4, Category: "log", Message: "before do"}, + }, + goroutines: []tracev2.GoID{1}, + }, + 12: { + parent: parent(4), + children: []tracev2.TaskID{13}, + logs: []tracev2.Log{ + // TODO(mknyszek): This is computed asynchronously in the trace, + // which makes regenerating this test very annoying, since it will + // likely break this test. Resolve this by making the order not matter. + {Task: 12, Category: "log2", Message: "do"}, + {Task: 12, Category: "log", Message: "fanout region4"}, + {Task: 12, Category: "log", Message: "fanout region0"}, + {Task: 12, Category: "log", Message: "fanout region1"}, + {Task: 12, Category: "log", Message: "fanout region2"}, + {Task: 12, Category: "log", Message: "before do"}, + {Task: 12, Category: "log", Message: "fanout region3"}, + }, + goroutines: []tracev2.GoID{1, 5, 6, 7, 8, 9}, + }, + 13: { + // Explicitly, no children. + parent: parent(12), + logs: []tracev2.Log{ + {Task: 13, Category: "log2", Message: "do"}, + }, + goroutines: []tracev2.GoID{7}, + }, + } + for id, summary := range summaries { + want, ok := wantTasks[id] + if !ok { + continue + } + if id != summary.ID { + t.Errorf("ambiguous task %d (or %d?): field likely set incorrectly", id, summary.ID) + } + + // Check parent. + if want.parent != nil { + if summary.Parent == nil { + t.Errorf("expected parent %d for task %d without a parent", *want.parent, id) + } else if summary.Parent.ID != *want.parent { + t.Errorf("bad parent for task %d: want %d, got %d", id, *want.parent, summary.Parent.ID) + } + } else if summary.Parent != nil { + t.Errorf("unexpected parent %d for task %d", summary.Parent.ID, id) + } + + // Check children. + gotChildren := make(map[tracev2.TaskID]struct{}) + for _, child := range summary.Children { + gotChildren[child.ID] = struct{}{} + } + for _, wantChild := range want.children { + if _, ok := gotChildren[wantChild]; ok { + delete(gotChildren, wantChild) + } else { + t.Errorf("expected child task %d for task %d not found", wantChild, id) + } + } + if len(gotChildren) != 0 { + for child := range gotChildren { + t.Errorf("unexpected child task %d for task %d", child, id) + } + } + + // Check logs. + if len(want.logs) != len(summary.Logs) { + t.Errorf("wanted %d logs for task %d, got %d logs instead", len(want.logs), id, len(summary.Logs)) + } else { + for i := range want.logs { + if want.logs[i] != summary.Logs[i].Log() { + t.Errorf("log mismatch: want %#v, got %#v", want.logs[i], summary.Logs[i].Log()) + } + } + } + + // Check goroutines. + if len(want.goroutines) != len(summary.Goroutines) { + t.Errorf("wanted %d goroutines for task %d, got %d goroutines instead", len(want.goroutines), id, len(summary.Goroutines)) + } else { + for _, goid := range want.goroutines { + g, ok := summary.Goroutines[goid] + if !ok { + t.Errorf("want goroutine %d for task %d, not found", goid, id) + continue + } + if g.ID != goid { + t.Errorf("goroutine summary for %d does not match task %d listing of %d", g.ID, id, goid) + } + } + } + + // Marked as seen. + delete(wantTasks, id) + } + if len(wantTasks) != 0 { + t.Errorf("failed to find tasks: %#v", wantTasks) + } +} + +func basicGoroutineSummaryChecks(t *testing.T, summary *GoroutineSummary) { if summary.ID == tracev2.NoGoroutine { t.Error("summary found for no goroutine") return @@ -90,16 +250,31 @@ func basicSummaryChecks(t *testing.T, summary *GoroutineSummary) { } } -func summarizeTraceTest(t *testing.T, testPath string) map[tracev2.GoID]*GoroutineSummary { - r, _, err := testtrace.ParseFile(testPath) +func summarizeTraceTest(t *testing.T, testPath string) *Summary { + trace, _, err := testtrace.ParseFile(testPath) if err != nil { t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } - summaries, err := SummarizeGoroutines(r) + // Create the analysis state. + s := NewSummarizer() + + // Create a reader. + r, err := tracev2.NewReader(trace) if err != nil { - t.Fatalf("failed to process trace %s: %v", testPath, err) + t.Fatalf("failed to create trace reader for %s: %v", testPath, err) } - return summaries + // Process the trace. + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("failed to process trace %s: %v", testPath, err) + } + s.Event(&ev) + } + return s.Finalize() } func checkRegionEvents(t *testing.T, wantStart, wantEnd tracev2.EventKind, goid tracev2.GoID, region *UserRegionSummary) { @@ -200,15 +375,33 @@ func basicGoroutineExecStatsChecks(t *testing.T, stats *GoroutineExecStats) { func TestRelatedGoroutinesV2Trace(t *testing.T) { testPath := "v2/testdata/tests/go122-gc-stress.test" - r, _, err := testtrace.ParseFile(testPath) + trace, _, err := testtrace.ParseFile(testPath) if err != nil { t.Fatalf("malformed test %s: bad trace file: %v", testPath, err) } - targetg := tracev2.GoID(86) - got, err := RelatedGoroutinesV2(r, targetg) + + // Create a reader. + r, err := tracev2.NewReader(trace) if err != nil { - t.Fatalf("failed to find related goroutines for %s: %v", testPath, err) + t.Fatalf("failed to create trace reader for %s: %v", testPath, err) } + + // Collect all the events. + var events []tracev2.Event + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("failed to process trace %s: %v", testPath, err) + } + events = append(events, ev) + } + + // Test the function. + targetg := tracev2.GoID(86) + got := RelatedGoroutinesV2(events, targetg) want := map[tracev2.GoID]struct{}{ tracev2.GoID(86): struct{}{}, // N.B. Result includes target. tracev2.GoID(71): struct{}{}, diff --git a/src/internal/trace/traceviewer/emitter.go b/src/internal/trace/traceviewer/emitter.go new file mode 100644 index 0000000000..c91c743a7b --- /dev/null +++ b/src/internal/trace/traceviewer/emitter.go @@ -0,0 +1,813 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package traceviewer + +import ( + "encoding/json" + "fmt" + "internal/trace" + "internal/trace/traceviewer/format" + "io" + "strconv" + "time" +) + +type TraceConsumer struct { + ConsumeTimeUnit func(unit string) + ConsumeViewerEvent func(v *format.Event, required bool) + ConsumeViewerFrame func(key string, f format.Frame) + Flush func() +} + +// ViewerDataTraceConsumer returns a TraceConsumer that writes to w. The +// startIdx and endIdx are used for splitting large traces. They refer to +// indexes in the traceEvents output array, not the events in the trace input. +func ViewerDataTraceConsumer(w io.Writer, startIdx, endIdx int64) TraceConsumer { + allFrames := make(map[string]format.Frame) + requiredFrames := make(map[string]format.Frame) + enc := json.NewEncoder(w) + written := 0 + index := int64(-1) + + io.WriteString(w, "{") + return TraceConsumer{ + ConsumeTimeUnit: func(unit string) { + io.WriteString(w, `"displayTimeUnit":`) + enc.Encode(unit) + io.WriteString(w, ",") + }, + ConsumeViewerEvent: func(v *format.Event, required bool) { + index++ + if !required && (index < startIdx || index > endIdx) { + // not in the range. Skip! + return + } + WalkStackFrames(allFrames, v.Stack, func(id int) { + s := strconv.Itoa(id) + requiredFrames[s] = allFrames[s] + }) + WalkStackFrames(allFrames, v.EndStack, func(id int) { + s := strconv.Itoa(id) + requiredFrames[s] = allFrames[s] + }) + if written == 0 { + io.WriteString(w, `"traceEvents": [`) + } + if written > 0 { + io.WriteString(w, ",") + } + enc.Encode(v) + // TODO(mknyszek): get rid of the extra \n inserted by enc.Encode. + // Same should be applied to splittingTraceConsumer. + written++ + }, + ConsumeViewerFrame: func(k string, v format.Frame) { + allFrames[k] = v + }, + Flush: func() { + io.WriteString(w, `], "stackFrames":`) + enc.Encode(requiredFrames) + io.WriteString(w, `}`) + }, + } +} + +func SplittingTraceConsumer(max int) (*splitter, TraceConsumer) { + type eventSz struct { + Time float64 + Sz int + Frames []int + } + + var ( + // data.Frames contains only the frames for required events. + data = format.Data{Frames: make(map[string]format.Frame)} + + allFrames = make(map[string]format.Frame) + + sizes []eventSz + cw countingWriter + ) + + s := new(splitter) + + return s, TraceConsumer{ + ConsumeTimeUnit: func(unit string) { + data.TimeUnit = unit + }, + ConsumeViewerEvent: func(v *format.Event, required bool) { + if required { + // Store required events inside data so flush + // can include them in the required part of the + // trace. + data.Events = append(data.Events, v) + WalkStackFrames(allFrames, v.Stack, func(id int) { + s := strconv.Itoa(id) + data.Frames[s] = allFrames[s] + }) + WalkStackFrames(allFrames, v.EndStack, func(id int) { + s := strconv.Itoa(id) + data.Frames[s] = allFrames[s] + }) + return + } + enc := json.NewEncoder(&cw) + enc.Encode(v) + size := eventSz{Time: v.Time, Sz: cw.size + 1} // +1 for ",". + // Add referenced stack frames. Their size is computed + // in flush, where we can dedup across events. + WalkStackFrames(allFrames, v.Stack, func(id int) { + size.Frames = append(size.Frames, id) + }) + WalkStackFrames(allFrames, v.EndStack, func(id int) { + size.Frames = append(size.Frames, id) // This may add duplicates. We'll dedup later. + }) + sizes = append(sizes, size) + cw.size = 0 + }, + ConsumeViewerFrame: func(k string, v format.Frame) { + allFrames[k] = v + }, + Flush: func() { + // Calculate size of the mandatory part of the trace. + // This includes thread names and stack frames for + // required events. + cw.size = 0 + enc := json.NewEncoder(&cw) + enc.Encode(data) + requiredSize := cw.size + + // Then calculate size of each individual event and + // their stack frames, grouping them into ranges. We + // only include stack frames relevant to the events in + // the range to reduce overhead. + + var ( + start = 0 + + eventsSize = 0 + + frames = make(map[string]format.Frame) + framesSize = 0 + ) + for i, ev := range sizes { + eventsSize += ev.Sz + + // Add required stack frames. Note that they + // may already be in the map. + for _, id := range ev.Frames { + s := strconv.Itoa(id) + _, ok := frames[s] + if ok { + continue + } + f := allFrames[s] + frames[s] = f + framesSize += stackFrameEncodedSize(uint(id), f) + } + + total := requiredSize + framesSize + eventsSize + if total < max { + continue + } + + // Reached max size, commit this range and + // start a new range. + startTime := time.Duration(sizes[start].Time * 1000) + endTime := time.Duration(ev.Time * 1000) + s.Ranges = append(s.Ranges, Range{ + Name: fmt.Sprintf("%v-%v", startTime, endTime), + Start: start, + End: i + 1, + StartTime: int64(startTime), + EndTime: int64(endTime), + }) + start = i + 1 + frames = make(map[string]format.Frame) + framesSize = 0 + eventsSize = 0 + } + if len(s.Ranges) <= 1 { + s.Ranges = nil + return + } + + if end := len(sizes) - 1; start < end { + s.Ranges = append(s.Ranges, Range{ + Name: fmt.Sprintf("%v-%v", time.Duration(sizes[start].Time*1000), time.Duration(sizes[end].Time*1000)), + Start: start, + End: end, + StartTime: int64(sizes[start].Time * 1000), + EndTime: int64(sizes[end].Time * 1000), + }) + } + }, + } +} + +type splitter struct { + Ranges []Range +} + +type countingWriter struct { + size int +} + +func (cw *countingWriter) Write(data []byte) (int, error) { + cw.size += len(data) + return len(data), nil +} + +func stackFrameEncodedSize(id uint, f format.Frame) int { + // We want to know the marginal size of traceviewer.Data.Frames for + // each event. Running full JSON encoding of the map for each event is + // far too slow. + // + // Since the format is fixed, we can easily compute the size without + // encoding. + // + // A single entry looks like one of the following: + // + // "1":{"name":"main.main:30"}, + // "10":{"name":"pkg.NewSession:173","parent":9}, + // + // The parent is omitted if 0. The trailing comma is omitted from the + // last entry, but we don't need that much precision. + const ( + baseSize = len(`"`) + len(`":{"name":"`) + len(`"},`) + + // Don't count the trailing quote on the name, as that is + // counted in baseSize. + parentBaseSize = len(`,"parent":`) + ) + + size := baseSize + + size += len(f.Name) + + // Bytes for id (always positive). + for id > 0 { + size += 1 + id /= 10 + } + + if f.Parent > 0 { + size += parentBaseSize + // Bytes for parent (always positive). + for f.Parent > 0 { + size += 1 + f.Parent /= 10 + } + } + + return size +} + +// WalkStackFrames calls fn for id and all of its parent frames from allFrames. +func WalkStackFrames(allFrames map[string]format.Frame, id int, fn func(id int)) { + for id != 0 { + f, ok := allFrames[strconv.Itoa(id)] + if !ok { + break + } + fn(id) + id = f.Parent + } +} + +type Mode int + +const ( + ModeGoroutineOriented Mode = 1 << iota + ModeTaskOriented + ModeThreadOriented // Mutually exclusive with ModeGoroutineOriented. +) + +// NewEmitter returns a new Emitter that writes to c. The rangeStart and +// rangeEnd args are used for splitting large traces. +func NewEmitter(c TraceConsumer, rangeStart, rangeEnd time.Duration) *Emitter { + c.ConsumeTimeUnit("ns") + + return &Emitter{ + c: c, + rangeStart: rangeStart, + rangeEnd: rangeEnd, + frameTree: frameNode{children: make(map[uint64]frameNode)}, + resources: make(map[uint64]string), + tasks: make(map[uint64]task), + } +} + +type Emitter struct { + c TraceConsumer + rangeStart time.Duration + rangeEnd time.Duration + + heapStats, prevHeapStats heapStats + gstates, prevGstates [gStateCount]int64 + threadStats, prevThreadStats [threadStateCount]int64 + gomaxprocs uint64 + frameTree frameNode + frameSeq int + arrowSeq uint64 + filter func(uint64) bool + resourceType string + resources map[uint64]string + focusResource uint64 + tasks map[uint64]task + asyncSliceSeq uint64 +} + +type task struct { + name string + sortIndex int +} + +func (e *Emitter) Gomaxprocs(v uint64) { + if v > e.gomaxprocs { + e.gomaxprocs = v + } +} + +func (e *Emitter) Resource(id uint64, name string) { + if e.filter != nil && !e.filter(id) { + return + } + e.resources[id] = name +} + +func (e *Emitter) SetResourceType(name string) { + e.resourceType = name +} + +func (e *Emitter) SetResourceFilter(filter func(uint64) bool) { + e.filter = filter +} + +func (e *Emitter) Task(id uint64, name string, sortIndex int) { + e.tasks[id] = task{name, sortIndex} +} + +func (e *Emitter) Slice(s SliceEvent) { + if e.filter != nil && !e.filter(s.Resource) { + return + } + e.slice(s, format.ProcsSection, "") +} + +func (e *Emitter) TaskSlice(s SliceEvent) { + e.slice(s, format.TasksSection, pickTaskColor(s.Resource)) +} + +func (e *Emitter) slice(s SliceEvent, sectionID uint64, cname string) { + if !e.tsWithinRange(s.Ts) && !e.tsWithinRange(s.Ts+s.Dur) { + return + } + e.OptionalEvent(&format.Event{ + Name: s.Name, + Phase: "X", + Time: viewerTime(s.Ts), + Dur: viewerTime(s.Dur), + PID: sectionID, + TID: s.Resource, + Stack: s.Stack, + EndStack: s.EndStack, + Arg: s.Arg, + Cname: cname, + }) +} + +type SliceEvent struct { + Name string + Ts time.Duration + Dur time.Duration + Resource uint64 + Stack int + EndStack int + Arg any +} + +func (e *Emitter) AsyncSlice(s AsyncSliceEvent) { + if !e.tsWithinRange(s.Ts) && !e.tsWithinRange(s.Ts+s.Dur) { + return + } + if e.filter != nil && !e.filter(s.Resource) { + return + } + cname := "" + if s.TaskColorIndex != 0 { + cname = pickTaskColor(s.TaskColorIndex) + } + e.asyncSliceSeq++ + e.OptionalEvent(&format.Event{ + Category: s.Category, + Name: s.Name, + Phase: "b", + Time: viewerTime(s.Ts), + TID: s.Resource, + ID: e.asyncSliceSeq, + Scope: s.Scope, + Stack: s.Stack, + Cname: cname, + }) + e.OptionalEvent(&format.Event{ + Category: s.Category, + Name: s.Name, + Phase: "e", + Time: viewerTime(s.Ts + s.Dur), + TID: s.Resource, + ID: e.asyncSliceSeq, + Scope: s.Scope, + Stack: s.EndStack, + Arg: s.Arg, + Cname: cname, + }) +} + +type AsyncSliceEvent struct { + SliceEvent + Category string + Scope string + TaskColorIndex uint64 // Take on the same color as the task with this ID. +} + +func (e *Emitter) Instant(i InstantEvent) { + if !e.tsWithinRange(i.Ts) { + return + } + if e.filter != nil && !e.filter(i.Resource) { + return + } + cname := "" + e.OptionalEvent(&format.Event{ + Name: i.Name, + Category: i.Category, + Phase: "I", + Scope: "t", + Time: viewerTime(i.Ts), + PID: format.ProcsSection, + TID: i.Resource, + Stack: i.Stack, + Cname: cname, + Arg: i.Arg, + }) +} + +type InstantEvent struct { + Ts time.Duration + Name string + Category string + Resource uint64 + Stack int + Arg any +} + +func (e *Emitter) Arrow(a ArrowEvent) { + if e.filter != nil && (!e.filter(a.FromResource) || !e.filter(a.ToResource)) { + return + } + e.arrow(a, format.ProcsSection) +} + +func (e *Emitter) TaskArrow(a ArrowEvent) { + e.arrow(a, format.TasksSection) +} + +func (e *Emitter) arrow(a ArrowEvent, sectionID uint64) { + if !e.tsWithinRange(a.Start) || !e.tsWithinRange(a.End) { + return + } + e.arrowSeq++ + e.OptionalEvent(&format.Event{ + Name: a.Name, + Phase: "s", + TID: a.FromResource, + PID: sectionID, + ID: e.arrowSeq, + Time: viewerTime(a.Start), + Stack: a.FromStack, + }) + e.OptionalEvent(&format.Event{ + Name: a.Name, + Phase: "t", + TID: a.ToResource, + PID: sectionID, + ID: e.arrowSeq, + Time: viewerTime(a.End), + }) +} + +type ArrowEvent struct { + Name string + Start time.Duration + End time.Duration + FromResource uint64 + FromStack int + ToResource uint64 +} + +func (e *Emitter) Event(ev *format.Event) { + e.c.ConsumeViewerEvent(ev, true) +} + +func (e *Emitter) HeapAlloc(ts time.Duration, v uint64) { + e.heapStats.heapAlloc = v + e.emitHeapCounters(ts) +} + +func (e *Emitter) Focus(id uint64) { + e.focusResource = id +} + +func (e *Emitter) GoroutineTransition(ts time.Duration, from, to GState) { + e.gstates[from]-- + e.gstates[to]++ + if e.prevGstates == e.gstates { + return + } + if e.tsWithinRange(ts) { + e.OptionalEvent(&format.Event{ + Name: "Goroutines", + Phase: "C", + Time: viewerTime(ts), + PID: 1, + Arg: &format.GoroutineCountersArg{ + Running: uint64(e.gstates[GRunning]), + Runnable: uint64(e.gstates[GRunnable]), + GCWaiting: uint64(e.gstates[GWaitingGC]), + }, + }) + } + e.prevGstates = e.gstates +} + +func (e *Emitter) IncThreadStateCount(ts time.Duration, state ThreadState, delta int64) { + e.threadStats[state] += delta + if e.prevThreadStats == e.threadStats { + return + } + if e.tsWithinRange(ts) { + e.OptionalEvent(&format.Event{ + Name: "Threads", + Phase: "C", + Time: viewerTime(ts), + PID: 1, + Arg: &format.ThreadCountersArg{ + Running: int64(e.threadStats[ThreadStateRunning]), + InSyscall: int64(e.threadStats[ThreadStateInSyscall]), + // TODO(mknyszek): Why is InSyscallRuntime not included here? + }, + }) + } + e.prevThreadStats = e.threadStats +} + +func (e *Emitter) HeapGoal(ts time.Duration, v uint64) { + // This cutoff at 1 PiB is a Workaround for https://github.com/golang/go/issues/63864. + // + // TODO(mknyszek): Remove this once the problem has been fixed. + const PB = 1 << 50 + if v > PB { + v = 0 + } + e.heapStats.nextGC = v + e.emitHeapCounters(ts) +} + +func (e *Emitter) emitHeapCounters(ts time.Duration) { + if e.prevHeapStats == e.heapStats { + return + } + diff := uint64(0) + if e.heapStats.nextGC > e.heapStats.heapAlloc { + diff = e.heapStats.nextGC - e.heapStats.heapAlloc + } + if e.tsWithinRange(ts) { + e.OptionalEvent(&format.Event{ + Name: "Heap", + Phase: "C", + Time: viewerTime(ts), + PID: 1, + Arg: &format.HeapCountersArg{Allocated: e.heapStats.heapAlloc, NextGC: diff}, + }) + } + e.prevHeapStats = e.heapStats +} + +// Err returns an error if the emitter is in an invalid state. +func (e *Emitter) Err() error { + if e.gstates[GRunnable] < 0 || e.gstates[GRunning] < 0 || e.threadStats[ThreadStateInSyscall] < 0 || e.threadStats[ThreadStateInSyscallRuntime] < 0 { + return fmt.Errorf( + "runnable=%d running=%d insyscall=%d insyscallRuntime=%d", + e.gstates[GRunnable], + e.gstates[GRunning], + e.threadStats[ThreadStateInSyscall], + e.threadStats[ThreadStateInSyscallRuntime], + ) + } + return nil +} + +func (e *Emitter) tsWithinRange(ts time.Duration) bool { + return e.rangeStart <= ts && ts <= e.rangeEnd +} + +// OptionalEvent emits ev if it's within the time range of of the consumer, i.e. +// the selected trace split range. +func (e *Emitter) OptionalEvent(ev *format.Event) { + e.c.ConsumeViewerEvent(ev, false) +} + +func (e *Emitter) Flush() { + e.processMeta(format.StatsSection, "STATS", 0) + + if len(e.tasks) != 0 { + e.processMeta(format.TasksSection, "TASKS", 1) + } + for id, task := range e.tasks { + e.threadMeta(format.TasksSection, id, task.name, task.sortIndex) + } + + e.processMeta(format.ProcsSection, e.resourceType, 2) + + e.threadMeta(format.ProcsSection, trace.GCP, "GC", -6) + e.threadMeta(format.ProcsSection, trace.NetpollP, "Network", -5) + e.threadMeta(format.ProcsSection, trace.TimerP, "Timers", -4) + e.threadMeta(format.ProcsSection, trace.SyscallP, "Syscalls", -3) + + for id, name := range e.resources { + priority := int(id) + if e.focusResource != 0 && id == e.focusResource { + // Put the focus goroutine on top. + priority = -2 + } + e.threadMeta(format.ProcsSection, id, name, priority) + } + + e.c.Flush() +} + +func (e *Emitter) threadMeta(sectionID, tid uint64, name string, priority int) { + e.Event(&format.Event{ + Name: "thread_name", + Phase: "M", + PID: sectionID, + TID: tid, + Arg: &format.NameArg{Name: name}, + }) + e.Event(&format.Event{ + Name: "thread_sort_index", + Phase: "M", + PID: sectionID, + TID: tid, + Arg: &format.SortIndexArg{Index: priority}, + }) +} + +func (e *Emitter) processMeta(sectionID uint64, name string, priority int) { + e.Event(&format.Event{ + Name: "process_name", + Phase: "M", + PID: sectionID, + Arg: &format.NameArg{Name: name}, + }) + e.Event(&format.Event{ + Name: "process_sort_index", + Phase: "M", + PID: sectionID, + Arg: &format.SortIndexArg{Index: priority}, + }) +} + +// Stack emits the given frames and returns a unique id for the stack. No +// pointers to the given data are being retained beyond the call to Stack. +func (e *Emitter) Stack(stk []*trace.Frame) int { + return e.buildBranch(e.frameTree, stk) +} + +// buildBranch builds one branch in the prefix tree rooted at ctx.frameTree. +func (e *Emitter) buildBranch(parent frameNode, stk []*trace.Frame) int { + if len(stk) == 0 { + return parent.id + } + last := len(stk) - 1 + frame := stk[last] + stk = stk[:last] + + node, ok := parent.children[frame.PC] + if !ok { + e.frameSeq++ + node.id = e.frameSeq + node.children = make(map[uint64]frameNode) + parent.children[frame.PC] = node + e.c.ConsumeViewerFrame(strconv.Itoa(node.id), format.Frame{Name: fmt.Sprintf("%v:%v", frame.Fn, frame.Line), Parent: parent.id}) + } + return e.buildBranch(node, stk) +} + +type heapStats struct { + heapAlloc uint64 + nextGC uint64 +} + +func viewerTime(t time.Duration) float64 { + return float64(t) / float64(time.Microsecond) +} + +type GState int + +const ( + GDead GState = iota + GRunnable + GRunning + GWaiting + GWaitingGC + + gStateCount +) + +type ThreadState int + +const ( + ThreadStateInSyscall ThreadState = iota + ThreadStateInSyscallRuntime + ThreadStateRunning + + threadStateCount +) + +type frameNode struct { + id int + children map[uint64]frameNode +} + +// Mapping from more reasonable color names to the reserved color names in +// https://github.com/catapult-project/catapult/blob/master/tracing/tracing/base/color_scheme.html#L50 +// The chrome trace viewer allows only those as cname values. +const ( + colorLightMauve = "thread_state_uninterruptible" // 182, 125, 143 + colorOrange = "thread_state_iowait" // 255, 140, 0 + colorSeafoamGreen = "thread_state_running" // 126, 200, 148 + colorVistaBlue = "thread_state_runnable" // 133, 160, 210 + colorTan = "thread_state_unknown" // 199, 155, 125 + colorIrisBlue = "background_memory_dump" // 0, 180, 180 + colorMidnightBlue = "light_memory_dump" // 0, 0, 180 + colorDeepMagenta = "detailed_memory_dump" // 180, 0, 180 + colorBlue = "vsync_highlight_color" // 0, 0, 255 + colorGrey = "generic_work" // 125, 125, 125 + colorGreen = "good" // 0, 125, 0 + colorDarkGoldenrod = "bad" // 180, 125, 0 + colorPeach = "terrible" // 180, 0, 0 + colorBlack = "black" // 0, 0, 0 + colorLightGrey = "grey" // 221, 221, 221 + colorWhite = "white" // 255, 255, 255 + colorYellow = "yellow" // 255, 255, 0 + colorOlive = "olive" // 100, 100, 0 + colorCornflowerBlue = "rail_response" // 67, 135, 253 + colorSunsetOrange = "rail_animation" // 244, 74, 63 + colorTangerine = "rail_idle" // 238, 142, 0 + colorShamrockGreen = "rail_load" // 13, 168, 97 + colorGreenishYellow = "startup" // 230, 230, 0 + colorDarkGrey = "heap_dump_stack_frame" // 128, 128, 128 + colorTawny = "heap_dump_child_node_arrow" // 204, 102, 0 + colorLemon = "cq_build_running" // 255, 255, 119 + colorLime = "cq_build_passed" // 153, 238, 102 + colorPink = "cq_build_failed" // 238, 136, 136 + colorSilver = "cq_build_abandoned" // 187, 187, 187 + colorManzGreen = "cq_build_attempt_runnig" // 222, 222, 75 + colorKellyGreen = "cq_build_attempt_passed" // 108, 218, 35 + colorAnotherGrey = "cq_build_attempt_failed" // 187, 187, 187 +) + +var colorForTask = []string{ + colorLightMauve, + colorOrange, + colorSeafoamGreen, + colorVistaBlue, + colorTan, + colorMidnightBlue, + colorIrisBlue, + colorDeepMagenta, + colorGreen, + colorDarkGoldenrod, + colorPeach, + colorOlive, + colorCornflowerBlue, + colorSunsetOrange, + colorTangerine, + colorShamrockGreen, + colorTawny, + colorLemon, + colorLime, + colorPink, + colorSilver, + colorManzGreen, + colorKellyGreen, +} + +func pickTaskColor(id uint64) string { + idx := id % uint64(len(colorForTask)) + return colorForTask[idx] +} diff --git a/src/cmd/internal/traceviewer/format.go b/src/internal/trace/traceviewer/format/format.go similarity index 60% rename from src/cmd/internal/traceviewer/format.go rename to src/internal/trace/traceviewer/format/format.go index 3636c1053d..83f3276704 100644 --- a/src/cmd/internal/traceviewer/format.go +++ b/src/internal/trace/traceviewer/format/format.go @@ -7,7 +7,10 @@ // // The official description of the format is in this file: // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview -package traceviewer +// +// Note: This can't be part of the parent traceviewer package as that would +// throw. go_bootstrap cannot depend on the cgo version of package net in ./make.bash. +package format type Data struct { Events []*Event `json:"traceEvents"` @@ -36,3 +39,41 @@ type Frame struct { Name string `json:"name"` Parent int `json:"parent,omitempty"` } + +type NameArg struct { + Name string `json:"name"` +} + +type BlockedArg struct { + Blocked string `json:"blocked"` +} + +type SortIndexArg struct { + Index int `json:"sort_index"` +} + +type HeapCountersArg struct { + Allocated uint64 + NextGC uint64 +} + +const ( + ProcsSection = 0 // where Goroutines or per-P timelines are presented. + StatsSection = 1 // where counters are presented. + TasksSection = 2 // where Task hierarchy & timeline is presented. +) + +type GoroutineCountersArg struct { + Running uint64 + Runnable uint64 + GCWaiting uint64 +} + +type ThreadCountersArg struct { + Running int64 + InSyscall int64 +} + +type ThreadIDArg struct { + ThreadID uint64 +} diff --git a/src/internal/trace/traceviewer/histogram.go b/src/internal/trace/traceviewer/histogram.go new file mode 100644 index 0000000000..d4c8749dc9 --- /dev/null +++ b/src/internal/trace/traceviewer/histogram.go @@ -0,0 +1,86 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package traceviewer + +import ( + "fmt" + "html/template" + "math" + "strings" + "time" +) + +// TimeHistogram is an high-dynamic-range histogram for durations. +type TimeHistogram struct { + Count int + Buckets []int + MinBucket, MaxBucket int +} + +// Five buckets for every power of 10. +var logDiv = math.Log(math.Pow(10, 1.0/5)) + +// Add adds a single sample to the histogram. +func (h *TimeHistogram) Add(d time.Duration) { + var bucket int + if d > 0 { + bucket = int(math.Log(float64(d)) / logDiv) + } + if len(h.Buckets) <= bucket { + h.Buckets = append(h.Buckets, make([]int, bucket-len(h.Buckets)+1)...) + h.Buckets = h.Buckets[:cap(h.Buckets)] + } + h.Buckets[bucket]++ + if bucket < h.MinBucket || h.MaxBucket == 0 { + h.MinBucket = bucket + } + if bucket > h.MaxBucket { + h.MaxBucket = bucket + } + h.Count++ +} + +// BucketMin returns the minimum duration value for a provided bucket. +func (h *TimeHistogram) BucketMin(bucket int) time.Duration { + return time.Duration(math.Exp(float64(bucket) * logDiv)) +} + +// ToHTML renders the histogram as HTML. +func (h *TimeHistogram) ToHTML(urlmaker func(min, max time.Duration) string) template.HTML { + if h == nil || h.Count == 0 { + return template.HTML("") + } + + const barWidth = 400 + + maxCount := 0 + for _, count := range h.Buckets { + if count > maxCount { + maxCount = count + } + } + + w := new(strings.Builder) + fmt.Fprintf(w, `
    WhenElapsedGoroutineEvents
    {{$el.WhenString}}{{$el.Duration}} + Task {{$el.ID}} + (goroutine view) + ({{if .Complete}}complete{{else}}incomplete{{end}}) +
    {{.WhenString}}{{elapsed .Elapsed}}{{.Goroutine}}{{.What}}
    `) + for i := h.MinBucket; i <= h.MaxBucket; i++ { + // Tick label. + if h.Buckets[i] > 0 { + fmt.Fprintf(w, ``, urlmaker(h.BucketMin(i), h.BucketMin(i+1)), h.BucketMin(i)) + } else { + fmt.Fprintf(w, ``, h.BucketMin(i)) + } + // Bucket bar. + width := h.Buckets[i] * barWidth / maxCount + fmt.Fprintf(w, ``, width) + // Bucket count. + fmt.Fprintf(w, ``, h.Buckets[i]) + fmt.Fprintf(w, "\n") + + } + // Final tick label. + fmt.Fprintf(w, ``, h.BucketMin(h.MaxBucket+1)) + fmt.Fprintf(w, `
    %s
    %s
     
    %d
    %s
    `) + return template.HTML(w.String()) +} diff --git a/src/internal/trace/traceviewer/http.go b/src/internal/trace/traceviewer/http.go new file mode 100644 index 0000000000..5258db05d8 --- /dev/null +++ b/src/internal/trace/traceviewer/http.go @@ -0,0 +1,422 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package traceviewer + +import ( + "embed" + "fmt" + "html/template" + "net/http" + "strings" +) + +func MainHandler(views []View) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + if err := templMain.Execute(w, views); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + }) +} + +const CommonStyle = ` +/* See https://github.com/golang/pkgsite/blob/master/static/shared/typography/typography.css */ +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; + font-size: 1rem; + line-height: normal; + max-width: 9in; + margin: 1em; +} +h1 { font-size: 1.5rem; } +h2 { font-size: 1.375rem; } +h1,h2 { + font-weight: 600; + line-height: 1.25em; + word-break: break-word; +} +p { color: grey85; font-size:85%; } +code, +pre, +textarea.code { + font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; + font-size: 0.875rem; + line-height: 1.5em; +} + +pre, +textarea.code { + background-color: var(--color-background-accented); + border: var(--border); + border-radius: var(--border-radius); + color: var(--color-text); + overflow-x: auto; + padding: 0.625rem; + tab-size: 4; + white-space: pre; +} +` + +var templMain = template.Must(template.New("").Parse(` + + + +

    cmd/trace: the Go trace event viewer

    +

    + This web server provides various visualizations of an event log gathered during + the execution of a Go program that uses the runtime/trace package. +

    + +

    Event timelines for running goroutines

    +{{range $i, $view := $}} +{{if $view.Ranges}} +{{if eq $i 0}} +

    + Large traces are split into multiple sections of equal data size + (not duration) to avoid overwhelming the visualizer. +

    +{{end}} + +{{else}} + +{{end}} +{{end}} +

    + This view displays a series of timelines for a type of resource. + The "by proc" view consists of a timeline for each of the GOMAXPROCS + logical processors, showing which goroutine (if any) was running on that + logical processor at each moment. + The "by thread" view (if available) consists of a similar timeline for each + OS thread. + + Each goroutine has an identifying number (e.g. G123), main function, + and color. + + A colored bar represents an uninterrupted span of execution. + + Execution of a goroutine may migrate from one logical processor to another, + causing a single colored bar to be horizontally continuous but + vertically displaced. +

    +

    + Clicking on a span reveals information about it, such as its + duration, its causal predecessors and successors, and the stack trace + at the final moment when it yielded the logical processor, for example + because it made a system call or tried to acquire a mutex. + + Directly underneath each bar, a smaller bar or more commonly a fine + vertical line indicates an event occurring during its execution. + Some of these are related to garbage collection; most indicate that + a goroutine yielded its logical processor but then immediately resumed execution + on the same logical processor. Clicking on the event displays the stack trace + at the moment it occurred. +

    +

    + The causal relationships between spans of goroutine execution + can be displayed by clicking the Flow Events button at the top. +

    +

    + At the top ("STATS"), there are three additional timelines that + display statistical information. + + "Goroutines" is a time series of the count of existing goroutines; + clicking on it displays their breakdown by state at that moment: + running, runnable, or waiting. + + "Heap" is a time series of the amount of heap memory allocated (in orange) + and (in green) the allocation limit at which the next GC cycle will begin. + + "Threads" shows the number of kernel threads in existence: there is + always one kernel thread per logical processor, and additional threads + are created for calls to non-Go code such as a system call or a + function written in C. +

    +

    + Above the event trace for the first logical processor are + traces for various runtime-internal events. + + The "GC" bar shows when the garbage collector is running, and in which stage. + Garbage collection may temporarily affect all the logical processors + and the other metrics. + + The "Network", "Timers", and "Syscalls" traces indicate events in + the runtime that cause goroutines to wake up. +

    +

    + The visualization allows you to navigate events at scales ranging from several + seconds to a handful of nanoseconds. + + Consult the documentation for the Chromium Trace Event Profiling Tool + for help navigating the view. +

    + + +

    + This view displays information about each set of goroutines that + shares the same main function. + + Clicking on a main function shows links to the four types of + blocking profile (see below) applied to that subset of goroutines. + + It also shows a table of specific goroutine instances, with various + execution statistics and a link to the event timeline for each one. + + The timeline displays only the selected goroutine and any others it + interacts with via block/unblock events. (The timeline is + goroutine-oriented rather than logical processor-oriented.) +

    + +

    Profiles

    +

    + Each link below displays a global profile in zoomable graph form as + produced by pprof's "web" command. + + In addition there is a link to download the profile for offline + analysis with pprof. + + All four profiles represent causes of delay that prevent a goroutine + from running on a logical processor: because it was waiting for the network, + for a synchronization operation on a mutex or channel, for a system call, + or for a logical processor to become available. +

    + + +

    User-defined tasks and regions

    +

    + The trace API allows a target program to annotate a region of code + within a goroutine, such as a key function, so that its performance + can be analyzed. + + Log events may be + associated with a region to record progress and relevant values. + + The API also allows annotation of higher-level + tasks, + which may involve work across many goroutines. +

    +

    + The links below display, for each region and task, a histogram of its execution times. + + Each histogram bucket contains a sample trace that records the + sequence of events such as goroutine creations, log events, and + subregion start/end times. + + For each task, you can click through to a logical-processor or + goroutine-oriented view showing the tasks and regions on the + timeline. + + Such information may help uncover which steps in a region are + unexpectedly slow, or reveal relationships between the data values + logged in a request and its running time. +

    + + +

    Garbage collection metrics

    + +

    + This chart indicates the maximum GC pause time (the largest x value + for which y is zero), and more generally, the fraction of time that + the processors are available to application goroutines ("mutators"), + for any time window of a specified size, in the worst case. +

    + + +`)) + +type View struct { + Type ViewType + Ranges []Range +} + +type ViewType string + +const ( + ViewProc ViewType = "proc" + ViewThread ViewType = "thread" +) + +func (v View) URL(rangeIdx int) string { + if rangeIdx < 0 { + return fmt.Sprintf("/trace?view=%s", v.Type) + } + return v.Ranges[rangeIdx].URL(v.Type) +} + +type Range struct { + Name string + Start int + End int + StartTime int64 + EndTime int64 +} + +func (r Range) URL(viewType ViewType) string { + return fmt.Sprintf("/trace?view=%s&start=%d&end=%d", viewType, r.Start, r.End) +} + +func TraceHandler() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + html := strings.ReplaceAll(templTrace, "{{PARAMS}}", r.Form.Encode()) + w.Write([]byte(html)) + }) +} + +// https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962/tracing/docs/embedding-trace-viewer.md +// This is almost verbatim copy of https://chromium-review.googlesource.com/c/catapult/+/2062938/2/tracing/bin/index.html +var templTrace = ` + + + + + + + + + + + + + +` + +//go:embed static/trace_viewer_full.html static/webcomponents.min.js +var staticContent embed.FS + +func StaticHandler() http.Handler { + return http.FileServer(http.FS(staticContent)) +} diff --git a/src/cmd/trace/mmu.go b/src/internal/trace/traceviewer/mmu.go similarity index 83% rename from src/cmd/trace/mmu.go rename to src/internal/trace/traceviewer/mmu.go index b71dcd6411..0bc1233b44 100644 --- a/src/cmd/trace/mmu.go +++ b/src/internal/trace/traceviewer/mmu.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Go Authors. All rights reserved. +// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -23,7 +23,7 @@ // could potentially put confidence intervals on these estimates and // render this progressively as we refine the distributions. -package main +package traceviewer import ( "encoding/json" @@ -38,10 +38,25 @@ import ( "time" ) -func init() { - http.HandleFunc("/mmu", httpMMU) - http.HandleFunc("/mmuPlot", httpMMUPlot) - http.HandleFunc("/mmuDetails", httpMMUDetails) +type MutatorUtilFunc func(trace.UtilFlags) ([][]trace.MutatorUtil, error) + +func MMUHandlerFunc(ranges []Range, f MutatorUtilFunc) http.HandlerFunc { + mmu := &mmu{ + cache: make(map[trace.UtilFlags]*mmuCacheEntry), + f: f, + ranges: ranges, + } + return func(w http.ResponseWriter, r *http.Request) { + switch r.FormValue("mode") { + case "plot": + mmu.HandlePlot(w, r) + return + case "details": + mmu.HandleDetails(w, r) + return + } + http.ServeContent(w, r, "", time.Time{}, strings.NewReader(templMMU)) + } } var utilFlagNames = map[string]trace.UtilFlags{ @@ -52,6 +67,14 @@ var utilFlagNames = map[string]trace.UtilFlags{ "sweep": trace.UtilSweep, } +func requestUtilFlags(r *http.Request) trace.UtilFlags { + var flags trace.UtilFlags + for _, flagStr := range strings.Split(r.FormValue("flags"), "|") { + flags |= utilFlagNames[flagStr] + } + return flags +} + type mmuCacheEntry struct { init sync.Once util [][]trace.MutatorUtil @@ -59,51 +82,39 @@ type mmuCacheEntry struct { err error } -var mmuCache struct { - m map[trace.UtilFlags]*mmuCacheEntry - lock sync.Mutex +type mmu struct { + mu sync.Mutex + cache map[trace.UtilFlags]*mmuCacheEntry + f MutatorUtilFunc + ranges []Range } -func init() { - mmuCache.m = make(map[trace.UtilFlags]*mmuCacheEntry) -} - -func getMMUCurve(r *http.Request) ([][]trace.MutatorUtil, *trace.MMUCurve, error) { - var flags trace.UtilFlags - for _, flagStr := range strings.Split(r.FormValue("flags"), "|") { - flags |= utilFlagNames[flagStr] +func (m *mmu) get(flags trace.UtilFlags) ([][]trace.MutatorUtil, *trace.MMUCurve, error) { + m.mu.Lock() + entry := m.cache[flags] + if entry == nil { + entry = new(mmuCacheEntry) + m.cache[flags] = entry } + m.mu.Unlock() - mmuCache.lock.Lock() - c := mmuCache.m[flags] - if c == nil { - c = new(mmuCacheEntry) - mmuCache.m[flags] = c - } - mmuCache.lock.Unlock() - - c.init.Do(func() { - events, err := parseEvents() + entry.init.Do(func() { + util, err := m.f(flags) if err != nil { - c.err = err + entry.err = err } else { - c.util = trace.MutatorUtilization(events, flags) - c.mmuCurve = trace.NewMMUCurve(c.util) + entry.util = util + entry.mmuCurve = trace.NewMMUCurve(util) } }) - return c.util, c.mmuCurve, c.err + return entry.util, entry.mmuCurve, entry.err } -// httpMMU serves the MMU plot page. -func httpMMU(w http.ResponseWriter, r *http.Request) { - http.ServeContent(w, r, "", time.Time{}, strings.NewReader(templMMU)) -} - -// httpMMUPlot serves the JSON data for the MMU plot. -func httpMMUPlot(w http.ResponseWriter, r *http.Request) { - mu, mmuCurve, err := getMMUCurve(r) +// HandlePlot serves the JSON data for the MMU plot. +func (m *mmu) HandlePlot(w http.ResponseWriter, r *http.Request) { + mu, mmuCurve, err := m.get(requestUtilFlags(r)) if err != nil { - http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("failed to produce MMU data: %v", err), http.StatusInternalServerError) return } @@ -202,7 +213,7 @@ var templMMU = ` container.css('opacity', '.5'); refreshChart.count++; var seq = refreshChart.count; - $.getJSON('/mmuPlot?flags=' + mmuFlags()) + $.getJSON('?mode=plot&flags=' + mmuFlags()) .fail(function(xhr, status, error) { alert('failed to load plot: ' + status); }) @@ -275,7 +286,7 @@ var templMMU = ` var details = $('#details'); details.empty(); var windowNS = curve[items[0].row][0]; - var url = '/mmuDetails?window=' + windowNS + '&flags=' + mmuFlags(); + var url = '?mode=details&window=' + windowNS + '&flags=' + mmuFlags(); $.getJSON(url) .fail(function(xhr, status, error) { details.text(status + ': ' + url + ' could not be loaded'); @@ -357,11 +368,11 @@ var templMMU = ` ` -// httpMMUDetails serves details of an MMU graph at a particular window. -func httpMMUDetails(w http.ResponseWriter, r *http.Request) { - _, mmuCurve, err := getMMUCurve(r) +// HandleDetails serves details of an MMU graph at a particular window. +func (m *mmu) HandleDetails(w http.ResponseWriter, r *http.Request) { + _, mmuCurve, err := m.get(requestUtilFlags(r)) if err != nil { - http.Error(w, fmt.Sprintf("failed to parse events: %v", err), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("failed to produce MMU data: %v", err), http.StatusInternalServerError) return } @@ -376,7 +387,7 @@ func httpMMUDetails(w http.ResponseWriter, r *http.Request) { // Construct a link for each window. var links []linkedUtilWindow for _, ui := range worst { - links = append(links, newLinkedUtilWindow(ui, time.Duration(window))) + links = append(links, m.newLinkedUtilWindow(ui, time.Duration(window))) } err = json.NewEncoder(w).Encode(links) @@ -391,13 +402,13 @@ type linkedUtilWindow struct { URL string } -func newLinkedUtilWindow(ui trace.UtilWindow, window time.Duration) linkedUtilWindow { +func (m *mmu) newLinkedUtilWindow(ui trace.UtilWindow, window time.Duration) linkedUtilWindow { // Find the range containing this window. var r Range - for _, r = range ranges { + for _, r = range m.ranges { if r.EndTime > ui.Time { break } } - return linkedUtilWindow{ui, fmt.Sprintf("%s#%v:%v", r.URL(), float64(ui.Time)/1e6, float64(ui.Time+int64(window))/1e6)} + return linkedUtilWindow{ui, fmt.Sprintf("%s#%v:%v", r.URL(ViewProc), float64(ui.Time)/1e6, float64(ui.Time+int64(window))/1e6)} } diff --git a/src/internal/trace/traceviewer/pprof.go b/src/internal/trace/traceviewer/pprof.go new file mode 100644 index 0000000000..1377b3c614 --- /dev/null +++ b/src/internal/trace/traceviewer/pprof.go @@ -0,0 +1,150 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Serving of pprof-like profiles. + +package traceviewer + +import ( + "bufio" + "fmt" + "internal/profile" + "internal/trace" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "time" +) + +type ProfileFunc func(r *http.Request) ([]ProfileRecord, error) + +// SVGProfileHandlerFunc serves pprof-like profile generated by prof as svg. +func SVGProfileHandlerFunc(f ProfileFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.FormValue("raw") != "" { + w.Header().Set("Content-Type", "application/octet-stream") + + failf := func(s string, args ...any) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.Header().Set("X-Go-Pprof", "1") + http.Error(w, fmt.Sprintf(s, args...), http.StatusInternalServerError) + } + records, err := f(r) + if err != nil { + failf("failed to get records: %v", err) + return + } + if err := BuildProfile(records).Write(w); err != nil { + failf("failed to write profile: %v", err) + return + } + return + } + + blockf, err := os.CreateTemp("", "block") + if err != nil { + http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError) + return + } + defer func() { + blockf.Close() + os.Remove(blockf.Name()) + }() + records, err := f(r) + if err != nil { + http.Error(w, fmt.Sprintf("failed to generate profile: %v", err), http.StatusInternalServerError) + } + blockb := bufio.NewWriter(blockf) + if err := BuildProfile(records).Write(blockb); err != nil { + http.Error(w, fmt.Sprintf("failed to write profile: %v", err), http.StatusInternalServerError) + return + } + if err := blockb.Flush(); err != nil { + http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError) + return + } + if err := blockf.Close(); err != nil { + http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError) + return + } + svgFilename := blockf.Name() + ".svg" + if output, err := exec.Command(goCmd(), "tool", "pprof", "-svg", "-output", svgFilename, blockf.Name()).CombinedOutput(); err != nil { + http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", err, output), http.StatusInternalServerError) + return + } + defer os.Remove(svgFilename) + w.Header().Set("Content-Type", "image/svg+xml") + http.ServeFile(w, r, svgFilename) + } +} + +type ProfileRecord struct { + Stack []*trace.Frame + Count uint64 + Time time.Duration +} + +func BuildProfile(prof []ProfileRecord) *profile.Profile { + p := &profile.Profile{ + PeriodType: &profile.ValueType{Type: "trace", Unit: "count"}, + Period: 1, + SampleType: []*profile.ValueType{ + {Type: "contentions", Unit: "count"}, + {Type: "delay", Unit: "nanoseconds"}, + }, + } + locs := make(map[uint64]*profile.Location) + funcs := make(map[string]*profile.Function) + for _, rec := range prof { + var sloc []*profile.Location + for _, frame := range rec.Stack { + loc := locs[frame.PC] + if loc == nil { + fn := funcs[frame.File+frame.Fn] + if fn == nil { + fn = &profile.Function{ + ID: uint64(len(p.Function) + 1), + Name: frame.Fn, + SystemName: frame.Fn, + Filename: frame.File, + } + p.Function = append(p.Function, fn) + funcs[frame.File+frame.Fn] = fn + } + loc = &profile.Location{ + ID: uint64(len(p.Location) + 1), + Address: frame.PC, + Line: []profile.Line{ + { + Function: fn, + Line: int64(frame.Line), + }, + }, + } + p.Location = append(p.Location, loc) + locs[frame.PC] = loc + } + sloc = append(sloc, loc) + } + p.Sample = append(p.Sample, &profile.Sample{ + Value: []int64{int64(rec.Count), int64(rec.Time)}, + Location: sloc, + }) + } + return p +} + +func goCmd() string { + var exeSuffix string + if runtime.GOOS == "windows" { + exeSuffix = ".exe" + } + path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) + if _, err := os.Stat(path); err == nil { + return path + } + return "go" +} diff --git a/src/cmd/trace/static/README.md b/src/internal/trace/traceviewer/static/README.md similarity index 100% rename from src/cmd/trace/static/README.md rename to src/internal/trace/traceviewer/static/README.md diff --git a/src/cmd/trace/static/trace_viewer_full.html b/src/internal/trace/traceviewer/static/trace_viewer_full.html similarity index 100% rename from src/cmd/trace/static/trace_viewer_full.html rename to src/internal/trace/traceviewer/static/trace_viewer_full.html diff --git a/src/cmd/trace/static/webcomponents.min.js b/src/internal/trace/traceviewer/static/webcomponents.min.js similarity index 100% rename from src/cmd/trace/static/webcomponents.min.js rename to src/internal/trace/traceviewer/static/webcomponents.min.js diff --git a/src/internal/trace/v2/event.go b/src/internal/trace/v2/event.go index 7ec4698d88..763313c332 100644 --- a/src/internal/trace/v2/event.go +++ b/src/internal/trace/v2/event.go @@ -203,8 +203,14 @@ type RangeAttribute struct { // are of the same type). type TaskID uint64 -// NoTask indicates the lack of a task. -const NoTask = TaskID(0) +const ( + // NoTask indicates the lack of a task. + NoTask = TaskID(^uint64(0)) + + // BackgroundTask is the global task that events are attached to if there was + // no other task in the context at the point the event was emitted. + BackgroundTask = TaskID(0) +) // Task provides details about a Task event. type Task struct { @@ -568,22 +574,28 @@ func (e Event) StateTransition() StateTransition { s = goStateTransition(GoID(e.base.args[0]), GoRunnable, GoRunning) case go122.EvGoDestroy: s = goStateTransition(e.ctx.G, GoRunning, GoNotExist) + s.Stack = e.Stack() // This event references the resource the event happened on. case go122.EvGoDestroySyscall: s = goStateTransition(e.ctx.G, GoSyscall, GoNotExist) case go122.EvGoStop: s = goStateTransition(e.ctx.G, GoRunning, GoRunnable) s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) + s.Stack = e.Stack() // This event references the resource the event happened on. case go122.EvGoBlock: s = goStateTransition(e.ctx.G, GoRunning, GoWaiting) s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) + s.Stack = e.Stack() // This event references the resource the event happened on. case go122.EvGoUnblock: s = goStateTransition(GoID(e.base.args[0]), GoWaiting, GoRunnable) case go122.EvGoSyscallBegin: s = goStateTransition(e.ctx.G, GoRunning, GoSyscall) + s.Stack = e.Stack() // This event references the resource the event happened on. case go122.EvGoSyscallEnd: s = goStateTransition(e.ctx.G, GoSyscall, GoRunning) + s.Stack = e.Stack() // This event references the resource the event happened on. case go122.EvGoSyscallEndBlocked: s = goStateTransition(e.ctx.G, GoSyscall, GoRunnable) + s.Stack = e.Stack() // This event references the resource the event happened on. case go122.EvGoStatus: // N.B. ordering.advance populates e.base.extra. s = goStateTransition(GoID(e.base.args[0]), GoState(e.base.extra(version.Go122)[0]), go122GoStatus2GoState[e.base.args[2]]) diff --git a/src/internal/trace/v2/order.go b/src/internal/trace/v2/order.go index 8b503d4dc4..e1abddca6c 100644 --- a/src/internal/trace/v2/order.go +++ b/src/internal/trace/v2/order.go @@ -23,6 +23,23 @@ type ordering struct { gcSeq uint64 gcState gcState initialGen uint64 + + // Some events like GoDestroySyscall produce two events instead of one. + // extraEvent is this extra space. advance must not be called unless + // the extraEvent has been consumed with consumeExtraEvent. + // + // TODO(mknyszek): Replace this with a more formal queue. + extraEvent Event +} + +// consumeExtraEvent consumes the extra event. +func (o *ordering) consumeExtraEvent() Event { + if o.extraEvent.Kind() == EventBad { + return Event{} + } + r := o.extraEvent + o.extraEvent = Event{} + return r } // advance checks if it's valid to proceed with ev which came from thread m. @@ -83,6 +100,12 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) // we haven't lost the relevant information. Promote the status and advance. oldState = ProcRunning ev.args[1] = uint64(go122.ProcSyscall) + } else if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscallAbandoned { + // If we're passing through ProcSyscallAbandoned, then there's no promotion + // to do. We've lost the M that this P is associated with. However it got there, + // it's going to appear as idle in the API, so pass through as idle. + oldState = ProcIdle + ev.args[1] = uint64(go122.ProcSyscallAbandoned) } else if s.status != status { return curCtx, false, fmt.Errorf("inconsistent status for proc %d: old %v vs. new %v", pid, s.status, status) } @@ -101,9 +124,13 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) if status == go122.ProcRunning || status == go122.ProcSyscall { newCtx.P = pid } - // Set the current context to the state of the M current running this G. Otherwise - // we'll emit a Running -> Running event that doesn't correspond to the right M. - if status == go122.ProcSyscallAbandoned && oldState != ProcUndetermined { + // If we're advancing through ProcSyscallAbandoned *but* oldState is running then we've + // promoted it to ProcSyscall. However, because it's ProcSyscallAbandoned, we know this + // P is about to get stolen and its status very likely isn't being emitted by the same + // thread it was bound to. Since this status is Running -> Running and Running is binding, + // we need to make sure we emit it in the right context: the context to which it is bound. + // Find it, and set our current context to it. + if status == go122.ProcSyscallAbandoned && oldState == ProcRunning { // N.B. This is slow but it should be fairly rare. found := false for mid, ms := range o.mStates { @@ -206,6 +233,17 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) // Validate that the M we're stealing from is what we expect. mid := ThreadID(ev.args[2]) // The M we're stealing from. + + if mid == curCtx.M { + // We're stealing from ourselves. This behaves like a ProcStop. + if curCtx.P != pid { + return curCtx, false, fmt.Errorf("tried to self-steal proc %d (thread %d), but got proc %d instead", pid, mid, curCtx.P) + } + newCtx.P = NoProc + return curCtx, true, nil + } + + // We're stealing from some other M. mState, ok := o.mStates[mid] if !ok { return curCtx, false, fmt.Errorf("stole proc from non-existent thread %d", mid) @@ -474,7 +512,7 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) // This event indicates that a goroutine is effectively // being created out of a cgo callback. Such a goroutine // is 'created' in the syscall state. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustNotHave, Goroutine: event.MustNotHave}); err != nil { + if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustNotHave}); err != nil { return curCtx, false, err } // This goroutine is effectively being created. Add a state for it. @@ -491,6 +529,15 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) // cgo callback is disappearing, either because the callback // ending or the C thread that called it is being destroyed. // + // Also, treat this as if we lost our P too. + // The thread ID may be reused by the platform and we'll get + // really confused if we try to steal the P is this is running + // with later. The new M with the same ID could even try to + // steal back this P from itself! + // + // The runtime is careful to make sure that any GoCreateSyscall + // event will enter the runtime emitting events for reacquiring a P. + // // Note: we might have a P here. The P might not be released // eagerly by the runtime, and it might get stolen back later // (or never again, if the program is going to exit). @@ -508,6 +555,32 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) // This goroutine is exiting itself. delete(o.gStates, curCtx.G) newCtx.G = NoGoroutine + + // If we have a proc, then we're dissociating from it now. See the comment at the top of the case. + if curCtx.P != NoProc { + pState, ok := o.pStates[curCtx.P] + if !ok { + return curCtx, false, fmt.Errorf("found invalid proc %d during %s", curCtx.P, go122.EventString(typ)) + } + if pState.status != go122.ProcSyscall { + return curCtx, false, fmt.Errorf("proc %d in unexpected state %s during %s", curCtx.P, pState.status, go122.EventString(typ)) + } + // See the go122-create-syscall-reuse-thread-id test case for more details. + pState.status = go122.ProcSyscallAbandoned + newCtx.P = NoProc + + // Queue an extra self-ProcSteal event. + o.extraEvent = Event{ + table: evt, + ctx: curCtx, + base: baseEvent{ + typ: go122.EvProcSteal, + time: ev.time, + }, + } + o.extraEvent.base.args[0] = uint64(curCtx.P) + o.extraEvent.base.extra(version.Go122)[0] = uint64(go122.ProcSyscall) + } return curCtx, true, nil // Handle tasks. Tasks are interesting because: @@ -525,6 +598,13 @@ func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) // Get the parent ID, but don't validate it. There's no guarantee // we actually have information on whether it's active. parentID := TaskID(ev.args[1]) + if parentID == BackgroundTask { + // Note: a value of 0 here actually means no parent, *not* the + // background task. Automatic background task attachment only + // applies to regions. + parentID = NoTask + ev.args[1] = uint64(NoTask) + } // Validate the name and record it. We'll need to pass it through to // EvUserTaskEnd. diff --git a/src/internal/trace/v2/reader.go b/src/internal/trace/v2/reader.go index b58cc6fcb1..446b2add30 100644 --- a/src/internal/trace/v2/reader.go +++ b/src/internal/trace/v2/reader.go @@ -85,6 +85,11 @@ func (r *Reader) ReadEvent() (e Event, err error) { r.lastTs = e.base.time }() + // Consume any extra events produced during parsing. + if ev := r.order.consumeExtraEvent(); ev.Kind() != EventBad { + return ev, nil + } + // Check if we need to refresh the generation. if len(r.frontier) == 0 && len(r.cpuSamples) == 0 { if !r.emittedSync { diff --git a/src/internal/trace/v2/testdata/generators/go122-create-syscall-reuse-thread-id.go b/src/internal/trace/v2/testdata/generators/go122-create-syscall-reuse-thread-id.go new file mode 100644 index 0000000000..107cce2cc2 --- /dev/null +++ b/src/internal/trace/v2/testdata/generators/go122-create-syscall-reuse-thread-id.go @@ -0,0 +1,61 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests a G being created from within a syscall. +// +// Specifically, it tests a scenerio wherein a C +// thread is calling into Go, creating a goroutine in +// a syscall (in the tracer's model). The system is free +// to reuse thread IDs, so first a thread ID is used to +// call into Go, and then is used for a Go-created thread. +// +// This is a regression test. The trace parser didn't correctly +// model GoDestroySyscall as dropping its P (even if the runtime +// did). It turns out this is actually fine if all the threads +// in the trace have unique IDs, since the P just stays associated +// with an eternally dead thread, and it's stolen by some other +// thread later. But if thread IDs are reused, then the tracer +// gets confused when trying to advance events on the new thread. +// The now-dead thread which exited on a GoDestroySyscall still has +// its P associated and this transfers to the newly-live thread +// in the parser's state because they share a thread ID. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + g := t.Generation(1) + + // A C thread calls into Go and acquires a P. It returns + // back to C, destroying the G. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("GoCreateSyscall", trace.GoID(4)) + b0.Event("GoSyscallEndBlocked") + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) + b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) + b0.Event("GoStatus", trace.GoID(4), trace.NoThread, go122.GoRunnable) + b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) + b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) + b0.Event("GoDestroySyscall") + + // A new Go-created thread with the same ID appears and + // starts running, then tries to steal the P from the + // first thread. The stealing is interesting because if + // the parser handles GoDestroySyscall wrong, then we + // have a self-steal here potentially that doesn't make + // sense. + b1 := g.Batch(trace.ThreadID(0), 0) + b1.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b1.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) + b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(3), trace.ThreadID(0)) +} diff --git a/src/internal/trace/v2/testdata/generators/go122-create-syscall-with-p.go b/src/internal/trace/v2/testdata/generators/go122-create-syscall-with-p.go new file mode 100644 index 0000000000..4cb1c4a9a7 --- /dev/null +++ b/src/internal/trace/v2/testdata/generators/go122-create-syscall-with-p.go @@ -0,0 +1,52 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests a G being created from within a syscall. +// +// Specifically, it tests a scenerio wherein a C +// thread is calling into Go, creating a goroutine in +// a syscall (in the tracer's model). Because the actual +// m can be reused, it's possible for that m to have never +// had its P (in _Psyscall) stolen if the runtime doesn't +// model the scenario correctly. Make sure we reject such +// traces. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + t.ExpectFailure(".*expected a proc but didn't have one.*") + + g := t.Generation(1) + + // A C thread calls into Go and acquires a P. It returns + // back to C, destroying the G. It then comes back to Go + // on the same thread and again returns to C. + // + // Note: on pthread platforms this can't happen on the + // same thread because the m is stashed in TLS between + // calls into Go, until the thread dies. This is still + // possible on other platforms, however. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("GoCreateSyscall", trace.GoID(4)) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) + b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) + b0.Event("GoSyscallEndBlocked") + b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) + b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) + b0.Event("GoDestroySyscall") + b0.Event("GoCreateSyscall", trace.GoID(4)) + b0.Event("GoSyscallEnd") + b0.Event("GoSyscallBegin", testgen.Seq(3), testgen.NoStack) + b0.Event("GoDestroySyscall") +} diff --git a/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-self.go b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-self.go new file mode 100644 index 0000000000..dd947346c6 --- /dev/null +++ b/src/internal/trace/v2/testdata/generators/go122-syscall-steal-proc-self.go @@ -0,0 +1,37 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests syscall P stealing. +// +// Specifically, it tests a scenario where a thread 'steals' +// a P from itself. It's just a ProcStop with extra steps when +// it happens on the same P. + +package main + +import ( + "internal/trace/v2" + "internal/trace/v2/event/go122" + testgen "internal/trace/v2/internal/testgen/go122" +) + +func main() { + testgen.Main(gen) +} + +func gen(t *testgen.Trace) { + t.DisableTimestamps() + + g := t.Generation(1) + + // A goroutine execute a syscall and steals its own P, then starts running + // on that P. + b0 := g.Batch(trace.ThreadID(0), 0) + b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) + b0.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) + b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(3)) + b0.Event("GoSyscallEndBlocked") +} diff --git a/src/internal/trace/v2/testdata/generators/go122-task-across-generations.go b/src/internal/trace/v2/testdata/generators/go122-task-across-generations.go index 94e9933996..06ef96e51a 100644 --- a/src/internal/trace/v2/testdata/generators/go122-task-across-generations.go +++ b/src/internal/trace/v2/testdata/generators/go122-task-across-generations.go @@ -29,7 +29,7 @@ func gen(t *testgen.Trace) { b1 := g1.Batch(trace.ThreadID(0), 0) b1.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) b1.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) - b1.Event("UserTaskBegin", trace.TaskID(2), trace.NoTask, "my task", testgen.NoStack) + b1.Event("UserTaskBegin", trace.TaskID(2), trace.TaskID(0) /* 0 means no parent, not background */, "my task", testgen.NoStack) g2 := t.Generation(2) diff --git a/src/internal/trace/v2/testdata/mktests.go b/src/internal/trace/v2/testdata/mktests.go index 5242163594..96cbbe4b1f 100644 --- a/src/internal/trace/v2/testdata/mktests.go +++ b/src/internal/trace/v2/testdata/mktests.go @@ -7,7 +7,12 @@ package main import ( + "bytes" "fmt" + "internal/trace/v2/raw" + "internal/trace/v2/version" + "internal/txtar" + "io" "log" "os" "os/exec" @@ -17,27 +22,58 @@ import ( func main() { log.SetFlags(0) - if err := run(); err != nil { + ctx, err := newContext() + if err != nil { + log.Fatal(err) + } + if err := ctx.runGenerators(); err != nil { + log.Fatal(err) + } + if err := ctx.runTestProg("./testprog/annotations.go"); err != nil { + log.Fatal(err) + } + if err := ctx.runTestProg("./testprog/annotations-stress.go"); err != nil { log.Fatal(err) } } -func run() error { +type context struct { + testNames map[string]struct{} + filter *regexp.Regexp +} + +func newContext() (*context, error) { + var filter *regexp.Regexp + var err error + if pattern := os.Getenv("GOTRACETEST"); pattern != "" { + filter, err = regexp.Compile(pattern) + if err != nil { + return nil, fmt.Errorf("compiling regexp %q for GOTRACETEST: %v", pattern, err) + } + } + return &context{ + testNames: make(map[string]struct{}), + filter: filter, + }, nil +} + +func (ctx *context) register(testName string) (skip bool, err error) { + if _, ok := ctx.testNames[testName]; ok { + return true, fmt.Errorf("duplicate test %s found", testName) + } + if ctx.filter != nil { + return !ctx.filter.MatchString(testName), nil + } + return false, nil +} + +func (ctx *context) runGenerators() error { generators, err := filepath.Glob("./generators/*.go") if err != nil { return fmt.Errorf("reading generators: %v", err) } genroot := "./tests" - // Grab a pattern, if any. - var re *regexp.Regexp - if pattern := os.Getenv("GOTRACETEST"); pattern != "" { - re, err = regexp.Compile(pattern) - if err != nil { - return fmt.Errorf("compiling regexp %q for GOTRACETEST: %v", pattern, err) - } - } - if err := os.MkdirAll(genroot, 0777); err != nil { return fmt.Errorf("creating generated root: %v", err) } @@ -46,7 +82,11 @@ func run() error { name = name[:len(name)-len(filepath.Ext(name))] // Skip if we have a pattern and this test doesn't match. - if re != nil && !re.MatchString(name) { + skip, err := ctx.register(name) + if err != nil { + return err + } + if skip { continue } @@ -64,3 +104,59 @@ func run() error { } return nil } + +func (ctx *context) runTestProg(progPath string) error { + name := filepath.Base(progPath) + name = name[:len(name)-len(filepath.Ext(name))] + name = fmt.Sprintf("go1%d-%s", version.Current, name) + + // Skip if we have a pattern and this test doesn't match. + skip, err := ctx.register(name) + if err != nil { + return err + } + if skip { + return nil + } + + // Create command. + var trace, stderr bytes.Buffer + cmd := exec.Command("go", "run", progPath) + // TODO(mknyszek): Remove if goexperiment.Exectracer2 becomes the default. + cmd.Env = append(os.Environ(), "GOEXPERIMENT=exectracer2") + cmd.Stdout = &trace + cmd.Stderr = &stderr + + // Run trace program; the trace will appear in stdout. + fmt.Fprintf(os.Stderr, "running trace program %s...\n", name) + if err := cmd.Run(); err != nil { + log.Fatalf("running trace program: %v:\n%s", err, stderr.String()) + } + + // Write out the trace. + var textTrace bytes.Buffer + r, err := raw.NewReader(&trace) + if err != nil { + log.Fatalf("reading trace: %v", err) + } + w, err := raw.NewTextWriter(&textTrace, version.Current) + for { + ev, err := r.ReadEvent() + if err == io.EOF { + break + } + if err != nil { + log.Fatalf("reading trace: %v", err) + } + if err := w.WriteEvent(ev); err != nil { + log.Fatalf("writing trace: %v", err) + } + } + testData := txtar.Format(&txtar.Archive{ + Files: []txtar.File{ + {Name: "expect", Data: []byte("SUCCESS")}, + {Name: "trace", Data: textTrace.Bytes()}, + }, + }) + return os.WriteFile(fmt.Sprintf("./tests/%s.test", name), testData, 0o664) +} diff --git a/src/internal/trace/v2/testdata/tests/go122-annotations-stress.test b/src/internal/trace/v2/testdata/tests/go122-annotations-stress.test new file mode 100644 index 0000000000..fe3c84b16e --- /dev/null +++ b/src/internal/trace/v2/testdata/tests/go122-annotations-stress.test @@ -0,0 +1,1179 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=18446744073709551615 time=2753926854385 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=1986497 time=2753925247434 size=1430 +ProcStart dt=336 p=2 p_seq=1 +GoStart dt=191 g=19 g_seq=1 +HeapAlloc dt=389 heapalloc_value=1622016 +HeapAlloc dt=4453 heapalloc_value=1662976 +GoBlock dt=572 reason_string=12 stack=29 +ProcStop dt=26 +ProcStart dt=160734 p=2 p_seq=2 +ProcStop dt=21 +ProcStart dt=159292 p=0 p_seq=7 +GoStart dt=299 g=49 g_seq=1 +UserRegionBegin dt=183 task=8 name_string=33 stack=26 +UserLog dt=26 task=8 key_string=24 value_string=49 stack=27 +UserRegionEnd dt=8 task=8 name_string=33 stack=28 +GoDestroy dt=3 +GoStart dt=20 g=50 g_seq=1 +UserRegionBegin dt=40 task=8 name_string=35 stack=26 +UserLog dt=9 task=8 key_string=24 value_string=50 stack=27 +UserRegionEnd dt=2 task=8 name_string=35 stack=28 +GoDestroy dt=1 +ProcStop dt=18 +ProcStart dt=141801 p=4 p_seq=5 +ProcStop dt=18 +ProcStart dt=16860 p=4 p_seq=6 +GoUnblock dt=53 g=1 g_seq=5 stack=0 +GoUnblock dt=9 g=51 g_seq=3 stack=0 +GoStart dt=162 g=51 g_seq=4 +UserTaskEnd dt=35 task=9 stack=36 +UserRegionEnd dt=16 task=8 name_string=31 stack=28 +GoDestroy dt=2 +GoStart dt=20 g=1 g_seq=6 +UserTaskEnd dt=14 task=8 stack=54 +UserLog dt=26 task=3 key_string=24 value_string=51 stack=55 +UserTaskBegin dt=14 task=10 parent_task=3 name_string=26 stack=56 +UserLog dt=42 task=10 key_string=27 value_string=52 stack=57 +UserRegionBegin dt=12 task=10 name_string=29 stack=58 +GoCreate dt=36 new_g=35 new_stack=17 stack=59 +GoCreate dt=11 new_g=36 new_stack=17 stack=59 +GoCreate dt=18 new_g=37 new_stack=17 stack=59 +GoCreate dt=10 new_g=38 new_stack=17 stack=59 +GoCreate dt=6 new_g=39 new_stack=17 stack=59 +GoCreate dt=8 new_g=40 new_stack=17 stack=59 +UserRegionEnd dt=7 task=10 name_string=29 stack=60 +GoBlock dt=9 reason_string=19 stack=61 +GoStart dt=15 g=40 g_seq=1 +UserRegionBegin dt=110 task=10 name_string=53 stack=26 +UserLog dt=16 task=10 key_string=24 value_string=54 stack=27 +UserRegionEnd dt=2 task=10 name_string=53 stack=28 +GoDestroy dt=2 +GoStart dt=6 g=38 g_seq=1 +UserRegionBegin dt=31 task=10 name_string=30 stack=26 +UserLog dt=5 task=10 key_string=24 value_string=55 stack=27 +UserRegionEnd dt=2 task=10 name_string=30 stack=28 +GoDestroy dt=1 +GoStart dt=2 g=39 g_seq=1 +UserRegionBegin dt=23 task=10 name_string=56 stack=26 +UserLog dt=6 task=10 key_string=24 value_string=57 stack=27 +UserRegionEnd dt=1 task=10 name_string=56 stack=28 +GoDestroy dt=1 +GoStart dt=8 g=35 g_seq=1 +UserRegionBegin dt=17 task=10 name_string=33 stack=26 +UserLog dt=4 task=10 key_string=24 value_string=58 stack=27 +UserRegionEnd dt=2 task=10 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=3 g=36 g_seq=1 +UserRegionBegin dt=19 task=10 name_string=35 stack=26 +UserLog dt=4 task=10 key_string=24 value_string=59 stack=27 +UserRegionEnd dt=2 task=10 name_string=35 stack=28 +GoDestroy dt=1 +ProcStop dt=11 +ProcStart dt=142205 p=0 p_seq=9 +ProcStop dt=19 +ProcStart dt=16811 p=0 p_seq=10 +GoUnblock dt=26 g=1 g_seq=7 stack=0 +GoStart dt=201 g=1 g_seq=8 +UserTaskEnd dt=24 task=10 stack=62 +UserLog dt=18 task=4 key_string=24 value_string=63 stack=63 +UserTaskBegin dt=11 task=12 parent_task=4 name_string=26 stack=64 +UserLog dt=21 task=12 key_string=27 value_string=64 stack=65 +UserRegionBegin dt=7 task=12 name_string=29 stack=66 +GoCreate dt=33 new_g=5 new_stack=17 stack=67 +GoCreate dt=12 new_g=6 new_stack=17 stack=67 +GoCreate dt=9 new_g=7 new_stack=17 stack=67 +GoCreate dt=8 new_g=8 new_stack=17 stack=67 +GoCreate dt=19 new_g=9 new_stack=17 stack=67 +UserRegionEnd dt=14 task=12 name_string=29 stack=68 +GoBlock dt=11 reason_string=19 stack=69 +GoStart dt=13 g=9 g_seq=1 +UserRegionBegin dt=70 task=12 name_string=56 stack=26 +UserLog dt=11 task=12 key_string=24 value_string=65 stack=27 +UserRegionEnd dt=3 task=12 name_string=56 stack=28 +GoDestroy dt=2 +GoStart dt=7 g=5 g_seq=1 +UserRegionBegin dt=24 task=12 name_string=33 stack=26 +UserLog dt=5 task=12 key_string=24 value_string=66 stack=27 +UserRegionEnd dt=2 task=12 name_string=33 stack=28 +GoDestroy dt=2 +GoStart dt=8 g=6 g_seq=1 +UserRegionBegin dt=15 task=12 name_string=35 stack=26 +UserLog dt=7 task=12 key_string=24 value_string=67 stack=27 +UserRegionEnd dt=2 task=12 name_string=35 stack=28 +GoDestroy dt=1 +GoStart dt=2 g=7 g_seq=1 +UserRegionBegin dt=13 task=12 name_string=31 stack=26 +UserLog dt=5 task=12 key_string=24 value_string=68 stack=27 +UserLog dt=6 task=12 key_string=24 value_string=69 stack=30 +UserTaskBegin dt=5 task=13 parent_task=12 name_string=26 stack=31 +UserLog dt=7 task=13 key_string=27 value_string=70 stack=32 +UserRegionBegin dt=4 task=13 name_string=29 stack=33 +UserRegionEnd dt=6 task=13 name_string=29 stack=34 +GoBlock dt=18 reason_string=19 stack=35 +GoStart dt=12 g=8 g_seq=1 +UserRegionBegin dt=22 task=12 name_string=30 stack=26 +UserLog dt=5 task=12 key_string=24 value_string=71 stack=27 +UserRegionEnd dt=2 task=12 name_string=30 stack=28 +GoDestroy dt=1 +ProcStop dt=20 +ProcStart dt=141838 p=4 p_seq=8 +ProcStop dt=16 +ProcStart dt=17652 p=4 p_seq=9 +GoUnblock dt=48 g=1 g_seq=9 stack=0 +GoUnblock dt=8 g=7 g_seq=2 stack=0 +GoStart dt=271 g=7 g_seq=3 +UserTaskEnd dt=25 task=13 stack=36 +UserRegionEnd dt=15 task=12 name_string=31 stack=28 +GoDestroy dt=4 +GoStart dt=19 g=1 g_seq=10 +UserTaskEnd dt=19 task=12 stack=70 +UserLog dt=21 task=0 key_string=24 value_string=72 stack=13 +UserTaskBegin dt=19 task=14 parent_task=0 name_string=26 stack=14 +UserLog dt=37 task=14 key_string=27 value_string=73 stack=15 +UserRegionBegin dt=6 task=14 name_string=29 stack=16 +GoCreate dt=28 new_g=41 new_stack=17 stack=18 +GoCreate dt=14 new_g=42 new_stack=17 stack=18 +GoCreate dt=12 new_g=43 new_stack=17 stack=18 +GoCreate dt=10 new_g=44 new_stack=17 stack=18 +UserRegionEnd dt=5 task=14 name_string=29 stack=19 +GoBlock dt=9 reason_string=19 stack=20 +GoStart dt=16 g=44 g_seq=1 +UserRegionBegin dt=107 task=14 name_string=30 stack=26 +UserLog dt=16 task=14 key_string=24 value_string=74 stack=27 +UserRegionEnd dt=3 task=14 name_string=30 stack=28 +GoDestroy dt=2 +GoStart dt=7 g=41 g_seq=1 +UserRegionBegin dt=30 task=14 name_string=33 stack=26 +UserLog dt=7 task=14 key_string=24 value_string=75 stack=27 +UserRegionEnd dt=2 task=14 name_string=33 stack=28 +GoDestroy dt=2 +GoStart dt=7 g=42 g_seq=1 +UserRegionBegin dt=27 task=14 name_string=35 stack=26 +UserLog dt=7 task=14 key_string=24 value_string=76 stack=27 +UserRegionEnd dt=2 task=14 name_string=35 stack=28 +GoDestroy dt=2 +ProcStop dt=28 +ProcStart dt=141923 p=0 p_seq=12 +ProcStop dt=19 +ProcStart dt=16780 p=0 p_seq=13 +GoUnblock dt=22 g=43 g_seq=2 stack=0 +GoStart dt=162 g=43 g_seq=3 +UserTaskEnd dt=16 task=15 stack=36 +UserRegionEnd dt=12 task=14 name_string=31 stack=28 +GoDestroy dt=2 +ProcStop dt=8 +ProcStart dt=1532 p=2 p_seq=9 +ProcStop dt=12 +ProcStart dt=141906 p=4 p_seq=11 +ProcStop dt=16 +ProcStart dt=16784 p=4 p_seq=12 +GoUnblock dt=20 g=1 g_seq=13 stack=0 +GoStart dt=191 g=1 g_seq=14 +UserTaskEnd dt=15 task=16 stack=45 +UserLog dt=17 task=2 key_string=24 value_string=84 stack=46 +UserTaskBegin dt=8 task=17 parent_task=2 name_string=26 stack=47 +UserLog dt=20 task=17 key_string=27 value_string=85 stack=48 +UserRegionBegin dt=6 task=17 name_string=29 stack=49 +GoCreate dt=28 new_g=45 new_stack=17 stack=50 +GoCreate dt=9 new_g=46 new_stack=17 stack=50 +GoCreate dt=10 new_g=47 new_stack=17 stack=50 +UserRegionEnd dt=5 task=17 name_string=29 stack=51 +GoBlock dt=6 reason_string=19 stack=52 +GoStart dt=10 g=47 g_seq=1 +UserRegionBegin dt=69 task=17 name_string=31 stack=26 +UserLog dt=11 task=17 key_string=24 value_string=86 stack=27 +UserLog dt=7 task=17 key_string=24 value_string=87 stack=30 +UserTaskBegin dt=5 task=18 parent_task=17 name_string=26 stack=31 +UserLog dt=7 task=18 key_string=27 value_string=88 stack=32 +UserRegionBegin dt=5 task=18 name_string=29 stack=33 +UserRegionEnd dt=4 task=18 name_string=29 stack=34 +HeapAlloc dt=35 heapalloc_value=1818624 +GoBlock dt=14 reason_string=19 stack=35 +HeapAlloc dt=11 heapalloc_value=1826816 +GoStart dt=10 g=45 g_seq=1 +UserRegionBegin dt=29 task=17 name_string=33 stack=26 +UserLog dt=9 task=17 key_string=24 value_string=89 stack=27 +UserRegionEnd dt=3 task=17 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=5 g=46 g_seq=1 +UserRegionBegin dt=15 task=17 name_string=35 stack=26 +UserLog dt=8 task=17 key_string=24 value_string=90 stack=27 +UserRegionEnd dt=2 task=17 name_string=35 stack=28 +GoDestroy dt=1 +ProcStop dt=3 +ProcStart dt=141981 p=0 p_seq=16 +ProcStop dt=19 +ProcStart dt=17153 p=0 p_seq=17 +GoUnblock dt=44 g=1 g_seq=15 stack=0 +GoUnblock dt=11 g=47 g_seq=2 stack=0 +GoStart dt=215 g=47 g_seq=3 +UserTaskEnd dt=22 task=18 stack=36 +UserRegionEnd dt=9 task=17 name_string=31 stack=28 +GoDestroy dt=3 +GoStart dt=19 g=1 g_seq=16 +UserTaskEnd dt=13 task=17 stack=54 +UserLog dt=18 task=3 key_string=24 value_string=91 stack=55 +UserTaskBegin dt=7 task=19 parent_task=3 name_string=26 stack=56 +UserLog dt=27 task=19 key_string=27 value_string=92 stack=57 +UserRegionBegin dt=8 task=19 name_string=29 stack=58 +GoCreate dt=30 new_g=10 new_stack=17 stack=59 +GoCreate dt=9 new_g=11 new_stack=17 stack=59 +GoCreate dt=11 new_g=12 new_stack=17 stack=59 +GoCreate dt=7 new_g=13 new_stack=17 stack=59 +GoCreate dt=7 new_g=14 new_stack=17 stack=59 +GoCreate dt=9 new_g=15 new_stack=17 stack=59 +UserRegionEnd dt=5 task=19 name_string=29 stack=60 +GoBlock dt=7 reason_string=19 stack=61 +GoStart dt=17 g=15 g_seq=1 +UserRegionBegin dt=61 task=19 name_string=53 stack=26 +UserLog dt=10 task=19 key_string=24 value_string=93 stack=27 +UserRegionEnd dt=3 task=19 name_string=53 stack=28 +GoDestroy dt=1 +GoStart dt=4 g=10 g_seq=1 +UserRegionBegin dt=26 task=19 name_string=33 stack=26 +UserLog dt=7 task=19 key_string=24 value_string=94 stack=27 +UserRegionEnd dt=2 task=19 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=4 g=11 g_seq=1 +UserRegionBegin dt=20 task=19 name_string=35 stack=26 +UserLog dt=5 task=19 key_string=24 value_string=95 stack=27 +UserRegionEnd dt=2 task=19 name_string=35 stack=28 +GoDestroy dt=1 +GoStart dt=7 g=12 g_seq=1 +UserRegionBegin dt=14 task=19 name_string=31 stack=26 +UserLog dt=4 task=19 key_string=24 value_string=96 stack=27 +UserLog dt=4 task=19 key_string=24 value_string=97 stack=30 +UserTaskBegin dt=7 task=20 parent_task=19 name_string=26 stack=31 +UserLog dt=5 task=20 key_string=27 value_string=98 stack=32 +UserRegionBegin dt=4 task=20 name_string=29 stack=33 +UserRegionEnd dt=5 task=20 name_string=29 stack=34 +GoBlock dt=9 reason_string=19 stack=35 +GoStart dt=9 g=14 g_seq=1 +UserRegionBegin dt=28 task=19 name_string=56 stack=26 +UserLog dt=7 task=19 key_string=24 value_string=99 stack=27 +UserRegionEnd dt=2 task=19 name_string=56 stack=28 +GoDestroy dt=2 +ProcStop dt=17 +ProcStart dt=141933 p=2 p_seq=11 +ProcStop dt=13 +ProcStart dt=16744 p=2 p_seq=12 +GoUnblock dt=29 g=1 g_seq=17 stack=0 +GoUnblock dt=7 g=12 g_seq=2 stack=0 +GoStart dt=172 g=12 g_seq=3 +UserTaskEnd dt=15 task=20 stack=36 +UserRegionEnd dt=8 task=19 name_string=31 stack=28 +GoDestroy dt=2 +GoStart dt=11 g=1 g_seq=18 +UserTaskEnd dt=14 task=19 stack=62 +UserLog dt=16 task=4 key_string=24 value_string=101 stack=63 +UserTaskBegin dt=6 task=21 parent_task=4 name_string=26 stack=64 +UserLog dt=25 task=21 key_string=27 value_string=102 stack=65 +UserRegionBegin dt=7 task=21 name_string=29 stack=66 +GoCreate dt=23 new_g=54 new_stack=17 stack=67 +GoCreate dt=8 new_g=55 new_stack=17 stack=67 +GoCreate dt=17 new_g=56 new_stack=17 stack=67 +GoCreate dt=8 new_g=57 new_stack=17 stack=67 +GoCreate dt=7 new_g=58 new_stack=17 stack=67 +UserRegionEnd dt=4 task=21 name_string=29 stack=68 +GoBlock dt=9 reason_string=19 stack=69 +GoStart dt=7 g=58 g_seq=1 +UserRegionBegin dt=46 task=21 name_string=56 stack=26 +UserLog dt=8 task=21 key_string=24 value_string=103 stack=27 +UserRegionEnd dt=4 task=21 name_string=56 stack=28 +GoDestroy dt=1 +GoStart dt=3 g=54 g_seq=1 +UserRegionBegin dt=19 task=21 name_string=33 stack=26 +UserLog dt=7 task=21 key_string=24 value_string=104 stack=27 +UserRegionEnd dt=2 task=21 name_string=33 stack=28 +GoDestroy dt=1 +GoStart dt=2 g=55 g_seq=1 +UserRegionBegin dt=17 task=21 name_string=35 stack=26 +UserLog dt=4 task=21 key_string=24 value_string=105 stack=27 +UserRegionEnd dt=2 task=21 name_string=35 stack=28 +GoDestroy dt=1 +GoStart dt=5 g=56 g_seq=1 +UserRegionBegin dt=16 task=21 name_string=31 stack=26 +UserLog dt=4 task=21 key_string=24 value_string=106 stack=27 +UserLog dt=3 task=21 key_string=24 value_string=107 stack=30 +UserTaskBegin dt=4 task=22 parent_task=21 name_string=26 stack=31 +UserLog dt=6 task=22 key_string=27 value_string=108 stack=32 +UserRegionBegin dt=4 task=22 name_string=29 stack=33 +UserRegionEnd dt=7 task=22 name_string=29 stack=34 +GoBlock dt=14 reason_string=19 stack=35 +GoStart dt=3 g=57 g_seq=1 +UserRegionBegin dt=22 task=21 name_string=30 stack=26 +UserLog dt=6 task=21 key_string=24 value_string=109 stack=27 +UserRegionEnd dt=2 task=21 name_string=30 stack=28 +GoDestroy dt=2 +ProcStop dt=10 +ProcStart dt=128031 p=4 p_seq=15 +ProcStop dt=16 +ProcStart dt=33758 p=2 p_seq=15 +ProcStop dt=18 +EventBatch gen=1 m=1986496 time=2753925246280 size=267 +ProcStart dt=549 p=0 p_seq=1 +GoStart dt=211 g=18 g_seq=1 +GoBlock dt=3533 reason_string=12 stack=21 +GoStart dt=41 g=21 g_seq=1 +GoBlock dt=150 reason_string=10 stack=22 +GoStart dt=93 g=20 g_seq=1 +GoSyscallBegin dt=51 p_seq=2 stack=23 +GoSyscallEnd dt=400 +GoBlock dt=582 reason_string=15 stack=25 +GoStart dt=26 g=23 g_seq=1 +HeapAlloc dt=50 heapalloc_value=1646592 +UserRegionBegin dt=2921 task=5 name_string=31 stack=26 +UserLog dt=28 task=5 key_string=24 value_string=37 stack=27 +UserLog dt=13 task=5 key_string=24 value_string=38 stack=30 +UserTaskBegin dt=15 task=6 parent_task=5 name_string=26 stack=31 +HeapAlloc dt=26 heapalloc_value=1687552 +UserLog dt=14 task=6 key_string=27 value_string=39 stack=32 +UserRegionBegin dt=9 task=6 name_string=29 stack=33 +UserRegionEnd dt=6 task=6 name_string=29 stack=34 +GoBlock dt=15 reason_string=19 stack=35 +ProcStop dt=30 +ProcStart dt=156949 p=4 p_seq=2 +GoUnblock dt=46 g=1 g_seq=1 stack=0 +GoStart dt=253 g=1 g_seq=2 +UserTaskEnd dt=27 task=5 stack=37 +UserLog dt=23 task=1 key_string=24 value_string=40 stack=38 +UserTaskBegin dt=14 task=7 parent_task=1 name_string=26 stack=39 +HeapAlloc dt=596 heapalloc_value=1695744 +HeapAlloc dt=18 heapalloc_value=1703936 +UserLog dt=17 task=7 key_string=27 value_string=41 stack=40 +UserRegionBegin dt=14 task=7 name_string=29 stack=41 +HeapAlloc dt=10 heapalloc_value=1712128 +HeapAlloc dt=17 heapalloc_value=1720320 +GoCreate dt=44 new_g=33 new_stack=17 stack=42 +GoCreate dt=175 new_g=34 new_stack=17 stack=42 +UserRegionEnd dt=50 task=7 name_string=29 stack=43 +GoBlock dt=9 reason_string=19 stack=44 +HeapAlloc dt=16 heapalloc_value=1728512 +GoStart dt=239 g=34 g_seq=1 +HeapAlloc dt=21 heapalloc_value=1736704 +UserRegionBegin dt=92 task=7 name_string=35 stack=26 +UserLog dt=15 task=7 key_string=24 value_string=42 stack=27 +UserRegionEnd dt=4 task=7 name_string=35 stack=28 +GoDestroy dt=2 +ProcStop dt=21 +ProcStart dt=800974 p=4 p_seq=10 +ProcStop dt=39 +ProcStart dt=158775 p=0 p_seq=15 +ProcStop dt=24 +ProcStart dt=159722 p=4 p_seq=13 +GoStart dt=254 g=13 g_seq=1 +UserRegionBegin dt=239 task=19 name_string=30 stack=26 +UserLog dt=23 task=19 key_string=24 value_string=100 stack=27 +UserRegionEnd dt=6 task=19 name_string=30 stack=28 +GoDestroy dt=7 +ProcStop dt=22 +EventBatch gen=1 m=1986495 time=2753925251756 size=320 +ProcStart dt=705 p=4 p_seq=1 +ProcStop dt=1279 +ProcStart dt=158975 p=0 p_seq=5 +ProcStop dt=23 +ProcStart dt=792 p=0 p_seq=6 +GoStart dt=187 g=33 g_seq=1 +UserRegionBegin dt=244 task=7 name_string=33 stack=26 +UserLog dt=32 task=7 key_string=24 value_string=43 stack=27 +UserRegionEnd dt=7 task=7 name_string=33 stack=28 +GoDestroy dt=5 +ProcStop dt=24 +ProcStart dt=160255 p=4 p_seq=4 +ProcStop dt=27 +ProcStart dt=159067 p=2 p_seq=5 +GoStart dt=222 g=37 g_seq=1 +UserRegionBegin dt=114 task=10 name_string=31 stack=26 +UserLog dt=16 task=10 key_string=24 value_string=60 stack=27 +UserLog dt=8 task=10 key_string=24 value_string=61 stack=30 +UserTaskBegin dt=8 task=11 parent_task=10 name_string=26 stack=31 +UserLog dt=19 task=11 key_string=27 value_string=62 stack=32 +UserRegionBegin dt=6 task=11 name_string=29 stack=33 +UserRegionEnd dt=7 task=11 name_string=29 stack=34 +GoBlock dt=15 reason_string=19 stack=35 +ProcStop dt=11 +ProcStart dt=160101 p=4 p_seq=7 +ProcStop dt=21 +ProcStart dt=159647 p=2 p_seq=7 +GoStart dt=277 g=43 g_seq=1 +UserRegionBegin dt=126 task=14 name_string=31 stack=26 +UserLog dt=21 task=14 key_string=24 value_string=77 stack=27 +UserLog dt=9 task=14 key_string=24 value_string=78 stack=30 +UserTaskBegin dt=8 task=15 parent_task=14 name_string=26 stack=31 +UserLog dt=17 task=15 key_string=27 value_string=79 stack=32 +UserRegionBegin dt=6 task=15 name_string=29 stack=33 +UserRegionEnd dt=8 task=15 name_string=29 stack=34 +GoBlock dt=23 reason_string=19 stack=35 +ProcStop dt=17 +ProcStart dt=159706 p=0 p_seq=14 +GoStart dt=229 g=52 g_seq=1 +UserRegionBegin dt=103 task=16 name_string=33 stack=26 +UserLog dt=20 task=16 key_string=24 value_string=83 stack=27 +UserRegionEnd dt=4 task=16 name_string=33 stack=28 +GoDestroy dt=3 +ProcStop dt=17 +ProcStart dt=319699 p=2 p_seq=10 +ProcStop dt=20 +ProcStart dt=158728 p=4 p_seq=14 +ProcStop dt=17 +ProcStart dt=110606 p=2 p_seq=13 +ProcStop dt=10 +ProcStart dt=16732 p=2 p_seq=14 +GoUnblock dt=45 g=18 g_seq=2 stack=0 +GoStart dt=184 g=18 g_seq=3 +GoBlock dt=114 reason_string=12 stack=21 +ProcStop dt=8 +ProcStart dt=16779 p=4 p_seq=16 +ProcStop dt=11 +ProcStart dt=16790 p=4 p_seq=17 +GoUnblock dt=23 g=1 g_seq=19 stack=0 +GoUnblock dt=8 g=56 g_seq=2 stack=0 +GoStart dt=142 g=56 g_seq=3 +UserTaskEnd dt=14 task=22 stack=36 +UserRegionEnd dt=8 task=21 name_string=31 stack=28 +GoDestroy dt=5 +GoStart dt=18 g=1 g_seq=20 +UserTaskEnd dt=17 task=21 stack=70 +UserTaskEnd dt=12 task=4 stack=71 +HeapAlloc dt=802 heapalloc_value=1835008 +HeapAlloc dt=41 heapalloc_value=1843200 +HeapAlloc dt=13 heapalloc_value=1851392 +EventBatch gen=1 m=1986494 time=2753925248778 size=47 +ProcStart dt=390 p=3 p_seq=1 +GoStart dt=1718 g=22 g_seq=1 +HeapAlloc dt=1807 heapalloc_value=1654784 +HeapAlloc dt=406 heapalloc_value=1671168 +HeapAlloc dt=15 heapalloc_value=1679360 +UserRegionBegin dt=49 task=5 name_string=35 stack=26 +UserLog dt=30 task=5 key_string=24 value_string=36 stack=27 +UserRegionEnd dt=5 task=5 name_string=35 stack=28 +GoDestroy dt=5 +ProcStop dt=42 +EventBatch gen=1 m=1986492 time=2753925244400 size=582 +ProcStatus dt=67 p=1 pstatus=1 +GoStatus dt=4 g=1 m=1986492 gstatus=2 +ProcsChange dt=220 procs_value=8 stack=1 +STWBegin dt=127 kind_string=21 stack=2 +HeapGoal dt=3 heapgoal_value=4194304 +ProcStatus dt=2 p=0 pstatus=2 +ProcStatus dt=2 p=2 pstatus=2 +ProcStatus dt=1 p=3 pstatus=2 +ProcStatus dt=1 p=4 pstatus=2 +ProcStatus dt=1 p=5 pstatus=2 +ProcStatus dt=1 p=6 pstatus=2 +ProcStatus dt=1 p=7 pstatus=2 +ProcsChange dt=353 procs_value=8 stack=3 +STWEnd dt=277 +HeapAlloc dt=243 heapalloc_value=1605632 +HeapAlloc dt=24 heapalloc_value=1613824 +GoCreate dt=209 new_g=18 new_stack=4 stack=5 +GoCreate dt=561 new_g=19 new_stack=6 stack=7 +GoCreate dt=25 new_g=20 new_stack=8 stack=9 +UserTaskEnd dt=309 task=2 stack=10 +UserTaskBegin dt=26 task=3 parent_task=1 name_string=22 stack=11 +UserTaskBegin dt=918 task=4 parent_task=0 name_string=23 stack=12 +UserLog dt=461 task=0 key_string=24 value_string=25 stack=13 +UserTaskBegin dt=420 task=5 parent_task=0 name_string=26 stack=14 +UserLog dt=673 task=5 key_string=27 value_string=28 stack=15 +UserRegionBegin dt=15 task=5 name_string=29 stack=16 +HeapAlloc dt=51 heapalloc_value=1630208 +GoCreate dt=24 new_g=21 new_stack=17 stack=18 +GoCreate dt=17 new_g=22 new_stack=17 stack=18 +GoCreate dt=10 new_g=23 new_stack=17 stack=18 +GoCreate dt=9 new_g=24 new_stack=17 stack=18 +UserRegionEnd dt=549 task=5 name_string=29 stack=19 +GoBlock dt=14 reason_string=19 stack=20 +GoStart dt=378 g=24 g_seq=1 +HeapAlloc dt=65 heapalloc_value=1638400 +GoUnblock dt=559 g=21 g_seq=2 stack=24 +UserRegionBegin dt=1498 task=5 name_string=30 stack=26 +UserLog dt=35 task=5 key_string=24 value_string=32 stack=27 +UserRegionEnd dt=8 task=5 name_string=30 stack=28 +GoDestroy dt=5 +GoStart dt=24 g=21 g_seq=3 +UserRegionBegin dt=60 task=5 name_string=33 stack=26 +UserLog dt=7 task=5 key_string=24 value_string=34 stack=27 +UserRegionEnd dt=2 task=5 name_string=33 stack=28 +GoDestroy dt=2 +ProcStop dt=34 +ProcStart dt=141874 p=0 p_seq=3 +ProcStop dt=21 +ProcStart dt=16770 p=0 p_seq=4 +GoUnblock dt=29 g=23 g_seq=2 stack=0 +GoStart dt=176 g=23 g_seq=3 +UserTaskEnd dt=19 task=6 stack=36 +UserRegionEnd dt=14 task=5 name_string=31 stack=28 +GoDestroy dt=2 +ProcStop dt=12 +ProcStart dt=2251 p=4 p_seq=3 +ProcStop dt=22 +ProcStart dt=141952 p=2 p_seq=3 +ProcStop dt=27 +ProcStart dt=16789 p=2 p_seq=4 +GoUnblock dt=35 g=1 g_seq=3 stack=0 +GoStart dt=214 g=1 g_seq=4 +UserTaskEnd dt=26 task=7 stack=45 +UserLog dt=27 task=2 key_string=24 value_string=44 stack=46 +UserTaskBegin dt=10 task=8 parent_task=2 name_string=26 stack=47 +HeapAlloc dt=52 heapalloc_value=1744896 +HeapAlloc dt=22 heapalloc_value=1753088 +UserLog dt=13 task=8 key_string=27 value_string=45 stack=48 +UserRegionBegin dt=11 task=8 name_string=29 stack=49 +HeapAlloc dt=7 heapalloc_value=1761280 +HeapAlloc dt=18 heapalloc_value=1769472 +GoCreate dt=52 new_g=49 new_stack=17 stack=50 +GoCreate dt=12 new_g=50 new_stack=17 stack=50 +HeapAlloc dt=11 heapalloc_value=1777664 +GoCreate dt=9 new_g=51 new_stack=17 stack=50 +UserRegionEnd dt=9 task=8 name_string=29 stack=51 +GoBlock dt=11 reason_string=19 stack=52 +HeapAlloc dt=12 heapalloc_value=1785856 +GoStart dt=14 g=51 g_seq=1 +HeapAlloc dt=18 heapalloc_value=1794048 +UserRegionBegin dt=95 task=8 name_string=31 stack=26 +UserLog dt=22 task=8 key_string=24 value_string=46 stack=27 +UserLog dt=8 task=8 key_string=24 value_string=47 stack=30 +UserTaskBegin dt=5 task=9 parent_task=8 name_string=26 stack=31 +UserLog dt=7 task=9 key_string=27 value_string=48 stack=32 +UserRegionBegin dt=4 task=9 name_string=29 stack=33 +UserRegionEnd dt=7 task=9 name_string=29 stack=34 +HeapAlloc dt=11 heapalloc_value=1802240 +GoStop dt=674 reason_string=16 stack=53 +GoStart dt=12 g=51 g_seq=2 +GoBlock dt=8 reason_string=19 stack=35 +HeapAlloc dt=16 heapalloc_value=1810432 +ProcStop dt=8 +ProcStart dt=159907 p=0 p_seq=8 +ProcStop dt=25 +ProcStart dt=159186 p=2 p_seq=6 +GoUnblock dt=22 g=37 g_seq=2 stack=0 +GoStart dt=217 g=37 g_seq=3 +UserTaskEnd dt=19 task=11 stack=36 +UserRegionEnd dt=15 task=10 name_string=31 stack=28 +GoDestroy dt=5 +ProcStop dt=16 +ProcStart dt=160988 p=0 p_seq=11 +ProcStop dt=29 +ProcStart dt=158554 p=2 p_seq=8 +GoUnblock dt=38 g=1 g_seq=11 stack=0 +GoStart dt=240 g=1 g_seq=12 +UserTaskEnd dt=25 task=14 stack=37 +UserLog dt=23 task=1 key_string=24 value_string=80 stack=38 +UserTaskBegin dt=11 task=16 parent_task=1 name_string=26 stack=39 +UserLog dt=36 task=16 key_string=27 value_string=81 stack=40 +UserRegionBegin dt=13 task=16 name_string=29 stack=41 +GoCreate dt=39 new_g=52 new_stack=17 stack=42 +GoCreate dt=23 new_g=53 new_stack=17 stack=42 +UserRegionEnd dt=11 task=16 name_string=29 stack=43 +GoBlock dt=9 reason_string=19 stack=44 +GoStart dt=244 g=53 g_seq=1 +UserRegionBegin dt=101 task=16 name_string=35 stack=26 +UserLog dt=17 task=16 key_string=24 value_string=82 stack=27 +UserRegionEnd dt=4 task=16 name_string=35 stack=28 +GoDestroy dt=3 +ProcStop dt=28 +EventBatch gen=1 m=18446744073709551615 time=2753926855140 size=56 +GoStatus dt=74 g=2 m=18446744073709551615 gstatus=4 +GoStatus dt=3 g=3 m=18446744073709551615 gstatus=4 +GoStatus dt=1 g=4 m=18446744073709551615 gstatus=4 +GoStatus dt=1 g=17 m=18446744073709551615 gstatus=4 +EventBatch gen=1 m=18446744073709551615 time=2753926855560 size=1759 +Stacks +Stack id=45 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803566 func=114 file=113 line=44 +Stack id=22 nframes=7 + pc=4633935 func=115 file=116 line=90 + pc=4633896 func=117 file=118 line=223 + pc=4633765 func=119 file=118 line=216 + pc=4633083 func=120 file=118 line=131 + pc=4764601 func=121 file=122 line=152 + pc=4765335 func=123 file=122 line=238 + pc=4804612 func=124 file=113 line=70 +Stack id=9 nframes=2 + pc=4802543 func=125 file=126 line=128 + pc=4803332 func=114 file=113 line=30 +Stack id=71 nframes=2 + pc=4803671 func=110 file=111 line=80 + pc=4803666 func=114 file=113 line=51 +Stack id=10 nframes=2 + pc=4803415 func=110 file=111 line=80 + pc=4803410 func=114 file=113 line=33 +Stack id=18 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803543 func=114 file=113 line=43 +Stack id=37 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803543 func=114 file=113 line=43 +Stack id=31 nframes=4 + pc=4803865 func=112 file=113 line=61 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=55 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803609 func=114 file=113 line=46 +Stack id=47 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803589 func=114 file=113 line=45 +Stack id=38 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803566 func=114 file=113 line=44 +Stack id=56 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803609 func=114 file=113 line=46 +Stack id=33 nframes=4 + pc=4804022 func=112 file=113 line=67 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=44 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803566 func=114 file=113 line=44 +Stack id=3 nframes=4 + pc=4421707 func=132 file=133 line=1382 + pc=4533555 func=134 file=135 line=255 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=6 nframes=1 + pc=4539520 func=136 file=135 line=868 +Stack id=58 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803609 func=114 file=113 line=46 +Stack id=64 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803629 func=114 file=113 line=47 +Stack id=62 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803609 func=114 file=113 line=46 +Stack id=34 nframes=4 + pc=4804022 func=112 file=113 line=67 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=30 nframes=4 + pc=4803832 func=112 file=113 line=58 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=32 nframes=4 + pc=4803943 func=112 file=113 line=64 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=26 nframes=1 + pc=4804691 func=124 file=113 line=70 +Stack id=46 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803589 func=114 file=113 line=45 +Stack id=50 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803589 func=114 file=113 line=45 +Stack id=59 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803609 func=114 file=113 line=46 +Stack id=7 nframes=4 + pc=4539492 func=137 file=135 line=868 + pc=4533572 func=134 file=135 line=258 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=17 nframes=1 + pc=4804512 func=124 file=113 line=69 +Stack id=57 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803609 func=114 file=113 line=46 +Stack id=41 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803566 func=114 file=113 line=44 +Stack id=63 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803629 func=114 file=113 line=47 +Stack id=60 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803609 func=114 file=113 line=46 +Stack id=5 nframes=4 + pc=4542549 func=138 file=139 line=42 + pc=4533560 func=134 file=135 line=257 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=40 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803566 func=114 file=113 line=44 +Stack id=21 nframes=3 + pc=4217905 func=140 file=141 line=442 + pc=4539946 func=142 file=135 line=928 + pc=4542714 func=143 file=139 line=54 +Stack id=2 nframes=3 + pc=4533284 func=134 file=135 line=238 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=53 nframes=6 + pc=4247492 func=144 file=145 line=1374 + pc=4599676 func=130 file=131 line=186 + pc=4804036 func=112 file=113 line=83 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=20 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803543 func=114 file=113 line=43 +Stack id=70 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803629 func=114 file=113 line=47 +Stack id=15 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803543 func=114 file=113 line=43 +Stack id=65 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803629 func=114 file=113 line=47 +Stack id=28 nframes=1 + pc=4804691 func=124 file=113 line=70 +Stack id=48 nframes=2 + pc=4803943 func=112 file=113 line=64 + pc=4803589 func=114 file=113 line=45 +Stack id=61 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803609 func=114 file=113 line=46 +Stack id=13 nframes=2 + pc=4803832 func=112 file=113 line=58 + pc=4803543 func=114 file=113 line=43 +Stack id=29 nframes=3 + pc=4217905 func=140 file=141 line=442 + pc=4539946 func=142 file=135 line=928 + pc=4539559 func=136 file=135 line=871 +Stack id=51 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803589 func=114 file=113 line=45 +Stack id=42 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803566 func=114 file=113 line=44 +Stack id=14 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803543 func=114 file=113 line=43 +Stack id=39 nframes=2 + pc=4803865 func=112 file=113 line=61 + pc=4803566 func=114 file=113 line=44 +Stack id=49 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803589 func=114 file=113 line=45 +Stack id=52 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803589 func=114 file=113 line=45 +Stack id=24 nframes=7 + pc=4634510 func=146 file=116 line=223 + pc=4634311 func=117 file=118 line=240 + pc=4633765 func=119 file=118 line=216 + pc=4633083 func=120 file=118 line=131 + pc=4764601 func=121 file=122 line=152 + pc=4765335 func=123 file=122 line=238 + pc=4804612 func=124 file=113 line=70 +Stack id=43 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803566 func=114 file=113 line=44 +Stack id=19 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803543 func=114 file=113 line=43 +Stack id=69 nframes=3 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4803629 func=114 file=113 line=47 +Stack id=16 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803543 func=114 file=113 line=43 +Stack id=54 nframes=3 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4803589 func=114 file=113 line=45 +Stack id=35 nframes=5 + pc=4599892 func=130 file=131 line=195 + pc=4804036 func=112 file=113 line=83 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=27 nframes=3 + pc=4804862 func=129 file=113 line=71 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=4 nframes=1 + pc=4542656 func=143 file=139 line=42 +Stack id=8 nframes=1 + pc=4802720 func=147 file=126 line=128 +Stack id=66 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803629 func=114 file=113 line=47 +Stack id=1 nframes=4 + pc=4548715 func=148 file=149 line=255 + pc=4533263 func=134 file=135 line=237 + pc=4802469 func=125 file=126 line=125 + pc=4803332 func=114 file=113 line=30 +Stack id=67 nframes=4 + pc=4804196 func=127 file=113 line=69 + pc=4802140 func=128 file=111 line=141 + pc=4804022 func=112 file=113 line=67 + pc=4803629 func=114 file=113 line=47 +Stack id=23 nframes=7 + pc=4641050 func=150 file=151 line=964 + pc=4751591 func=152 file=153 line=209 + pc=4751583 func=154 file=155 line=736 + pc=4751136 func=156 file=155 line=380 + pc=4753008 func=157 file=158 line=46 + pc=4753000 func=159 file=160 line=183 + pc=4802778 func=147 file=126 line=134 +Stack id=11 nframes=1 + pc=4803445 func=114 file=113 line=36 +Stack id=68 nframes=2 + pc=4804022 func=112 file=113 line=67 + pc=4803629 func=114 file=113 line=47 +Stack id=36 nframes=5 + pc=4804964 func=110 file=111 line=80 + pc=4804052 func=112 file=113 line=84 + pc=4804890 func=129 file=113 line=73 + pc=4802140 func=128 file=111 line=141 + pc=4804691 func=124 file=113 line=70 +Stack id=12 nframes=1 + pc=4803492 func=114 file=113 line=39 +Stack id=25 nframes=1 + pc=4802788 func=147 file=126 line=130 +EventBatch gen=1 m=18446744073709551615 time=2753925243266 size=3466 +Strings +String id=1 + data="Not worker" +String id=2 + data="GC (dedicated)" +String id=3 + data="GC (fractional)" +String id=4 + data="GC (idle)" +String id=5 + data="unspecified" +String id=6 + data="forever" +String id=7 + data="network" +String id=8 + data="select" +String id=9 + data="sync.(*Cond).Wait" +String id=10 + data="sync" +String id=11 + data="chan send" +String id=12 + data="chan receive" +String id=13 + data="GC mark assist wait for work" +String id=14 + data="GC background sweeper wait" +String id=15 + data="system goroutine wait" +String id=16 + data="preempted" +String id=17 + data="wait for debug call" +String id=18 + data="wait until GC ends" +String id=19 + data="sleep" +String id=20 + data="runtime.GoSched" +String id=21 + data="start trace" +String id=22 + data="type2" +String id=23 + data="type3" +String id=24 + data="log" +String id=25 + data="before do" +String id=26 + data="do" +String id=27 + data="log2" +String id=28 + data="do" +String id=29 + data="fanout" +String id=30 + data="region3" +String id=31 + data="region2" +String id=32 + data="fanout region3" +String id=33 + data="region0" +String id=34 + data="fanout region0" +String id=35 + data="region1" +String id=36 + data="fanout region1" +String id=37 + data="fanout region2" +String id=38 + data="before do" +String id=39 + data="do" +String id=40 + data="before do" +String id=41 + data="do" +String id=42 + data="fanout region1" +String id=43 + data="fanout region0" +String id=44 + data="before do" +String id=45 + data="do" +String id=46 + data="fanout region2" +String id=47 + data="before do" +String id=48 + data="do" +String id=49 + data="fanout region0" +String id=50 + data="fanout region1" +String id=51 + data="before do" +String id=52 + data="do" +String id=53 + data="region5" +String id=54 + data="fanout region5" +String id=55 + data="fanout region3" +String id=56 + data="region4" +String id=57 + data="fanout region4" +String id=58 + data="fanout region0" +String id=59 + data="fanout region1" +String id=60 + data="fanout region2" +String id=61 + data="before do" +String id=62 + data="do" +String id=63 + data="before do" +String id=64 + data="do" +String id=65 + data="fanout region4" +String id=66 + data="fanout region0" +String id=67 + data="fanout region1" +String id=68 + data="fanout region2" +String id=69 + data="before do" +String id=70 + data="do" +String id=71 + data="fanout region3" +String id=72 + data="before do" +String id=73 + data="do" +String id=74 + data="fanout region3" +String id=75 + data="fanout region0" +String id=76 + data="fanout region1" +String id=77 + data="fanout region2" +String id=78 + data="before do" +String id=79 + data="do" +String id=80 + data="before do" +String id=81 + data="do" +String id=82 + data="fanout region1" +String id=83 + data="fanout region0" +String id=84 + data="before do" +String id=85 + data="do" +String id=86 + data="fanout region2" +String id=87 + data="before do" +String id=88 + data="do" +String id=89 + data="fanout region0" +String id=90 + data="fanout region1" +String id=91 + data="before do" +String id=92 + data="do" +String id=93 + data="fanout region5" +String id=94 + data="fanout region0" +String id=95 + data="fanout region1" +String id=96 + data="fanout region2" +String id=97 + data="before do" +String id=98 + data="do" +String id=99 + data="fanout region4" +String id=100 + data="fanout region3" +String id=101 + data="before do" +String id=102 + data="do" +String id=103 + data="fanout region4" +String id=104 + data="fanout region0" +String id=105 + data="fanout region1" +String id=106 + data="fanout region2" +String id=107 + data="before do" +String id=108 + data="do" +String id=109 + data="fanout region3" +String id=110 + data="runtime/trace.(*Task).End" +String id=111 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/annotation.go" +String id=112 + data="main.do" +String id=113 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/trace/v2/testdata/testprog/annotations-stress.go" +String id=114 + data="main.main" +String id=115 + data="sync.(*Mutex).Lock" +String id=116 + data="/usr/local/google/home/mknyszek/work/go-1/src/sync/mutex.go" +String id=117 + data="sync.(*Pool).pinSlow" +String id=118 + data="/usr/local/google/home/mknyszek/work/go-1/src/sync/pool.go" +String id=119 + data="sync.(*Pool).pin" +String id=120 + data="sync.(*Pool).Get" +String id=121 + data="fmt.newPrinter" +String id=122 + data="/usr/local/google/home/mknyszek/work/go-1/src/fmt/print.go" +String id=123 + data="fmt.Sprintf" +String id=124 + data="main.do.func1.1" +String id=125 + data="runtime/trace.Start" +String id=126 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace/trace.go" +String id=127 + data="main.do.func1" +String id=128 + data="runtime/trace.WithRegion" +String id=129 + data="main.do.func1.1.1" +String id=130 + data="time.Sleep" +String id=131 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/time.go" +String id=132 + data="runtime.startTheWorld" +String id=133 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/proc.go" +String id=134 + data="runtime.StartTrace" +String id=135 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2.go" +String id=136 + data="runtime.(*traceAdvancerState).start.func1" +String id=137 + data="runtime.(*traceAdvancerState).start" +String id=138 + data="runtime.traceStartReadCPU" +String id=139 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2cpu.go" +String id=140 + data="runtime.chanrecv1" +String id=141 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/chan.go" +String id=142 + data="runtime.(*wakeableSleep).sleep" +String id=143 + data="runtime.traceStartReadCPU.func1" +String id=144 + data="runtime.newobject" +String id=145 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/malloc.go" +String id=146 + data="sync.(*Mutex).Unlock" +String id=147 + data="runtime/trace.Start.func1" +String id=148 + data="runtime.traceLocker.Gomaxprocs" +String id=149 + data="/usr/local/google/home/mknyszek/work/go-1/src/runtime/trace2runtime.go" +String id=150 + data="syscall.write" +String id=151 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/zsyscall_linux_amd64.go" +String id=152 + data="syscall.Write" +String id=153 + data="/usr/local/google/home/mknyszek/work/go-1/src/syscall/syscall_unix.go" +String id=154 + data="internal/poll.ignoringEINTRIO" +String id=155 + data="/usr/local/google/home/mknyszek/work/go-1/src/internal/poll/fd_unix.go" +String id=156 + data="internal/poll.(*FD).Write" +String id=157 + data="os.(*File).write" +String id=158 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file_posix.go" +String id=159 + data="os.(*File).Write" +String id=160 + data="/usr/local/google/home/mknyszek/work/go-1/src/os/file.go" diff --git a/src/internal/trace/v2/testdata/tests/go122-create-syscall-reuse-thread-id.test b/src/internal/trace/v2/testdata/tests/go122-create-syscall-reuse-thread-id.test new file mode 100644 index 0000000000..1820738384 --- /dev/null +++ b/src/internal/trace/v2/testdata/tests/go122-create-syscall-reuse-thread-id.test @@ -0,0 +1,23 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=37 +GoCreateSyscall dt=1 new_g=4 +GoSyscallEndBlocked dt=1 +ProcStatus dt=1 p=0 pstatus=2 +ProcStart dt=1 p=0 p_seq=1 +GoStatus dt=1 g=4 m=18446744073709551615 gstatus=1 +GoStart dt=1 g=4 g_seq=1 +GoSyscallBegin dt=1 p_seq=2 stack=0 +GoDestroySyscall dt=1 +EventBatch gen=1 m=0 time=0 size=13 +ProcStatus dt=1 p=1 pstatus=2 +ProcStart dt=1 p=1 p_seq=1 +ProcSteal dt=1 p=0 p_seq=3 m=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-create-syscall-with-p.test b/src/internal/trace/v2/testdata/tests/go122-create-syscall-with-p.test new file mode 100644 index 0000000000..9b329b8bae --- /dev/null +++ b/src/internal/trace/v2/testdata/tests/go122-create-syscall-with-p.test @@ -0,0 +1,22 @@ +-- expect -- +FAILURE ".*expected a proc but didn't have one.*" +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=34 +GoCreateSyscall dt=1 new_g=4 +ProcStatus dt=1 p=0 pstatus=2 +ProcStart dt=1 p=0 p_seq=1 +GoSyscallEndBlocked dt=1 +GoStart dt=1 g=4 g_seq=1 +GoSyscallBegin dt=1 p_seq=2 stack=0 +GoDestroySyscall dt=1 +GoCreateSyscall dt=1 new_g=4 +GoSyscallEnd dt=1 +GoSyscallBegin dt=1 p_seq=3 stack=0 +GoDestroySyscall dt=1 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-self.test b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-self.test new file mode 100644 index 0000000000..6484eb6d35 --- /dev/null +++ b/src/internal/trace/v2/testdata/tests/go122-syscall-steal-proc-self.test @@ -0,0 +1,17 @@ +-- expect -- +SUCCESS +-- trace -- +Trace Go1.22 +EventBatch gen=1 m=0 time=0 size=24 +ProcStatus dt=0 p=0 pstatus=1 +GoStatus dt=0 g=1 m=0 gstatus=2 +GoSyscallBegin dt=0 p_seq=1 stack=0 +ProcSteal dt=0 p=0 p_seq=2 m=0 +ProcStart dt=0 p=0 p_seq=3 +GoSyscallEndBlocked dt=0 +EventBatch gen=1 m=18446744073709551615 time=0 size=5 +Frequency freq=15625000 +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Stacks +EventBatch gen=1 m=18446744073709551615 time=0 size=1 +Strings diff --git a/src/internal/trace/v2/testtrace/validation.go b/src/internal/trace/v2/testtrace/validation.go index fcbc10801b..021c7785fd 100644 --- a/src/internal/trace/v2/testtrace/validation.go +++ b/src/internal/trace/v2/testtrace/validation.go @@ -169,6 +169,11 @@ func (v *Validator) Event(ev trace.Event) error { state.binding = ctx } } else if old.Executing() && !new.Executing() { + if tr.Stack != ev.Stack() { + // This is a case where the transition is happening to a goroutine that is also executing, so + // these two stacks should always match. + e.Errorf("StateTransition.Stack doesn't match Event.Stack") + } ctx := state.binding if ctx != nil { if ctx.G != id { @@ -220,7 +225,7 @@ func (v *Validator) Event(ev trace.Event) error { ctx := state.binding if ctx != nil { if ctx.P != id { - e.Errorf("tried to stop proc %d when it wasn't currently executing (currently executing %d) on thread %d", id, ctx.P, ev.Thread()) + e.Errorf("tried to stop proc %d when it wasn't currently executing (currently executing %d) on thread %d", id, ctx.P, ctx.M) } ctx.P = trace.NoProc state.binding = nil @@ -251,9 +256,14 @@ func (v *Validator) Event(ev trace.Event) error { case trace.EventTaskBegin: // Validate task begin. t := ev.Task() - if t.ID == trace.NoTask { + if t.ID == trace.NoTask || t.ID == trace.BackgroundTask { + // The background task should never have an event emitted for it. e.Errorf("found invalid task ID for task of type %s", t.Type) } + if t.Parent == trace.BackgroundTask { + // It's not possible for a task to be a subtask of the background task. + e.Errorf("found background task as the parent for task of type %s", t.Type) + } // N.B. Don't check the task type. Empty string is a valid task type. v.tasks[t.ID] = t.Type case trace.EventTaskEnd: diff --git a/src/internal/trace/v2/trace_test.go b/src/internal/trace/v2/trace_test.go index 7823b01e93..3300c00fe8 100644 --- a/src/internal/trace/v2/trace_test.go +++ b/src/internal/trace/v2/trace_test.go @@ -35,7 +35,7 @@ func TestTraceAnnotations(t *testing.T) { {trace.EventRegionEnd, trace.TaskID(1), []string{"region0"}}, {trace.EventTaskEnd, trace.TaskID(1), []string{"task0"}}, // Currently, pre-existing region is not recorded to avoid allocations. - {trace.EventRegionBegin, trace.NoTask, []string{"post-existing region"}}, + {trace.EventRegionBegin, trace.BackgroundTask, []string{"post-existing region"}}, } r, err := trace.NewReader(bytes.NewReader(tb)) if err != nil { @@ -526,6 +526,7 @@ func testTraceProg(t *testing.T, progName string, extra func(t *testing.T, trace // Check if we're on a builder. onBuilder := testenv.Builder() != "" + onOldBuilder := !strings.Contains(testenv.Builder(), "gotip") && !strings.Contains(testenv.Builder(), "go1") testPath := filepath.Join("./testdata/testprog", progName) testName := progName @@ -567,7 +568,18 @@ func testTraceProg(t *testing.T, progName string, extra func(t *testing.T, trace // data is critical for debugging and this is the only way // we can currently make sure it's retained. t.Log("found bad trace; dumping to test log...") - t.Log(dumpTraceToText(t, tb)) + s := dumpTraceToText(t, tb) + if onOldBuilder && len(s) > 1<<20+512<<10 { + // The old build infrastructure truncates logs at ~2 MiB. + // Let's assume we're the only failure and give ourselves + // up to 1.5 MiB to dump the trace. + // + // TODO(mknyszek): Remove this when we've migrated off of + // the old infrastructure. + t.Logf("text trace too large to dump (%d bytes)", len(s)) + } else { + t.Log(s) + } } else if t.Failed() || *dumpTraces { // We asked to dump the trace or failed. Write the trace to a file. t.Logf("wrote trace to file: %s", dumpTraceToFile(t, testName, stress, tb)) diff --git a/src/internal/trace/v2/version/version.go b/src/internal/trace/v2/version/version.go index bb4df8469e..28189f80db 100644 --- a/src/internal/trace/v2/version/version.go +++ b/src/internal/trace/v2/version/version.go @@ -16,7 +16,8 @@ import ( type Version uint32 const ( - Go122 Version = 22 + Go122 Version = 22 + Current = Go122 ) var versions = map[Version][]event.Spec{ diff --git a/src/internal/types/testdata/check/typeinference.go b/src/internal/types/testdata/check/typeinference.go index 0478d9390f..8dac938ef9 100644 --- a/src/internal/types/testdata/check/typeinference.go +++ b/src/internal/types/testdata/check/typeinference.go @@ -8,8 +8,9 @@ package typeInference // basic inference type Tb[P ~*Q, Q any] int + func _() { - var x Tb /* ERROR "got 1 arguments" */ [*int] + var x Tb /* ERROR "not enough type arguments for type Tb: have 1, want 2" */ [*int] var y Tb[*int, int] x = y /* ERRORx `cannot use y .* in assignment` */ _ = x @@ -17,8 +18,9 @@ func _() { // recursive inference type Tr[A any, B *C, C *D, D *A] int + func _() { - var x Tr /* ERROR "got 1 arguments" */ [string] + var x Tr /* ERROR "not enough type arguments for type Tr: have 1, want 4" */ [string] var y Tr[string, ***string, **string, *string] var z Tr[int, ***int, **int, *int] x = y /* ERRORx `cannot use y .* in assignment` */ @@ -28,22 +30,30 @@ func _() { // other patterns of inference type To0[A any, B []A] int -type To1[A any, B struct{a A}] int +type To1[A any, B struct{ a A }] int type To2[A any, B [][]A] int type To3[A any, B [3]*A] int -type To4[A any, B any, C struct{a A; b B}] int +type To4[A any, B any, C struct { + a A + b B +}] int + func _() { - var _ To0 /* ERROR "got 1 arguments" */ [int] - var _ To1 /* ERROR "got 1 arguments" */ [int] - var _ To2 /* ERROR "got 1 arguments" */ [int] - var _ To3 /* ERROR "got 1 arguments" */ [int] - var _ To4 /* ERROR "got 2 arguments" */ [int, string] + var _ To0 /* ERROR "not enough type arguments for type To0: have 1, want 2" */ [int] + var _ To1 /* ERROR "not enough type arguments for type To1: have 1, want 2" */ [int] + var _ To2 /* ERROR "not enough type arguments for type To2: have 1, want 2" */ [int] + var _ To3 /* ERROR "not enough type arguments for type To3: have 1, want 2" */ [int] + var _ To4 /* ERROR "not enough type arguments for type To4: have 2, want 3" */ [int, string] } // failed inference type Tf0[A, B any] int -type Tf1[A any, B ~struct{a A; c C}, C any] int +type Tf1[A any, B ~struct { + a A + c C +}, C any] int + func _() { - var _ Tf0 /* ERROR "got 1 arguments but 2 type parameters" */ [int] - var _ Tf1 /* ERROR "got 1 arguments but 3 type parameters" */ [int] + var _ Tf0 /* ERROR "not enough type arguments for type Tf0: have 1, want 2" */ [int] + var _ Tf1 /* ERROR "not enough type arguments for type Tf1: have 1, want 3" */ [int] } diff --git a/src/internal/types/testdata/check/typeinst0.go b/src/internal/types/testdata/check/typeinst0.go index bbcdaec04a..155f1ef440 100644 --- a/src/internal/types/testdata/check/typeinst0.go +++ b/src/internal/types/testdata/check/typeinst0.go @@ -42,7 +42,7 @@ type _ myInt /* ERROR "not a generic type" */ [] // ERROR "expected type argumen // TODO(gri) better error messages type _ T1[] // ERROR "expected type argument list" type _ T1[x /* ERROR "not a type" */ ] -type _ T1 /* ERROR "got 2 arguments but 1 type parameters" */ [int, float32] +type _ T1 /* ERROR "too many type arguments for type T1: have 2, want 1" */ [int, float32] var _ T2[int] = T2[int]{} diff --git a/src/internal/types/testdata/examples/inference2.go b/src/internal/types/testdata/examples/inference2.go index 6097c2b5eb..91f9df1d84 100644 --- a/src/internal/types/testdata/examples/inference2.go +++ b/src/internal/types/testdata/examples/inference2.go @@ -27,9 +27,9 @@ var ( _ func(int) int = f3[int] v6 func(int, int) = f4 - v7 func(int, string) = f4 // ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)" + v7 func(int, string) = f4 // ERROR "inferred type func(int, int) for func(P, P) does not match type func(int, string) of v7" v8 func(int) []int = f5 - v9 func(string) []int = f5 // ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P" + v9 func(string) []int = f5 // ERROR "inferred type func(string) []string for func(P) []P does not match type func(string) []int of v9" _, _ func(int) = f1, f1 _, _ func(int) = f1, f2 // ERROR "cannot infer P" @@ -49,9 +49,13 @@ func _() { v5 = f3[int] v6 = f4 - v7 = f4 // ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)" + v7 = f4 // ERROR "inferred type func(int, int) for func(P, P) does not match type func(int, string) of v7" v8 = f5 - v9 = f5 // ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P" + v9 = f5 // ERROR "inferred type func(string) []string for func(P) []P does not match type func(string) []int of v9" + + // non-trivial LHS + var a [2]func(string) []int + a[0] = f5 // ERROR "inferred type func(string) []string for func(P) []P does not match type func(string) []int of a[0]" } // Return statements @@ -62,11 +66,11 @@ func _() func(int) int { return f3[int] } func _() func(int, int) { return f4 } func _() func(int, string) { - return f4 /* ERROR "type func(int, string) of variable in assignment does not match inferred type func(int, int) for func(P, P)" */ + return f4 /* ERROR "inferred type func(int, int) for func(P, P) does not match type func(int, string) of result variable" */ } func _() func(int) []int { return f5 } func _() func(string) []int { - return f5 /* ERROR "type func(string) []int of variable in assignment does not match inferred type func(string) []string for func(P) []P" */ + return f5 /* ERROR "inferred type func(string) []string for func(P) []P does not match type func(string) []int of result variable" */ } func _() (_, _ func(int)) { return f1, f1 } diff --git a/src/internal/types/testdata/fixedbugs/issue49541.go b/src/internal/types/testdata/fixedbugs/issue49541.go index da3731195b..665ed1da7c 100644 --- a/src/internal/types/testdata/fixedbugs/issue49541.go +++ b/src/internal/types/testdata/fixedbugs/issue49541.go @@ -13,7 +13,7 @@ func (S[A, B]) m() {} // TODO(gri): with type-type inference enabled we should only report one error // below. See issue #50588. -func _[A any](s S /* ERROR "got 1 arguments but 2 type parameters" */ [A]) { +func _[A any](s S /* ERROR "not enough type arguments for type S: have 1, want 2" */ [A]) { // we should see no follow-on errors below s.f = 1 s.m() @@ -22,7 +22,7 @@ func _[A any](s S /* ERROR "got 1 arguments but 2 type parameters" */ [A]) { // another test case from the issue func _() { - X /* ERROR "cannot infer Q" */ (Interface[*F /* ERROR "got 1 arguments but 2 type parameters" */ [string]](Impl{})) + X /* ERROR "cannot infer Q" */ (Interface[*F /* ERROR "not enough type arguments for type F: have 1, want 2" */ [string]](Impl{})) } func X[Q Qer](fs Interface[Q]) { diff --git a/src/internal/types/testdata/fixedbugs/issue50729b.go b/src/internal/types/testdata/fixedbugs/issue50729b.go new file mode 100644 index 0000000000..bc1f4406e5 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue50729b.go @@ -0,0 +1,15 @@ +// -gotypesalias=1 + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type d[T any] struct{} +type ( + b d[a] +) + +type a = func(c) +type c struct{ a } diff --git a/src/internal/types/testdata/fixedbugs/issue50929.go b/src/internal/types/testdata/fixedbugs/issue50929.go index 64c7cd664f..a665e229be 100644 --- a/src/internal/types/testdata/fixedbugs/issue50929.go +++ b/src/internal/types/testdata/fixedbugs/issue50929.go @@ -16,7 +16,7 @@ func G[A, B any](F[A, B]) { func _() { // TODO(gri) only report one error below (issue #50932) - var x F /* ERROR "got 1 arguments but 2 type parameters" */ [int] + var x F /* ERROR "not enough type arguments for type F: have 1, want 2" */ [int] G(x /* ERROR "does not match" */) } @@ -46,9 +46,9 @@ func NSG[G any](c RSC[G]) { fmt.Println(c) } -func MMD[Rc RC /* ERROR "got 1 arguments" */ [RG], RG any, G any]() M /* ERROR "got 2 arguments" */ [Rc, RG] { +func MMD[Rc RC /* ERROR "not enough type arguments for type RC: have 1, want 2" */ [RG], RG any, G any]() M /* ERROR "not enough type arguments for type" */ [Rc, RG] { - var nFn NFn /* ERROR "got 2 arguments" */ [Rc, RG] + var nFn NFn /* ERROR "not enough type arguments for type NFn: have 2, want 3" */ [Rc, RG] var empty Rc switch any(empty).(type) { @@ -58,11 +58,11 @@ func MMD[Rc RC /* ERROR "got 1 arguments" */ [RG], RG any, G any]() M /* ERROR " nFn = NSG /* ERROR "cannot use NSG[G]" */ [G] } - return M /* ERROR "got 2 arguments" */ [Rc, RG]{ + return M /* ERROR "not enough type arguments for type M: have 2, want 3" */ [Rc, RG]{ Fn: func(rc Rc) { - NC(nFn /* ERROR "does not match" */ ) + NC(nFn /* ERROR "does not match" */) }, } - return M /* ERROR "got 2 arguments" */ [Rc, RG]{} + return M /* ERROR "not enough type arguments for type M: have 2, want 3" */ [Rc, RG]{} } diff --git a/src/internal/types/testdata/fixedbugs/issue51232.go b/src/internal/types/testdata/fixedbugs/issue51232.go index 27693a3e4d..c5832d2976 100644 --- a/src/internal/types/testdata/fixedbugs/issue51232.go +++ b/src/internal/types/testdata/fixedbugs/issue51232.go @@ -11,20 +11,20 @@ type RC[RG any] interface { type Fn[RCT RC[RG], RG any] func(RCT) type F[RCT RC[RG], RG any] interface { - Fn() Fn /* ERROR "got 1 arguments" */ [RCT] + Fn() Fn /* ERROR "not enough type arguments for type Fn: have 1, want 2" */ [RCT] } type concreteF[RCT RC[RG], RG any] struct { - makeFn func() Fn /* ERROR "got 1 arguments" */ [RCT] + makeFn func() Fn /* ERROR "not enough type arguments for type Fn: have 1, want 2" */ [RCT] } -func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR "got 1 arguments" */ [RCT] { +func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR "not enough type arguments for type Fn: have 1, want 2" */ [RCT] { return c.makeFn() } -func NewConcrete[RCT RC[RG], RG any](Rc RCT) F /* ERROR "got 1 arguments" */ [RCT] { +func NewConcrete[RCT RC[RG], RG any](Rc RCT) F /* ERROR "not enough type arguments for type F: have 1, want 2" */ [RCT] { // TODO(rfindley): eliminate the duplicate error below. - return & /* ERRORx `cannot use .* as F\[RCT\]` */ concreteF /* ERROR "got 1 arguments" */ [RCT]{ + return & /* ERRORx `cannot use .* as F\[RCT\]` */ concreteF /* ERROR "not enough type arguments for type concreteF: have 1, want 2" */ [RCT]{ makeFn: nil, } } diff --git a/src/internal/types/testdata/fixedbugs/issue51233.go b/src/internal/types/testdata/fixedbugs/issue51233.go index e2f97fc456..d96d3d1aa0 100644 --- a/src/internal/types/testdata/fixedbugs/issue51233.go +++ b/src/internal/types/testdata/fixedbugs/issue51233.go @@ -12,16 +12,16 @@ type RC[RG any] interface { type Fn[RCT RC[RG], RG any] func(RCT) -type FFn[RCT RC[RG], RG any] func() Fn /* ERROR "got 1 arguments" */ [RCT] +type FFn[RCT RC[RG], RG any] func() Fn /* ERROR "not enough type arguments for type Fn: have 1, want 2" */ [RCT] type F[RCT RC[RG], RG any] interface { - Fn() Fn /* ERROR "got 1 arguments" */ [RCT] + Fn() Fn /* ERROR "not enough type arguments for type Fn: have 1, want 2" */ [RCT] } type concreteF[RCT RC[RG], RG any] struct { - makeFn FFn /* ERROR "got 1 arguments" */ [RCT] + makeFn FFn /* ERROR "not enough type arguments for type FFn: have 1, want 2" */ [RCT] } -func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR "got 1 arguments" */ [RCT] { +func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR "not enough type arguments for type Fn: have 1, want 2" */ [RCT] { return c.makeFn() } diff --git a/src/internal/types/testdata/fixedbugs/issue51339.go b/src/internal/types/testdata/fixedbugs/issue51339.go index 65c213462b..fd10daa2c2 100644 --- a/src/internal/types/testdata/fixedbugs/issue51339.go +++ b/src/internal/types/testdata/fixedbugs/issue51339.go @@ -9,10 +9,12 @@ package p type T[P any, B *P] struct{} -func (T /* ERROR "cannot use generic type" */ ) m0() {} +func (T /* ERROR "cannot use generic type" */) m0() {} // TODO(rfindley): eliminate the duplicate errors here. -func (/* ERROR "got 1 type parameter, but receiver base type declares 2" */ T /* ERROR "got 1 arguments but 2 type parameters" */ [_]) m1() {} +func ( /* ERROR "got 1 type parameter, but receiver base type declares 2" */ T /* ERROR "not enough type arguments for type" */ [_]) m1() { +} func (T[_, _]) m2() {} + // TODO(gri) this error is unfortunate (issue #51343) -func (T /* ERROR "got 3 arguments but 2 type parameters" */ [_, _, _]) m3() {} +func (T /* ERROR "too many type arguments for type" */ [_, _, _]) m3() {} diff --git a/src/internal/types/testdata/fixedbugs/issue60688.go b/src/internal/types/testdata/fixedbugs/issue60688.go index 38d90ee8cc..61b9f91510 100644 --- a/src/internal/types/testdata/fixedbugs/issue60688.go +++ b/src/internal/types/testdata/fixedbugs/issue60688.go @@ -13,4 +13,4 @@ func g[P any](P, string) {} // be identical to match). // The result is an error from type inference, rather than an // error from an assignment mismatch. -var f func(int, String) = g // ERROR "type func(int, String) of variable in assignment does not match inferred type func(int, string) for func(P, string)" +var f func(int, String) = g // ERROR "inferred type func(int, string) for func(P, string) does not match type func(int, String) of f" diff --git a/src/internal/types/testdata/fixedbugs/issue60747.go b/src/internal/types/testdata/fixedbugs/issue60747.go new file mode 100644 index 0000000000..6587a4e557 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue60747.go @@ -0,0 +1,13 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func f[P any](P) P { panic(0) } + +var v func(string) int = f // ERROR "inferred type func(string) string for func(P) P does not match type func(string) int of v" + +func _() func(string) int { + return f // ERROR "inferred type func(string) string for func(P) P does not match type func(string) int of result variable" +} diff --git a/src/internal/types/testdata/fixedbugs/issue61685.go b/src/internal/types/testdata/fixedbugs/issue61685.go new file mode 100644 index 0000000000..b88b222eb9 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue61685.go @@ -0,0 +1,15 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func _[T any](x any) { + f /* ERROR "T (type I[T]) does not satisfy I[T] (wrong type for method m)" */ (x.(I[T])) +} + +func f[T I[T]](T) {} + +type I[T any] interface { + m(T) +} diff --git a/src/internal/types/testdata/fixedbugs/issue64406.go b/src/internal/types/testdata/fixedbugs/issue64406.go new file mode 100644 index 0000000000..54b959dbba --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue64406.go @@ -0,0 +1,23 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue64406 + +import ( + "unsafe" +) + +func sliceData[E any, S ~[]E](s S) *E { + return unsafe.SliceData(s) +} + +func slice[E any, S ~*E](s S) []E { + return unsafe.Slice(s, 0) +} + +func f() { + s := []uint32{0} + _ = sliceData(s) + _ = slice(&s) +} diff --git a/src/internal/types/testdata/fixedbugs/issue64704.go b/src/internal/types/testdata/fixedbugs/issue64704.go new file mode 100644 index 0000000000..c8e9056cdd --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue64704.go @@ -0,0 +1,12 @@ +// -lang=go1.21 + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func _() { + for range 10 /* ERROR "cannot range over 10 (untyped int constant): requires go1.22 or later" */ { + } +} diff --git a/src/io/fs/readdir_test.go b/src/io/fs/readdir_test.go index a2b2c121ff..4c409ae7a0 100644 --- a/src/io/fs/readdir_test.go +++ b/src/io/fs/readdir_test.go @@ -5,6 +5,7 @@ package fs_test import ( + "errors" . "io/fs" "os" "testing" @@ -91,3 +92,20 @@ func TestFileInfoToDirEntry(t *testing.T) { }) } } + +func errorPath(err error) string { + var perr *PathError + if !errors.As(err, &perr) { + return "" + } + return perr.Path +} + +func TestReadDirPath(t *testing.T) { + fsys := os.DirFS(t.TempDir()) + _, err1 := ReadDir(fsys, "non-existent") + _, err2 := ReadDir(struct{ FS }{fsys}, "non-existent") + if s1, s2 := errorPath(err1), errorPath(err2); s1 != s2 { + t.Fatalf("s1: %s != s2: %s", s1, s2) + } +} diff --git a/src/io/fs/readfile_test.go b/src/io/fs/readfile_test.go index 07219c1445..3c521f6142 100644 --- a/src/io/fs/readfile_test.go +++ b/src/io/fs/readfile_test.go @@ -6,6 +6,7 @@ package fs_test import ( . "io/fs" + "os" "testing" "testing/fstest" "time" @@ -57,3 +58,12 @@ func TestReadFile(t *testing.T) { t.Fatalf(`ReadFile(sub(.), "hello.txt") = %q, %v, want %q, nil`, data, err, "hello, world") } } + +func TestReadFilePath(t *testing.T) { + fsys := os.DirFS(t.TempDir()) + _, err1 := ReadFile(fsys, "non-existent") + _, err2 := ReadFile(struct{ FS }{fsys}, "non-existent") + if s1, s2 := errorPath(err1), errorPath(err2); s1 != s2 { + t.Fatalf("s1: %s != s2: %s", s1, s2) + } +} diff --git a/src/io/fs/walk.go b/src/io/fs/walk.go index 48145d4cfc..2e8a8db111 100644 --- a/src/io/fs/walk.go +++ b/src/io/fs/walk.go @@ -9,12 +9,12 @@ import ( "path" ) -// SkipDir is used as a return value from WalkDirFuncs to indicate that +// SkipDir is used as a return value from [WalkDirFunc] to indicate that // the directory named in the call is to be skipped. It is not returned // as an error by any function. var SkipDir = errors.New("skip this directory") -// SkipAll is used as a return value from WalkDirFuncs to indicate that +// SkipAll is used as a return value from [WalkDirFunc] to indicate that // all remaining files and directories are to be skipped. It is not returned // as an error by any function. var SkipAll = errors.New("skip everything and stop the walk") diff --git a/src/iter/iter.go b/src/iter/iter.go new file mode 100644 index 0000000000..40e4770347 --- /dev/null +++ b/src/iter/iter.go @@ -0,0 +1,169 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.rangefunc + +// Package iter provides basic definitions and operations +// related to iteration in Go. +// +// This package is experimental and can only be imported +// when building with GOEXPERIMENT=rangefunc. +package iter + +import ( + "internal/race" + "unsafe" +) + +// Seq is an iterator over sequences of individual values. +// When called as seq(yield), seq calls yield(v) for each value v in the sequence, +// stopping early if yield returns false. +type Seq[V any] func(yield func(V) bool) + +// Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs. +// When called as seq(yield), seq calls yield(k, v) for each pair (k, v) in the sequence, +// stopping early if yield returns false. +type Seq2[K, V any] func(yield func(K, V) bool) + +type coro struct{} + +//go:linkname newcoro runtime.newcoro +func newcoro(func(*coro)) *coro + +//go:linkname coroswitch runtime.coroswitch +func coroswitch(*coro) + +// Pull converts the “push-style” iterator sequence seq +// into a “pull-style” iterator accessed by the two functions +// next and stop. +// +// Next returns the next value in the sequence +// and a boolean indicating whether the value is valid. +// When the sequence is over, next returns the zero V and false. +// It is valid to call next after reaching the end of the sequence +// or after calling stop. These calls will continue +// to return the zero V and false. +// +// Stop ends the iteration. It must be called when the caller is +// no longer interested in next values and next has not yet +// signaled that the sequence is over (with a false boolean return). +// It is valid to call stop multiple times and when next has +// already returned false. +// +// It is an error to call next or stop from multiple goroutines +// simultaneously. +func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) { + var ( + v V + ok bool + done bool + racer int + ) + c := newcoro(func(c *coro) { + race.Acquire(unsafe.Pointer(&racer)) + yield := func(v1 V) bool { + if done { + return false + } + v, ok = v1, true + race.Release(unsafe.Pointer(&racer)) + coroswitch(c) + race.Acquire(unsafe.Pointer(&racer)) + return !done + } + seq(yield) + var v0 V + v, ok = v0, false + done = true + race.Release(unsafe.Pointer(&racer)) + }) + next = func() (v1 V, ok1 bool) { + race.Write(unsafe.Pointer(&racer)) // detect races + if done { + return + } + race.Release(unsafe.Pointer(&racer)) + coroswitch(c) + race.Acquire(unsafe.Pointer(&racer)) + return v, ok + } + stop = func() { + race.Write(unsafe.Pointer(&racer)) // detect races + if !done { + done = true + race.Release(unsafe.Pointer(&racer)) + coroswitch(c) + race.Acquire(unsafe.Pointer(&racer)) + } + } + return next, stop +} + +// Pull2 converts the “push-style” iterator sequence seq +// into a “pull-style” iterator accessed by the two functions +// next and stop. +// +// Next returns the next pair in the sequence +// and a boolean indicating whether the pair is valid. +// When the sequence is over, next returns a pair of zero values and false. +// It is valid to call next after reaching the end of the sequence +// or after calling stop. These calls will continue +// to return a pair of zero values and false. +// +// Stop ends the iteration. It must be called when the caller is +// no longer interested in next values and next has not yet +// signaled that the sequence is over (with a false boolean return). +// It is valid to call stop multiple times and when next has +// already returned false. +// +// It is an error to call next or stop from multiple goroutines +// simultaneously. +func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) { + var ( + k K + v V + ok bool + done bool + racer int + ) + c := newcoro(func(c *coro) { + race.Acquire(unsafe.Pointer(&racer)) + yield := func(k1 K, v1 V) bool { + if done { + return false + } + k, v, ok = k1, v1, true + race.Release(unsafe.Pointer(&racer)) + coroswitch(c) + race.Acquire(unsafe.Pointer(&racer)) + return !done + } + seq(yield) + var k0 K + var v0 V + k, v, ok = k0, v0, false + done = true + race.Release(unsafe.Pointer(&racer)) + }) + next = func() (k1 K, v1 V, ok1 bool) { + race.Write(unsafe.Pointer(&racer)) // detect races + if done { + return + } + race.Release(unsafe.Pointer(&racer)) + coroswitch(c) + race.Acquire(unsafe.Pointer(&racer)) + return k, v, ok + } + stop = func() { + race.Write(unsafe.Pointer(&racer)) // detect races + if !done { + done = true + race.Release(unsafe.Pointer(&racer)) + coroswitch(c) + race.Acquire(unsafe.Pointer(&racer)) + } + } + return next, stop +} diff --git a/src/iter/pull_test.go b/src/iter/pull_test.go new file mode 100644 index 0000000000..38e0ee993a --- /dev/null +++ b/src/iter/pull_test.go @@ -0,0 +1,118 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.rangefunc + +package iter + +import ( + "fmt" + "runtime" + "testing" +) + +func count(n int) Seq[int] { + return func(yield func(int) bool) { + for i := range n { + if !yield(i) { + break + } + } + } +} + +func squares(n int) Seq2[int, int64] { + return func(yield func(int, int64) bool) { + for i := range n { + if !yield(i, int64(i)*int64(i)) { + break + } + } + } +} + +func TestPull(t *testing.T) { + + for end := 0; end <= 3; end++ { + t.Run(fmt.Sprint(end), func(t *testing.T) { + ng := runtime.NumGoroutine() + wantNG := func(want int) { + if xg := runtime.NumGoroutine() - ng; xg != want { + t.Helper() + t.Errorf("have %d extra goroutines, want %d", xg, want) + } + } + wantNG(0) + next, stop := Pull(count(3)) + wantNG(1) + for i := range end { + v, ok := next() + if v != i || ok != true { + t.Fatalf("next() = %d, %v, want %d, %v", v, ok, i, true) + } + wantNG(1) + } + wantNG(1) + if end < 3 { + stop() + wantNG(0) + } + for range 2 { + v, ok := next() + if v != 0 || ok != false { + t.Fatalf("next() = %d, %v, want %d, %v", v, ok, 0, false) + } + wantNG(0) + } + wantNG(0) + + stop() + stop() + stop() + wantNG(0) + }) + } +} + +func TestPull2(t *testing.T) { + for end := 0; end <= 3; end++ { + t.Run(fmt.Sprint(end), func(t *testing.T) { + ng := runtime.NumGoroutine() + wantNG := func(want int) { + if xg := runtime.NumGoroutine() - ng; xg != want { + t.Helper() + t.Errorf("have %d extra goroutines, want %d", xg, want) + } + } + wantNG(0) + next, stop := Pull2(squares(3)) + wantNG(1) + for i := range end { + k, v, ok := next() + if k != i || v != int64(i*i) || ok != true { + t.Fatalf("next() = %d, %d, %v, want %d, %d, %v", k, v, ok, i, i*i, true) + } + wantNG(1) + } + wantNG(1) + if end < 3 { + stop() + wantNG(0) + } + for range 2 { + k, v, ok := next() + if v != 0 || ok != false { + t.Fatalf("next() = %d, %d, %v, want %d, %d, %v", k, v, ok, 0, 0, false) + } + wantNG(0) + } + wantNG(0) + + stop() + stop() + stop() + wantNG(0) + }) + } +} diff --git a/src/log/slog/handler.go b/src/log/slog/handler.go index 2182bfb609..2ff85b582e 100644 --- a/src/log/slog/handler.go +++ b/src/log/slog/handler.go @@ -76,11 +76,11 @@ type Handler interface { // A Handler should treat WithGroup as starting a Group of Attrs that ends // at the end of the log event. That is, // - // logger.WithGroup("s").LogAttrs(level, msg, slog.Int("a", 1), slog.Int("b", 2)) + // logger.WithGroup("s").LogAttrs(ctx, level, msg, slog.Int("a", 1), slog.Int("b", 2)) // // should behave like // - // logger.LogAttrs(level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2))) + // logger.LogAttrs(ctx, level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2))) // // If the name is empty, WithGroup returns the receiver. WithGroup(name string) Handler diff --git a/src/log/slog/logger.go b/src/log/slog/logger.go index f03aeec295..10aa6a2b31 100644 --- a/src/log/slog/logger.go +++ b/src/log/slog/logger.go @@ -53,7 +53,8 @@ func init() { // Default returns the default [Logger]. func Default() *Logger { return defaultLogger.Load() } -// SetDefault makes l the default [Logger]. +// SetDefault makes l the default [Logger], which is used by +// the top-level functions [Info], [Debug] and so on. // After this call, output from the log package's default Logger // (as with [log.Print], etc.) will be logged using l's Handler, // at a level controlled by [SetLogLoggerLevel]. @@ -145,7 +146,6 @@ func (l *Logger) WithGroup(name string) *Logger { c := l.clone() c.handler = l.handler.WithGroup(name) return c - } // New creates a new Logger with the given non-nil Handler. diff --git a/src/maps/maps_test.go b/src/maps/maps_test.go index 5e3f9ca03b..fa30fe8c2b 100644 --- a/src/maps/maps_test.go +++ b/src/maps/maps_test.go @@ -182,3 +182,61 @@ func TestCloneWithMapAssign(t *testing.T) { } } } + +func TestCloneLarge(t *testing.T) { + // See issue 64474. + type K [17]float64 // > 128 bytes + type V [17]float64 + + var zero float64 + negZero := -zero + + for tst := 0; tst < 3; tst++ { + // Initialize m with a key and value. + m := map[K]V{} + var k1 K + var v1 V + m[k1] = v1 + + switch tst { + case 0: // nothing, just a 1-entry map + case 1: + // Add more entries to make it 2 buckets + // 1 entry already + // 7 more fill up 1 bucket + // 1 more to grow to 2 buckets + for i := 0; i < 7+1; i++ { + m[K{float64(i) + 1}] = V{} + } + case 2: + // Capture the map mid-grow + // 1 entry already + // 7 more fill up 1 bucket + // 5 more (13 total) fill up 2 buckets + // 13 more (26 total) fill up 4 buckets + // 1 more to start the 4->8 bucket grow + for i := 0; i < 7+5+13+1; i++ { + m[K{float64(i) + 1}] = V{} + } + } + + // Clone m, which should freeze the map's contents. + c := Clone(m) + + // Update m with new key and value. + k2, v2 := k1, v1 + k2[0] = negZero + v2[0] = 1.0 + m[k2] = v2 + + // Make sure c still has its old key and value. + for k, v := range c { + if math.Signbit(k[0]) { + t.Errorf("tst%d: sign bit of key changed; got %v want %v", tst, k, k1) + } + if v != v1 { + t.Errorf("tst%d: value changed; got %v want %v", tst, v, v1) + } + } + } +} diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go index 7d6bf034df..bb045a0b48 100644 --- a/src/math/big/float_test.go +++ b/src/math/big/float_test.go @@ -194,13 +194,11 @@ func alike(x, y *Float) bool { func alike32(x, y float32) bool { // we can ignore NaNs return x == y && math.Signbit(float64(x)) == math.Signbit(float64(y)) - } func alike64(x, y float64) bool { // we can ignore NaNs return x == y && math.Signbit(x) == math.Signbit(y) - } func TestFloatMantExp(t *testing.T) { diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go index cb964a43cd..088bce09f9 100644 --- a/src/math/big/int_test.go +++ b/src/math/big/int_test.go @@ -200,12 +200,22 @@ var mulRangesZ = []struct { "638952175999932299156089414639761565182862536979208272237582" + "511852109168640000000000000000000000", // -99! }, + + // overflow situations + {math.MaxInt64 - 0, math.MaxInt64, "9223372036854775807"}, + {math.MaxInt64 - 1, math.MaxInt64, "85070591730234615838173535747377725442"}, + {math.MaxInt64 - 2, math.MaxInt64, "784637716923335094969050127519550606919189611815754530810"}, + {math.MaxInt64 - 3, math.MaxInt64, "7237005577332262206126809393809643289012107973151163787181513908099760521240"}, } func TestMulRangeZ(t *testing.T) { var tmp Int // test entirely positive ranges for i, r := range mulRangesN { + // skip mulRangesN entries that overflow int64 + if int64(r.a) < 0 || int64(r.b) < 0 { + continue + } prod := tmp.MulRange(int64(r.a), int64(r.b)).String() if prod != r.prod { t.Errorf("#%da: got %s; want %s", i, prod, r.prod) diff --git a/src/math/big/nat.go b/src/math/big/nat.go index b9f4026a04..ecb7d363d4 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -624,7 +624,7 @@ func (z nat) mulRange(a, b uint64) nat { case a+1 == b: return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) } - m := (a + b) / 2 + m := a + (b-a)/2 // avoid overflow return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) } diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go index b84a7be5bc..4722548fa9 100644 --- a/src/math/big/nat_test.go +++ b/src/math/big/nat_test.go @@ -6,6 +6,7 @@ package big import ( "fmt" + "math" "runtime" "strings" "testing" @@ -155,6 +156,10 @@ var mulRangesN = []struct { "638952175999932299156089414639761565182862536979208272237582" + "51185210916864000000000000000000000000", // 100! }, + {math.MaxUint64 - 0, math.MaxUint64, "18446744073709551615"}, + {math.MaxUint64 - 1, math.MaxUint64, "340282366920938463408034375210639556610"}, + {math.MaxUint64 - 2, math.MaxUint64, "6277101735386680761794095221682035635525021984684230311930"}, + {math.MaxUint64 - 3, math.MaxUint64, "115792089237316195360799967654821100226821973275796746098729803619699194331160"}, } func TestMulRangeN(t *testing.T) { diff --git a/src/math/rand/rand.go b/src/math/rand/rand.go index 78e176e78f..a8ed9c0cb7 100644 --- a/src/math/rand/rand.go +++ b/src/math/rand/rand.go @@ -273,7 +273,7 @@ func (r *Rand) Read(p []byte) (n int, err error) { switch src := r.src.(type) { case *lockedSource: return src.read(p, &r.readVal, &r.readPos) - case *fastSource: + case *runtimeSource: return src.read(p, &r.readVal, &r.readPos) } return read(p, r.src, &r.readVal, &r.readPos) @@ -328,8 +328,8 @@ func globalRand() *Rand { r.Seed(1) } else { r = &Rand{ - src: &fastSource{}, - s64: &fastSource{}, + src: &runtimeSource{}, + s64: &runtimeSource{}, } } @@ -346,29 +346,29 @@ func globalRand() *Rand { return r } -//go:linkname fastrand64 -func fastrand64() uint64 +//go:linkname runtime_rand runtime.rand +func runtime_rand() uint64 -// fastSource is an implementation of Source64 that uses the runtime +// runtimeSource is an implementation of Source64 that uses the runtime // fastrand functions. -type fastSource struct { +type runtimeSource struct { // The mutex is used to avoid race conditions in Read. mu sync.Mutex } -func (*fastSource) Int63() int64 { - return int64(fastrand64() & rngMask) +func (*runtimeSource) Int63() int64 { + return int64(runtime_rand() & rngMask) } -func (*fastSource) Seed(int64) { - panic("internal error: call to fastSource.Seed") +func (*runtimeSource) Seed(int64) { + panic("internal error: call to runtimeSource.Seed") } -func (*fastSource) Uint64() uint64 { - return fastrand64() +func (*runtimeSource) Uint64() uint64 { + return runtime_rand() } -func (fs *fastSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) { +func (fs *runtimeSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) { fs.mu.Lock() n, err = read(p, fs, readVal, readPos) fs.mu.Unlock() @@ -405,7 +405,7 @@ func Seed(seed int64) { // Otherwise either // 1) orig == nil, which is the normal case when Seed is the first // top-level function to be called, or - // 2) orig is already a fastSource, in which case we need to change + // 2) orig is already a runtimeSource, in which case we need to change // to a lockedSource. // Either way we do the same thing. diff --git a/src/math/rand/rand_test.go b/src/math/rand/rand_test.go index 4ad2ae2230..9f074fea00 100644 --- a/src/math/rand/rand_test.go +++ b/src/math/rand/rand_test.go @@ -14,6 +14,7 @@ import ( . "math/rand" "os" "runtime" + "strings" "sync" "testing" "testing/iotest" @@ -331,7 +332,7 @@ func TestExpTables(t *testing.T) { func hasSlowFloatingPoint() bool { switch runtime.GOARCH { case "arm": - return os.Getenv("GOARM") == "5" + return os.Getenv("GOARM") == "5" || strings.HasSuffix(os.Getenv("GOARM"), ",softfloat") case "mips", "mipsle", "mips64", "mips64le": // Be conservative and assume that all mips boards // have emulated floating point. diff --git a/src/math/rand/v2/chacha8.go b/src/math/rand/v2/chacha8.go new file mode 100644 index 0000000000..6b9aa72782 --- /dev/null +++ b/src/math/rand/v2/chacha8.go @@ -0,0 +1,46 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import "internal/chacha8rand" + +// A ChaCha8 is a ChaCha8-based cryptographically strong +// random number generator. +type ChaCha8 struct { + state chacha8rand.State +} + +// NewChaCha8 returns a new ChaCha8 seeded with the given seed. +func NewChaCha8(seed [32]byte) *ChaCha8 { + c := new(ChaCha8) + c.state.Init(seed) + return c +} + +// Seed resets the ChaCha8 to behave the same way as NewChaCha8(seed). +func (c *ChaCha8) Seed(seed [32]byte) { + c.state.Init(seed) +} + +// Uint64 returns a uniformly distributed random uint64 value. +func (c *ChaCha8) Uint64() uint64 { + for { + x, ok := c.state.Next() + if ok { + return x + } + c.state.Refill() + } +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (c *ChaCha8) UnmarshalBinary(data []byte) error { + return chacha8rand.Unmarshal(&c.state, data) +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (c *ChaCha8) MarshalBinary() ([]byte, error) { + return chacha8rand.Marshal(&c.state), nil +} diff --git a/src/math/rand/v2/chacha8_test.go b/src/math/rand/v2/chacha8_test.go new file mode 100644 index 0000000000..2c55b479b2 --- /dev/null +++ b/src/math/rand/v2/chacha8_test.go @@ -0,0 +1,531 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand_test + +import ( + . "math/rand/v2" + "testing" +) + +func TestChaCha8(t *testing.T) { + p := NewChaCha8(chacha8seed) + for i, x := range chacha8output { + if u := p.Uint64(); u != x { + t.Errorf("ChaCha8 #%d = %#x, want %#x", i, u, x) + } + } + + p.Seed(chacha8seed) + for i, x := range chacha8output { + if u := p.Uint64(); u != x { + t.Errorf("ChaCha8 #%d = %#x, want %#x", i, u, x) + } + } +} + +func TestChaCha8Marshal(t *testing.T) { + p := NewChaCha8(chacha8seed) + for i, x := range chacha8output { + enc, err := p.MarshalBinary() + if err != nil { + t.Fatalf("#%d: MarshalBinary: %v", i, err) + } + if string(enc) != chacha8marshal[i] { + t.Fatalf("#%d: MarshalBinary=%q, want %q", i, enc, chacha8marshal[i]) + } + *p = ChaCha8{} + if err := p.UnmarshalBinary(enc); err != nil { + t.Fatalf("#%d: UnmarshalBinary: %v", i, err) + } + if u := p.Uint64(); u != x { + t.Errorf("ChaCha8 #%d = %#x, want %#x", i, u, x) + } + } +} + +func BenchmarkChaCha8(b *testing.B) { + p := NewChaCha8([32]byte{1, 2, 3, 4, 5}) + var t uint64 + for n := b.N; n > 0; n-- { + t += p.Uint64() + } + Sink = t +} + +// Golden output test to make sure algorithm never changes, +// so that its use in math/rand/v2 stays stable. + +var chacha8seed = [32]byte([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ123456")) + +var chacha8output = []uint64{ + 0xb773b6063d4616a5, 0x1160af22a66abc3c, 0x8c2599d9418d287c, 0x7ee07e037edc5cd6, + 0xcfaa9ee02d1c16ad, 0x0e090eef8febea79, 0x3c82d271128b5b3e, 0x9c5addc11252a34f, + 0xdf79bb617d6ceea6, 0x36d553591f9d736a, 0xeef0d14e181ee01f, 0x089bfc760ae58436, + 0xd9e52b59cc2ad268, 0xeb2fb4444b1b8aba, 0x4f95c8a692c46661, 0xc3c6323217cae62c, + 0x91ebb4367f4e2e7e, 0x784cf2c6a0ec9bc6, 0x5c34ec5c34eabe20, 0x4f0a8f515570daa8, + 0xfc35dcb4113d6bf2, 0x5b0da44c645554bc, 0x6d963da3db21d9e1, 0xeeaefc3150e500f3, + 0x2d37923dda3750a5, 0x380d7a626d4bc8b0, 0xeeaf68ede3d7ee49, 0xf4356695883b717c, + 0x846a9021392495a4, 0x8e8510549630a61b, 0x18dc02545dbae493, 0x0f8f9ff0a65a3d43, + 0xccf065f7190ff080, 0xfd76d1aa39673330, 0x95d232936cba6433, 0x6c7456d1070cbd17, + 0x462acfdaff8c6562, 0x5bafab866d34fc6a, 0x0c862f78030a2988, 0xd39a83e407c3163d, + 0xc00a2b7b45f22ebf, 0x564307c62466b1a9, 0x257e0424b0c072d4, 0x6fb55e99496c28fe, + 0xae9873a88f5cd4e0, 0x4657362ac60d3773, 0x1c83f91ecdf23e8e, 0x6fdc0792c15387c0, + 0x36dad2a30dfd2b5c, 0xa4b593290595bdb7, 0x4de18934e4cc02c5, 0xcdc0d604f015e3a7, + 0xfba0dbf69ad80321, 0x60e8bea3d139de87, 0xd18a4d851ef48756, 0x6366447c2215f34a, + 0x05682e97d3d007ee, 0x4c0e8978c6d54ab2, 0xcf1e9f6a6712edc2, 0x061439414c80cfd3, + 0xd1a8b6e2745c0ead, 0x31a7918d45c410e8, 0xabcc61ad90216eec, 0x4040d92d2032a71a, + 0x3cd2f66ffb40cd68, 0xdcd051c07295857a, 0xeab55cbcd9ab527e, 0x18471dce781bdaac, + 0xf7f08cd144dc7252, 0x5804e0b13d7f40d1, 0x5cb1a446e4b2d35b, 0xe6d4a728d2138a06, + 0x05223e40ca60dad8, 0x2d61ec3206ac6a68, 0xab692356874c17b8, 0xc30954417676de1c, + 0x4f1ace3732225624, 0xfba9510813988338, 0x997f200f52752e11, 0x1116aaafe86221fa, + 0x07ce3b5cb2a13519, 0x2956bc72bc458314, 0x4188b7926140eb78, 0x56ca6dbfd4adea4d, + 0x7fe3c22349340ce5, 0x35c08f9c37675f8a, 0x11e1c7fbef5ed521, 0x98adc8464ec1bc75, + 0xd163b2c73d1203f8, 0x8c761ee043a2f3f3, 0x24b99d6accecd7b7, 0x793e31aa112f0370, + 0x8e87dc2a19285139, 0x4247ae04f7096e25, 0x514f3122926fe20f, 0xdc6fb3f045d2a7e9, + 0x15cb30cecdd18eba, 0xcbc7fdecf6900274, 0x3fb5c696dc8ba021, 0xd1664417c8d274e6, + 0x05f7e445ea457278, 0xf920bbca1b9db657, 0x0c1950b4da22cb99, 0xf875baf1af09e292, + 0xbed3d7b84250f838, 0xf198e8080fd74160, 0xc9eda51d9b7ea703, 0xf709ef55439bf8f6, + 0xd20c74feebf116fc, 0x305668eb146d7546, 0x829af3ec10d89787, 0x15b8f9697b551dbc, + 0xfc823c6c8e64b8c9, 0x345585e8183b40bc, 0x674b4171d6581368, 0x1234d81cd670e9f7, + 0x0e505210d8a55e19, 0xe8258d69eeeca0dc, 0x05d4c452e8baf67e, 0xe8dbe30116a45599, + 0x1cf08ce1b1176f00, 0xccf7d0a4b81ecb49, 0x303fea136b2c430e, 0x861d6c139c06c871, + 0x5f41df72e05e0487, 0x25bd7e1e1ae26b1d, 0xbe9f4004d662a41d, 0x65bf58d483188546, + 0xd1b27cff69db13cc, 0x01a6663372c1bb36, 0x578dd7577b727f4d, 0x19c78f066c083cf6, + 0xdbe014d4f9c391bb, 0x97fbb2dd1d13ffb3, 0x31c91e0af9ef8d4f, 0x094dfc98402a43ba, + 0x069bd61bea37b752, 0x5b72d762e8d986ca, 0x72ee31865904bc85, 0xd1f5fdc5cd36c33e, + 0xba9b4980a8947cad, 0xece8f05eac49ab43, 0x65fe1184abae38e7, 0x2d7cb9dea5d31452, + 0xcc71489476e467e3, 0x4c03a258a578c68c, 0x00efdf9ecb0fd8fc, 0x9924cad471e2666d, + 0x87f8668318f765e9, 0xcb4dc57c1b55f5d8, 0xd373835a86604859, 0xe526568b5540e482, + 0x1f39040f08586fec, 0xb764f3f00293f8e6, 0x049443a2f6bd50a8, 0x76fec88697d3941a, + 0x3efb70d039bae7a2, 0xe2f4611368eca8a8, 0x7c007a96e01d2425, 0xbbcce5768e69c5bf, + 0x784fb4985c42aac3, 0xf72b5091aa223874, 0x3630333fb1e62e07, 0x8e7319ebdebbb8de, + 0x2a3982bca959fa00, 0xb2b98b9f964ba9b3, 0xf7e31014adb71951, 0xebd0fca3703acc82, + 0xec654e2a2fe6419a, 0xb326132d55a52e2c, 0x2248c57f44502978, 0x32710c2f342daf16, + 0x0517b47b5acb2bec, 0x4c7a718fca270937, 0xd69142bed0bcc541, 0xe40ebcb8ff52ce88, + 0x3e44a2dbc9f828d4, 0xc74c2f4f8f873f58, 0x3dbf648eb799e45b, 0x33f22475ee0e86f8, + 0x1eb4f9ee16d47f65, 0x40f8d2b8712744e3, 0xb886b4da3cb14572, 0x2086326fbdd6f64d, + 0xcc3de5907dd882b9, 0xa2e8b49a5ee909df, 0xdbfb8e7823964c10, 0x70dd6089ef0df8d5, + 0x30141663cdd9c99f, 0x04b805325c240365, 0x7483d80314ac12d6, 0x2b271cb91aa7f5f9, + 0x97e2245362abddf0, 0x5a84f614232a9fab, 0xf71125fcda4b7fa2, 0x1ca5a61d74b27267, + 0x38cc6a9b3adbcb45, 0xdde1bb85dc653e39, 0xe9d0c8fa64f89fd4, 0x02c5fb1ecd2b4188, + 0xf2bd137bca5756e5, 0xadefe25d121be155, 0x56cd1c3c5d893a8e, 0x4c50d337beb65bb9, + 0x918c5151675cf567, 0xaba649ffcfb56a1e, 0x20c74ab26a2247cd, 0x71166bac853c08da, + 0xb07befe2e584fc5d, 0xda45ff2a588dbf32, 0xdb98b03c4d75095e, 0x60285ae1aaa65a4c, + 0xf93b686a263140b8, 0xde469752ee1c180e, 0xcec232dc04129aae, 0xeb916baa1835ea04, + 0xd49c21c8b64388ff, 0x72a82d9658864888, 0x003348ef7eac66a8, 0x7f6f67e655b209eb, + 0x532ffb0b7a941b25, 0xd940ade6128deede, 0xdf24f2a1af89fe23, 0x95aa3b4988195ae0, + 0x3da649404f94be4a, 0x692dad132c3f7e27, 0x40aee76ecaaa9eb8, 0x1294a01e09655024, + 0x6df797abdba4e4f5, 0xea2fb6024c1d7032, 0x5f4e0492295489fc, 0x57972914ea22e06a, + 0x9a8137d133aad473, 0xa2e6dd6ae7cdf2f3, 0x9f42644f18086647, 0x16d03301c170bd3e, + 0x908c416fa546656d, 0xe081503be22e123e, 0x077cf09116c4cc72, 0xcbd25cd264b7f229, + 0x3db2f468ec594031, 0x46c00e734c9badd5, 0xd0ec0ac72075d861, 0x3037cb3cf80b7630, + 0x574c3d7b3a2721c6, 0xae99906a0076824b, 0xb175a5418b532e70, 0xd8b3e251ee231ddd, + 0xb433eec25dca1966, 0x530f30dc5cff9a93, 0x9ff03d98b53cd335, 0xafc4225076558cdf, + 0xef81d3a28284402a, 0x110bdbf51c110a28, 0x9ae1b255d027e8f6, 0x7de3e0aa24688332, + 0xe483c3ecd2067ee2, 0xf829328b276137e6, 0xa413ccad57562cad, 0xe6118e8b496acb1f, + 0x8288dca6da5ec01f, 0xa53777dc88c17255, 0x8a00f1e0d5716eda, 0x618e6f47b7a720a8, + 0x9e3907b0c692a841, 0x978b42ca963f34f3, 0x75e4b0cd98a7d7ef, 0xde4dbd6e0b5f4752, + 0x0252e4153f34493f, 0x50f0e7d803734ef9, 0x237766a38ed167ee, 0x4124414001ee39a0, + 0xd08df643e535bb21, 0x34f575b5a9a80b74, 0x2c343af87297f755, 0xcd8b6d99d821f7cb, + 0xe376fd7256fc48ae, 0xe1b06e7334352885, 0xfa87b26f86c169eb, 0x36c1604665a971de, + 0xdba147c2239c8e80, 0x6b208e69fc7f0e24, 0x8795395b6f2b60c3, 0x05dabee9194907f4, + 0xb98175142f5ed902, 0x5e1701e2021ddc81, 0x0875aba2755eed08, 0x778d83289251de95, + 0x3bfbe46a039ecb31, 0xb24704fce4cbd7f9, 0x6985ffe9a7c91e3d, 0xc8efb13df249dabb, + 0xb1037e64b0f4c9f6, 0x55f69fd197d6b7c3, 0x672589d71d68a90c, 0xbebdb8224f50a77e, + 0x3f589f80007374a7, 0xd307f4635954182a, 0xcff5850c10d4fd90, 0xc6da02dfb6408e15, + 0x93daeef1e2b1a485, 0x65d833208aeea625, 0xe2b13fa13ed3b5fa, 0x67053538130fb68e, + 0xc1042f6598218fa9, 0xee5badca749b8a2e, 0x6d22a3f947dae37d, 0xb62c6d1657f4dbaf, + 0x6e007de69704c20b, 0x1af2b913fc3841d8, 0xdc0e47348e2e8e22, 0x9b1ddef1cf958b22, + 0x632ed6b0233066b8, 0xddd02d3311bed8f2, 0xf147cfe1834656e9, 0x399aaa49d511597a, + 0x6b14886979ec0309, 0x64fc4ac36b5afb97, 0xb82f78e07f7cf081, 0x10925c9a323d0e1b, + 0xf451c79ee13c63f6, 0x7c2fc180317876c7, 0x35a12bd9eecb7d22, 0x335654a539621f90, + 0xcc32a3f35db581f0, 0xc60748a80b2369cb, 0x7c4dd3b08591156b, 0xac1ced4b6de22291, + 0xa32cfa2df134def5, 0x627108918dea2a53, 0x0555b1608fcb4ff4, 0x143ee7ac43aaa33c, + 0xdae90ce7cf4fc218, 0x4d68fc2582bcf4b5, 0x37094e1849135d71, 0xf7857e09f3d49fd8, + 0x007538c503768be7, 0xedf648ba2f6be601, 0xaa347664dd72513e, 0xbe63893c6ef23b86, + 0x130b85710605af97, 0xdd765c6b1ef6ab56, 0xf3249a629a97dc6b, 0x2a114f9020fab8e5, + 0x5a69e027cfc6ad08, 0x3c4ccb36f1a5e050, 0x2e9e7d596834f0a5, 0x2430be6858fce789, + 0xe90b862f2466e597, 0x895e2884f159a9ec, 0x26ab8fa4902fcb57, 0xa6efff5c54e1fa50, + 0x333ac4e5811a8255, 0xa58d515f02498611, 0xfe5a09dcb25c6ef4, 0x03898988ab5f5818, + 0x289ff6242af6c617, 0x3d9dd59fd381ea23, 0x52d7d93d8a8aae51, 0xc76a123d511f786f, + 0xf68901edaf00c46c, 0x8c630871b590de80, 0x05209c308991e091, 0x1f809f99b4788177, + 0x11170c2eb6c19fd8, 0x44433c779062ba58, 0xc0acb51af1874c45, 0x9f2e134284809fa1, + 0xedb523bd15c619fa, 0x02d97fd53ecc23c0, 0xacaf05a34462374c, 0xddd9c6d34bffa11f, +} + +var chacha8marshal = []string{ + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x01ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x02ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x03ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x04ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x05ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x06ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\aABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\bABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\tABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\nABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\vABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\fABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\rABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x0eABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x0fABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x10ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x11ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x12ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x13ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x14ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x15ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x16ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x17ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x18ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x19ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1aABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1bABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1cABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1dABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1eABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1fABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00 ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00!ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\"ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00#ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00$ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00%ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00&ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00(ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00)ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00*ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00+ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00,ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00-ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00.ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00/ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x000ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x001ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x002ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x003ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x004ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x005ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x006ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x007ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x008ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x009ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00;ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00?ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00@ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00AABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00BABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00CABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00DABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00EABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00FABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00GABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00HABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00IABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00JABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00KABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00LABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00MABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00NABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00OABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00PABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00QABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00RABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00SABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00TABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00UABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00VABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00WABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00XABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00YABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00ZABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00[ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\\ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00]ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00^ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00_ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00`ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00aABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00bABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00cABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00dABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00eABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00fABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00gABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00hABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00iABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00jABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00kABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00lABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00mABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00nABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00oABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00pABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00qABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00rABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00sABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00tABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00uABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00vABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00wABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00xABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00yABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00zABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00{ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00|ABCDEFGHIJKLMNOPQRSTUVWXYZ123456", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x01>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x02>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x03>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x04>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x05>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x06>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\a>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\b>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\t>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\n>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\v>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\f>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\r>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x0e>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x0f>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x10>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x11>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x12>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x13>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x14>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x15>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x16>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x17>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x18>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x19>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1a>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1b>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1c>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1d>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1e>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1f>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00 >\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00!>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\">\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00#>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00$>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00%>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00&>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00'>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00(>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00)>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00*>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00+>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00,>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00->\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00.>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00/>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x000>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x001>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x002>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x003>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x004>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x005>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x006>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x007>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x008>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x009>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00:>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00;>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00<>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00=>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00>>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00?>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00@>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00A>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00B>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00C>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00D>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00E>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00F>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00G>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00H>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00I>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00J>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00K>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00L>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00M>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00N>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00O>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00P>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00Q>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00R>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00S>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00T>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00U>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00V>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00W>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00X>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00Y>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00Z>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00[>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\\>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00]>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00^>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00_>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00`>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00a>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00b>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00c>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00d>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00e>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00f>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00g>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00h>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00i>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00j>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00k>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00l>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00m>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00n>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00o>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00p>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00q>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00r>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00s>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00t>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00u>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00v>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00w>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00x>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00y>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00z>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00{>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00|>\x15\x0e\xacHk4O\x11a\xa8R\xcd5\x9atr\x8cXO\x9c]\x10\xdf\xf61\xea\x11\x18\x06\x8a\xaa", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x01K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x02K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x03K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x04K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x05K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x06K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\aK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\bK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\tK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\nK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\vK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\fK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\rK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x0eK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x0fK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x10K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x11K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x12K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x13K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x14K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x15K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x16K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x17K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x18K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x19K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1aK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1bK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1cK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1dK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1eK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\x1fK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00 K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00!K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\"K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00#K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00$K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00%K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00&K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00'K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00(K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00)K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00*K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00+K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00,K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00-K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00.K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00/K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x000K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x001K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x002K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x003K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x004K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x005K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x006K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x007K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x008K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x009K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00:K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00;K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00?K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00@K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00AK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00BK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00CK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00DK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00EK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00FK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00GK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00HK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00IK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00JK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00KK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00LK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00MK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00NK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00OK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00PK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00QK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00RK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00SK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00TK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00UK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00VK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00WK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00XK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00YK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00ZK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00[K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00\\K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00]K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00^K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00_K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00`K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00aK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00bK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00cK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00dK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00eK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00fK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00gK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00hK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00iK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00jK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00kK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00lK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00mK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00nK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00oK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00pK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00qK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00rK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00sK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00tK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00uK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00vK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00wK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00xK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00yK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00zK3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", + "chacha8:\x00\x00\x00\x00\x00\x00\x00{K3\x9bB!,\x94\x9d\x975\xce'O_t\xee|\xb21\x87\xbb\xbb\xfd)\x8f\xe52\x01\vP\fk", +} diff --git a/src/math/rand/v2/rand.go b/src/math/rand/v2/rand.go index 5382f809e0..f490408472 100644 --- a/src/math/rand/v2/rand.go +++ b/src/math/rand/v2/rand.go @@ -250,20 +250,16 @@ func (r *Rand) Shuffle(n int, swap func(i, j int)) { // globalRand is the source of random numbers for the top-level // convenience functions. -var globalRand = &Rand{src: &fastSource{}} +var globalRand = &Rand{src: &runtimeSource{}} -//go:linkname fastrand64 -func fastrand64() uint64 +//go:linkname runtime_rand runtime.rand +func runtime_rand() uint64 -// fastSource is a Source that uses the runtime fastrand functions. -type fastSource struct{} +// runtimeSource is a Source that uses the runtime fastrand functions. +type runtimeSource struct{} -func (*fastSource) Int64() int64 { - return int64(fastrand64() << 1 >> 1) -} - -func (*fastSource) Uint64() uint64 { - return fastrand64() +func (*runtimeSource) Uint64() uint64 { + return runtime_rand() } // Int64 returns a non-negative pseudo-random 63-bit integer as an int64 diff --git a/src/net/conf.go b/src/net/conf.go index 649ebcfb18..15d73cf6ce 100644 --- a/src/net/conf.go +++ b/src/net/conf.go @@ -522,7 +522,7 @@ func isGateway(h string) bool { return stringsEqualFold(h, "_gateway") } -// isOutbound reports whether h should be considered a "outbound" +// isOutbound reports whether h should be considered an "outbound" // name for the myhostname NSS module. func isOutbound(h string) bool { return stringsEqualFold(h, "_outbound") diff --git a/src/net/dial.go b/src/net/dial.go index 7ca9b4a468..a6565c3ce5 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -65,7 +65,7 @@ func (m *mptcpStatus) set(use bool) { // // The zero value for each field is equivalent to dialing // without that option. Dialing with the zero value of Dialer -// is therefore equivalent to just calling the Dial function. +// is therefore equivalent to just calling the [Dial] function. // // It is safe to call Dialer's methods concurrently. type Dialer struct { @@ -338,7 +338,7 @@ func (d *Dialer) MultipathTCP() bool { return d.mptcpStatus.get() } -// SetMultipathTCP directs the Dial methods to use, or not use, MPTCP, +// SetMultipathTCP directs the [Dial] methods to use, or not use, MPTCP, // if supported by the operating system. This method overrides the // system default and the GODEBUG=multipathtcp=... setting if any. // @@ -363,7 +363,7 @@ func (d *Dialer) SetMultipathTCP(use bool) { // brackets, as in "[2001:db8::1]:80" or "[fe80::1%zone]:80". // The zone specifies the scope of the literal IPv6 address as defined // in RFC 4007. -// The functions JoinHostPort and SplitHostPort manipulate a pair of +// The functions [JoinHostPort] and [SplitHostPort] manipulate a pair of // host and port in this form. // When using TCP, and the host resolves to multiple IP addresses, // Dial will try each IP address in order until one succeeds. @@ -401,7 +401,7 @@ func Dial(network, address string) (Conn, error) { return d.Dial(network, address) } -// DialTimeout acts like Dial but takes a timeout. +// DialTimeout acts like [Dial] but takes a timeout. // // The timeout includes name resolution, if required. // When using TCP, and the host in the address parameter resolves to @@ -428,8 +428,8 @@ type sysDialer struct { // See func Dial for a description of the network and address // parameters. // -// Dial uses context.Background internally; to specify the context, use -// DialContext. +// Dial uses [context.Background] internally; to specify the context, use +// [Dialer.DialContext]. func (d *Dialer) Dial(network, address string) (Conn, error) { return d.DialContext(context.Background(), network, address) } @@ -450,7 +450,7 @@ func (d *Dialer) Dial(network, address string) (Conn, error) { // the connect to each single address will be given 15 seconds to complete // before trying the next one. // -// See func Dial for a description of the network and address +// See func [Dial] for a description of the network and address // parameters. func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) { if ctx == nil { @@ -700,7 +700,7 @@ func (lc *ListenConfig) MultipathTCP() bool { return lc.mptcpStatus.get() } -// SetMultipathTCP directs the Listen method to use, or not use, MPTCP, +// SetMultipathTCP directs the [Listen] method to use, or not use, MPTCP, // if supported by the operating system. This method overrides the // system default and the GODEBUG=multipathtcp=... setting if any. // @@ -795,14 +795,14 @@ type sysListener struct { // addresses. // If the port in the address parameter is empty or "0", as in // "127.0.0.1:" or "[::1]:0", a port number is automatically chosen. -// The Addr method of Listener can be used to discover the chosen +// The [Addr] method of [Listener] can be used to discover the chosen // port. // -// See func Dial for a description of the network and address +// See func [Dial] for a description of the network and address // parameters. // // Listen uses context.Background internally; to specify the context, use -// ListenConfig.Listen. +// [ListenConfig.Listen]. func Listen(network, address string) (Listener, error) { var lc ListenConfig return lc.Listen(context.Background(), network, address) @@ -825,14 +825,14 @@ func Listen(network, address string) (Listener, error) { // addresses. // If the port in the address parameter is empty or "0", as in // "127.0.0.1:" or "[::1]:0", a port number is automatically chosen. -// The LocalAddr method of PacketConn can be used to discover the +// The LocalAddr method of [PacketConn] can be used to discover the // chosen port. // -// See func Dial for a description of the network and address +// See func [Dial] for a description of the network and address // parameters. // // ListenPacket uses context.Background internally; to specify the context, use -// ListenConfig.ListenPacket. +// [ListenConfig.ListenPacket]. func ListenPacket(network, address string) (PacketConn, error) { var lc ListenConfig return lc.ListenPacket(context.Background(), network, address) diff --git a/src/net/dnsclient.go b/src/net/dnsclient.go index b609dbd468..204620b2ed 100644 --- a/src/net/dnsclient.go +++ b/src/net/dnsclient.go @@ -8,15 +8,17 @@ import ( "internal/bytealg" "internal/itoa" "sort" + _ "unsafe" // for go:linkname "golang.org/x/net/dns/dnsmessage" ) // provided by runtime -func fastrandu() uint +//go:linkname runtime_rand runtime.rand +func runtime_rand() uint64 func randInt() int { - return int(fastrandu() >> 1) // clear sign bit + return int(uint(runtime_rand()) >> 1) // clear sign bit } func randIntn(n int) int { diff --git a/src/net/fd_fake.go b/src/net/fd_fake.go index b9361a3c4e..ae567acc69 100644 --- a/src/net/fd_fake.go +++ b/src/net/fd_fake.go @@ -30,7 +30,7 @@ type netFD struct { raddr Addr // The only networking available in WASI preview 1 is the ability to - // sock_accept on an pre-opened socket, and then fd_read, fd_write, + // sock_accept on a pre-opened socket, and then fd_read, fd_write, // fd_close, and sock_shutdown on the resulting connection. We // intercept applicable netFD calls on this instance, and then pass // the remainder of the netFD calls to fakeNetFD. diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go index 1411f0b8e8..e29fe20d7d 100644 --- a/src/net/http/cgi/child.go +++ b/src/net/http/cgi/child.go @@ -46,7 +46,7 @@ func envMap(env []string) map[string]string { return m } -// RequestFromMap creates an http.Request from CGI variables. +// RequestFromMap creates an [http.Request] from CGI variables. // The returned Request's Body field is not populated. func RequestFromMap(params map[string]string) (*http.Request, error) { r := new(http.Request) @@ -138,10 +138,10 @@ func RequestFromMap(params map[string]string) (*http.Request, error) { return r, nil } -// Serve executes the provided Handler on the currently active CGI +// Serve executes the provided [Handler] on the currently active CGI // request, if any. If there's no current CGI environment // an error is returned. The provided handler may be nil to use -// http.DefaultServeMux. +// [http.DefaultServeMux]. func Serve(handler http.Handler) error { req, err := Request() if err != nil { diff --git a/src/net/http/cgi/host_test.go b/src/net/http/cgi/host_test.go index 78e05d592a..f29395fe84 100644 --- a/src/net/http/cgi/host_test.go +++ b/src/net/http/cgi/host_test.go @@ -17,8 +17,8 @@ import ( "os" "path/filepath" "reflect" + "regexp" "runtime" - "strconv" "strings" "testing" "time" @@ -363,11 +363,12 @@ func TestInternalRedirect(t *testing.T) { // TestCopyError tests that we kill the process if there's an error copying // its output. (for example, from the client having gone away) +// +// If we fail to do so, the test will time out (and dump its goroutines) with a +// call to [Handler.ServeHTTP] blocked on a deferred call to [exec.Cmd.Wait]. func TestCopyError(t *testing.T) { testenv.MustHaveExec(t) - if runtime.GOOS == "windows" { - t.Skipf("skipping test on %q", runtime.GOOS) - } + h := &Handler{ Path: os.Args[0], Root: "/test.cgi", @@ -390,37 +391,42 @@ func TestCopyError(t *testing.T) { t.Fatalf("ReadResponse: %v", err) } - pidstr := res.Header.Get("X-CGI-Pid") - if pidstr == "" { - t.Fatalf("expected an X-CGI-Pid header in response") - } - pid, err := strconv.Atoi(pidstr) - if err != nil { - t.Fatalf("invalid X-CGI-Pid value") - } - var buf [5000]byte n, err := io.ReadFull(res.Body, buf[:]) if err != nil { t.Fatalf("ReadFull: %d bytes, %v", n, err) } - childRunning := func() bool { - return isProcessRunning(pid) - } - - if !childRunning() { - t.Fatalf("pre-conn.Close, expected child to be running") + if !handlerRunning() { + t.Fatalf("pre-conn.Close, expected handler to still be running") } conn.Close() + closed := time.Now() - tries := 0 - for tries < 25 && childRunning() { - time.Sleep(50 * time.Millisecond * time.Duration(tries)) - tries++ + nextSleep := 1 * time.Millisecond + for { + time.Sleep(nextSleep) + nextSleep *= 2 + if !handlerRunning() { + break + } + t.Logf("handler still running %v after conn.Close", time.Since(closed)) } - if childRunning() { - t.Fatalf("post-conn.Close, expected child to be gone") +} + +// handlerRunning reports whether any goroutine is currently running +// [Handler.ServeHTTP]. +func handlerRunning() bool { + r := regexp.MustCompile(`net/http/cgi\.\(\*Handler\)\.ServeHTTP`) + buf := make([]byte, 64<<10) + for { + n := runtime.Stack(buf, true) + if n < len(buf) { + return r.Match(buf[:n]) + } + // Buffer wasn't large enough for a full goroutine dump. + // Resize it and try again. + buf = make([]byte, 2*len(buf)) } } diff --git a/src/net/http/cgi/plan9_test.go b/src/net/http/cgi/plan9_test.go deleted file mode 100644 index b7ace3f81c..0000000000 --- a/src/net/http/cgi/plan9_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build plan9 - -package cgi - -import ( - "os" - "strconv" -) - -func isProcessRunning(pid int) bool { - _, err := os.Stat("/proc/" + strconv.Itoa(pid)) - return err == nil -} diff --git a/src/net/http/cgi/posix_test.go b/src/net/http/cgi/posix_test.go deleted file mode 100644 index 49b9470d4a..0000000000 --- a/src/net/http/cgi/posix_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !plan9 - -package cgi - -import ( - "os" - "syscall" -) - -func isProcessRunning(pid int) bool { - p, err := os.FindProcess(pid) - if err != nil { - return false - } - return p.Signal(syscall.Signal(0)) == nil -} diff --git a/src/net/http/client.go b/src/net/http/client.go index 5fd86a1ec8..ee6de24fc1 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -27,19 +27,19 @@ import ( "time" ) -// A Client is an HTTP client. Its zero value (DefaultClient) is a -// usable client that uses DefaultTransport. +// A Client is an HTTP client. Its zero value ([DefaultClient]) is a +// usable client that uses [DefaultTransport]. // -// The Client's Transport typically has internal state (cached TCP +// The [Client.Transport] typically has internal state (cached TCP // connections), so Clients should be reused instead of created as // needed. Clients are safe for concurrent use by multiple goroutines. // -// A Client is higher-level than a RoundTripper (such as Transport) +// A Client is higher-level than a [RoundTripper] (such as [Transport]) // and additionally handles HTTP details such as cookies and // redirects. // // When following redirects, the Client will forward all headers set on the -// initial Request except: +// initial [Request] except: // // - when forwarding sensitive headers like "Authorization", // "WWW-Authenticate", and "Cookie" to untrusted targets. @@ -105,11 +105,11 @@ type Client struct { Timeout time.Duration } -// DefaultClient is the default Client and is used by Get, Head, and Post. +// DefaultClient is the default [Client] and is used by [Get], [Head], and [Post]. var DefaultClient = &Client{} // RoundTripper is an interface representing the ability to execute a -// single HTTP transaction, obtaining the Response for a given Request. +// single HTTP transaction, obtaining the [Response] for a given [Request]. // // A RoundTripper must be safe for concurrent use by multiple // goroutines. @@ -439,7 +439,7 @@ func basicAuth(username, password string) string { // // An error is returned if there were too many redirects or if there // was an HTTP protocol error. A non-2xx response doesn't cause an -// error. Any returned error will be of type *url.Error. The url.Error +// error. Any returned error will be of type [*url.Error]. The url.Error // value's Timeout method will report true if the request timed out. // // When err is nil, resp always contains a non-nil resp.Body. @@ -447,10 +447,10 @@ func basicAuth(username, password string) string { // // Get is a wrapper around DefaultClient.Get. // -// To make a request with custom headers, use NewRequest and +// To make a request with custom headers, use [NewRequest] and // DefaultClient.Do. // -// To make a request with a specified context.Context, use NewRequestWithContext +// To make a request with a specified context.Context, use [NewRequestWithContext] // and DefaultClient.Do. func Get(url string) (resp *Response, err error) { return DefaultClient.Get(url) @@ -458,7 +458,7 @@ func Get(url string) (resp *Response, err error) { // Get issues a GET to the specified URL. If the response is one of the // following redirect codes, Get follows the redirect after calling the -// Client's CheckRedirect function: +// [Client.CheckRedirect] function: // // 301 (Moved Permanently) // 302 (Found) @@ -466,18 +466,18 @@ func Get(url string) (resp *Response, err error) { // 307 (Temporary Redirect) // 308 (Permanent Redirect) // -// An error is returned if the Client's CheckRedirect function fails +// An error is returned if the [Client.CheckRedirect] function fails // or if there was an HTTP protocol error. A non-2xx response doesn't -// cause an error. Any returned error will be of type *url.Error. The +// cause an error. Any returned error will be of type [*url.Error]. The // url.Error value's Timeout method will report true if the request // timed out. // // When err is nil, resp always contains a non-nil resp.Body. // Caller should close resp.Body when done reading from it. // -// To make a request with custom headers, use NewRequest and Client.Do. +// To make a request with custom headers, use [NewRequest] and [Client.Do]. // -// To make a request with a specified context.Context, use NewRequestWithContext +// To make a request with a specified context.Context, use [NewRequestWithContext] // and Client.Do. func (c *Client) Get(url string) (resp *Response, err error) { req, err := NewRequest("GET", url, nil) @@ -558,10 +558,10 @@ func urlErrorOp(method string) string { // connectivity problem). A non-2xx status code doesn't cause an // error. // -// If the returned error is nil, the Response will contain a non-nil +// If the returned error is nil, the [Response] will contain a non-nil // Body which the user is expected to close. If the Body is not both -// read to EOF and closed, the Client's underlying RoundTripper -// (typically Transport) may not be able to re-use a persistent TCP +// read to EOF and closed, the [Client]'s underlying [RoundTripper] +// (typically [Transport]) may not be able to re-use a persistent TCP // connection to the server for a subsequent "keep-alive" request. // // The request Body, if non-nil, will be closed by the underlying @@ -570,9 +570,9 @@ func urlErrorOp(method string) string { // // On error, any Response can be ignored. A non-nil Response with a // non-nil error only occurs when CheckRedirect fails, and even then -// the returned Response.Body is already closed. +// the returned [Response.Body] is already closed. // -// Generally Get, Post, or PostForm will be used instead of Do. +// Generally [Get], [Post], or [PostForm] will be used instead of Do. // // If the server replies with a redirect, the Client first uses the // CheckRedirect function to determine whether the redirect should be @@ -580,11 +580,11 @@ func urlErrorOp(method string) string { // subsequent requests to use HTTP method GET // (or HEAD if the original request was HEAD), with no body. // A 307 or 308 redirect preserves the original HTTP method and body, -// provided that the Request.GetBody function is defined. -// The NewRequest function automatically sets GetBody for common +// provided that the [Request.GetBody] function is defined. +// The [NewRequest] function automatically sets GetBody for common // standard library body types. // -// Any returned error will be of type *url.Error. The url.Error +// Any returned error will be of type [*url.Error]. The url.Error // value's Timeout method will report true if the request timed out. func (c *Client) Do(req *Request) (*Response, error) { return c.do(req) @@ -818,17 +818,17 @@ func defaultCheckRedirect(req *Request, via []*Request) error { // // Caller should close resp.Body when done reading from it. // -// If the provided body is an io.Closer, it is closed after the +// If the provided body is an [io.Closer], it is closed after the // request. // // Post is a wrapper around DefaultClient.Post. // -// To set custom headers, use NewRequest and DefaultClient.Do. +// To set custom headers, use [NewRequest] and DefaultClient.Do. // -// See the Client.Do method documentation for details on how redirects +// See the [Client.Do] method documentation for details on how redirects // are handled. // -// To make a request with a specified context.Context, use NewRequestWithContext +// To make a request with a specified context.Context, use [NewRequestWithContext] // and DefaultClient.Do. func Post(url, contentType string, body io.Reader) (resp *Response, err error) { return DefaultClient.Post(url, contentType, body) @@ -838,13 +838,13 @@ func Post(url, contentType string, body io.Reader) (resp *Response, err error) { // // Caller should close resp.Body when done reading from it. // -// If the provided body is an io.Closer, it is closed after the +// If the provided body is an [io.Closer], it is closed after the // request. // -// To set custom headers, use NewRequest and Client.Do. +// To set custom headers, use [NewRequest] and [Client.Do]. // -// To make a request with a specified context.Context, use NewRequestWithContext -// and Client.Do. +// To make a request with a specified context.Context, use [NewRequestWithContext] +// and [Client.Do]. // // See the Client.Do method documentation for details on how redirects // are handled. @@ -861,17 +861,17 @@ func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, // values URL-encoded as the request body. // // The Content-Type header is set to application/x-www-form-urlencoded. -// To set other headers, use NewRequest and DefaultClient.Do. +// To set other headers, use [NewRequest] and DefaultClient.Do. // // When err is nil, resp always contains a non-nil resp.Body. // Caller should close resp.Body when done reading from it. // // PostForm is a wrapper around DefaultClient.PostForm. // -// See the Client.Do method documentation for details on how redirects +// See the [Client.Do] method documentation for details on how redirects // are handled. // -// To make a request with a specified context.Context, use NewRequestWithContext +// To make a request with a specified [context.Context], use [NewRequestWithContext] // and DefaultClient.Do. func PostForm(url string, data url.Values) (resp *Response, err error) { return DefaultClient.PostForm(url, data) @@ -881,7 +881,7 @@ func PostForm(url string, data url.Values) (resp *Response, err error) { // with data's keys and values URL-encoded as the request body. // // The Content-Type header is set to application/x-www-form-urlencoded. -// To set other headers, use NewRequest and Client.Do. +// To set other headers, use [NewRequest] and [Client.Do]. // // When err is nil, resp always contains a non-nil resp.Body. // Caller should close resp.Body when done reading from it. @@ -889,7 +889,7 @@ func PostForm(url string, data url.Values) (resp *Response, err error) { // See the Client.Do method documentation for details on how redirects // are handled. // -// To make a request with a specified context.Context, use NewRequestWithContext +// To make a request with a specified context.Context, use [NewRequestWithContext] // and Client.Do. func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) { return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) @@ -907,7 +907,7 @@ func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro // // Head is a wrapper around DefaultClient.Head. // -// To make a request with a specified context.Context, use NewRequestWithContext +// To make a request with a specified [context.Context], use [NewRequestWithContext] // and DefaultClient.Do. func Head(url string) (resp *Response, err error) { return DefaultClient.Head(url) @@ -915,7 +915,7 @@ func Head(url string) (resp *Response, err error) { // Head issues a HEAD to the specified URL. If the response is one of the // following redirect codes, Head follows the redirect after calling the -// Client's CheckRedirect function: +// [Client.CheckRedirect] function: // // 301 (Moved Permanently) // 302 (Found) @@ -923,8 +923,8 @@ func Head(url string) (resp *Response, err error) { // 307 (Temporary Redirect) // 308 (Permanent Redirect) // -// To make a request with a specified context.Context, use NewRequestWithContext -// and Client.Do. +// To make a request with a specified [context.Context], use [NewRequestWithContext] +// and [Client.Do]. func (c *Client) Head(url string) (resp *Response, err error) { req, err := NewRequest("HEAD", url, nil) if err != nil { @@ -933,12 +933,12 @@ func (c *Client) Head(url string) (resp *Response, err error) { return c.Do(req) } -// CloseIdleConnections closes any connections on its Transport which +// CloseIdleConnections closes any connections on its [Transport] which // were previously connected from previous requests but are now // sitting idle in a "keep-alive" state. It does not interrupt any // connections currently in use. // -// If the Client's Transport does not have a CloseIdleConnections method +// If [Client.Transport] does not have a [Client.CloseIdleConnections] method // then this method does nothing. func (c *Client) CloseIdleConnections() { type closeIdler interface { diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go index 912fde6b95..c22897f3f9 100644 --- a/src/net/http/cookie.go +++ b/src/net/http/cookie.go @@ -163,7 +163,7 @@ func readSetCookies(h Header) []*Cookie { return cookies } -// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers. +// SetCookie adds a Set-Cookie header to the provided [ResponseWriter]'s headers. // The provided cookie must have a valid Name. Invalid cookies may be // silently dropped. func SetCookie(w ResponseWriter, cookie *Cookie) { @@ -172,7 +172,7 @@ func SetCookie(w ResponseWriter, cookie *Cookie) { } } -// String returns the serialization of the cookie for use in a Cookie +// String returns the serialization of the cookie for use in a [Cookie] // header (if only Name and Value are set) or a Set-Cookie response // header (if other fields are set). // If c is nil or c.Name is invalid, the empty string is returned. diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go index 46d1193951..59cde82cb3 100644 --- a/src/net/http/cookiejar/jar.go +++ b/src/net/http/cookiejar/jar.go @@ -73,7 +73,7 @@ type Jar struct { nextSeqNum uint64 } -// New returns a new cookie jar. A nil *Options is equivalent to a zero +// New returns a new cookie jar. A nil [*Options] is equivalent to a zero // Options. func New(o *Options) (*Jar, error) { jar := &Jar{ @@ -151,7 +151,7 @@ func hasDotSuffix(s, suffix string) bool { return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix } -// Cookies implements the Cookies method of the http.CookieJar interface. +// Cookies implements the Cookies method of the [http.CookieJar] interface. // // It returns an empty slice if the URL's scheme is not HTTP or HTTPS. func (j *Jar) Cookies(u *url.URL) (cookies []*http.Cookie) { @@ -226,7 +226,7 @@ func (j *Jar) cookies(u *url.URL, now time.Time) (cookies []*http.Cookie) { return cookies } -// SetCookies implements the SetCookies method of the http.CookieJar interface. +// SetCookies implements the SetCookies method of the [http.CookieJar] interface. // // It does nothing if the URL's scheme is not HTTP or HTTPS. func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) { diff --git a/src/net/http/doc.go b/src/net/http/doc.go index d9e6aafb4e..f7ad3ae762 100644 --- a/src/net/http/doc.go +++ b/src/net/http/doc.go @@ -5,7 +5,7 @@ /* Package http provides HTTP client and server implementations. -Get, Head, Post, and PostForm make HTTP (or HTTPS) requests: +[Get], [Head], [Post], and [PostForm] make HTTP (or HTTPS) requests: resp, err := http.Get("http://example.com/") ... @@ -27,7 +27,7 @@ The caller must close the response body when finished with it: # Clients and Transports For control over HTTP client headers, redirect policy, and other -settings, create a Client: +settings, create a [Client]: client := &http.Client{ CheckRedirect: redirectPolicyFunc, @@ -43,7 +43,7 @@ settings, create a Client: // ... For control over proxies, TLS configuration, keep-alives, -compression, and other settings, create a Transport: +compression, and other settings, create a [Transport]: tr := &http.Transport{ MaxIdleConns: 10, @@ -59,8 +59,8 @@ goroutines and for efficiency should only be created once and re-used. # Servers ListenAndServe starts an HTTP server with a given address and handler. -The handler is usually nil, which means to use DefaultServeMux. -Handle and HandleFunc add handlers to DefaultServeMux: +The handler is usually nil, which means to use [DefaultServeMux]. +[Handle] and [HandleFunc] add handlers to [DefaultServeMux]: http.Handle("/foo", fooHandler) @@ -86,8 +86,8 @@ custom Server: Starting with Go 1.6, the http package has transparent support for the HTTP/2 protocol when using HTTPS. Programs that must disable HTTP/2 -can do so by setting Transport.TLSNextProto (for clients) or -Server.TLSNextProto (for servers) to a non-nil, empty +can do so by setting [Transport.TLSNextProto] (for clients) or +[Server.TLSNextProto] (for servers) to a non-nil, empty map. Alternatively, the following GODEBUG settings are currently supported: @@ -98,7 +98,7 @@ currently supported: Please report any issues before disabling HTTP/2 support: https://golang.org/s/http2bug -The http package's Transport and Server both automatically enable +The http package's [Transport] and [Server] both automatically enable HTTP/2 support for simple configurations. To enable HTTP/2 for more complex configurations, to use lower-level HTTP/2 features, or to use a newer version of Go's http2 package, import "golang.org/x/net/http2" diff --git a/src/net/http/fcgi/child.go b/src/net/http/fcgi/child.go index dc82bf7c3a..7665e7d252 100644 --- a/src/net/http/fcgi/child.go +++ b/src/net/http/fcgi/child.go @@ -335,7 +335,7 @@ func (c *child) cleanUp() { // goroutine for each. The goroutine reads requests and then calls handler // to reply to them. // If l is nil, Serve accepts connections from os.Stdin. -// If handler is nil, http.DefaultServeMux is used. +// If handler is nil, [http.DefaultServeMux] is used. func Serve(l net.Listener, handler http.Handler) error { if l == nil { var err error diff --git a/src/net/http/filetransport.go b/src/net/http/filetransport.go index 2a9e9b02ba..7384b22fbe 100644 --- a/src/net/http/filetransport.go +++ b/src/net/http/filetransport.go @@ -15,13 +15,13 @@ type fileTransport struct { fh fileHandler } -// NewFileTransport returns a new RoundTripper, serving the provided -// FileSystem. The returned RoundTripper ignores the URL host in its +// NewFileTransport returns a new [RoundTripper], serving the provided +// [FileSystem]. The returned RoundTripper ignores the URL host in its // incoming requests, as well as most other properties of the // request. // // The typical use case for NewFileTransport is to register the "file" -// protocol with a Transport, as in: +// protocol with a [Transport], as in: // // t := &http.Transport{} // t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) @@ -32,13 +32,13 @@ func NewFileTransport(fs FileSystem) RoundTripper { return fileTransport{fileHandler{fs}} } -// NewFileTransportFS returns a new RoundTripper, serving the provided +// NewFileTransportFS returns a new [RoundTripper], serving the provided // file system fsys. The returned RoundTripper ignores the URL host in its // incoming requests, as well as most other properties of the // request. // // The typical use case for NewFileTransportFS is to register the "file" -// protocol with a Transport, as in: +// protocol with a [Transport], as in: // // fsys := os.DirFS("/") // t := &http.Transport{} diff --git a/src/net/http/fs.go b/src/net/http/fs.go index 54b12c68e0..3432ee68ec 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -25,12 +25,12 @@ import ( "time" ) -// A Dir implements FileSystem using the native file system restricted to a +// A Dir implements [FileSystem] using the native file system restricted to a // specific directory tree. // -// While the FileSystem.Open method takes '/'-separated paths, a Dir's string +// While the [FileSystem.Open] method takes '/'-separated paths, a Dir's string // value is a directory path on the native file system, not a URL, so it is separated -// by filepath.Separator, which isn't necessarily '/'. +// by [filepath.Separator], which isn't necessarily '/'. // // Note that Dir could expose sensitive files and directories. Dir will follow // symlinks pointing out of the directory tree, which can be especially dangerous @@ -67,7 +67,7 @@ func mapOpenError(originalErr error, name string, sep rune, stat func(string) (f return originalErr } -// Open implements FileSystem using os.Open, opening files for reading rooted +// Open implements [FileSystem] using [os.Open], opening files for reading rooted // and relative to the directory d. func (d Dir) Open(name string) (File, error) { path, err := safefilepath.FromFS(path.Clean("/" + name)) @@ -89,18 +89,18 @@ func (d Dir) Open(name string) (File, error) { // A FileSystem implements access to a collection of named files. // The elements in a file path are separated by slash ('/', U+002F) // characters, regardless of host operating system convention. -// See the FileServer function to convert a FileSystem to a Handler. +// See the [FileServer] function to convert a FileSystem to a [Handler]. // -// This interface predates the fs.FS interface, which can be used instead: -// the FS adapter function converts an fs.FS to a FileSystem. +// This interface predates the [fs.FS] interface, which can be used instead: +// the [FS] adapter function converts an fs.FS to a FileSystem. type FileSystem interface { Open(name string) (File, error) } -// A File is returned by a FileSystem's Open method and can be -// served by the FileServer implementation. +// A File is returned by a [FileSystem]'s Open method and can be +// served by the [FileServer] implementation. // -// The methods should behave the same as those on an *os.File. +// The methods should behave the same as those on an [*os.File]. type File interface { io.Closer io.Reader @@ -167,7 +167,7 @@ func dirList(w ResponseWriter, r *Request, f File) { } // ServeContent replies to the request using the content in the -// provided ReadSeeker. The main benefit of ServeContent over io.Copy +// provided ReadSeeker. The main benefit of ServeContent over [io.Copy] // is that it handles Range requests properly, sets the MIME type, and // handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since, // and If-Range requests. @@ -175,7 +175,7 @@ func dirList(w ResponseWriter, r *Request, f File) { // If the response's Content-Type header is not set, ServeContent // first tries to deduce the type from name's file extension and, // if that fails, falls back to reading the first block of the content -// and passing it to DetectContentType. +// and passing it to [DetectContentType]. // The name is otherwise unused; in particular it can be empty and is // never sent in the response. // @@ -190,7 +190,7 @@ func dirList(w ResponseWriter, r *Request, f File) { // If the caller has set w's ETag header formatted per RFC 7232, section 2.3, // ServeContent uses it to handle requests using If-Match, If-None-Match, or If-Range. // -// Note that *os.File implements the io.ReadSeeker interface. +// Note that [*os.File] implements the [io.ReadSeeker] interface. func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) { sizeFunc := func() (int64, error) { size, err := content.Seek(0, io.SeekEnd) @@ -746,13 +746,13 @@ func localRedirect(w ResponseWriter, r *Request, newPath string) { // // As a precaution, ServeFile will reject requests where r.URL.Path // contains a ".." path element; this protects against callers who -// might unsafely use filepath.Join on r.URL.Path without sanitizing +// might unsafely use [filepath.Join] on r.URL.Path without sanitizing // it and then use that filepath.Join result as the name argument. // // As another special case, ServeFile redirects any request where r.URL.Path // ends in "/index.html" to the same path, without the final // "index.html". To avoid such redirects either modify the path or -// use ServeContent. +// use [ServeContent]. // // Outside of those two special cases, ServeFile does not use // r.URL.Path for selecting the file or directory to serve; only the @@ -777,11 +777,11 @@ func ServeFile(w ResponseWriter, r *Request, name string) { // If the provided file or directory name is a relative path, it is // interpreted relative to the current directory and may ascend to // parent directories. If the provided name is constructed from user -// input, it should be sanitized before calling ServeFile. +// input, it should be sanitized before calling [ServeFile]. // // As a precaution, ServeFile will reject requests where r.URL.Path // contains a ".." path element; this protects against callers who -// might unsafely use filepath.Join on r.URL.Path without sanitizing +// might unsafely use [filepath.Join] on r.URL.Path without sanitizing // it and then use that filepath.Join result as the name argument. // // As another special case, ServeFile redirects any request where r.URL.Path @@ -895,9 +895,9 @@ func (f ioFile) Readdir(count int) ([]fs.FileInfo, error) { return list, nil } -// FS converts fsys to a FileSystem implementation, -// for use with FileServer and NewFileTransport. -// The files provided by fsys must implement io.Seeker. +// FS converts fsys to a [FileSystem] implementation, +// for use with [FileServer] and [NewFileTransport]. +// The files provided by fsys must implement [io.Seeker]. func FS(fsys fs.FS) FileSystem { return ioFS{fsys} } @@ -910,11 +910,11 @@ func FS(fsys fs.FS) FileSystem { // "index.html". // // To use the operating system's file system implementation, -// use http.Dir: +// use [http.Dir]: // // http.Handle("/", http.FileServer(http.Dir("/tmp"))) // -// To use an fs.FS implementation, use http.FileServerFS instead. +// To use an [fs.FS] implementation, use [http.FileServerFS] instead. func FileServer(root FileSystem) Handler { return &fileHandler{root} } diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index fea33276d8..ac41144d5b 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -1041,41 +1041,44 @@ func http2shouldRetryDial(call *http2dialCall, req *Request) bool { // TODO: Benchmark to determine if the pools are necessary. The GC may have // improved enough that we can instead allocate chunks like this: // make([]byte, max(16<<10, expectedBytesRemaining)) -var ( - http2dataChunkSizeClasses = []int{ - 1 << 10, - 2 << 10, - 4 << 10, - 8 << 10, - 16 << 10, - } - http2dataChunkPools = [...]sync.Pool{ - {New: func() interface{} { return make([]byte, 1<<10) }}, - {New: func() interface{} { return make([]byte, 2<<10) }}, - {New: func() interface{} { return make([]byte, 4<<10) }}, - {New: func() interface{} { return make([]byte, 8<<10) }}, - {New: func() interface{} { return make([]byte, 16<<10) }}, - } -) +var http2dataChunkPools = [...]sync.Pool{ + {New: func() interface{} { return new([1 << 10]byte) }}, + {New: func() interface{} { return new([2 << 10]byte) }}, + {New: func() interface{} { return new([4 << 10]byte) }}, + {New: func() interface{} { return new([8 << 10]byte) }}, + {New: func() interface{} { return new([16 << 10]byte) }}, +} func http2getDataBufferChunk(size int64) []byte { - i := 0 - for ; i < len(http2dataChunkSizeClasses)-1; i++ { - if size <= int64(http2dataChunkSizeClasses[i]) { - break - } + switch { + case size <= 1<<10: + return http2dataChunkPools[0].Get().(*[1 << 10]byte)[:] + case size <= 2<<10: + return http2dataChunkPools[1].Get().(*[2 << 10]byte)[:] + case size <= 4<<10: + return http2dataChunkPools[2].Get().(*[4 << 10]byte)[:] + case size <= 8<<10: + return http2dataChunkPools[3].Get().(*[8 << 10]byte)[:] + default: + return http2dataChunkPools[4].Get().(*[16 << 10]byte)[:] } - return http2dataChunkPools[i].Get().([]byte) } func http2putDataBufferChunk(p []byte) { - for i, n := range http2dataChunkSizeClasses { - if len(p) == n { - http2dataChunkPools[i].Put(p) - return - } + switch len(p) { + case 1 << 10: + http2dataChunkPools[0].Put((*[1 << 10]byte)(p)) + case 2 << 10: + http2dataChunkPools[1].Put((*[2 << 10]byte)(p)) + case 4 << 10: + http2dataChunkPools[2].Put((*[4 << 10]byte)(p)) + case 8 << 10: + http2dataChunkPools[3].Put((*[8 << 10]byte)(p)) + case 16 << 10: + http2dataChunkPools[4].Put((*[16 << 10]byte)(p)) + default: + panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } - panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } // dataBuffer is an io.ReadWriter backed by a list of data chunks. @@ -3058,41 +3061,6 @@ func http2summarizeFrame(f http2Frame) string { return buf.String() } -func http2traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { - return trace != nil && trace.WroteHeaderField != nil -} - -func http2traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(k, []string{v}) - } -} - -func http2traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - if trace != nil { - return trace.Got1xxResponse - } - return nil -} - -// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS -// connection. -func (t *http2Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - dialer := &tls.Dialer{ - Config: cfg, - } - cn, err := dialer.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed - return tlsCn, nil -} - -func http2tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return tc.NetConn() -} - var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" type http2goroutineLock uint64 @@ -6366,7 +6334,6 @@ type http2responseWriterState struct { wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. sentHeader bool // have we sent the header frame? handlerDone bool // handler has finished - dirty bool // a Write failed; don't reuse this responseWriterState sentContentLen int64 // non-zero if handler set a Content-Length header wroteBytes int64 @@ -6486,7 +6453,6 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { date: date, }) if err != nil { - rws.dirty = true return 0, err } if endStream { @@ -6507,7 +6473,6 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { if len(p) > 0 || endStream { // only send a 0 byte DATA frame if we're ending the stream. if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { - rws.dirty = true return 0, err } } @@ -6519,9 +6484,6 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { trailers: rws.trailers, endStream: true, }) - if err != nil { - rws.dirty = true - } return len(p), err } return len(p), nil @@ -6737,14 +6699,12 @@ func (rws *http2responseWriterState) writeHeader(code int) { h.Del("Transfer-Encoding") } - if rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{ + rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{ streamID: rws.stream.id, httpResCode: code, h: h, endStream: rws.handlerDone && !rws.hasTrailers(), - }) != nil { - rws.dirty = true - } + }) return } @@ -6809,19 +6769,10 @@ func (w *http2responseWriter) write(lenData int, dataB []byte, dataS string) (n func (w *http2responseWriter) handlerDone() { rws := w.rws - dirty := rws.dirty rws.handlerDone = true w.Flush() w.rws = nil - if !dirty { - // Only recycle the pool if all prior Write calls to - // the serverConn goroutine completed successfully. If - // they returned earlier due to resets from the peer - // there might still be write goroutines outstanding - // from the serverConn referencing the rws memory. See - // issue 20704. - http2responseWriterStatePool.Put(rws) - } + http2responseWriterStatePool.Put(rws) } // Push errors. @@ -8094,7 +8045,7 @@ func (cc *http2ClientConn) forceCloseConn() { if !ok { return } - if nc := http2tlsUnderlyingConn(tc); nc != nil { + if nc := tc.NetConn(); nc != nil { nc.Close() } } @@ -10282,6 +10233,37 @@ func http2traceFirstResponseByte(trace *httptrace.ClientTrace) { } } +func http2traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { + return trace != nil && trace.WroteHeaderField != nil +} + +func http2traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(k, []string{v}) + } +} + +func http2traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { + if trace != nil { + return trace.Got1xxResponse + } + return nil +} + +// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS +// connection. +func (t *http2Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { + dialer := &tls.Dialer{ + Config: cfg, + } + cn, err := dialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed + return tlsCn, nil +} + // writeFramer is implemented by any type that is used to write frames. type http2writeFramer interface { writeFrame(http2writeContext) error diff --git a/src/net/http/header.go b/src/net/http/header.go index e0b342c63c..9d0f3a125d 100644 --- a/src/net/http/header.go +++ b/src/net/http/header.go @@ -20,13 +20,13 @@ import ( // A Header represents the key-value pairs in an HTTP header. // // The keys should be in canonical form, as returned by -// CanonicalHeaderKey. +// [CanonicalHeaderKey]. type Header map[string][]string // Add adds the key, value pair to the header. // It appends to any existing values associated with key. // The key is case insensitive; it is canonicalized by -// CanonicalHeaderKey. +// [CanonicalHeaderKey]. func (h Header) Add(key, value string) { textproto.MIMEHeader(h).Add(key, value) } @@ -34,7 +34,7 @@ func (h Header) Add(key, value string) { // Set sets the header entries associated with key to the // single element value. It replaces any existing values // associated with key. The key is case insensitive; it is -// canonicalized by textproto.CanonicalMIMEHeaderKey. +// canonicalized by [textproto.CanonicalMIMEHeaderKey]. // To use non-canonical keys, assign to the map directly. func (h Header) Set(key, value string) { textproto.MIMEHeader(h).Set(key, value) @@ -42,7 +42,7 @@ func (h Header) Set(key, value string) { // Get gets the first value associated with the given key. If // there are no values associated with the key, Get returns "". -// It is case insensitive; textproto.CanonicalMIMEHeaderKey is +// It is case insensitive; [textproto.CanonicalMIMEHeaderKey] is // used to canonicalize the provided key. Get assumes that all // keys are stored in canonical form. To use non-canonical keys, // access the map directly. @@ -51,7 +51,7 @@ func (h Header) Get(key string) string { } // Values returns all values associated with the given key. -// It is case insensitive; textproto.CanonicalMIMEHeaderKey is +// It is case insensitive; [textproto.CanonicalMIMEHeaderKey] is // used to canonicalize the provided key. To use non-canonical // keys, access the map directly. // The returned slice is not a copy. @@ -76,7 +76,7 @@ func (h Header) has(key string) bool { // Del deletes the values associated with key. // The key is case insensitive; it is canonicalized by -// CanonicalHeaderKey. +// [CanonicalHeaderKey]. func (h Header) Del(key string) { textproto.MIMEHeader(h).Del(key) } @@ -125,7 +125,7 @@ var timeFormats = []string{ // ParseTime parses a time header (such as the Date: header), // trying each of the three formats allowed by HTTP/1.1: -// TimeFormat, time.RFC850, and time.ANSIC. +// [TimeFormat], [time.RFC850], and [time.ANSIC]. func ParseTime(text string) (t time.Time, err error) { for _, layout := range timeFormats { t, err = time.Parse(layout, text) diff --git a/src/net/http/http.go b/src/net/http/http.go index 9b81654fcc..6e2259adbf 100644 --- a/src/net/http/http.go +++ b/src/net/http/http.go @@ -103,10 +103,10 @@ func hexEscapeNonASCII(s string) string { return string(b) } -// NoBody is an io.ReadCloser with no bytes. Read always returns EOF +// NoBody is an [io.ReadCloser] with no bytes. Read always returns EOF // and Close always returns nil. It can be used in an outgoing client // request to explicitly signal that a request has zero bytes. -// An alternative, however, is to simply set Request.Body to nil. +// An alternative, however, is to simply set [Request.Body] to nil. var NoBody = noBody{} type noBody struct{} @@ -121,7 +121,7 @@ var ( _ io.ReadCloser = NoBody ) -// PushOptions describes options for Pusher.Push. +// PushOptions describes options for [Pusher.Push]. type PushOptions struct { // Method specifies the HTTP method for the promised request. // If set, it must be "GET" or "HEAD". Empty means "GET". diff --git a/src/net/http/httptest/httptest.go b/src/net/http/httptest/httptest.go index 9bedefd2bc..f0ca64362d 100644 --- a/src/net/http/httptest/httptest.go +++ b/src/net/http/httptest/httptest.go @@ -15,7 +15,7 @@ import ( ) // NewRequest returns a new incoming server Request, suitable -// for passing to an http.Handler for testing. +// for passing to an [http.Handler] for testing. // // The target is the RFC 7230 "request-target": it may be either a // path or an absolute URL. If target is an absolute URL, the host name diff --git a/src/net/http/httptest/recorder.go b/src/net/http/httptest/recorder.go index 1c1d880155..dd51901b0d 100644 --- a/src/net/http/httptest/recorder.go +++ b/src/net/http/httptest/recorder.go @@ -16,7 +16,7 @@ import ( "golang.org/x/net/http/httpguts" ) -// ResponseRecorder is an implementation of http.ResponseWriter that +// ResponseRecorder is an implementation of [http.ResponseWriter] that // records its mutations for later inspection in tests. type ResponseRecorder struct { // Code is the HTTP response code set by WriteHeader. @@ -47,7 +47,7 @@ type ResponseRecorder struct { wroteHeader bool } -// NewRecorder returns an initialized ResponseRecorder. +// NewRecorder returns an initialized [ResponseRecorder]. func NewRecorder() *ResponseRecorder { return &ResponseRecorder{ HeaderMap: make(http.Header), @@ -57,12 +57,12 @@ func NewRecorder() *ResponseRecorder { } // DefaultRemoteAddr is the default remote address to return in RemoteAddr if -// an explicit DefaultRemoteAddr isn't set on ResponseRecorder. +// an explicit DefaultRemoteAddr isn't set on [ResponseRecorder]. const DefaultRemoteAddr = "1.2.3.4" -// Header implements http.ResponseWriter. It returns the response +// Header implements [http.ResponseWriter]. It returns the response // headers to mutate within a handler. To test the headers that were -// written after a handler completes, use the Result method and see +// written after a handler completes, use the [ResponseRecorder.Result] method and see // the returned Response value's Header. func (rw *ResponseRecorder) Header() http.Header { m := rw.HeaderMap @@ -112,7 +112,7 @@ func (rw *ResponseRecorder) Write(buf []byte) (int, error) { return len(buf), nil } -// WriteString implements io.StringWriter. The data in str is written +// WriteString implements [io.StringWriter]. The data in str is written // to rw.Body, if not nil. func (rw *ResponseRecorder) WriteString(str string) (int, error) { rw.writeHeader(nil, str) @@ -139,7 +139,7 @@ func checkWriteHeaderCode(code int) { } } -// WriteHeader implements http.ResponseWriter. +// WriteHeader implements [http.ResponseWriter]. func (rw *ResponseRecorder) WriteHeader(code int) { if rw.wroteHeader { return @@ -154,7 +154,7 @@ func (rw *ResponseRecorder) WriteHeader(code int) { rw.snapHeader = rw.HeaderMap.Clone() } -// Flush implements http.Flusher. To test whether Flush was +// Flush implements [http.Flusher]. To test whether Flush was // called, see rw.Flushed. func (rw *ResponseRecorder) Flush() { if !rw.wroteHeader { @@ -175,7 +175,7 @@ func (rw *ResponseRecorder) Flush() { // did a write. // // The Response.Body is guaranteed to be non-nil and Body.Read call is -// guaranteed to not return any error other than io.EOF. +// guaranteed to not return any error other than [io.EOF]. // // Result must only be called after the handler has finished running. func (rw *ResponseRecorder) Result() *http.Response { diff --git a/src/net/http/httptest/server.go b/src/net/http/httptest/server.go index c962749e85..5095b438ec 100644 --- a/src/net/http/httptest/server.go +++ b/src/net/http/httptest/server.go @@ -100,7 +100,7 @@ func strSliceContainsPrefix(v []string, pre string) bool { return false } -// NewServer starts and returns a new Server. +// NewServer starts and returns a new [Server]. // The caller should call Close when finished, to shut it down. func NewServer(handler http.Handler) *Server { ts := NewUnstartedServer(handler) @@ -108,7 +108,7 @@ func NewServer(handler http.Handler) *Server { return ts } -// NewUnstartedServer returns a new Server but doesn't start it. +// NewUnstartedServer returns a new [Server] but doesn't start it. // // After changing its configuration, the caller should call Start or // StartTLS. @@ -185,7 +185,7 @@ func (s *Server) StartTLS() { s.goServe() } -// NewTLSServer starts and returns a new Server using TLS. +// NewTLSServer starts and returns a new [Server] using TLS. // The caller should call Close when finished, to shut it down. func NewTLSServer(handler http.Handler) *Server { ts := NewUnstartedServer(handler) @@ -298,7 +298,7 @@ func (s *Server) Certificate() *x509.Certificate { // Client returns an HTTP client configured for making requests to the server. // It is configured to trust the server's TLS test certificate and will -// close its idle connections on Server.Close. +// close its idle connections on [Server.Close]. func (s *Server) Client() *http.Client { return s.client } diff --git a/src/net/http/httptrace/trace.go b/src/net/http/httptrace/trace.go index 6af30f78d1..706a432957 100644 --- a/src/net/http/httptrace/trace.go +++ b/src/net/http/httptrace/trace.go @@ -19,7 +19,7 @@ import ( // unique type to prevent assignment. type clientEventContextKey struct{} -// ContextClientTrace returns the ClientTrace associated with the +// ContextClientTrace returns the [ClientTrace] associated with the // provided context. If none, it returns nil. func ContextClientTrace(ctx context.Context) *ClientTrace { trace, _ := ctx.Value(clientEventContextKey{}).(*ClientTrace) @@ -233,7 +233,7 @@ func (t *ClientTrace) hasNetHooks() bool { return t.DNSStart != nil || t.DNSDone != nil || t.ConnectStart != nil || t.ConnectDone != nil } -// GotConnInfo is the argument to the ClientTrace.GotConn function and +// GotConnInfo is the argument to the [ClientTrace.GotConn] function and // contains information about the obtained connection. type GotConnInfo struct { // Conn is the connection that was obtained. It is owned by diff --git a/src/net/http/httputil/dump.go b/src/net/http/httputil/dump.go index 7affe5e61a..2edb9bc98d 100644 --- a/src/net/http/httputil/dump.go +++ b/src/net/http/httputil/dump.go @@ -71,8 +71,8 @@ func outgoingLength(req *http.Request) int64 { return -1 } -// DumpRequestOut is like DumpRequest but for outgoing client requests. It -// includes any headers that the standard http.Transport adds, such as +// DumpRequestOut is like [DumpRequest] but for outgoing client requests. It +// includes any headers that the standard [http.Transport] adds, such as // User-Agent. func DumpRequestOut(req *http.Request, body bool) ([]byte, error) { save := req.Body @@ -203,17 +203,17 @@ var reqWriteExcludeHeaderDump = map[string]bool{ // representation. It should only be used by servers to debug client // requests. The returned representation is an approximation only; // some details of the initial request are lost while parsing it into -// an http.Request. In particular, the order and case of header field +// an [http.Request]. In particular, the order and case of header field // names are lost. The order of values in multi-valued headers is kept // intact. HTTP/2 requests are dumped in HTTP/1.x form, not in their // original binary representations. // // If body is true, DumpRequest also returns the body. To do so, it -// consumes req.Body and then replaces it with a new io.ReadCloser +// consumes req.Body and then replaces it with a new [io.ReadCloser] // that yields the same bytes. If DumpRequest returns an error, // the state of req is undefined. // -// The documentation for http.Request.Write details which fields +// The documentation for [http.Request.Write] details which fields // of req are included in the dump. func DumpRequest(req *http.Request, body bool) ([]byte, error) { var err error diff --git a/src/net/http/httputil/httputil.go b/src/net/http/httputil/httputil.go index 09ea74d6d1..431930ea65 100644 --- a/src/net/http/httputil/httputil.go +++ b/src/net/http/httputil/httputil.go @@ -13,7 +13,7 @@ import ( // NewChunkedReader returns a new chunkedReader that translates the data read from r // out of HTTP "chunked" format before returning it. -// The chunkedReader returns io.EOF when the final 0-length chunk is read. +// The chunkedReader returns [io.EOF] when the final 0-length chunk is read. // // NewChunkedReader is not needed by normal applications. The http package // automatically decodes chunking when reading response bodies. diff --git a/src/net/http/httputil/persist.go b/src/net/http/httputil/persist.go index 84b116df8c..0cbe3ebf10 100644 --- a/src/net/http/httputil/persist.go +++ b/src/net/http/httputil/persist.go @@ -33,7 +33,7 @@ var errClosed = errors.New("i/o operation on closed connection") // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: Use the Server in package net/http instead. +// Deprecated: Use the Server in package [net/http] instead. type ServerConn struct { mu sync.Mutex // read-write protects the following fields c net.Conn @@ -50,7 +50,7 @@ type ServerConn struct { // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: Use the Server in package net/http instead. +// Deprecated: Use the Server in package [net/http] instead. func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn { if r == nil { r = bufio.NewReader(c) @@ -58,10 +58,10 @@ func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn { return &ServerConn{c: c, r: r, pipereq: make(map[*http.Request]uint)} } -// Hijack detaches the ServerConn and returns the underlying connection as well +// Hijack detaches the [ServerConn] and returns the underlying connection as well // as the read-side bufio which may have some left over data. Hijack may be // called before Read has signaled the end of the keep-alive logic. The user -// should not call Hijack while Read or Write is in progress. +// should not call Hijack while [ServerConn.Read] or [ServerConn.Write] is in progress. func (sc *ServerConn) Hijack() (net.Conn, *bufio.Reader) { sc.mu.Lock() defer sc.mu.Unlock() @@ -72,7 +72,7 @@ func (sc *ServerConn) Hijack() (net.Conn, *bufio.Reader) { return c, r } -// Close calls Hijack and then also closes the underlying connection. +// Close calls [ServerConn.Hijack] and then also closes the underlying connection. func (sc *ServerConn) Close() error { c, _ := sc.Hijack() if c != nil { @@ -81,7 +81,7 @@ func (sc *ServerConn) Close() error { return nil } -// Read returns the next request on the wire. An ErrPersistEOF is returned if +// Read returns the next request on the wire. An [ErrPersistEOF] is returned if // it is gracefully determined that there are no more requests (e.g. after the // first request on an HTTP/1.0 connection, or after a Connection:close on a // HTTP/1.1 connection). @@ -171,7 +171,7 @@ func (sc *ServerConn) Pending() int { // Write writes resp in response to req. To close the connection gracefully, set the // Response.Close field to true. Write should be considered operational until -// it returns an error, regardless of any errors returned on the Read side. +// it returns an error, regardless of any errors returned on the [ServerConn.Read] side. func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error { // Retrieve the pipeline ID of this request/response pair @@ -226,7 +226,7 @@ func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error { // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: Use Client or Transport in package net/http instead. +// Deprecated: Use Client or Transport in package [net/http] instead. type ClientConn struct { mu sync.Mutex // read-write protects the following fields c net.Conn @@ -244,7 +244,7 @@ type ClientConn struct { // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: Use the Client or Transport in package net/http instead. +// Deprecated: Use the Client or Transport in package [net/http] instead. func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn { if r == nil { r = bufio.NewReader(c) @@ -261,17 +261,17 @@ func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn { // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: Use the Client or Transport in package net/http instead. +// Deprecated: Use the Client or Transport in package [net/http] instead. func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn { cc := NewClientConn(c, r) cc.writeReq = (*http.Request).WriteProxy return cc } -// Hijack detaches the ClientConn and returns the underlying connection as well +// Hijack detaches the [ClientConn] and returns the underlying connection as well // as the read-side bufio which may have some left over data. Hijack may be // called before the user or Read have signaled the end of the keep-alive -// logic. The user should not call Hijack while Read or Write is in progress. +// logic. The user should not call Hijack while [ClientConn.Read] or ClientConn.Write is in progress. func (cc *ClientConn) Hijack() (c net.Conn, r *bufio.Reader) { cc.mu.Lock() defer cc.mu.Unlock() @@ -282,7 +282,7 @@ func (cc *ClientConn) Hijack() (c net.Conn, r *bufio.Reader) { return } -// Close calls Hijack and then also closes the underlying connection. +// Close calls [ClientConn.Hijack] and then also closes the underlying connection. func (cc *ClientConn) Close() error { c, _ := cc.Hijack() if c != nil { @@ -291,7 +291,7 @@ func (cc *ClientConn) Close() error { return nil } -// Write writes a request. An ErrPersistEOF error is returned if the connection +// Write writes a request. An [ErrPersistEOF] error is returned if the connection // has been closed in an HTTP keep-alive sense. If req.Close equals true, the // keep-alive connection is logically closed after this request and the opposing // server is informed. An ErrUnexpectedEOF indicates the remote closed the @@ -357,9 +357,9 @@ func (cc *ClientConn) Pending() int { } // Read reads the next response from the wire. A valid response might be -// returned together with an ErrPersistEOF, which means that the remote +// returned together with an [ErrPersistEOF], which means that the remote // requested that this be the last request serviced. Read can be called -// concurrently with Write, but not with another Read. +// concurrently with [ClientConn.Write], but not with another Read. func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) { // Retrieve the pipeline ID of this request/response pair cc.mu.Lock() diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index 719ab62d1a..5c70f0d27b 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -26,7 +26,7 @@ import ( "golang.org/x/net/http/httpguts" ) -// A ProxyRequest contains a request to be rewritten by a ReverseProxy. +// A ProxyRequest contains a request to be rewritten by a [ReverseProxy]. type ProxyRequest struct { // In is the request received by the proxy. // The Rewrite function must not modify In. @@ -45,7 +45,7 @@ type ProxyRequest struct { // // SetURL rewrites the outbound Host header to match the target's host. // To preserve the inbound request's Host header (the default behavior -// of NewSingleHostReverseProxy): +// of [NewSingleHostReverseProxy]): // // rewriteFunc := func(r *httputil.ProxyRequest) { // r.SetURL(url) @@ -68,7 +68,7 @@ func (r *ProxyRequest) SetURL(target *url.URL) { // If the outbound request contains an existing X-Forwarded-For header, // SetXForwarded appends the client IP address to it. To append to the // inbound request's X-Forwarded-For header (the default behavior of -// ReverseProxy when using a Director function), copy the header +// [ReverseProxy] when using a Director function), copy the header // from the inbound request before calling SetXForwarded: // // rewriteFunc := func(r *httputil.ProxyRequest) { @@ -200,7 +200,7 @@ type ReverseProxy struct { } // A BufferPool is an interface for getting and returning temporary -// byte slices for use by io.CopyBuffer. +// byte slices for use by [io.CopyBuffer]. type BufferPool interface { Get() []byte Put([]byte) @@ -239,7 +239,7 @@ func joinURLPath(a, b *url.URL) (path, rawpath string) { return a.Path + b.Path, apath + bpath } -// NewSingleHostReverseProxy returns a new ReverseProxy that routes +// NewSingleHostReverseProxy returns a new [ReverseProxy] that routes // URLs to the scheme, host, and base path provided in target. If the // target's path is "/base" and the incoming request was for "/dir", // the target request will be for /base/dir. diff --git a/src/net/http/internal/ascii/print.go b/src/net/http/internal/ascii/print.go index 585e5baba4..98dbf4e3d2 100644 --- a/src/net/http/internal/ascii/print.go +++ b/src/net/http/internal/ascii/print.go @@ -9,7 +9,7 @@ import ( "unicode" ) -// EqualFold is strings.EqualFold, ASCII only. It reports whether s and t +// EqualFold is [strings.EqualFold], ASCII only. It reports whether s and t // are equal, ASCII-case-insensitively. func EqualFold(s, t string) bool { if len(s) != len(t) { diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go index 5a174415dc..196b5d8925 100644 --- a/src/net/http/internal/chunked.go +++ b/src/net/http/internal/chunked.go @@ -22,7 +22,7 @@ var ErrLineTooLong = errors.New("header line too long") // NewChunkedReader returns a new chunkedReader that translates the data read from r // out of HTTP "chunked" format before returning it. -// The chunkedReader returns io.EOF when the final 0-length chunk is read. +// The chunkedReader returns [io.EOF] when the final 0-length chunk is read. // // NewChunkedReader is not needed by normal applications. The http package // automatically decodes chunking when reading response bodies. @@ -39,7 +39,8 @@ type chunkedReader struct { n uint64 // unread bytes in chunk err error buf [2]byte - checkEnd bool // whether need to check for \r\n chunk footer + checkEnd bool // whether need to check for \r\n chunk footer + excess int64 // "excessive" chunk overhead, for malicious sender detection } func (cr *chunkedReader) beginChunk() { @@ -49,10 +50,36 @@ func (cr *chunkedReader) beginChunk() { if cr.err != nil { return } + cr.excess += int64(len(line)) + 2 // header, plus \r\n after the chunk data + line = trimTrailingWhitespace(line) + line, cr.err = removeChunkExtension(line) + if cr.err != nil { + return + } cr.n, cr.err = parseHexUint(line) if cr.err != nil { return } + // A sender who sends one byte per chunk will send 5 bytes of overhead + // for every byte of data. ("1\r\nX\r\n" to send "X".) + // We want to allow this, since streaming a byte at a time can be legitimate. + // + // A sender can use chunk extensions to add arbitrary amounts of additional + // data per byte read. ("1;very long extension\r\nX\r\n" to send "X".) + // We don't want to disallow extensions (although we discard them), + // but we also don't want to allow a sender to reduce the signal/noise ratio + // arbitrarily. + // + // We track the amount of excess overhead read, + // and produce an error if it grows too large. + // + // Currently, we say that we're willing to accept 16 bytes of overhead per chunk, + // plus twice the amount of real data in the chunk. + cr.excess -= 16 + (2 * int64(cr.n)) + cr.excess = max(cr.excess, 0) + if cr.excess > 16*1024 { + cr.err = errors.New("chunked encoding contains too much non-data") + } if cr.n == 0 { cr.err = io.EOF } @@ -140,11 +167,6 @@ func readChunkLine(b *bufio.Reader) ([]byte, error) { if len(p) >= maxLineLength { return nil, ErrLineTooLong } - p = trimTrailingWhitespace(p) - p, err = removeChunkExtension(p) - if err != nil { - return nil, err - } return p, nil } @@ -199,7 +221,7 @@ type chunkedWriter struct { // Write the contents of data as one chunk to Wire. // NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has -// a bug since it does not check for success of io.WriteString +// a bug since it does not check for success of [io.WriteString] func (cw *chunkedWriter) Write(data []byte) (n int, err error) { // Don't send 0-length data. It looks like EOF for chunked encoding. @@ -231,9 +253,9 @@ func (cw *chunkedWriter) Close() error { return err } -// FlushAfterChunkWriter signals from the caller of NewChunkedWriter +// FlushAfterChunkWriter signals from the caller of [NewChunkedWriter] // that each chunk should be followed by a flush. It is used by the -// http.Transport code to keep the buffering behavior for headers and +// [net/http.Transport] code to keep the buffering behavior for headers and // trailers, but flush out chunks aggressively in the middle for // request bodies which may be generated slowly. See Issue 6574. type FlushAfterChunkWriter struct { @@ -241,6 +263,9 @@ type FlushAfterChunkWriter struct { } func parseHexUint(v []byte) (n uint64, err error) { + if len(v) == 0 { + return 0, errors.New("empty hex number for chunk length") + } for i, b := range v { switch { case '0' <= b && b <= '9': diff --git a/src/net/http/internal/chunked_test.go b/src/net/http/internal/chunked_test.go index 5e29a786dd..af79711781 100644 --- a/src/net/http/internal/chunked_test.go +++ b/src/net/http/internal/chunked_test.go @@ -153,6 +153,7 @@ func TestParseHexUint(t *testing.T) { {"00000000000000000", 0, "http chunk length too large"}, // could accept if we wanted {"10000000000000000", 0, "http chunk length too large"}, {"00000000000000001", 0, "http chunk length too large"}, // could accept if we wanted + {"", 0, "empty hex number for chunk length"}, } for i := uint64(0); i <= 1234; i++ { tests = append(tests, testCase{in: fmt.Sprintf("%x", i), want: i}) @@ -239,3 +240,62 @@ func TestChunkEndReadError(t *testing.T) { t.Errorf("expected %v, got %v", readErr, err) } } + +func TestChunkReaderTooMuchOverhead(t *testing.T) { + // If the sender is sending 100x as many chunk header bytes as chunk data, + // we should reject the stream at some point. + chunk := []byte("1;") + for i := 0; i < 100; i++ { + chunk = append(chunk, 'a') // chunk extension + } + chunk = append(chunk, "\r\nX\r\n"...) + const bodylen = 1 << 20 + r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) { + if i < bodylen { + return chunk, nil + } + return []byte("0\r\n"), nil + }}) + _, err := io.ReadAll(r) + if err == nil { + t.Fatalf("successfully read body with excessive overhead; want error") + } +} + +func TestChunkReaderByteAtATime(t *testing.T) { + // Sending one byte per chunk should not trip the excess-overhead detection. + const bodylen = 1 << 20 + r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) { + if i < bodylen { + return []byte("1\r\nX\r\n"), nil + } + return []byte("0\r\n"), nil + }}) + got, err := io.ReadAll(r) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if len(got) != bodylen { + t.Errorf("read %v bytes, want %v", len(got), bodylen) + } +} + +type funcReader struct { + f func(iteration int) ([]byte, error) + i int + b []byte + err error +} + +func (r *funcReader) Read(p []byte) (n int, err error) { + if len(r.b) == 0 && r.err == nil { + r.b, r.err = r.f(r.i) + r.i++ + } + n = copy(p, r.b) + r.b = r.b[n:] + if len(r.b) > 0 { + return n, nil + } + return n, r.err +} diff --git a/src/net/http/pprof/pprof.go b/src/net/http/pprof/pprof.go index bc3225daca..bc48f11834 100644 --- a/src/net/http/pprof/pprof.go +++ b/src/net/http/pprof/pprof.go @@ -47,12 +47,12 @@ // go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 // // Or to look at the goroutine blocking profile, after calling -// runtime.SetBlockProfileRate in your program: +// [runtime.SetBlockProfileRate] in your program: // // go tool pprof http://localhost:6060/debug/pprof/block // // Or to look at the holders of contended mutexes, after calling -// runtime.SetMutexProfileFraction in your program: +// [runtime.SetMutexProfileFraction] in your program: // // go tool pprof http://localhost:6060/debug/pprof/mutex // diff --git a/src/net/http/pprof/pprof_test.go b/src/net/http/pprof/pprof_test.go index f82ad45bf6..24ad59ab39 100644 --- a/src/net/http/pprof/pprof_test.go +++ b/src/net/http/pprof/pprof_test.go @@ -6,12 +6,14 @@ package pprof import ( "bytes" + "encoding/base64" "fmt" "internal/profile" "internal/testenv" "io" "net/http" "net/http/httptest" + "path/filepath" "runtime" "runtime/pprof" "strings" @@ -261,3 +263,64 @@ func seen(p *profile.Profile, fname string) bool { } return false } + +// TestDeltaProfileEmptyBase validates that we still receive a valid delta +// profile even if the base contains no samples. +// +// Regression test for https://go.dev/issue/64566. +func TestDeltaProfileEmptyBase(t *testing.T) { + if testing.Short() { + // Delta profile collection has a 1s minimum. + t.Skip("skipping in -short mode") + } + + testenv.MustHaveGoRun(t) + + gotool, err := testenv.GoTool() + if err != nil { + t.Fatalf("error finding go tool: %v", err) + } + + out, err := testenv.Command(t, gotool, "run", filepath.Join("testdata", "delta_mutex.go")).CombinedOutput() + if err != nil { + t.Fatalf("error running profile collection: %v\noutput: %s", err, out) + } + + // Log the binary output for debugging failures. + b64 := make([]byte, base64.StdEncoding.EncodedLen(len(out))) + base64.StdEncoding.Encode(b64, out) + t.Logf("Output in base64.StdEncoding: %s", b64) + + p, err := profile.Parse(bytes.NewReader(out)) + if err != nil { + t.Fatalf("Parse got err %v want nil", err) + } + + t.Logf("Output as parsed Profile: %s", p) + + if len(p.SampleType) != 2 { + t.Errorf("len(p.SampleType) got %d want 2", len(p.SampleType)) + } + if p.SampleType[0].Type != "contentions" { + t.Errorf(`p.SampleType[0].Type got %q want "contentions"`, p.SampleType[0].Type) + } + if p.SampleType[0].Unit != "count" { + t.Errorf(`p.SampleType[0].Unit got %q want "count"`, p.SampleType[0].Unit) + } + if p.SampleType[1].Type != "delay" { + t.Errorf(`p.SampleType[1].Type got %q want "delay"`, p.SampleType[1].Type) + } + if p.SampleType[1].Unit != "nanoseconds" { + t.Errorf(`p.SampleType[1].Unit got %q want "nanoseconds"`, p.SampleType[1].Unit) + } + + if p.PeriodType == nil { + t.Fatal("p.PeriodType got nil want not nil") + } + if p.PeriodType.Type != "contentions" { + t.Errorf(`p.PeriodType.Type got %q want "contentions"`, p.PeriodType.Type) + } + if p.PeriodType.Unit != "count" { + t.Errorf(`p.PeriodType.Unit got %q want "count"`, p.PeriodType.Unit) + } +} diff --git a/src/net/http/pprof/testdata/delta_mutex.go b/src/net/http/pprof/testdata/delta_mutex.go new file mode 100644 index 0000000000..634069c8a0 --- /dev/null +++ b/src/net/http/pprof/testdata/delta_mutex.go @@ -0,0 +1,43 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This binary collects a 1s delta mutex profile and dumps it to os.Stdout. +// +// This is in a subprocess because we want the base mutex profile to be empty +// (as a regression test for https://go.dev/issue/64566) and the only way to +// force reset the profile is to create a new subprocess. +// +// This manually collects the HTTP response and dumps to stdout in order to +// avoid any flakiness around port selection for a real HTTP server. +package main + +import ( + "bytes" + "fmt" + "log" + "net/http" + "net/http/pprof" + "net/http/httptest" + "runtime" +) + +func main() { + // Disable the mutex profiler. This is the default, but that default is + // load-bearing for this test, which needs the base profile to be empty. + runtime.SetMutexProfileFraction(0) + + h := pprof.Handler("mutex") + + req := httptest.NewRequest("GET", "/debug/pprof/mutex?seconds=1", nil) + rec := httptest.NewRecorder() + rec.Body = new(bytes.Buffer) + + h.ServeHTTP(rec, req) + resp := rec.Result() + if resp.StatusCode != http.StatusOK { + log.Fatalf("Request failed: %s\n%s", resp.Status, rec.Body) + } + + fmt.Print(rec.Body) +} diff --git a/src/net/http/request.go b/src/net/http/request.go index ed2cdac136..99fdebcf9b 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -107,7 +107,7 @@ var reqWriteExcludeHeader = map[string]bool{ // // The field semantics differ slightly between client and server // usage. In addition to the notes on the fields below, see the -// documentation for Request.Write and RoundTripper. +// documentation for [Request.Write] and [RoundTripper]. type Request struct { // Method specifies the HTTP method (GET, POST, PUT, etc.). // For client requests, an empty string means GET. @@ -333,7 +333,7 @@ type Request struct { } // Context returns the request's context. To change the context, use -// Clone or WithContext. +// [Request.Clone] or [Request.WithContext]. // // The returned context is always non-nil; it defaults to the // background context. @@ -357,8 +357,8 @@ func (r *Request) Context() context.Context { // lifetime of a request and its response: obtaining a connection, // sending the request, and reading the response headers and body. // -// To create a new request with a context, use NewRequestWithContext. -// To make a deep copy of a request with a new context, use Request.Clone. +// To create a new request with a context, use [NewRequestWithContext]. +// To make a deep copy of a request with a new context, use [Request.Clone]. func (r *Request) WithContext(ctx context.Context) *Request { if ctx == nil { panic("nil context") @@ -397,6 +397,20 @@ func (r *Request) Clone(ctx context.Context) *Request { r2.Form = cloneURLValues(r.Form) r2.PostForm = cloneURLValues(r.PostForm) r2.MultipartForm = cloneMultipartForm(r.MultipartForm) + + // Copy matches and otherValues. See issue 61410. + if s := r.matches; s != nil { + s2 := make([]string, len(s)) + copy(s2, s) + r2.matches = s2 + } + if s := r.otherValues; s != nil { + s2 := make(map[string]string, len(s)) + for k, v := range s { + s2[k] = v + } + r2.otherValues = s2 + } return r2 } @@ -421,7 +435,7 @@ func (r *Request) Cookies() []*Cookie { var ErrNoCookie = errors.New("http: named cookie not present") // Cookie returns the named cookie provided in the request or -// ErrNoCookie if not found. +// [ErrNoCookie] if not found. // If multiple cookies match the given name, only one cookie will // be returned. func (r *Request) Cookie(name string) (*Cookie, error) { @@ -435,7 +449,7 @@ func (r *Request) Cookie(name string) (*Cookie, error) { } // AddCookie adds a cookie to the request. Per RFC 6265 section 5.4, -// AddCookie does not attach more than one Cookie header field. That +// AddCookie does not attach more than one [Cookie] header field. That // means all cookies, if any, are written into the same line, // separated by semicolon. // AddCookie only sanitizes c's name and value, and does not sanitize @@ -453,7 +467,7 @@ func (r *Request) AddCookie(c *Cookie) { // // Referer is misspelled as in the request itself, a mistake from the // earliest days of HTTP. This value can also be fetched from the -// Header map as Header["Referer"]; the benefit of making it available +// [Header] map as Header["Referer"]; the benefit of making it available // as a method is that the compiler can diagnose programs that use the // alternate (correct English) spelling req.Referrer() but cannot // diagnose programs that use Header["Referrer"]. @@ -471,7 +485,7 @@ var multipartByReader = &multipart.Form{ // MultipartReader returns a MIME multipart reader if this is a // multipart/form-data or a multipart/mixed POST request, else returns nil and an error. -// Use this function instead of ParseMultipartForm to +// Use this function instead of [Request.ParseMultipartForm] to // process the request body as a stream. func (r *Request) MultipartReader() (*multipart.Reader, error) { if r.MultipartForm == multipartByReader { @@ -534,15 +548,15 @@ const defaultUserAgent = "Go-http-client/1.1" // TransferEncoding // Body // -// If Body is present, Content-Length is <= 0 and TransferEncoding +// If Body is present, Content-Length is <= 0 and [Request.TransferEncoding] // hasn't been set to "identity", Write adds "Transfer-Encoding: // chunked" to the header. Body is closed after it is sent. func (r *Request) Write(w io.Writer) error { return r.write(w, false, nil, nil) } -// WriteProxy is like Write but writes the request in the form -// expected by an HTTP proxy. In particular, WriteProxy writes the +// WriteProxy is like [Request.Write] but writes the request in the form +// expected by an HTTP proxy. In particular, [Request.WriteProxy] writes the // initial Request-URI line of the request with an absolute URI, per // section 5.3 of RFC 7230, including the scheme and host. // In either case, WriteProxy also writes a Host header, using @@ -837,33 +851,33 @@ func validMethod(method string) bool { return len(method) > 0 && strings.IndexFunc(method, isNotToken) == -1 } -// NewRequest wraps NewRequestWithContext using context.Background. +// NewRequest wraps [NewRequestWithContext] using [context.Background]. func NewRequest(method, url string, body io.Reader) (*Request, error) { return NewRequestWithContext(context.Background(), method, url, body) } -// NewRequestWithContext returns a new Request given a method, URL, and +// NewRequestWithContext returns a new [Request] given a method, URL, and // optional body. // -// If the provided body is also an io.Closer, the returned -// Request.Body is set to body and will be closed (possibly +// If the provided body is also an [io.Closer], the returned +// [Request.Body] is set to body and will be closed (possibly // asynchronously) by the Client methods Do, Post, and PostForm, -// and Transport.RoundTrip. +// and [Transport.RoundTrip]. // // NewRequestWithContext returns a Request suitable for use with -// Client.Do or Transport.RoundTrip. To create a request for use with -// testing a Server Handler, either use the NewRequest function in the -// net/http/httptest package, use ReadRequest, or manually update the +// [Client.Do] or [Transport.RoundTrip]. To create a request for use with +// testing a Server Handler, either use the [NewRequest] function in the +// net/http/httptest package, use [ReadRequest], or manually update the // Request fields. For an outgoing client request, the context // controls the entire lifetime of a request and its response: // obtaining a connection, sending the request, and reading the // response headers and body. See the Request type's documentation for // the difference between inbound and outbound request fields. // -// If body is of type *bytes.Buffer, *bytes.Reader, or -// *strings.Reader, the returned request's ContentLength is set to its +// If body is of type [*bytes.Buffer], [*bytes.Reader], or +// [*strings.Reader], the returned request's ContentLength is set to its // exact value (instead of -1), GetBody is populated (so 307 and 308 -// redirects can replay the body), and Body is set to NoBody if the +// redirects can replay the body), and Body is set to [NoBody] if the // ContentLength is 0. func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error) { if method == "" { @@ -987,7 +1001,7 @@ func parseBasicAuth(auth string) (username, password string, ok bool) { // The username may not contain a colon. Some protocols may impose // additional requirements on pre-escaping the username and // password. For instance, when used with OAuth2, both arguments must -// be URL encoded first with url.QueryEscape. +// be URL encoded first with [url.QueryEscape]. func (r *Request) SetBasicAuth(username, password string) { r.Header.Set("Authorization", "Basic "+basicAuth(username, password)) } @@ -1021,8 +1035,8 @@ func putTextprotoReader(r *textproto.Reader) { // ReadRequest reads and parses an incoming request from b. // // ReadRequest is a low-level function and should only be used for -// specialized applications; most code should use the Server to read -// requests and handle them via the Handler interface. ReadRequest +// specialized applications; most code should use the [Server] to read +// requests and handle them via the [Handler] interface. ReadRequest // only supports HTTP/1.x requests. For HTTP/2, use golang.org/x/net/http2. func ReadRequest(b *bufio.Reader) (*Request, error) { req, err := readRequest(b) @@ -1131,15 +1145,15 @@ func readRequest(b *bufio.Reader) (req *Request, err error) { return req, nil } -// MaxBytesReader is similar to io.LimitReader but is intended for +// MaxBytesReader is similar to [io.LimitReader] but is intended for // limiting the size of incoming request bodies. In contrast to // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a -// non-nil error of type *MaxBytesError for a Read beyond the limit, +// non-nil error of type [*MaxBytesError] for a Read beyond the limit, // and closes the underlying reader when its Close method is called. // // MaxBytesReader prevents clients from accidentally or maliciously // sending a large request and wasting server resources. If possible, -// it tells the ResponseWriter to close the connection after the limit +// it tells the [ResponseWriter] to close the connection after the limit // has been reached. func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { if n < 0 { // Treat negative limits as equivalent to 0. @@ -1148,7 +1162,7 @@ func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { return &maxBytesReader{w: w, r: r, i: n, n: n} } -// MaxBytesError is returned by MaxBytesReader when its read limit is exceeded. +// MaxBytesError is returned by [MaxBytesReader] when its read limit is exceeded. type MaxBytesError struct { Limit int64 } @@ -1273,14 +1287,14 @@ func parsePostForm(r *Request) (vs url.Values, err error) { // as a form and puts the results into both r.PostForm and r.Form. Request body // parameters take precedence over URL query string values in r.Form. // -// If the request Body's size has not already been limited by MaxBytesReader, +// If the request Body's size has not already been limited by [MaxBytesReader], // the size is capped at 10MB. // // For other HTTP methods, or when the Content-Type is not // application/x-www-form-urlencoded, the request Body is not read, and // r.PostForm is initialized to a non-nil, empty value. // -// ParseMultipartForm calls ParseForm automatically. +// [Request.ParseMultipartForm] calls ParseForm automatically. // ParseForm is idempotent. func (r *Request) ParseForm() error { var err error @@ -1321,7 +1335,7 @@ func (r *Request) ParseForm() error { // The whole request body is parsed and up to a total of maxMemory bytes of // its file parts are stored in memory, with the remainder stored on // disk in temporary files. -// ParseMultipartForm calls ParseForm if necessary. +// ParseMultipartForm calls [Request.ParseForm] if necessary. // If ParseForm returns an error, ParseMultipartForm returns it but also // continues parsing the request body. // After one call to ParseMultipartForm, subsequent calls have no effect. @@ -1364,12 +1378,16 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error { } // FormValue returns the first value for the named component of the query. -// POST, PUT, and PATCH body parameters take precedence over URL query string values. -// FormValue calls ParseMultipartForm and ParseForm if necessary and ignores -// any errors returned by these functions. +// The precedence order: +// 1. application/x-www-form-urlencoded form body (POST, PUT, PATCH only) +// 2. query parameters (always) +// 3. multipart/form-data form body (always) +// +// FormValue calls [Request.ParseMultipartForm] and [Request.ParseForm] +// if necessary and ignores any errors returned by these functions. // If key is not present, FormValue returns the empty string. // To access multiple values of the same key, call ParseForm and -// then inspect Request.Form directly. +// then inspect [Request.Form] directly. func (r *Request) FormValue(key string) string { if r.Form == nil { r.ParseMultipartForm(defaultMaxMemory) @@ -1382,7 +1400,7 @@ func (r *Request) FormValue(key string) string { // PostFormValue returns the first value for the named component of the POST, // PUT, or PATCH request body. URL query parameters are ignored. -// PostFormValue calls ParseMultipartForm and ParseForm if necessary and ignores +// PostFormValue calls [Request.ParseMultipartForm] and [Request.ParseForm] if necessary and ignores // any errors returned by these functions. // If key is not present, PostFormValue returns the empty string. func (r *Request) PostFormValue(key string) string { @@ -1396,7 +1414,7 @@ func (r *Request) PostFormValue(key string) string { } // FormFile returns the first file for the provided form key. -// FormFile calls ParseMultipartForm and ParseForm if necessary. +// FormFile calls [Request.ParseMultipartForm] and [Request.ParseForm] if necessary. func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) { if r.MultipartForm == multipartByReader { return nil, nil, errors.New("http: multipart handled by MultipartReader") @@ -1416,7 +1434,7 @@ func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, e return nil, nil, ErrMissingFile } -// PathValue returns the value for the named path wildcard in the ServeMux pattern +// PathValue returns the value for the named path wildcard in the [ServeMux] pattern // that matched the request. // It returns the empty string if the request was not matched against a pattern // or there is no such wildcard in the pattern. @@ -1427,6 +1445,8 @@ func (r *Request) PathValue(name string) string { return r.otherValues[name] } +// SetPathValue sets name to value, so that subsequent calls to r.PathValue(name) +// return value. func (r *Request) SetPathValue(name, value string) { if i := r.patIndex(name); i >= 0 { r.matches[i] = value diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go index 1531da3d8c..6ce32332e7 100644 --- a/src/net/http/request_test.go +++ b/src/net/http/request_test.go @@ -1053,6 +1053,33 @@ func TestRequestCloneTransferEncoding(t *testing.T) { } } +// Ensure that Request.Clone works correctly with PathValue. +// See issue 64911. +func TestRequestClonePathValue(t *testing.T) { + req, _ := http.NewRequest("GET", "https://example.org/", nil) + req.SetPathValue("p1", "orig") + + clonedReq := req.Clone(context.Background()) + clonedReq.SetPathValue("p2", "copy") + + // Ensure that any modifications to the cloned + // request do not pollute the original request. + if g, w := req.PathValue("p2"), ""; g != w { + t.Fatalf("p2 mismatch got %q, want %q", g, w) + } + if g, w := req.PathValue("p1"), "orig"; g != w { + t.Fatalf("p1 mismatch got %q, want %q", g, w) + } + + // Assert on the changes to the cloned request. + if g, w := clonedReq.PathValue("p1"), "orig"; g != w { + t.Fatalf("p1 mismatch got %q, want %q", g, w) + } + if g, w := clonedReq.PathValue("p2"), "copy"; g != w { + t.Fatalf("p2 mismatch got %q, want %q", g, w) + } +} + // Issue 34878: verify we don't panic when including basic auth (Go 1.13 regression) func TestNoPanicOnRoundTripWithBasicAuth(t *testing.T) { run(t, testNoPanicWithBasicAuth) } func testNoPanicWithBasicAuth(t *testing.T, mode testMode) { diff --git a/src/net/http/response.go b/src/net/http/response.go index 755c696557..0c3d7f6d85 100644 --- a/src/net/http/response.go +++ b/src/net/http/response.go @@ -29,7 +29,7 @@ var respExcludeHeader = map[string]bool{ // Response represents the response from an HTTP request. // -// The Client and Transport return Responses from servers once +// The [Client] and [Transport] return Responses from servers once // the response headers have been received. The response body // is streamed on demand as the Body field is read. type Response struct { @@ -126,13 +126,13 @@ func (r *Response) Cookies() []*Cookie { return readSetCookies(r.Header) } -// ErrNoLocation is returned by Response's Location method +// ErrNoLocation is returned by the [Response.Location] method // when no Location header is present. var ErrNoLocation = errors.New("http: no Location header in response") // Location returns the URL of the response's "Location" header, // if present. Relative redirects are resolved relative to -// the Response's Request. ErrNoLocation is returned if no +// [Response.Request]. [ErrNoLocation] is returned if no // Location header is present. func (r *Response) Location() (*url.URL, error) { lv := r.Header.Get("Location") @@ -146,8 +146,8 @@ func (r *Response) Location() (*url.URL, error) { } // ReadResponse reads and returns an HTTP response from r. -// The req parameter optionally specifies the Request that corresponds -// to this Response. If nil, a GET request is assumed. +// The req parameter optionally specifies the [Request] that corresponds +// to this [Response]. If nil, a GET request is assumed. // Clients must call resp.Body.Close when finished reading resp.Body. // After that call, clients can inspect resp.Trailer to find key/value // pairs included in the response trailer. diff --git a/src/net/http/responsecontroller.go b/src/net/http/responsecontroller.go index 92276ffaf2..f3f24c1273 100644 --- a/src/net/http/responsecontroller.go +++ b/src/net/http/responsecontroller.go @@ -13,14 +13,14 @@ import ( // A ResponseController is used by an HTTP handler to control the response. // -// A ResponseController may not be used after the Handler.ServeHTTP method has returned. +// A ResponseController may not be used after the [Handler.ServeHTTP] method has returned. type ResponseController struct { rw ResponseWriter } -// NewResponseController creates a ResponseController for a request. +// NewResponseController creates a [ResponseController] for a request. // -// The ResponseWriter should be the original value passed to the Handler.ServeHTTP method, +// The ResponseWriter should be the original value passed to the [Handler.ServeHTTP] method, // or have an Unwrap method returning the original ResponseWriter. // // If the ResponseWriter implements any of the following methods, the ResponseController @@ -34,7 +34,7 @@ type ResponseController struct { // EnableFullDuplex() error // // If the ResponseWriter does not support a method, ResponseController returns -// an error matching ErrNotSupported. +// an error matching [ErrNotSupported]. func NewResponseController(rw ResponseWriter) *ResponseController { return &ResponseController{rw} } @@ -116,8 +116,8 @@ func (c *ResponseController) SetWriteDeadline(deadline time.Time) error { } } -// EnableFullDuplex indicates that the request handler will interleave reads from Request.Body -// with writes to the ResponseWriter. +// EnableFullDuplex indicates that the request handler will interleave reads from [Request.Body] +// with writes to the [ResponseWriter]. // // For HTTP/1 requests, the Go HTTP server by default consumes any unread portion of // the request body before beginning to write the response, preventing handlers from diff --git a/src/net/http/roundtrip.go b/src/net/http/roundtrip.go index 49ea1a71ed..08c270179a 100644 --- a/src/net/http/roundtrip.go +++ b/src/net/http/roundtrip.go @@ -6,10 +6,10 @@ package http -// RoundTrip implements the RoundTripper interface. +// RoundTrip implements the [RoundTripper] interface. // // For higher-level HTTP client support (such as handling of cookies -// and redirects), see Get, Post, and the Client type. +// and redirects), see [Get], [Post], and the [Client] type. // // Like the RoundTripper interface, the error types returned // by RoundTrip are unspecified. diff --git a/src/net/http/roundtrip_js.go b/src/net/http/roundtrip_js.go index cbf978af18..04c241eb4c 100644 --- a/src/net/http/roundtrip_js.go +++ b/src/net/http/roundtrip_js.go @@ -56,7 +56,7 @@ var jsFetchMissing = js.Global().Get("fetch").IsUndefined() var jsFetchDisabled = js.Global().Get("process").Type() == js.TypeObject && strings.HasPrefix(js.Global().Get("process").Get("argv0").String(), "node") -// RoundTrip implements the RoundTripper interface using the WHATWG Fetch API. +// RoundTrip implements the [RoundTripper] interface using the WHATWG Fetch API. func (t *Transport) RoundTrip(req *Request) (*Response, error) { // The Transport has a documented contract that states that if the DialContext or // DialTLSContext functions are set, they will be used to set up the connections. diff --git a/src/net/http/server.go b/src/net/http/server.go index 7fa785dfee..acac78bcd0 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -61,7 +61,7 @@ var ( // A Handler responds to an HTTP request. // -// ServeHTTP should write reply headers and data to the [ResponseWriter] +// [Handler.ServeHTTP] should write reply headers and data to the [ResponseWriter] // and then return. Returning signals that the request is finished; it // is not valid to use the [ResponseWriter] or read from the // [Request.Body] after or concurrently with the completion of the @@ -161,8 +161,8 @@ type ResponseWriter interface { // The Flusher interface is implemented by ResponseWriters that allow // an HTTP handler to flush buffered data to the client. // -// The default HTTP/1.x and HTTP/2 ResponseWriter implementations -// support Flusher, but ResponseWriter wrappers may not. Handlers +// The default HTTP/1.x and HTTP/2 [ResponseWriter] implementations +// support [Flusher], but ResponseWriter wrappers may not. Handlers // should always test for this ability at runtime. // // Note that even for ResponseWriters that support Flush, @@ -177,7 +177,7 @@ type Flusher interface { // The Hijacker interface is implemented by ResponseWriters that allow // an HTTP handler to take over the connection. // -// The default ResponseWriter for HTTP/1.x connections supports +// The default [ResponseWriter] for HTTP/1.x connections supports // Hijacker, but HTTP/2 connections intentionally do not. // ResponseWriter wrappers may also not support Hijacker. Handlers // should always test for this ability at runtime. @@ -211,7 +211,7 @@ type Hijacker interface { // if the client has disconnected before the response is ready. // // Deprecated: the CloseNotifier interface predates Go's context package. -// New code should use Request.Context instead. +// New code should use [Request.Context] instead. type CloseNotifier interface { // CloseNotify returns a channel that receives at most a // single value (true) when the client connection has gone @@ -505,7 +505,7 @@ func (c *response) EnableFullDuplex() error { return nil } -// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys +// TrailerPrefix is a magic prefix for [ResponseWriter.Header] map keys // that, if present, signals that the map entry is actually for // the response trailers, and not the response headers. The prefix // is stripped after the ServeHTTP call finishes and the values are @@ -571,13 +571,12 @@ type writerOnly struct { io.Writer } -// ReadFrom is here to optimize copying from an *os.File regular file -// to a *net.TCPConn with sendfile, or from a supported src type such +// ReadFrom is here to optimize copying from an [*os.File] regular file +// to a [*net.TCPConn] with sendfile, or from a supported src type such // as a *net.TCPConn on Linux with splice. func (w *response) ReadFrom(src io.Reader) (n int64, err error) { - bufp := copyBufPool.Get().(*[]byte) - buf := *bufp - defer copyBufPool.Put(bufp) + buf := getCopyBuf() + defer putCopyBuf(buf) // Our underlying w.conn.rwc is usually a *TCPConn (with its // own ReadFrom method). If not, just fall back to the normal @@ -807,11 +806,18 @@ var ( bufioWriter4kPool sync.Pool ) -var copyBufPool = sync.Pool{ - New: func() any { - b := make([]byte, 32*1024) - return &b - }, +const copyBufPoolSize = 32 * 1024 + +var copyBufPool = sync.Pool{New: func() any { return new([copyBufPoolSize]byte) }} + +func getCopyBuf() []byte { + return copyBufPool.Get().(*[copyBufPoolSize]byte)[:] +} +func putCopyBuf(b []byte) { + if len(b) != copyBufPoolSize { + panic("trying to put back buffer of the wrong size in the copyBufPool") + } + copyBufPool.Put((*[copyBufPoolSize]byte)(b)) } func bufioWriterPool(size int) *sync.Pool { @@ -861,7 +867,7 @@ func putBufioWriter(bw *bufio.Writer) { // DefaultMaxHeaderBytes is the maximum permitted size of the headers // in an HTTP request. -// This can be overridden by setting Server.MaxHeaderBytes. +// This can be overridden by setting [Server.MaxHeaderBytes]. const DefaultMaxHeaderBytes = 1 << 20 // 1 MB func (srv *Server) maxHeaderBytes() int { @@ -934,11 +940,11 @@ func (ecr *expectContinueReader) Close() error { } // TimeFormat is the time format to use when generating times in HTTP -// headers. It is like time.RFC1123 but hard-codes GMT as the time +// headers. It is like [time.RFC1123] but hard-codes GMT as the time // zone. The time being formatted must be in UTC for Format to // generate the correct format. // -// For parsing this time format, see ParseTime. +// For parsing this time format, see [ParseTime]. const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" // appendTime is a non-allocating version of []byte(t.UTC().Format(TimeFormat)) @@ -1584,13 +1590,13 @@ func (w *response) bodyAllowed() bool { // The Writers are wired together like: // // 1. *response (the ResponseWriter) -> -// 2. (*response).w, a *bufio.Writer of bufferBeforeChunkingSize bytes -> +// 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. +// 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. @@ -2091,8 +2097,8 @@ func (w *response) sendExpectationFailed() { w.finishRequest() } -// Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter -// and a Hijacker. +// Hijack implements the [Hijacker.Hijack] method. Our response is both a [ResponseWriter] +// and a [Hijacker]. func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { if w.handlerDone.Load() { panic("net/http: Hijack called after ServeHTTP finished") @@ -2152,7 +2158,7 @@ func requestBodyRemains(rc io.ReadCloser) bool { // The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a -// Handler that calls f. +// [Handler] that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). @@ -2211,9 +2217,9 @@ func StripPrefix(prefix string, h Handler) Handler { // which may be a path relative to the request path. // // The provided code should be in the 3xx range and is usually -// StatusMovedPermanently, StatusFound or StatusSeeOther. +// [StatusMovedPermanently], [StatusFound] or [StatusSeeOther]. // -// If the Content-Type header has not been set, Redirect sets it +// If the Content-Type header has not been set, [Redirect] sets it // to "text/html; charset=utf-8" and writes a small HTML body. // Setting the Content-Type header to any value, including nil, // disables that behavior. @@ -2301,7 +2307,7 @@ func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) { // status code. // // The provided code should be in the 3xx range and is usually -// StatusMovedPermanently, StatusFound or StatusSeeOther. +// [StatusMovedPermanently], [StatusFound] or [StatusSeeOther]. func RedirectHandler(url string, code int) Handler { return &redirectHandler{url, code} } @@ -2388,7 +2394,7 @@ func RedirectHandler(url string, code int) Handler { // // # Trailing-slash redirection // -// Consider a ServeMux with a handler for a subtree, registered using a trailing slash or "..." wildcard. +// Consider a [ServeMux] with a handler for a subtree, registered using a trailing slash or "..." wildcard. // If the ServeMux receives a request for the subtree root without a trailing slash, // it redirects the request by adding the trailing slash. // This behavior can be overridden with a separate registration for the path without @@ -2431,12 +2437,12 @@ type ServeMux struct { mux121 serveMux121 // used only when GODEBUG=httpmuxgo121=1 } -// NewServeMux allocates and returns a new ServeMux. +// NewServeMux allocates and returns a new [ServeMux]. func NewServeMux() *ServeMux { return &ServeMux{} } -// DefaultServeMux is the default ServeMux used by Serve. +// DefaultServeMux is the default [ServeMux] used by [Serve]. var DefaultServeMux = &defaultServeMux var defaultServeMux ServeMux @@ -2778,7 +2784,7 @@ func (mux *ServeMux) registerErr(patstr string, handler Handler) error { // // The handler is typically nil, in which case [DefaultServeMux] is used. // -// HTTP/2 support is only enabled if the Listener returns *tls.Conn +// HTTP/2 support is only enabled if the Listener returns [*tls.Conn] // connections and they were configured with "h2" in the TLS // Config.NextProtos. // @@ -2918,13 +2924,13 @@ type Server struct { } // Close immediately closes all active net.Listeners and any -// connections in state StateNew, StateActive, or StateIdle. For a -// graceful shutdown, use Shutdown. +// connections in state [StateNew], [StateActive], or [StateIdle]. For a +// graceful shutdown, use [Server.Shutdown]. // // Close does not attempt to close (and does not even know about) // any hijacked connections, such as WebSockets. // -// Close returns any error returned from closing the Server's +// Close returns any error returned from closing the [Server]'s // underlying Listener(s). func (srv *Server) Close() error { srv.inShutdown.Store(true) @@ -2962,16 +2968,16 @@ const shutdownPollIntervalMax = 500 * time.Millisecond // indefinitely for connections to return to idle and then shut down. // If the provided context expires before the shutdown is complete, // Shutdown returns the context's error, otherwise it returns any -// error returned from closing the Server's underlying Listener(s). +// error returned from closing the [Server]'s underlying Listener(s). // -// When Shutdown is called, Serve, ListenAndServe, and -// ListenAndServeTLS immediately return ErrServerClosed. Make sure the +// When Shutdown is called, [Serve], [ListenAndServe], and +// [ListenAndServeTLS] immediately return [ErrServerClosed]. Make sure the // program doesn't exit and waits instead for Shutdown to return. // // Shutdown does not attempt to close nor wait for hijacked // connections such as WebSockets. The caller of Shutdown should // separately notify such long-lived connections of shutdown and wait -// for them to close, if desired. See RegisterOnShutdown for a way to +// for them to close, if desired. See [Server.RegisterOnShutdown] for a way to // register shutdown notification functions. // // Once Shutdown has been called on a server, it may not be reused; @@ -3014,7 +3020,7 @@ func (srv *Server) Shutdown(ctx context.Context) error { } } -// RegisterOnShutdown registers a function to call on Shutdown. +// RegisterOnShutdown registers a function to call on [Server.Shutdown]. // This can be used to gracefully shutdown connections that have // undergone ALPN protocol upgrade or that have been hijacked. // This function should start protocol-specific graceful shutdown, @@ -3062,7 +3068,7 @@ func (s *Server) closeListenersLocked() error { } // A ConnState represents the state of a client connection to a server. -// It's used by the optional Server.ConnState hook. +// It's used by the optional [Server.ConnState] hook. type ConnState int const ( @@ -3139,7 +3145,7 @@ func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { // behavior doesn't match that of many proxies, and the mismatch can lead to // security issues. // -// AllowQuerySemicolons should be invoked before Request.ParseForm is called. +// AllowQuerySemicolons should be invoked before [Request.ParseForm] is called. func AllowQuerySemicolons(h Handler) Handler { return HandlerFunc(func(w ResponseWriter, r *Request) { if strings.Contains(r.URL.RawQuery, ";") { @@ -3156,13 +3162,13 @@ func AllowQuerySemicolons(h Handler) Handler { } // ListenAndServe listens on the TCP network address srv.Addr and then -// calls Serve to handle requests on incoming connections. +// calls [Serve] to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // // If srv.Addr is blank, ":http" is used. // -// ListenAndServe always returns a non-nil error. After Shutdown or Close, -// the returned error is ErrServerClosed. +// ListenAndServe always returns a non-nil error. After [Server.Shutdown] or [Server.Close], +// the returned error is [ErrServerClosed]. func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed @@ -3202,20 +3208,20 @@ func (srv *Server) shouldConfigureHTTP2ForServe() bool { return strSliceContains(srv.TLSConfig.NextProtos, http2NextProtoTLS) } -// ErrServerClosed is returned by the Server's Serve, ServeTLS, ListenAndServe, -// and ListenAndServeTLS methods after a call to Shutdown or Close. +// ErrServerClosed is returned by the [Server.Serve], [ServeTLS], [ListenAndServe], +// and [ListenAndServeTLS] methods after a call to [Server.Shutdown] or [Server.Close]. var ErrServerClosed = errors.New("http: Server closed") // Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. // -// HTTP/2 support is only enabled if the Listener returns *tls.Conn +// HTTP/2 support is only enabled if the Listener returns [*tls.Conn] // connections and they were configured with "h2" in the TLS // Config.NextProtos. // // Serve always returns a non-nil error and closes l. -// After Shutdown or Close, the returned error is ErrServerClosed. +// After [Server.Shutdown] or [Server.Close], the returned error is [ErrServerClosed]. func (srv *Server) Serve(l net.Listener) error { if fn := testHookServerServe; fn != nil { fn(srv, l) // call hook with unwrapped listener @@ -3285,14 +3291,14 @@ func (srv *Server) Serve(l net.Listener) error { // setup and then read requests, calling srv.Handler to reply to them. // // Files containing a certificate and matching private key for the -// server must be provided if neither the Server's +// server must be provided if neither the [Server]'s // TLSConfig.Certificates nor TLSConfig.GetCertificate are populated. // If the certificate is signed by a certificate authority, the // certFile should be the concatenation of the server's certificate, // any intermediates, and the CA's certificate. // -// ServeTLS always returns a non-nil error. After Shutdown or Close, the -// returned error is ErrServerClosed. +// ServeTLS always returns a non-nil error. After [Server.Shutdown] or [Server.Close], the +// returned error is [ErrServerClosed]. func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error { // Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig // before we clone it and create the TLS Listener. @@ -3421,7 +3427,7 @@ func logf(r *Request, format string, args ...any) { } // ListenAndServe listens on the TCP network address addr and then calls -// Serve with handler to handle requests on incoming connections. +// [Serve] with handler to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // // The handler is typically nil, in which case [DefaultServeMux] is used. @@ -3443,11 +3449,11 @@ func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error { } // ListenAndServeTLS listens on the TCP network address srv.Addr and -// then calls ServeTLS to handle requests on incoming TLS connections. +// then calls [ServeTLS] to handle requests on incoming TLS connections. // Accepted connections are configured to enable TCP keep-alives. // // Filenames containing a certificate and matching private key for the -// server must be provided if neither the Server's TLSConfig.Certificates +// server must be provided if neither the [Server]'s TLSConfig.Certificates // nor TLSConfig.GetCertificate are populated. If the certificate is // signed by a certificate authority, the certFile should be the // concatenation of the server's certificate, any intermediates, and @@ -3455,8 +3461,8 @@ func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error { // // If srv.Addr is blank, ":https" is used. // -// ListenAndServeTLS always returns a non-nil error. After Shutdown or -// Close, the returned error is ErrServerClosed. +// ListenAndServeTLS always returns a non-nil error. After [Server.Shutdown] or +// [Server.Close], the returned error is [ErrServerClosed]. func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { if srv.shuttingDown() { return ErrServerClosed @@ -3526,17 +3532,17 @@ func (srv *Server) onceSetNextProtoDefaults() { } } -// TimeoutHandler returns a Handler that runs h with the given time limit. +// TimeoutHandler returns a [Handler] that runs h with the given time limit. // // The new Handler calls h.ServeHTTP to handle each request, but if a // call runs for longer than its time limit, the handler responds with // a 503 Service Unavailable error and the given message in its body. // (If msg is empty, a suitable default message will be sent.) -// After such a timeout, writes by h to its ResponseWriter will return -// ErrHandlerTimeout. +// After such a timeout, writes by h to its [ResponseWriter] will return +// [ErrHandlerTimeout]. // -// TimeoutHandler supports the Pusher interface but does not support -// the Hijacker or Flusher interfaces. +// TimeoutHandler supports the [Pusher] interface but does not support +// the [Hijacker] or [Flusher] interfaces. func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler { return &timeoutHandler{ handler: h, @@ -3545,7 +3551,7 @@ func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler { } } -// ErrHandlerTimeout is returned on ResponseWriter Write calls +// ErrHandlerTimeout is returned on [ResponseWriter] Write calls // in handlers which have timed out. var ErrHandlerTimeout = errors.New("http: Handler timeout") @@ -3634,7 +3640,7 @@ type timeoutWriter struct { var _ Pusher = (*timeoutWriter)(nil) -// Push implements the Pusher interface. +// Push implements the [Pusher] interface. func (tw *timeoutWriter) Push(target string, opts *PushOptions) error { if pusher, ok := tw.w.(Pusher); ok { return pusher.Push(target, opts) @@ -3719,7 +3725,7 @@ type initALPNRequest struct { h serverHandler } -// BaseContext is an exported but unadvertised http.Handler method +// BaseContext is an exported but unadvertised [http.Handler] method // recognized by x/net/http2 to pass down a context; the TLSNextProto // API predates context support so we shoehorn through the only // interface we have available. @@ -3806,7 +3812,6 @@ func numLeadingCRorLF(v []byte) (n int) { break } return - } func strSliceContains(ss []string, s string) bool { @@ -3828,7 +3833,7 @@ func tlsRecordHeaderLooksLikeHTTP(hdr [5]byte) bool { return false } -// MaxBytesHandler returns a Handler that runs h with its ResponseWriter and Request.Body wrapped by a MaxBytesReader. +// MaxBytesHandler returns a [Handler] that runs h with its [ResponseWriter] and [Request.Body] wrapped by a MaxBytesReader. func MaxBytesHandler(h Handler, n int64) Handler { return HandlerFunc(func(w ResponseWriter, r *Request) { r2 := *r diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go index dffff56b31..315c6e2723 100644 --- a/src/net/http/transfer.go +++ b/src/net/http/transfer.go @@ -410,9 +410,8 @@ func (t *transferWriter) writeBody(w io.Writer) (err error) { // // This function is only intended for use in writeBody. func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err error) { - bufp := copyBufPool.Get().(*[]byte) - buf := *bufp - defer copyBufPool.Put(bufp) + buf := getCopyBuf() + defer putCopyBuf(buf) n, err = io.CopyBuffer(dst, src, buf) if err != nil && err != io.EOF { @@ -818,10 +817,10 @@ type body struct { onHitEOF func() // if non-nil, func to call when EOF is Read } -// ErrBodyReadAfterClose is returned when reading a Request or Response +// ErrBodyReadAfterClose is returned when reading a [Request] or [Response] // Body after the body has been closed. This typically happens when the body is -// read after an HTTP Handler calls WriteHeader or Write on its -// ResponseWriter. +// read after an HTTP [Handler] calls WriteHeader or Write on its +// [ResponseWriter]. var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body") func (b *body) Read(p []byte) (n int, err error) { diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 1cf41a5474..57c70e72f9 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -35,8 +35,8 @@ import ( "golang.org/x/net/http/httpproxy" ) -// DefaultTransport is the default implementation of Transport and is -// used by DefaultClient. It establishes network connections as needed +// DefaultTransport is the default implementation of [Transport] and is +// used by [DefaultClient]. It establishes network connections as needed // and caches them for reuse by subsequent calls. It uses HTTP proxies // as directed by the environment variables HTTP_PROXY, HTTPS_PROXY // and NO_PROXY (or the lowercase versions thereof). @@ -53,42 +53,42 @@ var DefaultTransport RoundTripper = &Transport{ ExpectContinueTimeout: 1 * time.Second, } -// DefaultMaxIdleConnsPerHost is the default value of Transport's +// DefaultMaxIdleConnsPerHost is the default value of [Transport]'s // MaxIdleConnsPerHost. const DefaultMaxIdleConnsPerHost = 2 -// Transport is an implementation of RoundTripper that supports HTTP, +// Transport is an implementation of [RoundTripper] that supports HTTP, // HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT). // // By default, Transport caches connections for future re-use. // This may leave many open connections when accessing many hosts. -// This behavior can be managed using Transport's CloseIdleConnections method -// and the MaxIdleConnsPerHost and DisableKeepAlives fields. +// This behavior can be managed using [Transport.CloseIdleConnections] method +// and the [Transport.MaxIdleConnsPerHost] and [Transport.DisableKeepAlives] fields. // // Transports should be reused instead of created as needed. // Transports are safe for concurrent use by multiple goroutines. // // A Transport is a low-level primitive for making HTTP and HTTPS requests. -// For high-level functionality, such as cookies and redirects, see Client. +// For high-level functionality, such as cookies and redirects, see [Client]. // // Transport uses HTTP/1.1 for HTTP URLs and either HTTP/1.1 or HTTP/2 // for HTTPS URLs, depending on whether the server supports HTTP/2, -// and how the Transport is configured. The DefaultTransport supports HTTP/2. +// and how the Transport is configured. The [DefaultTransport] supports HTTP/2. // To explicitly enable HTTP/2 on a transport, use golang.org/x/net/http2 // and call ConfigureTransport. See the package docs for more about HTTP/2. // // Responses with status codes in the 1xx range are either handled // automatically (100 expect-continue) or ignored. The one // exception is HTTP status code 101 (Switching Protocols), which is -// considered a terminal status and returned by RoundTrip. To see the +// considered a terminal status and returned by [Transport.RoundTrip]. To see the // ignored 1xx responses, use the httptrace trace package's // ClientTrace.Got1xxResponse. // // Transport only retries a request upon encountering a network error // if the connection has been already been used successfully and if the -// request is idempotent and either has no body or has its Request.GetBody +// request is idempotent and either has no body or has its [Request.GetBody] // defined. HTTP requests are considered idempotent if they have HTTP methods -// GET, HEAD, OPTIONS, or TRACE; or if their Header map contains an +// GET, HEAD, OPTIONS, or TRACE; or if their [Header] map contains an // "Idempotency-Key" or "X-Idempotency-Key" entry. If the idempotency key // value is a zero-length slice, the request is treated as idempotent but the // header is not sent on the wire. @@ -237,7 +237,7 @@ type Transport struct { // TLSNextProto specifies how the Transport switches to an // alternate protocol (such as HTTP/2) after a TLS ALPN - // protocol negotiation. If Transport dials an TLS connection + // protocol negotiation. If Transport dials a TLS connection // with a non-empty protocol name and TLSNextProto contains a // map entry for that key (such as "h2"), then the func is // called with the request's authority (such as "example.com" @@ -453,7 +453,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) { return envProxyFunc()(req.URL) } -// ProxyURL returns a proxy function (for use in a Transport) +// ProxyURL returns a proxy function (for use in a [Transport]) // that always returns the same URL. func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) { return func(*Request) (*url.URL, error) { @@ -752,14 +752,14 @@ func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool { var ErrSkipAltProtocol = errors.New("net/http: skip alternate protocol") // RegisterProtocol registers a new protocol with scheme. -// The Transport will pass requests using the given scheme to rt. +// The [Transport] will pass requests using the given scheme to rt. // It is rt's responsibility to simulate HTTP request semantics. // // RegisterProtocol can be used by other packages to provide // implementations of protocol schemes like "ftp" or "file". // -// If rt.RoundTrip returns ErrSkipAltProtocol, the Transport will -// handle the RoundTrip itself for that one request, as if the +// If rt.RoundTrip returns [ErrSkipAltProtocol], the Transport will +// handle the [Transport.RoundTrip] itself for that one request, as if the // protocol were not registered. func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { t.altMu.Lock() @@ -799,9 +799,9 @@ func (t *Transport) CloseIdleConnections() { } // CancelRequest cancels an in-flight request by closing its connection. -// CancelRequest should only be called after RoundTrip has returned. +// CancelRequest should only be called after [Transport.RoundTrip] has returned. // -// Deprecated: Use Request.WithContext to create a request with a +// Deprecated: Use [Request.WithContext] to create a request with a // cancelable context instead. CancelRequest cannot cancel HTTP/2 // requests. func (t *Transport) CancelRequest(req *Request) { diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 8c09de70ff..3057024b76 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -3499,6 +3499,7 @@ func testTransportNoReuseAfterEarlyResponse(t *testing.T, mode testMode) { c net.Conn } var getOkay bool + var copying sync.WaitGroup closeConn := func() { sconn.Lock() defer sconn.Unlock() @@ -3510,7 +3511,10 @@ func testTransportNoReuseAfterEarlyResponse(t *testing.T, mode testMode) { } } } - defer closeConn() + defer func() { + closeConn() + copying.Wait() + }() ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) { if r.Method == "GET" { @@ -3522,7 +3526,12 @@ func testTransportNoReuseAfterEarlyResponse(t *testing.T, mode testMode) { sconn.c = conn sconn.Unlock() conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo")) // keep-alive - go io.Copy(io.Discard, conn) + + copying.Add(1) + go func() { + io.Copy(io.Discard, conn) + copying.Done() + }() })).ts c := ts.Client() diff --git a/src/net/http/triv.go b/src/net/http/triv.go index f614922c24..c1696425cd 100644 --- a/src/net/http/triv.go +++ b/src/net/http/triv.go @@ -34,7 +34,7 @@ type Counter struct { n int } -// This makes Counter satisfy the expvar.Var interface, so we can export +// This makes Counter satisfy the [expvar.Var] interface, so we can export // it directly. func (ctr *Counter) String() string { ctr.mu.Lock() diff --git a/src/net/interface.go b/src/net/interface.go index e1c9a2e2ff..20ac07d31a 100644 --- a/src/net/interface.go +++ b/src/net/interface.go @@ -114,7 +114,7 @@ func Interfaces() ([]Interface, error) { // addresses. // // The returned list does not identify the associated interface; use -// Interfaces and Interface.Addrs for more detail. +// Interfaces and [Interface.Addrs] for more detail. func InterfaceAddrs() ([]Addr, error) { ifat, err := interfaceAddrTable(nil) if err != nil { @@ -127,7 +127,7 @@ func InterfaceAddrs() ([]Addr, error) { // // On Solaris, it returns one of the logical network interfaces // sharing the logical data link; for more precision use -// InterfaceByName. +// [InterfaceByName]. func InterfaceByIndex(index int) (*Interface, error) { if index <= 0 { return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex} diff --git a/src/net/internal/socktest/switch.go b/src/net/internal/socktest/switch.go index 3c37b6ff80..dea6d9288c 100644 --- a/src/net/internal/socktest/switch.go +++ b/src/net/internal/socktest/switch.go @@ -133,7 +133,7 @@ const ( // If the filter returns a non-nil error, the execution of system call // will be canceled and the system call function returns the non-nil // error. -// It can return a non-nil AfterFilter for filtering after the +// It can return a non-nil [AfterFilter] for filtering after the // execution of the system call. type Filter func(*Status) (AfterFilter, error) diff --git a/src/net/internal/socktest/sys_unix.go b/src/net/internal/socktest/sys_unix.go index 712462abf4..3eef26c70b 100644 --- a/src/net/internal/socktest/sys_unix.go +++ b/src/net/internal/socktest/sys_unix.go @@ -8,7 +8,7 @@ package socktest import "syscall" -// Socket wraps syscall.Socket. +// Socket wraps [syscall.Socket]. func (sw *Switch) Socket(family, sotype, proto int) (s int, err error) { sw.once.Do(sw.init) diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go index 1c42e5c7f3..2f02446075 100644 --- a/src/net/internal/socktest/sys_windows.go +++ b/src/net/internal/socktest/sys_windows.go @@ -9,7 +9,7 @@ import ( "syscall" ) -// WSASocket wraps syscall.WSASocket. +// WSASocket wraps [syscall.WSASocket]. func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) { sw.once.Do(sw.init) @@ -41,7 +41,7 @@ func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAPr return s, nil } -// Closesocket wraps syscall.Closesocket. +// Closesocket wraps [syscall.Closesocket]. func (sw *Switch) Closesocket(s syscall.Handle) (err error) { so := sw.sockso(s) if so == nil { @@ -71,7 +71,7 @@ func (sw *Switch) Closesocket(s syscall.Handle) (err error) { return nil } -// Connect wraps syscall.Connect. +// Connect wraps [syscall.Connect]. func (sw *Switch) Connect(s syscall.Handle, sa syscall.Sockaddr) (err error) { so := sw.sockso(s) if so == nil { @@ -100,7 +100,7 @@ func (sw *Switch) Connect(s syscall.Handle, sa syscall.Sockaddr) (err error) { return nil } -// ConnectEx wraps syscall.ConnectEx. +// ConnectEx wraps [syscall.ConnectEx]. func (sw *Switch) ConnectEx(s syscall.Handle, sa syscall.Sockaddr, b *byte, n uint32, nwr *uint32, o *syscall.Overlapped) (err error) { so := sw.sockso(s) if so == nil { @@ -129,7 +129,7 @@ func (sw *Switch) ConnectEx(s syscall.Handle, sa syscall.Sockaddr, b *byte, n ui return nil } -// Listen wraps syscall.Listen. +// Listen wraps [syscall.Listen]. func (sw *Switch) Listen(s syscall.Handle, backlog int) (err error) { so := sw.sockso(s) if so == nil { @@ -158,7 +158,7 @@ func (sw *Switch) Listen(s syscall.Handle, backlog int) (err error) { return nil } -// AcceptEx wraps syscall.AcceptEx. +// AcceptEx wraps [syscall.AcceptEx]. func (sw *Switch) AcceptEx(ls syscall.Handle, as syscall.Handle, b *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, rcvd *uint32, overlapped *syscall.Overlapped) error { so := sw.sockso(ls) if so == nil { diff --git a/src/net/ip.go b/src/net/ip.go index d51ba10eec..6083dd8bf9 100644 --- a/src/net/ip.go +++ b/src/net/ip.go @@ -38,7 +38,7 @@ type IP []byte // An IPMask is a bitmask that can be used to manipulate // IP addresses for IP addressing and routing. // -// See type IPNet and func ParseCIDR for details. +// See type [IPNet] and func [ParseCIDR] for details. type IPMask []byte // An IPNet represents an IP network. @@ -72,9 +72,9 @@ func IPv4Mask(a, b, c, d byte) IPMask { return p } -// CIDRMask returns an IPMask consisting of 'ones' 1 bits +// CIDRMask returns an [IPMask] consisting of 'ones' 1 bits // followed by 0s up to a total length of 'bits' bits. -// For a mask of this form, CIDRMask is the inverse of IPMask.Size. +// For a mask of this form, CIDRMask is the inverse of [IPMask.Size]. func CIDRMask(ones, bits int) IPMask { if bits != 8*IPv4len && bits != 8*IPv6len { return nil @@ -324,8 +324,8 @@ func ipEmptyString(ip IP) string { return ip.String() } -// MarshalText implements the encoding.TextMarshaler interface. -// The encoding is the same as returned by String, with one exception: +// MarshalText implements the [encoding.TextMarshaler] interface. +// The encoding is the same as returned by [IP.String], with one exception: // When len(ip) is zero, it returns an empty slice. func (ip IP) MarshalText() ([]byte, error) { if len(ip) == 0 { @@ -337,8 +337,8 @@ func (ip IP) MarshalText() ([]byte, error) { return []byte(ip.String()), nil } -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// The IP address is expected in a form accepted by ParseIP. +// UnmarshalText implements the [encoding.TextUnmarshaler] interface. +// The IP address is expected in a form accepted by [ParseIP]. func (ip *IP) UnmarshalText(text []byte) error { if len(text) == 0 { *ip = nil diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go index c3fd6deac5..4c06b1b5ac 100644 --- a/src/net/iprawsock.go +++ b/src/net/iprawsock.go @@ -72,7 +72,7 @@ func (a *IPAddr) opAddr() Addr { // recommended, because it will return at most one of the host name's // IP addresses. // -// See func Dial for a description of the network and address +// See func [Dial] for a description of the network and address // parameters. func ResolveIPAddr(network, address string) (*IPAddr, error) { if network == "" { // a hint wildcard for Go 1.0 undocumented behavior @@ -94,14 +94,14 @@ func ResolveIPAddr(network, address string) (*IPAddr, error) { return addrs.forResolve(network, address).(*IPAddr), nil } -// IPConn is the implementation of the Conn and PacketConn interfaces +// IPConn is the implementation of the [Conn] and [PacketConn] interfaces // for IP network connections. type IPConn struct { conn } // SyscallConn returns a raw network connection. -// This implements the syscall.Conn interface. +// This implements the [syscall.Conn] interface. func (c *IPConn) SyscallConn() (syscall.RawConn, error) { if !c.ok() { return nil, syscall.EINVAL @@ -121,7 +121,7 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) { return n, addr, err } -// ReadFrom implements the PacketConn ReadFrom method. +// ReadFrom implements the [PacketConn] ReadFrom method. func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL @@ -154,7 +154,7 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err return } -// WriteToIP acts like WriteTo but takes an IPAddr. +// WriteToIP acts like [IPConn.WriteTo] but takes an [IPAddr]. func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) { if !c.ok() { return 0, syscall.EINVAL @@ -166,7 +166,7 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) { return n, err } -// WriteTo implements the PacketConn WriteTo method. +// WriteTo implements the [PacketConn] WriteTo method. func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { return 0, syscall.EINVAL @@ -201,7 +201,7 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} } -// DialIP acts like Dial for IP networks. +// DialIP acts like [Dial] for IP networks. // // The network must be an IP network name; see func Dial for details. // @@ -220,7 +220,7 @@ func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error) { return c, nil } -// ListenIP acts like ListenPacket for IP networks. +// ListenIP acts like [ListenPacket] for IP networks. // // The network must be an IP network name; see func Dial for details. // diff --git a/src/net/lookup.go b/src/net/lookup.go index 15165970b6..3ec2660786 100644 --- a/src/net/lookup.go +++ b/src/net/lookup.go @@ -181,8 +181,8 @@ func (r *Resolver) getLookupGroup() *singleflight.Group { // LookupHost looks up the given host using the local resolver. // It returns a slice of that host's addresses. // -// LookupHost uses context.Background internally; to specify the context, use -// Resolver.LookupHost. +// LookupHost uses [context.Background] internally; to specify the context, use +// [Resolver.LookupHost]. func LookupHost(host string) (addrs []string, err error) { return DefaultResolver.LookupHost(context.Background(), host) } @@ -417,8 +417,8 @@ func ipAddrsEface(addrs []IPAddr) []any { // LookupPort looks up the port for the given network and service. // -// LookupPort uses context.Background internally; to specify the context, use -// Resolver.LookupPort. +// LookupPort uses [context.Background] internally; to specify the context, use +// [Resolver.LookupPort]. func LookupPort(network, service string) (port int, err error) { return DefaultResolver.LookupPort(context.Background(), network, service) } @@ -449,7 +449,7 @@ func (r *Resolver) LookupPort(ctx context.Context, network, service string) (por // LookupCNAME returns the canonical name for the given host. // Callers that do not care about the canonical name can call -// LookupHost or LookupIP directly; both take care of resolving +// [LookupHost] or [LookupIP] directly; both take care of resolving // the canonical name as part of the lookup. // // A canonical name is the final name after following zero @@ -461,15 +461,15 @@ func (r *Resolver) LookupPort(ctx context.Context, network, service string) (por // The returned canonical name is validated to be a properly // formatted presentation-format domain name. // -// LookupCNAME uses context.Background internally; to specify the context, use -// Resolver.LookupCNAME. +// LookupCNAME uses [context.Background] internally; to specify the context, use +// [Resolver.LookupCNAME]. func LookupCNAME(host string) (cname string, err error) { return DefaultResolver.LookupCNAME(context.Background(), host) } // LookupCNAME returns the canonical name for the given host. // Callers that do not care about the canonical name can call -// LookupHost or LookupIP directly; both take care of resolving +// [LookupHost] or [LookupIP] directly; both take care of resolving // the canonical name as part of the lookup. // // A canonical name is the final name after following zero @@ -491,7 +491,7 @@ func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) return cname, nil } -// LookupSRV tries to resolve an SRV query of the given service, +// LookupSRV tries to resolve an [SRV] query of the given service, // protocol, and domain name. The proto is "tcp" or "udp". // The returned records are sorted by priority and randomized // by weight within a priority. @@ -509,7 +509,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err return DefaultResolver.LookupSRV(context.Background(), service, proto, name) } -// LookupSRV tries to resolve an SRV query of the given service, +// LookupSRV tries to resolve an [SRV] query of the given service, // protocol, and domain name. The proto is "tcp" or "udp". // The returned records are sorted by priority and randomized // by weight within a priority. @@ -554,8 +554,8 @@ func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) ( // invalid names, those records are filtered out and an error // will be returned alongside the remaining results, if any. // -// LookupMX uses context.Background internally; to specify the context, use -// Resolver.LookupMX. +// LookupMX uses [context.Background] internally; to specify the context, use +// [Resolver.LookupMX]. func LookupMX(name string) ([]*MX, error) { return DefaultResolver.LookupMX(context.Background(), name) } @@ -594,8 +594,8 @@ func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { // invalid names, those records are filtered out and an error // will be returned alongside the remaining results, if any. // -// LookupNS uses context.Background internally; to specify the context, use -// Resolver.LookupNS. +// LookupNS uses [context.Background] internally; to specify the context, use +// [Resolver.LookupNS]. func LookupNS(name string) ([]*NS, error) { return DefaultResolver.LookupNS(context.Background(), name) } @@ -629,8 +629,8 @@ func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) { // LookupTXT returns the DNS TXT records for the given domain name. // -// LookupTXT uses context.Background internally; to specify the context, use -// Resolver.LookupTXT. +// LookupTXT uses [context.Background] internally; to specify the context, use +// [Resolver.LookupTXT]. func LookupTXT(name string) ([]string, error) { return DefaultResolver.lookupTXT(context.Background(), name) } @@ -648,10 +648,10 @@ func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) // out and an error will be returned alongside the remaining results, if any. // // When using the host C library resolver, at most one result will be -// returned. To bypass the host resolver, use a custom Resolver. +// returned. To bypass the host resolver, use a custom [Resolver]. // -// LookupAddr uses context.Background internally; to specify the context, use -// Resolver.LookupAddr. +// LookupAddr uses [context.Background] internally; to specify the context, use +// [Resolver.LookupAddr]. func LookupAddr(addr string) (names []string, err error) { return DefaultResolver.LookupAddr(context.Background(), addr) } diff --git a/src/net/net.go b/src/net/net.go index 02c2ceda32..c434c96bf8 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -8,8 +8,8 @@ TCP/IP, UDP, domain name resolution, and Unix domain sockets. Although the package provides access to low-level networking primitives, most clients will need only the basic interface provided -by the Dial, Listen, and Accept functions and the associated -Conn and Listener interfaces. The crypto/tls package uses +by the [Dial], [Listen], and Accept functions and the associated +[Conn] and [Listener] interfaces. The crypto/tls package uses the same interfaces and similar Dial and Listen functions. The Dial function connects to a server: @@ -39,7 +39,7 @@ The Listen function creates servers: # Name Resolution The method for resolving domain names, whether indirectly with functions like Dial -or directly with functions like LookupHost and LookupAddr, varies by operating system. +or directly with functions like [LookupHost] and [LookupAddr], varies by operating system. On Unix systems, the resolver has two options for resolving names. It can use a pure Go resolver that sends DNS requests directly to the servers @@ -95,8 +95,8 @@ import ( // Addr represents a network end point address. // -// The two methods Network and String conventionally return strings -// that can be passed as the arguments to Dial, but the exact form +// The two methods [Addr.Network] and [Addr.String] conventionally return strings +// that can be passed as the arguments to [Dial], but the exact form // and meaning of the strings is up to the implementation. type Addr interface { Network() string // name of the network (for example, "tcp", "udp") @@ -284,7 +284,7 @@ func (c *conn) SetWriteBuffer(bytes int) error { return nil } -// File returns a copy of the underlying os.File. +// File returns a copy of the underlying [os.File]. // It is the caller's responsibility to close f when finished. // Closing c does not affect f, and closing f does not affect c. // @@ -645,12 +645,12 @@ func (e *DNSError) Error() string { // Timeout reports whether the DNS lookup is known to have timed out. // This is not always known; a DNS lookup may fail due to a timeout -// and return a DNSError for which Timeout returns false. +// and return a [DNSError] for which Timeout returns false. func (e *DNSError) Timeout() bool { return e.IsTimeout } // Temporary reports whether the DNS error is known to be temporary. // This is not always known; a DNS lookup may fail due to a temporary -// error and return a DNSError for which Temporary returns false. +// error and return a [DNSError] for which Temporary returns false. func (e *DNSError) Temporary() bool { return e.IsTimeout || e.IsTemporary } // errClosed exists just so that the docs for ErrClosed don't mention @@ -756,7 +756,7 @@ var ( // WriteTo writes contents of the buffers to w. // -// WriteTo implements io.WriterTo for Buffers. +// WriteTo implements [io.WriterTo] for [Buffers]. // // WriteTo modifies the slice v as well as v[i] for 0 <= i < len(v), // but does not modify v[i][j] for any i, j. @@ -778,7 +778,7 @@ func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) { // Read from the buffers. // -// Read implements io.Reader for Buffers. +// Read implements [io.Reader] for [Buffers]. // // Read modifies the slice v as well as v[i] for 0 <= i < len(v), // but does not modify v[i][j] for any i, j. diff --git a/src/net/netip/export_test.go b/src/net/netip/export_test.go index 59971fa2e4..72347ee01b 100644 --- a/src/net/netip/export_test.go +++ b/src/net/netip/export_test.go @@ -28,3 +28,5 @@ var TestAppendToMarshal = testAppendToMarshal func (a Addr) IsZero() bool { return a.isZero() } func (p Prefix) IsZero() bool { return p.isZero() } + +func (p Prefix) Compare(p2 Prefix) int { return p.compare(p2) } diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go index 1d20a4aa7f..7a189e8e16 100644 --- a/src/net/netip/netip.go +++ b/src/net/netip/netip.go @@ -128,7 +128,7 @@ func ParseAddr(s string) (Addr, error) { return Addr{}, parseAddrError{in: s, msg: "unable to parse IP"} } -// MustParseAddr calls ParseAddr(s) and panics on error. +// MustParseAddr calls [ParseAddr](s) and panics on error. // It is intended for use in tests with hard-coded strings. func MustParseAddr(s string) Addr { ip, err := ParseAddr(s) @@ -335,8 +335,8 @@ func parseIPv6(in string) (Addr, error) { } // AddrFromSlice parses the 4- or 16-byte byte slice as an IPv4 or IPv6 address. -// Note that a net.IP can be passed directly as the []byte argument. -// If slice's length is not 4 or 16, AddrFromSlice returns Addr{}, false. +// Note that a [net.IP] can be passed directly as the []byte argument. +// If slice's length is not 4 or 16, AddrFromSlice returns [Addr]{}, false. func AddrFromSlice(slice []byte) (ip Addr, ok bool) { switch len(slice) { case 4: @@ -376,13 +376,13 @@ func (ip Addr) isZero() bool { return ip.z == z0 } -// IsValid reports whether the Addr is an initialized address (not the zero Addr). +// IsValid reports whether the [Addr] is an initialized address (not the zero Addr). // // Note that "0.0.0.0" and "::" are both valid values. func (ip Addr) IsValid() bool { return ip.z != z0 } // BitLen returns the number of bits in the IP address: -// 128 for IPv6, 32 for IPv4, and 0 for the zero Addr. +// 128 for IPv6, 32 for IPv4, and 0 for the zero [Addr]. // // Note that IPv4-mapped IPv6 addresses are considered IPv6 addresses // and therefore have bit length 128. @@ -407,7 +407,7 @@ func (ip Addr) Zone() string { // Compare returns an integer comparing two IPs. // The result will be 0 if ip == ip2, -1 if ip < ip2, and +1 if ip > ip2. -// The definition of "less than" is the same as the Less method. +// The definition of "less than" is the same as the [Addr.Less] method. func (ip Addr) Compare(ip2 Addr) int { f1, f2 := ip.BitLen(), ip2.BitLen() if f1 < f2 { @@ -449,7 +449,7 @@ func (ip Addr) Less(ip2 Addr) bool { return ip.Compare(ip2) == -1 } // Is4 reports whether ip is an IPv4 address. // -// It returns false for IPv4-mapped IPv6 addresses. See Addr.Unmap. +// It returns false for IPv4-mapped IPv6 addresses. See [Addr.Unmap]. func (ip Addr) Is4() bool { return ip.z == z4 } @@ -583,7 +583,7 @@ func (ip Addr) IsLinkLocalMulticast() bool { // IANA-allocated 2000::/3 global unicast space, with the exception of the // link-local address space. It also returns true even if ip is in the IPv4 // private address space or IPv6 unique local address space. -// It returns false for the zero Addr. +// It returns false for the zero [Addr]. // // For reference, see RFC 1122, RFC 4291, and RFC 4632. func (ip Addr) IsGlobalUnicast() bool { @@ -607,7 +607,7 @@ func (ip Addr) IsGlobalUnicast() bool { // IsPrivate reports whether ip is a private address, according to RFC 1918 // (IPv4 addresses) and RFC 4193 (IPv6 addresses). That is, it reports whether // ip is in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or fc00::/7. This is the -// same as net.IP.IsPrivate. +// same as [net.IP.IsPrivate]. func (ip Addr) IsPrivate() bool { // Match the stdlib's IsPrivate logic. if ip.Is4() { @@ -630,14 +630,14 @@ func (ip Addr) IsPrivate() bool { // IsUnspecified reports whether ip is an unspecified address, either the IPv4 // address "0.0.0.0" or the IPv6 address "::". // -// Note that the zero Addr is not an unspecified address. +// Note that the zero [Addr] is not an unspecified address. func (ip Addr) IsUnspecified() bool { return ip == IPv4Unspecified() || ip == IPv6Unspecified() } // Prefix keeps only the top b bits of IP, producing a Prefix // of the specified length. -// If ip is a zero Addr, Prefix always returns a zero Prefix and a nil error. +// If ip is a zero [Addr], Prefix always returns a zero Prefix and a nil error. // Otherwise, if bits is less than zero or greater than ip.BitLen(), // Prefix returns an error. func (ip Addr) Prefix(b int) (Prefix, error) { @@ -665,7 +665,7 @@ func (ip Addr) Prefix(b int) (Prefix, error) { // As16 returns the IP address in its 16-byte representation. // 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). +// [Addr.Zone] method to get it). // The ip zero value returns all zeroes. func (ip Addr) As16() (a16 [16]byte) { bePutUint64(a16[:8], ip.addr.hi) @@ -674,7 +674,7 @@ func (ip Addr) As16() (a16 [16]byte) { } // As4 returns an IPv4 or IPv4-in-IPv6 address in its 4-byte representation. -// If ip is the zero Addr or an IPv6 address, As4 panics. +// If ip is the zero [Addr] or an IPv6 address, As4 panics. // Note that 0.0.0.0 is not the zero Addr. func (ip Addr) As4() (a4 [4]byte) { if ip.z == z4 || ip.Is4In6() { @@ -705,7 +705,7 @@ func (ip Addr) AsSlice() []byte { } // Next returns the address following ip. -// If there is none, it returns the zero Addr. +// If there is none, it returns the zero [Addr]. func (ip Addr) Next() Addr { ip.addr = ip.addr.addOne() if ip.Is4() { @@ -739,10 +739,10 @@ func (ip Addr) Prev() Addr { // String returns the string form of the IP address ip. // It returns one of 5 forms: // -// - "invalid IP", if ip is the zero Addr +// - "invalid IP", if ip is the zero [Addr] // - IPv4 dotted decimal ("192.0.2.1") // - IPv6 ("2001:db8::1") -// - "::ffff:1.2.3.4" (if Is4In6) +// - "::ffff:1.2.3.4" (if [Addr.Is4In6]) // - IPv6 with zone ("fe80:db8::1%eth0") // // Note that unlike package net's IP.String method, @@ -767,7 +767,7 @@ func (ip Addr) String() string { } // AppendTo appends a text encoding of ip, -// as generated by MarshalText, +// as generated by [Addr.MarshalText], // to b and returns the extended buffer. func (ip Addr) AppendTo(b []byte) []byte { switch ip.z { @@ -899,7 +899,7 @@ func (ip Addr) appendTo6(ret []byte) []byte { return ret } -// StringExpanded is like String but IPv6 addresses are expanded with leading +// StringExpanded is like [Addr.String] but IPv6 addresses are expanded with leading // zeroes and no "::" compression. For example, "2001:db8::1" becomes // "2001:0db8:0000:0000:0000:0000:0000:0001". func (ip Addr) StringExpanded() string { @@ -927,9 +927,9 @@ func (ip Addr) StringExpanded() string { return string(ret) } -// MarshalText implements the encoding.TextMarshaler interface, -// The encoding is the same as returned by String, with one exception: -// If ip is the zero Addr, the encoding is the empty string. +// MarshalText implements the [encoding.TextMarshaler] interface, +// The encoding is the same as returned by [Addr.String], with one exception: +// If ip is the zero [Addr], the encoding is the empty string. func (ip Addr) MarshalText() ([]byte, error) { switch ip.z { case z0: @@ -956,9 +956,9 @@ func (ip Addr) MarshalText() ([]byte, error) { } // UnmarshalText implements the encoding.TextUnmarshaler interface. -// The IP address is expected in a form accepted by ParseAddr. +// The IP address is expected in a form accepted by [ParseAddr]. // -// If text is empty, UnmarshalText sets *ip to the zero Addr and +// If text is empty, UnmarshalText sets *ip to the zero [Addr] and // returns no error. func (ip *Addr) UnmarshalText(text []byte) error { if len(text) == 0 { @@ -988,15 +988,15 @@ func (ip Addr) marshalBinaryWithTrailingBytes(trailingBytes int) []byte { return b } -// MarshalBinary implements the encoding.BinaryMarshaler interface. -// It returns a zero-length slice for the zero Addr, +// MarshalBinary implements the [encoding.BinaryMarshaler] interface. +// It returns a zero-length slice for the zero [Addr], // the 4-byte form for an IPv4 address, // and the 16-byte form with zone appended for an IPv6 address. func (ip Addr) MarshalBinary() ([]byte, error) { return ip.marshalBinaryWithTrailingBytes(0), nil } -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. // It expects data in the form generated by MarshalBinary. func (ip *Addr) UnmarshalBinary(b []byte) error { n := len(b) @@ -1023,7 +1023,7 @@ type AddrPort struct { port uint16 } -// AddrPortFrom returns an AddrPort with the provided IP and port. +// AddrPortFrom returns an [AddrPort] with the provided IP and port. // It does not allocate. func AddrPortFrom(ip Addr, port uint16) AddrPort { return AddrPort{ip: ip, port: port} } @@ -1062,7 +1062,7 @@ func splitAddrPort(s string) (ip, port string, v6 bool, err error) { return ip, port, v6, nil } -// ParseAddrPort parses s as an AddrPort. +// ParseAddrPort parses s as an [AddrPort]. // // It doesn't do any name resolution: both the address and the port // must be numeric. @@ -1089,7 +1089,7 @@ func ParseAddrPort(s string) (AddrPort, error) { return ipp, nil } -// MustParseAddrPort calls ParseAddrPort(s) and panics on error. +// MustParseAddrPort calls [ParseAddrPort](s) and panics on error. // It is intended for use in tests with hard-coded strings. func MustParseAddrPort(s string) AddrPort { ip, err := ParseAddrPort(s) @@ -1131,7 +1131,7 @@ func (p AddrPort) String() string { } // AppendTo appends a text encoding of p, -// as generated by MarshalText, +// as generated by [AddrPort.MarshalText], // to b and returns the extended buffer. func (p AddrPort) AppendTo(b []byte) []byte { switch p.ip.z { @@ -1158,9 +1158,9 @@ func (p AddrPort) AppendTo(b []byte) []byte { return b } -// MarshalText implements the encoding.TextMarshaler interface. The -// encoding is the same as returned by String, with one exception: if -// p.Addr() is the zero Addr, the encoding is the empty string. +// MarshalText implements the [encoding.TextMarshaler] interface. The +// encoding is the same as returned by [AddrPort.String], with one exception: if +// p.Addr() is the zero [Addr], the encoding is the empty string. func (p AddrPort) MarshalText() ([]byte, error) { var max int switch p.ip.z { @@ -1176,8 +1176,8 @@ func (p AddrPort) MarshalText() ([]byte, error) { } // UnmarshalText implements the encoding.TextUnmarshaler -// interface. The AddrPort is expected in a form -// generated by MarshalText or accepted by ParseAddrPort. +// interface. The [AddrPort] is expected in a form +// generated by [AddrPort.MarshalText] or accepted by [ParseAddrPort]. func (p *AddrPort) UnmarshalText(text []byte) error { if len(text) == 0 { *p = AddrPort{} @@ -1188,8 +1188,8 @@ func (p *AddrPort) UnmarshalText(text []byte) error { return err } -// MarshalBinary implements the encoding.BinaryMarshaler interface. -// It returns Addr.MarshalBinary with an additional two bytes appended +// MarshalBinary implements the [encoding.BinaryMarshaler] interface. +// It returns [Addr.MarshalBinary] with an additional two bytes appended // containing the port in little-endian. func (p AddrPort) MarshalBinary() ([]byte, error) { b := p.Addr().marshalBinaryWithTrailingBytes(2) @@ -1197,8 +1197,8 @@ func (p AddrPort) MarshalBinary() ([]byte, error) { return b, nil } -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -// It expects data in the form generated by MarshalBinary. +// UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. +// It expects data in the form generated by [AddrPort.MarshalBinary]. func (p *AddrPort) UnmarshalBinary(b []byte) error { if len(b) < 2 { return errors.New("unexpected slice size") @@ -1214,7 +1214,7 @@ func (p *AddrPort) UnmarshalBinary(b []byte) error { // Prefix is an IP address prefix (CIDR) representing an IP network. // -// The first Bits() of Addr() are specified. The remaining bits match any address. +// The first [Prefix.Bits]() of [Addr]() are specified. The remaining bits match any address. // The range of Bits() is [0,32] for IPv4 or [0,128] for IPv6. type Prefix struct { ip Addr @@ -1224,13 +1224,13 @@ type Prefix struct { bitsPlusOne uint8 } -// PrefixFrom returns a Prefix with the provided IP address and bit +// PrefixFrom returns a [Prefix] with the provided IP address and bit // prefix length. // -// It does not allocate. Unlike Addr.Prefix, PrefixFrom does not mask +// It does not allocate. Unlike [Addr.Prefix], [PrefixFrom] does not mask // off the host bits of ip. // -// If bits is less than zero or greater than ip.BitLen, Prefix.Bits +// If bits is less than zero or greater than ip.BitLen, [Prefix.Bits] // will return an invalid value -1. func PrefixFrom(ip Addr, bits int) Prefix { var bitsPlusOne uint8 @@ -1252,8 +1252,8 @@ func (p Prefix) Addr() Addr { return p.ip } func (p Prefix) Bits() int { return int(p.bitsPlusOne) - 1 } // IsValid reports whether p.Bits() has a valid range for p.Addr(). -// If p.Addr() is the zero Addr, IsValid returns false. -// Note that if p is the zero Prefix, then p.IsValid() == false. +// If p.Addr() is the zero [Addr], IsValid returns false. +// Note that if p is the zero [Prefix], then p.IsValid() == false. func (p Prefix) IsValid() bool { return p.bitsPlusOne > 0 } func (p Prefix) isZero() bool { return p == Prefix{} } @@ -1261,12 +1261,15 @@ func (p Prefix) isZero() bool { return p == Prefix{} } // IsSingleIP reports whether p contains exactly one IP. func (p Prefix) IsSingleIP() bool { return p.IsValid() && p.Bits() == p.ip.BitLen() } -// Compare returns an integer comparing two prefixes. +// compare returns an integer comparing two prefixes. // The result will be 0 if p == p2, -1 if p < p2, and +1 if p > p2. // Prefixes sort first by validity (invalid before valid), then // address family (IPv4 before IPv6), then prefix length, then // address. -func (p Prefix) Compare(p2 Prefix) int { +// +// Unexported for Go 1.22 because we may want to compare by p.Addr first. +// See post-acceptance discussion on go.dev/issue/61642. +func (p Prefix) compare(p2 Prefix) int { if c := cmp.Compare(p.Addr().BitLen(), p2.Addr().BitLen()); c != 0 { return c } @@ -1318,7 +1321,7 @@ func ParsePrefix(s string) (Prefix, error) { return PrefixFrom(ip, bits), nil } -// MustParsePrefix calls ParsePrefix(s) and panics on error. +// MustParsePrefix calls [ParsePrefix](s) and panics on error. // It is intended for use in tests with hard-coded strings. func MustParsePrefix(s string) Prefix { ip, err := ParsePrefix(s) @@ -1331,7 +1334,7 @@ func MustParsePrefix(s string) Prefix { // Masked returns p in its canonical form, with all but the high // p.Bits() bits of p.Addr() masked off. // -// If p is zero or otherwise invalid, Masked returns the zero Prefix. +// If p is zero or otherwise invalid, Masked returns the zero [Prefix]. func (p Prefix) Masked() Prefix { m, _ := p.ip.Prefix(p.Bits()) return m @@ -1408,7 +1411,7 @@ func (p Prefix) Overlaps(o Prefix) bool { } // AppendTo appends a text encoding of p, -// as generated by MarshalText, +// as generated by [Prefix.MarshalText], // to b and returns the extended buffer. func (p Prefix) AppendTo(b []byte) []byte { if p.isZero() { @@ -1435,8 +1438,8 @@ func (p Prefix) AppendTo(b []byte) []byte { return b } -// MarshalText implements the encoding.TextMarshaler interface, -// The encoding is the same as returned by String, with one exception: +// MarshalText implements the [encoding.TextMarshaler] interface, +// The encoding is the same as returned by [Prefix.String], with one exception: // If p is the zero value, the encoding is the empty string. func (p Prefix) MarshalText() ([]byte, error) { var max int @@ -1453,8 +1456,8 @@ func (p Prefix) MarshalText() ([]byte, error) { } // UnmarshalText implements the encoding.TextUnmarshaler interface. -// The IP address is expected in a form accepted by ParsePrefix -// or generated by MarshalText. +// The IP address is expected in a form accepted by [ParsePrefix] +// or generated by [Prefix.MarshalText]. func (p *Prefix) UnmarshalText(text []byte) error { if len(text) == 0 { *p = Prefix{} @@ -1465,8 +1468,8 @@ func (p *Prefix) UnmarshalText(text []byte) error { return err } -// MarshalBinary implements the encoding.BinaryMarshaler interface. -// It returns Addr.MarshalBinary with an additional byte appended +// MarshalBinary implements the [encoding.BinaryMarshaler] interface. +// It returns [Addr.MarshalBinary] with an additional byte appended // containing the prefix bits. func (p Prefix) MarshalBinary() ([]byte, error) { b := p.Addr().withoutZone().marshalBinaryWithTrailingBytes(1) @@ -1474,8 +1477,8 @@ func (p Prefix) MarshalBinary() ([]byte, error) { return b, nil } -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -// It expects data in the form generated by MarshalBinary. +// UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. +// It expects data in the form generated by [Prefix.MarshalBinary]. func (p *Prefix) UnmarshalBinary(b []byte) error { if len(b) < 1 { return errors.New("unexpected slice size") diff --git a/src/net/pipe.go b/src/net/pipe.go index f1741938b0..69955e4617 100644 --- a/src/net/pipe.go +++ b/src/net/pipe.go @@ -106,7 +106,7 @@ type pipe struct { } // Pipe creates a synchronous, in-memory, full duplex -// network connection; both ends implement the Conn interface. +// network connection; both ends implement the [Conn] interface. // Reads on one end are matched with writes on the other, // copying data directly between the two; there is no internal // buffering. diff --git a/src/net/rawconn.go b/src/net/rawconn.go index 7a69fe5c25..19228e94ed 100644 --- a/src/net/rawconn.go +++ b/src/net/rawconn.go @@ -63,7 +63,7 @@ func (c *rawConn) Write(f func(uintptr) bool) error { // PollFD returns the poll.FD of the underlying connection. // -// Other packages in std that also import internal/poll (such as os) +// Other packages in std that also import [internal/poll] (such as os) // can use a type assertion to access this extension method so that // they can pass the *poll.FD to functions like poll.Splice. // diff --git a/src/net/rpc/client.go b/src/net/rpc/client.go index 42d13519b1..ffdc435965 100644 --- a/src/net/rpc/client.go +++ b/src/net/rpc/client.go @@ -53,13 +53,13 @@ type Client struct { // A ClientCodec implements writing of RPC requests and // reading of RPC responses for the client side of an RPC session. -// The client calls WriteRequest to write a request to the connection -// and calls ReadResponseHeader and ReadResponseBody in pairs -// to read responses. The client calls Close when finished with the +// The client calls [ClientCodec.WriteRequest] to write a request to the connection +// and calls [ClientCodec.ReadResponseHeader] and [ClientCodec.ReadResponseBody] in pairs +// to read responses. The client calls [ClientCodec.Close] when finished with the // connection. ReadResponseBody may be called with a nil // argument to force the body of the response to be read and then // discarded. -// See NewClient's comment for information about concurrent access. +// See [NewClient]'s comment for information about concurrent access. type ClientCodec interface { WriteRequest(*Request, any) error ReadResponseHeader(*Response) error @@ -181,7 +181,7 @@ func (call *Call) done() { } } -// NewClient returns a new Client to handle requests to the +// NewClient returns a new [Client] to handle requests to the // set of services at the other end of the connection. // It adds a buffer to the write side of the connection so // the header and payload are sent as a unit. @@ -196,7 +196,7 @@ func NewClient(conn io.ReadWriteCloser) *Client { return NewClientWithCodec(client) } -// NewClientWithCodec is like NewClient but uses the specified +// NewClientWithCodec is like [NewClient] but uses the specified // codec to encode requests and decode responses. func NewClientWithCodec(codec ClientCodec) *Client { client := &Client{ @@ -279,7 +279,7 @@ func Dial(network, address string) (*Client, error) { } // Close calls the underlying codec's Close method. If the connection is already -// shutting down, ErrShutdown is returned. +// shutting down, [ErrShutdown] is returned. func (client *Client) Close() error { client.mutex.Lock() if client.closing { @@ -291,7 +291,7 @@ func (client *Client) Close() error { return client.codec.Close() } -// Go invokes the function asynchronously. It returns the Call structure representing +// Go invokes the function asynchronously. It returns the [Call] structure representing // the invocation. The done channel will signal when the call is complete by returning // the same Call object. If done is nil, Go will allocate a new channel. // If non-nil, done must be buffered or Go will deliberately crash. diff --git a/src/net/rpc/jsonrpc/client.go b/src/net/rpc/jsonrpc/client.go index c473017d26..1beba0f364 100644 --- a/src/net/rpc/jsonrpc/client.go +++ b/src/net/rpc/jsonrpc/client.go @@ -33,7 +33,7 @@ type clientCodec struct { pending map[uint64]string // map request id to method name } -// NewClientCodec returns a new rpc.ClientCodec using JSON-RPC on conn. +// NewClientCodec returns a new [rpc.ClientCodec] using JSON-RPC on conn. func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec { return &clientCodec{ dec: json.NewDecoder(conn), @@ -108,7 +108,7 @@ func (c *clientCodec) Close() error { return c.c.Close() } -// NewClient returns a new rpc.Client to handle requests to the +// NewClient returns a new [rpc.Client] to handle requests to the // set of services at the other end of the connection. func NewClient(conn io.ReadWriteCloser) *rpc.Client { return rpc.NewClientWithCodec(NewClientCodec(conn)) diff --git a/src/net/rpc/jsonrpc/server.go b/src/net/rpc/jsonrpc/server.go index 3ee4ddfef2..57a4de1d0f 100644 --- a/src/net/rpc/jsonrpc/server.go +++ b/src/net/rpc/jsonrpc/server.go @@ -33,7 +33,7 @@ type serverCodec struct { pending map[uint64]*json.RawMessage } -// NewServerCodec returns a new rpc.ServerCodec using JSON-RPC on conn. +// NewServerCodec returns a new [rpc.ServerCodec] using JSON-RPC on conn. func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec { return &serverCodec{ dec: json.NewDecoder(conn), diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go index fc3ec484ad..1771726a93 100644 --- a/src/net/rpc/server.go +++ b/src/net/rpc/server.go @@ -30,17 +30,17 @@ These requirements apply even if a different codec is used. The method's first argument represents the arguments provided by the caller; the second argument represents the result parameters to be returned to the caller. The method's return value, if non-nil, is passed back as a string that the client -sees as if created by errors.New. If an error is returned, the reply parameter +sees as if created by [errors.New]. If an error is returned, the reply parameter will not be sent back to the client. -The server may handle requests on a single connection by calling ServeConn. More -typically it will create a network listener and call Accept or, for an HTTP -listener, HandleHTTP and http.Serve. +The server may handle requests on a single connection by calling [ServeConn]. More +typically it will create a network listener and call [Accept] or, for an HTTP +listener, [HandleHTTP] and [http.Serve]. A client wishing to use the service establishes a connection and then invokes -NewClient on the connection. The convenience function Dial (DialHTTP) performs +[NewClient] on the connection. The convenience function [Dial] ([DialHTTP]) performs both steps for a raw network connection (an HTTP connection). The resulting -Client object has two methods, Call and Go, that specify the service and method to +[Client] object has two methods, [Call] and Go, that specify the service and method to call, a pointer containing the arguments, and a pointer to receive the result parameters. @@ -48,7 +48,7 @@ The Call method waits for the remote call to complete while the Go method launches the call asynchronously and signals completion using the Call structure's Done channel. -Unless an explicit codec is set up, package encoding/gob is used to +Unless an explicit codec is set up, package [encoding/gob] is used to transport the data. Here is a simple example. A server wishes to export an object of type Arith: @@ -192,12 +192,12 @@ type Server struct { freeResp *Response } -// NewServer returns a new Server. +// NewServer returns a new [Server]. func NewServer() *Server { return &Server{} } -// DefaultServer is the default instance of *Server. +// DefaultServer is the default instance of [*Server]. var DefaultServer = NewServer() // Is this type exported or a builtin? @@ -225,7 +225,7 @@ func (server *Server) Register(rcvr any) error { return server.register(rcvr, "", false) } -// RegisterName is like Register but uses the provided name for the type +// RegisterName is like [Register] but uses the provided name for the type // instead of the receiver's concrete type. func (server *Server) RegisterName(name string, rcvr any) error { return server.register(rcvr, name, true) @@ -440,8 +440,8 @@ func (c *gobServerCodec) Close() error { // ServeConn blocks, serving the connection until the client hangs up. // The caller typically invokes ServeConn in a go statement. // ServeConn uses the gob wire format (see package gob) on the -// connection. To use an alternate codec, use ServeCodec. -// See NewClient's comment for information about concurrent access. +// connection. To use an alternate codec, use [ServeCodec]. +// See [NewClient]'s comment for information about concurrent access. func (server *Server) ServeConn(conn io.ReadWriteCloser) { buf := bufio.NewWriter(conn) srv := &gobServerCodec{ @@ -453,7 +453,7 @@ func (server *Server) ServeConn(conn io.ReadWriteCloser) { server.ServeCodec(srv) } -// ServeCodec is like ServeConn but uses the specified codec to +// ServeCodec is like [ServeConn] but uses the specified codec to // decode requests and encode responses. func (server *Server) ServeCodec(codec ServerCodec) { sending := new(sync.Mutex) @@ -483,7 +483,7 @@ func (server *Server) ServeCodec(codec ServerCodec) { codec.Close() } -// ServeRequest is like ServeCodec but synchronously serves a single request. +// ServeRequest is like [ServeCodec] but synchronously serves a single request. // It does not close the codec upon completion. func (server *Server) ServeRequest(codec ServerCodec) error { sending := new(sync.Mutex) @@ -635,10 +635,10 @@ func (server *Server) Accept(lis net.Listener) { } } -// Register publishes the receiver's methods in the DefaultServer. +// Register publishes the receiver's methods in the [DefaultServer]. func Register(rcvr any) error { return DefaultServer.Register(rcvr) } -// RegisterName is like Register but uses the provided name for the type +// RegisterName is like [Register] but uses the provided name for the type // instead of the receiver's concrete type. func RegisterName(name string, rcvr any) error { return DefaultServer.RegisterName(name, rcvr) @@ -646,12 +646,12 @@ func RegisterName(name string, rcvr any) error { // A ServerCodec implements reading of RPC requests and writing of // RPC responses for the server side of an RPC session. -// The server calls ReadRequestHeader and ReadRequestBody in pairs -// to read requests from the connection, and it calls WriteResponse to -// write a response back. The server calls Close when finished with the +// The server calls [ServerCodec.ReadRequestHeader] and [ServerCodec.ReadRequestBody] in pairs +// to read requests from the connection, and it calls [ServerCodec.WriteResponse] to +// write a response back. The server calls [ServerCodec.Close] when finished with the // connection. ReadRequestBody may be called with a nil // argument to force the body of the request to be read and discarded. -// See NewClient's comment for information about concurrent access. +// See [NewClient]'s comment for information about concurrent access. type ServerCodec interface { ReadRequestHeader(*Request) error ReadRequestBody(any) error @@ -661,37 +661,37 @@ type ServerCodec interface { Close() error } -// ServeConn runs the DefaultServer on a single connection. +// ServeConn runs the [DefaultServer] on a single connection. // ServeConn blocks, serving the connection until the client hangs up. // The caller typically invokes ServeConn in a go statement. // ServeConn uses the gob wire format (see package gob) on the -// connection. To use an alternate codec, use ServeCodec. -// See NewClient's comment for information about concurrent access. +// connection. To use an alternate codec, use [ServeCodec]. +// See [NewClient]'s comment for information about concurrent access. func ServeConn(conn io.ReadWriteCloser) { DefaultServer.ServeConn(conn) } -// ServeCodec is like ServeConn but uses the specified codec to +// ServeCodec is like [ServeConn] but uses the specified codec to // decode requests and encode responses. func ServeCodec(codec ServerCodec) { DefaultServer.ServeCodec(codec) } -// ServeRequest is like ServeCodec but synchronously serves a single request. +// ServeRequest is like [ServeCodec] but synchronously serves a single request. // It does not close the codec upon completion. func ServeRequest(codec ServerCodec) error { return DefaultServer.ServeRequest(codec) } // Accept accepts connections on the listener and serves requests -// to DefaultServer for each incoming connection. +// to [DefaultServer] for each incoming connection. // Accept blocks; the caller typically invokes it in a go statement. func Accept(lis net.Listener) { DefaultServer.Accept(lis) } // Can connect to RPC service using HTTP CONNECT to rpcPath. var connected = "200 Connected to Go RPC" -// ServeHTTP implements an http.Handler that answers RPC requests. +// ServeHTTP implements an [http.Handler] that answers RPC requests. func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { if req.Method != "CONNECT" { w.Header().Set("Content-Type", "text/plain; charset=utf-8") @@ -710,15 +710,15 @@ func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { // HandleHTTP registers an HTTP handler for RPC messages on rpcPath, // and a debugging handler on debugPath. -// It is still necessary to invoke http.Serve(), typically in a go statement. +// It is still necessary to invoke [http.Serve](), typically in a go statement. func (server *Server) HandleHTTP(rpcPath, debugPath string) { http.Handle(rpcPath, server) http.Handle(debugPath, debugHTTP{server}) } -// HandleHTTP registers an HTTP handler for RPC messages to DefaultServer -// on DefaultRPCPath and a debugging handler on DefaultDebugPath. -// It is still necessary to invoke http.Serve(), typically in a go statement. +// HandleHTTP registers an HTTP handler for RPC messages to [DefaultServer] +// on [DefaultRPCPath] and a debugging handler on [DefaultDebugPath]. +// It is still necessary to invoke [http.Serve](), typically in a go statement. func HandleHTTP() { DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath) } diff --git a/src/net/smtp/auth.go b/src/net/smtp/auth.go index 72eb16671f..6d461acc48 100644 --- a/src/net/smtp/auth.go +++ b/src/net/smtp/auth.go @@ -42,7 +42,7 @@ type plainAuth struct { host string } -// PlainAuth returns an Auth that implements the PLAIN authentication +// PlainAuth returns an [Auth] that implements the PLAIN authentication // mechanism as defined in RFC 4616. The returned Auth uses the given // username and password to authenticate to host and act as identity. // Usually identity should be the empty string, to act as username. @@ -86,7 +86,7 @@ type cramMD5Auth struct { username, secret string } -// CRAMMD5Auth returns an Auth that implements the CRAM-MD5 authentication +// CRAMMD5Auth returns an [Auth] that implements the CRAM-MD5 authentication // mechanism as defined in RFC 2195. // The returned Auth uses the given username and secret to authenticate // to the server using the challenge-response mechanism. diff --git a/src/net/smtp/smtp.go b/src/net/smtp/smtp.go index b5a025ef2a..b7877936da 100644 --- a/src/net/smtp/smtp.go +++ b/src/net/smtp/smtp.go @@ -48,7 +48,7 @@ type Client struct { helloError error // the error from the hello } -// Dial returns a new Client connected to an SMTP server at addr. +// Dial returns a new [Client] connected to an SMTP server at addr. // The addr must include a port, as in "mail.example.com:smtp". func Dial(addr string) (*Client, error) { conn, err := net.Dial("tcp", addr) @@ -59,7 +59,7 @@ func Dial(addr string) (*Client, error) { return NewClient(conn, host) } -// NewClient returns a new Client using an existing connection and host as a +// NewClient returns a new [Client] using an existing connection and host as a // server name to be used when authenticating. func NewClient(conn net.Conn, host string) (*Client, error) { text := textproto.NewConn(conn) @@ -166,7 +166,7 @@ func (c *Client) StartTLS(config *tls.Config) error { } // TLSConnectionState returns the client's TLS connection state. -// The return values are their zero values if StartTLS did +// The return values are their zero values if [Client.StartTLS] did // not succeed. func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool) { tc, ok := c.conn.(*tls.Conn) @@ -241,7 +241,7 @@ func (c *Client) Auth(a Auth) error { // If the server supports the 8BITMIME extension, Mail adds the BODY=8BITMIME // parameter. If the server supports the SMTPUTF8 extension, Mail adds the // SMTPUTF8 parameter. -// This initiates a mail transaction and is followed by one or more Rcpt calls. +// This initiates a mail transaction and is followed by one or more [Client.Rcpt] calls. func (c *Client) Mail(from string) error { if err := validateLine(from); err != nil { return err @@ -263,8 +263,8 @@ func (c *Client) Mail(from string) error { } // Rcpt issues a RCPT command to the server using the provided email address. -// A call to Rcpt must be preceded by a call to Mail and may be followed by -// a Data call or another Rcpt call. +// A call to Rcpt must be preceded by a call to [Client.Mail] and may be followed by +// a [Client.Data] call or another Rcpt call. func (c *Client) Rcpt(to string) error { if err := validateLine(to); err != nil { return err @@ -287,7 +287,7 @@ func (d *dataCloser) Close() error { // Data issues a DATA command to the server and returns a writer that // can be used to write the mail headers and body. The caller should // close the writer before calling any more methods on c. A call to -// Data must be preceded by one or more calls to Rcpt. +// Data must be preceded by one or more calls to [Client.Rcpt]. func (c *Client) Data() (io.WriteCloser, error) { _, _, err := c.cmd(354, "DATA") if err != nil { diff --git a/src/net/sock_windows.go b/src/net/sock_windows.go index 5540135a2c..a519909bb0 100644 --- a/src/net/sock_windows.go +++ b/src/net/sock_windows.go @@ -11,8 +11,9 @@ import ( ) func maxListenerBacklog() int { - // TODO: Implement this - // NOTE: Never return a number bigger than 1<<16 - 1. See issue 5030. + // When the socket backlog is SOMAXCONN, Windows will set the backlog to + // "a reasonable maximum value". + // See: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen return syscall.SOMAXCONN } diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go index 6257f2515b..590516bff1 100644 --- a/src/net/tcpsock.go +++ b/src/net/tcpsock.go @@ -24,7 +24,7 @@ type TCPAddr struct { Zone string // IPv6 scoped addressing zone } -// AddrPort returns the TCPAddr a as a netip.AddrPort. +// AddrPort returns the [TCPAddr] a as a [netip.AddrPort]. // // If a.Port does not fit in a uint16, it's silently truncated. // @@ -79,7 +79,7 @@ func (a *TCPAddr) opAddr() Addr { // recommended, because it will return at most one of the host name's // IP addresses. // -// See func Dial for a description of the network and address +// See func [Dial] for a description of the network and address // parameters. func ResolveTCPAddr(network, address string) (*TCPAddr, error) { switch network { @@ -96,7 +96,7 @@ func ResolveTCPAddr(network, address string) (*TCPAddr, error) { return addrs.forResolve(network, address).(*TCPAddr), nil } -// TCPAddrFromAddrPort returns addr as a TCPAddr. If addr.IsValid() is false, +// TCPAddrFromAddrPort returns addr as a [TCPAddr]. If addr.IsValid() is false, // then the returned TCPAddr will contain a nil IP field, indicating an // address family-agnostic unspecified address. func TCPAddrFromAddrPort(addr netip.AddrPort) *TCPAddr { @@ -107,14 +107,14 @@ func TCPAddrFromAddrPort(addr netip.AddrPort) *TCPAddr { } } -// TCPConn is an implementation of the Conn interface for TCP network +// TCPConn is an implementation of the [Conn] interface for TCP network // connections. type TCPConn struct { conn } // SyscallConn returns a raw network connection. -// This implements the syscall.Conn interface. +// This implements the [syscall.Conn] interface. func (c *TCPConn) SyscallConn() (syscall.RawConn, error) { if !c.ok() { return nil, syscall.EINVAL @@ -122,7 +122,7 @@ func (c *TCPConn) SyscallConn() (syscall.RawConn, error) { return newRawConn(c.fd), nil } -// ReadFrom implements the io.ReaderFrom ReadFrom method. +// ReadFrom implements the [io.ReaderFrom] ReadFrom method. func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) { if !c.ok() { return 0, syscall.EINVAL @@ -262,7 +262,7 @@ func newTCPConn(fd *netFD, keepAlive time.Duration, keepAliveHook func(time.Dura return &TCPConn{conn{fd}} } -// DialTCP acts like Dial for TCP networks. +// DialTCP acts like [Dial] for TCP networks. // // The network must be a TCP network name; see func Dial for details. // @@ -287,14 +287,14 @@ func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error) { } // TCPListener is a TCP network listener. Clients should typically -// use variables of type Listener instead of assuming TCP. +// use variables of type [Listener] instead of assuming TCP. type TCPListener struct { fd *netFD lc ListenConfig } // SyscallConn returns a raw network connection. -// This implements the syscall.Conn interface. +// This implements the [syscall.Conn] interface. // // The returned RawConn only supports calling Control. Read and // Write return an error. @@ -318,8 +318,8 @@ func (l *TCPListener) AcceptTCP() (*TCPConn, error) { return c, nil } -// Accept implements the Accept method in the Listener interface; it -// waits for the next call and returns a generic Conn. +// Accept implements the Accept method in the [Listener] interface; it +// waits for the next call and returns a generic [Conn]. func (l *TCPListener) Accept() (Conn, error) { if !l.ok() { return nil, syscall.EINVAL @@ -343,7 +343,7 @@ func (l *TCPListener) Close() error { return nil } -// Addr returns the listener's network address, a *TCPAddr. +// Addr returns the listener's network address, a [*TCPAddr]. // The Addr returned is shared by all invocations of Addr, so // do not modify it. func (l *TCPListener) Addr() Addr { return l.fd.laddr } @@ -357,7 +357,7 @@ func (l *TCPListener) SetDeadline(t time.Time) error { return l.fd.SetDeadline(t) } -// File returns a copy of the underlying os.File. +// File returns a copy of the underlying [os.File]. // It is the caller's responsibility to close f when finished. // Closing l does not affect f, and closing f does not affect l. // @@ -375,7 +375,7 @@ func (l *TCPListener) File() (f *os.File, err error) { return } -// ListenTCP acts like Listen for TCP networks. +// ListenTCP acts like [Listen] for TCP networks. // // The network must be a TCP network name; see func Dial for details. // diff --git a/src/net/textproto/header.go b/src/net/textproto/header.go index a58df7aebc..689a6827b9 100644 --- a/src/net/textproto/header.go +++ b/src/net/textproto/header.go @@ -23,7 +23,7 @@ func (h MIMEHeader) Set(key, value string) { } // Get gets the first value associated with the given key. -// It is case insensitive; CanonicalMIMEHeaderKey is used +// It is case insensitive; [CanonicalMIMEHeaderKey] is used // to canonicalize the provided key. // If there are no values associated with the key, Get returns "". // To use non-canonical keys, access the map directly. @@ -39,7 +39,7 @@ func (h MIMEHeader) Get(key string) string { } // Values returns all values associated with the given key. -// It is case insensitive; CanonicalMIMEHeaderKey is +// It is case insensitive; [CanonicalMIMEHeaderKey] is // used to canonicalize the provided key. To use non-canonical // keys, access the map directly. // The returned slice is not a copy. diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go index fc2590b1cd..a603564fb8 100644 --- a/src/net/textproto/reader.go +++ b/src/net/textproto/reader.go @@ -24,10 +24,10 @@ type Reader struct { buf []byte // a re-usable buffer for readContinuedLineSlice } -// NewReader returns a new Reader reading from r. +// NewReader returns a new [Reader] reading from r. // -// To avoid denial of service attacks, the provided bufio.Reader -// should be reading from an io.LimitReader or similar Reader to bound +// To avoid denial of service attacks, the provided [bufio.Reader] +// should be reading from an [io.LimitReader] or similar Reader to bound // the size of responses. func NewReader(r *bufio.Reader) *Reader { return &Reader{R: r} @@ -40,7 +40,7 @@ func (r *Reader) ReadLine() (string, error) { return string(line), err } -// ReadLineBytes is like ReadLine but returns a []byte instead of a string. +// ReadLineBytes is like [Reader.ReadLine] but returns a []byte instead of a string. func (r *Reader) ReadLineBytes() ([]byte, error) { line, err := r.readLineSlice() if line != nil { @@ -106,7 +106,7 @@ func trim(s []byte) []byte { return s[i:n] } -// ReadContinuedLineBytes is like ReadContinuedLine but +// ReadContinuedLineBytes is like [Reader.ReadContinuedLine] but // returns a []byte instead of a string. func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { line, err := r.readContinuedLineSlice(noValidation) @@ -289,7 +289,7 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err err return } -// DotReader returns a new Reader that satisfies Reads using the +// DotReader returns a new [Reader] that satisfies Reads using the // decoded text of a dot-encoded block read from r. // The returned Reader is only valid until the next call // to a method on r. @@ -303,7 +303,7 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err err // // The decoded form returned by the Reader's Read method // rewrites the "\r\n" line endings into the simpler "\n", -// removes leading dot escapes if present, and stops with error io.EOF +// removes leading dot escapes if present, and stops with error [io.EOF] // after consuming (and discarding) the end-of-sequence line. func (r *Reader) DotReader() io.Reader { r.closeDot() @@ -420,7 +420,7 @@ func (r *Reader) closeDot() { // ReadDotBytes reads a dot-encoding and returns the decoded data. // -// See the documentation for the DotReader method for details about dot-encoding. +// See the documentation for the [Reader.DotReader] method for details about dot-encoding. func (r *Reader) ReadDotBytes() ([]byte, error) { return io.ReadAll(r.DotReader()) } @@ -428,7 +428,7 @@ func (r *Reader) ReadDotBytes() ([]byte, error) { // ReadDotLines reads a dot-encoding and returns a slice // containing the decoded lines, with the final \r\n or \n elided from each. // -// See the documentation for the DotReader method for details about dot-encoding. +// See the documentation for the [Reader.DotReader] method for details about dot-encoding. func (r *Reader) ReadDotLines() ([]string, error) { // We could use ReadDotBytes and then Split it, // but reading a line at a time avoids needing a @@ -462,7 +462,7 @@ var colon = []byte(":") // ReadMIMEHeader reads a MIME-style header from r. // The header is a sequence of possibly continued Key: Value lines // ending in a blank line. -// The returned map m maps CanonicalMIMEHeaderKey(key) to a +// The returned map m maps [CanonicalMIMEHeaderKey](key) to a // sequence of values in the same order encountered in the input. // // For example, consider this input: diff --git a/src/net/textproto/textproto.go b/src/net/textproto/textproto.go index 70038d5888..4ae3ecff74 100644 --- a/src/net/textproto/textproto.go +++ b/src/net/textproto/textproto.go @@ -7,20 +7,20 @@ // // The package provides: // -// Error, which represents a numeric error response from +// [Error], which represents a numeric error response from // a server. // -// Pipeline, to manage pipelined requests and responses +// [Pipeline], to manage pipelined requests and responses // in a client. // -// Reader, to read numeric response code lines, +// [Reader], to read numeric response code lines, // key: value headers, lines wrapped with leading spaces // on continuation lines, and whole text blocks ending // with a dot on a line by itself. // -// Writer, to write dot-encoded text blocks. +// [Writer], to write dot-encoded text blocks. // -// Conn, a convenient packaging of Reader, Writer, and Pipeline for use +// [Conn], a convenient packaging of [Reader], [Writer], and [Pipeline] for use // with a single network connection. package textproto @@ -50,8 +50,8 @@ func (p ProtocolError) Error() string { } // A Conn represents a textual network protocol connection. -// It consists of a Reader and Writer to manage I/O -// and a Pipeline to sequence concurrent requests on the connection. +// It consists of a [Reader] and [Writer] to manage I/O +// and a [Pipeline] to sequence concurrent requests on the connection. // These embedded types carry methods with them; // see the documentation of those types for details. type Conn struct { @@ -61,7 +61,7 @@ type Conn struct { conn io.ReadWriteCloser } -// NewConn returns a new Conn using conn for I/O. +// NewConn returns a new [Conn] using conn for I/O. func NewConn(conn io.ReadWriteCloser) *Conn { return &Conn{ Reader: Reader{R: bufio.NewReader(conn)}, @@ -75,8 +75,8 @@ func (c *Conn) Close() error { return c.conn.Close() } -// Dial connects to the given address on the given network using net.Dial -// and then returns a new Conn for the connection. +// Dial connects to the given address on the given network using [net.Dial] +// and then returns a new [Conn] for the connection. func Dial(network, addr string) (*Conn, error) { c, err := net.Dial(network, addr) if err != nil { diff --git a/src/net/textproto/writer.go b/src/net/textproto/writer.go index 2ece3f511b..662515fb2c 100644 --- a/src/net/textproto/writer.go +++ b/src/net/textproto/writer.go @@ -17,7 +17,7 @@ type Writer struct { dot *dotWriter } -// NewWriter returns a new Writer writing to w. +// NewWriter returns a new [Writer] writing to w. func NewWriter(w *bufio.Writer) *Writer { return &Writer{W: w} } @@ -39,7 +39,7 @@ func (w *Writer) PrintfLine(format string, args ...any) error { // when the DotWriter is closed. The caller should close the // DotWriter before the next call to a method on w. // -// See the documentation for Reader's DotReader method for details about dot-encoding. +// See the documentation for the [Reader.DotReader] method for details about dot-encoding. func (w *Writer) DotWriter() io.WriteCloser { w.closeDot() w.dot = &dotWriter{w: w} diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index 563a842cf9..ca86f31ef2 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -242,35 +242,22 @@ func TestAcceptTimeoutMustReturn(t *testing.T) { ln := newLocalListener(t, "tcp") defer ln.Close() - max := time.NewTimer(time.Second) - defer max.Stop() - ch := make(chan error) - go func() { - if err := ln.(*TCPListener).SetDeadline(noDeadline); err != nil { - t.Error(err) - } - if err := ln.(*TCPListener).SetDeadline(time.Now().Add(10 * time.Millisecond)); err != nil { - t.Error(err) - } - c, err := ln.Accept() - if err == nil { - c.Close() - } - ch <- err - }() + if err := ln.(*TCPListener).SetDeadline(noDeadline); err != nil { + t.Error(err) + } + if err := ln.(*TCPListener).SetDeadline(time.Now().Add(10 * time.Millisecond)); err != nil { + t.Error(err) + } + c, err := ln.Accept() + if err == nil { + c.Close() + } - select { - case <-max.C: - ln.Close() - <-ch // wait for tester goroutine to stop - t.Fatal("Accept didn't return in an expected time") - case err := <-ch: - if perr := parseAcceptError(err); perr != nil { - t.Error(perr) - } - if !isDeadlineExceeded(err) { - t.Fatal(err) - } + if perr := parseAcceptError(err); perr != nil { + t.Error(perr) + } + if !isDeadlineExceeded(err) { + t.Fatal(err) } } diff --git a/src/net/unixsock.go b/src/net/unixsock.go index 7e5ffa036a..821be7bf74 100644 --- a/src/net/unixsock.go +++ b/src/net/unixsock.go @@ -52,7 +52,7 @@ func (a *UnixAddr) opAddr() Addr { // // The network must be a Unix network name. // -// See func Dial for a description of the network and address +// See func [Dial] for a description of the network and address // parameters. func ResolveUnixAddr(network, address string) (*UnixAddr, error) { switch network { @@ -63,14 +63,14 @@ func ResolveUnixAddr(network, address string) (*UnixAddr, error) { } } -// UnixConn is an implementation of the Conn interface for connections +// UnixConn is an implementation of the [Conn] interface for connections // to Unix domain sockets. type UnixConn struct { conn } // SyscallConn returns a raw network connection. -// This implements the syscall.Conn interface. +// This implements the [syscall.Conn] interface. func (c *UnixConn) SyscallConn() (syscall.RawConn, error) { if !c.ok() { return nil, syscall.EINVAL @@ -102,7 +102,7 @@ func (c *UnixConn) CloseWrite() error { return nil } -// ReadFromUnix acts like ReadFrom but returns a UnixAddr. +// ReadFromUnix acts like [UnixConn.ReadFrom] but returns a [UnixAddr]. func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) { if !c.ok() { return 0, nil, syscall.EINVAL @@ -114,7 +114,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) { return n, addr, err } -// ReadFrom implements the PacketConn ReadFrom method. +// ReadFrom implements the [PacketConn] ReadFrom method. func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL @@ -147,7 +147,7 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd return } -// WriteToUnix acts like WriteTo but takes a UnixAddr. +// WriteToUnix acts like [UnixConn.WriteTo] but takes a [UnixAddr]. func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) { if !c.ok() { return 0, syscall.EINVAL @@ -159,7 +159,7 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) { return n, err } -// WriteTo implements the PacketConn WriteTo method. +// WriteTo implements the [PacketConn] WriteTo method. func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { return 0, syscall.EINVAL @@ -194,7 +194,7 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} } -// DialUnix acts like Dial for Unix networks. +// DialUnix acts like [Dial] for Unix networks. // // The network must be a Unix network name; see func Dial for details. // @@ -215,7 +215,7 @@ func DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConn, error) { } // UnixListener is a Unix domain socket listener. Clients should -// typically use variables of type Listener instead of assuming Unix +// typically use variables of type [Listener] instead of assuming Unix // domain sockets. type UnixListener struct { fd *netFD @@ -227,7 +227,7 @@ type UnixListener struct { func (ln *UnixListener) ok() bool { return ln != nil && ln.fd != nil } // SyscallConn returns a raw network connection. -// This implements the syscall.Conn interface. +// This implements the [syscall.Conn] interface. // // The returned RawConn only supports calling Control. Read and // Write return an error. @@ -251,8 +251,8 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) { return c, nil } -// Accept implements the Accept method in the Listener interface. -// Returned connections will be of type *UnixConn. +// Accept implements the Accept method in the [Listener] interface. +// Returned connections will be of type [*UnixConn]. func (l *UnixListener) Accept() (Conn, error) { if !l.ok() { return nil, syscall.EINVAL @@ -290,7 +290,7 @@ func (l *UnixListener) SetDeadline(t time.Time) error { return l.fd.SetDeadline(t) } -// File returns a copy of the underlying os.File. +// File returns a copy of the underlying [os.File]. // It is the caller's responsibility to close f when finished. // Closing l does not affect f, and closing f does not affect l. // @@ -308,7 +308,7 @@ func (l *UnixListener) File() (f *os.File, err error) { return } -// ListenUnix acts like Listen for Unix networks. +// ListenUnix acts like [Listen] for Unix networks. // // The network must be "unix" or "unixpacket". func ListenUnix(network string, laddr *UnixAddr) (*UnixListener, error) { @@ -328,7 +328,7 @@ func ListenUnix(network string, laddr *UnixAddr) (*UnixListener, error) { return ln, nil } -// ListenUnixgram acts like ListenPacket for Unix networks. +// ListenUnixgram acts like [ListenPacket] for Unix networks. // // The network must be "unixgram". func ListenUnixgram(network string, laddr *UnixAddr) (*UnixConn, error) { diff --git a/src/net/url/url.go b/src/net/url/url.go index 902310c244..f362958edd 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -175,7 +175,7 @@ func shouldEscape(c byte, mode encoding) bool { return true } -// QueryUnescape does the inverse transformation of QueryEscape, +// QueryUnescape does the inverse transformation of [QueryEscape], // converting each 3-byte encoded substring of the form "%AB" into the // hex-decoded byte 0xAB. // It returns an error if any % is not followed by two hexadecimal @@ -184,12 +184,12 @@ func QueryUnescape(s string) (string, error) { return unescape(s, encodeQueryComponent) } -// PathUnescape does the inverse transformation of PathEscape, +// PathUnescape does the inverse transformation of [PathEscape], // converting each 3-byte encoded substring of the form "%AB" into the // hex-decoded byte 0xAB. It returns an error if any % is not followed // by two hexadecimal digits. // -// PathUnescape is identical to QueryUnescape except that it does not +// PathUnescape is identical to [QueryUnescape] except that it does not // unescape '+' to ' ' (space). func PathUnescape(s string) (string, error) { return unescape(s, encodePathSegment) @@ -271,12 +271,12 @@ func unescape(s string, mode encoding) (string, error) { } // QueryEscape escapes the string so it can be safely placed -// inside a URL query. +// inside a [URL] query. func QueryEscape(s string) string { return escape(s, encodeQueryComponent) } -// PathEscape escapes the string so it can be safely placed inside a URL path segment, +// PathEscape escapes the string so it can be safely placed inside a [URL] path segment, // replacing special characters (including /) with %XX sequences as needed. func PathEscape(s string) string { return escape(s, encodePathSegment) @@ -358,7 +358,7 @@ func escape(s string, mode encoding) string { // Note that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/. // A consequence is that it is impossible to tell which slashes in the Path were // slashes in the raw URL and which were %2f. This distinction is rarely important, -// but when it is, the code should use the EscapedPath method, which preserves +// but when it is, the code should use the [URL.EscapedPath] method, which preserves // the original encoding of Path. // // The RawPath field is an optional field which is only set when the default @@ -380,13 +380,13 @@ type URL struct { RawFragment string // encoded fragment hint (see EscapedFragment method) } -// User returns a Userinfo containing the provided username +// User returns a [Userinfo] containing the provided username // and no password set. func User(username string) *Userinfo { return &Userinfo{username, "", false} } -// UserPassword returns a Userinfo containing the provided username +// UserPassword returns a [Userinfo] containing the provided username // and password. // // This functionality should only be used with legacy web sites. @@ -399,7 +399,7 @@ func UserPassword(username, password string) *Userinfo { } // The Userinfo type is an immutable encapsulation of username and -// password details for a URL. An existing Userinfo value is guaranteed +// password details for a [URL]. An existing Userinfo value is guaranteed // to have a username set (potentially empty, as allowed by RFC 2396), // and optionally a password. type Userinfo struct { @@ -464,7 +464,7 @@ func getScheme(rawURL string) (scheme, path string, err error) { return "", rawURL, nil } -// Parse parses a raw url into a URL structure. +// Parse parses a raw url into a [URL] structure. // // The url may be relative (a path, without a host) or absolute // (starting with a scheme). Trying to parse a hostname and path @@ -486,7 +486,7 @@ func Parse(rawURL string) (*URL, error) { return url, nil } -// ParseRequestURI parses a raw url into a URL structure. It assumes that +// ParseRequestURI parses a raw url into a [URL] structure. It assumes that // url was received in an HTTP request, so the url is interpreted // only as an absolute URI or an absolute path. // The string url is assumed not to have a #fragment suffix. @@ -697,7 +697,7 @@ func (u *URL) setPath(p string) error { // EscapedPath returns u.RawPath when it is a valid escaping of u.Path. // Otherwise EscapedPath ignores u.RawPath and computes an escaped // form on its own. -// The String and RequestURI methods use EscapedPath to construct +// The [URL.String] and [URL.RequestURI] methods use EscapedPath to construct // their results. // In general, code should call EscapedPath instead of // reading u.RawPath directly. @@ -761,7 +761,7 @@ func (u *URL) setFragment(f string) error { // EscapedFragment returns u.RawFragment when it is a valid escaping of u.Fragment. // Otherwise EscapedFragment ignores u.RawFragment and computes an escaped // form on its own. -// The String method uses EscapedFragment to construct its result. +// The [URL.String] method uses EscapedFragment to construct its result. // In general, code should call EscapedFragment instead of // reading u.RawFragment directly. func (u *URL) EscapedFragment() string { @@ -791,7 +791,7 @@ func validOptionalPort(port string) bool { return true } -// String reassembles the URL into a valid URL string. +// String reassembles the [URL] into a valid URL string. // The general form of the result is one of: // // scheme:opaque?query#fragment @@ -865,7 +865,7 @@ func (u *URL) String() string { return buf.String() } -// Redacted is like String but replaces any password with "xxxxx". +// Redacted is like [URL.String] but replaces any password with "xxxxx". // Only the password in u.User is redacted. func (u *URL) Redacted() string { if u == nil { @@ -1060,15 +1060,15 @@ func resolvePath(base, ref string) string { return r } -// IsAbs reports whether the URL is absolute. +// IsAbs reports whether the [URL] is absolute. // Absolute means that it has a non-empty scheme. func (u *URL) IsAbs() bool { return u.Scheme != "" } -// Parse parses a URL in the context of the receiver. The provided URL +// Parse parses a [URL] in the context of the receiver. The provided URL // may be relative or absolute. Parse returns nil, err on parse -// failure, otherwise its return value is the same as ResolveReference. +// failure, otherwise its return value is the same as [URL.ResolveReference]. func (u *URL) Parse(ref string) (*URL, error) { refURL, err := Parse(ref) if err != nil { @@ -1080,7 +1080,7 @@ func (u *URL) Parse(ref string) (*URL, error) { // ResolveReference resolves a URI reference to an absolute URI from // an absolute base URI u, per RFC 3986 Section 5.2. The URI reference // may be relative or absolute. ResolveReference always returns a new -// URL instance, even if the returned URL is identical to either the +// [URL] instance, even if the returned URL is identical to either the // base or reference. If ref is an absolute URL, then ResolveReference // ignores base and returns a copy of ref. func (u *URL) ResolveReference(ref *URL) *URL { @@ -1117,7 +1117,7 @@ func (u *URL) ResolveReference(ref *URL) *URL { // Query parses RawQuery and returns the corresponding values. // It silently discards malformed value pairs. -// To check errors use ParseQuery. +// To check errors use [ParseQuery]. func (u *URL) Query() Values { v, _ := ParseQuery(u.RawQuery) return v @@ -1194,7 +1194,7 @@ func (u *URL) UnmarshalBinary(text []byte) error { return nil } -// JoinPath returns a new URL with the provided path elements joined to +// JoinPath returns a new [URL] with the provided path elements joined to // any existing path and the resulting path cleaned of any ./ or ../ elements. // Any sequences of multiple / characters will be reduced to a single /. func (u *URL) JoinPath(elem ...string) *URL { @@ -1260,7 +1260,7 @@ func stringContainsCTLByte(s string) bool { return false } -// JoinPath returns a URL string with the provided path elements joined to +// JoinPath returns a [URL] string with the provided path elements joined to // the existing path of base and the resulting path cleaned of any ./ or ../ elements. func JoinPath(base string, elem ...string) (result string, err error) { url, err := Parse(base) diff --git a/src/os/example_test.go b/src/os/example_test.go index e9657ed1fc..656232c472 100644 --- a/src/os/example_test.go +++ b/src/os/example_test.go @@ -263,3 +263,57 @@ func ExampleMkdirAll() { log.Fatal(err) } } + +func ExampleReadlink() { + // First, we create a relative symlink to a file. + d, err := os.MkdirTemp("", "") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(d) + targetPath := filepath.Join(d, "hello.txt") + if err := os.WriteFile(targetPath, []byte("Hello, Gophers!"), 0644); err != nil { + log.Fatal(err) + } + linkPath := filepath.Join(d, "hello.link") + if err := os.Symlink("hello.txt", filepath.Join(d, "hello.link")); err != nil { + if errors.Is(err, errors.ErrUnsupported) { + // Allow the example to run on platforms that do not support symbolic links. + fmt.Printf("%s links to %s\n", filepath.Base(linkPath), "hello.txt") + return + } + log.Fatal(err) + } + + // Readlink returns the relative path as passed to os.Symlink. + dst, err := os.Readlink(linkPath) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s links to %s\n", filepath.Base(linkPath), dst) + + var dstAbs string + if filepath.IsAbs(dst) { + dstAbs = dst + } else { + // Symlink targets are relative to the directory containing the link. + dstAbs = filepath.Join(filepath.Dir(linkPath), dst) + } + + // Check that the target is correct by comparing it with os.Stat + // on the original target path. + dstInfo, err := os.Stat(dstAbs) + if err != nil { + log.Fatal(err) + } + targetInfo, err := os.Stat(targetPath) + if err != nil { + log.Fatal(err) + } + if !os.SameFile(dstInfo, targetInfo) { + log.Fatalf("link destination (%s) is not the same file as %s", dstAbs, targetPath) + } + + // Output: + // hello.link links to hello.txt +} diff --git a/src/os/exec_unix.go b/src/os/exec_unix.go index f9063b4db4..36b320df18 100644 --- a/src/os/exec_unix.go +++ b/src/os/exec_unix.go @@ -48,9 +48,7 @@ func (p *Process) wait() (ps *ProcessState, err error) { if e != nil { return nil, NewSyscallError("wait", e) } - if pid1 != 0 { - p.setDone() - } + p.setDone() ps = &ProcessState{ pid: pid1, status: status, diff --git a/src/os/file.go b/src/os/file.go index 37a30ccf04..090ffba4dc 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -392,6 +392,15 @@ func Rename(oldpath, newpath string) error { return rename(oldpath, newpath) } +// Readlink returns the destination of the named symbolic link. +// If there is an error, it will be of type *PathError. +// +// If the link destination is relative, Readlink returns the relative path +// without resolving it to an absolute one. +func Readlink(name string) (string, error) { + return readlink(name) +} + // Many functions in package syscall return a count of -1 instead of 0. // Using fixCount(call()) instead of call() corrects the count. func fixCount(n int, err error) (int, error) { @@ -690,7 +699,15 @@ func (dir dirFS) ReadFile(name string) ([]byte, error) { if err != nil { return nil, &PathError{Op: "readfile", Path: name, Err: err} } - return ReadFile(fullname) + b, err := ReadFile(fullname) + if err != nil { + if e, ok := err.(*PathError); ok { + // See comment in dirFS.Open. + e.Path = name + } + return nil, err + } + return b, nil } // ReadDir reads the named directory, returning all its directory entries sorted @@ -700,7 +717,15 @@ func (dir dirFS) ReadDir(name string) ([]DirEntry, error) { if err != nil { return nil, &PathError{Op: "readdir", Path: name, Err: err} } - return ReadDir(fullname) + entries, err := ReadDir(fullname) + if err != nil { + if e, ok := err.(*PathError); ok { + // See comment in dirFS.Open. + e.Path = name + } + return nil, err + } + return entries, nil } func (dir dirFS) Stat(name string) (fs.FileInfo, error) { diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go index 03cdb5be4a..c0ee6b33f9 100644 --- a/src/os/file_plan9.go +++ b/src/os/file_plan9.go @@ -505,9 +505,7 @@ func Symlink(oldname, newname string) error { return &LinkError{"symlink", oldname, newname, syscall.EPLAN9} } -// Readlink returns the destination of the named symbolic link. -// If there is an error, it will be of type *PathError. -func Readlink(name string) (string, error) { +func readlink(name string) (string, error) { return "", &PathError{Op: "readlink", Path: name, Err: syscall.EPLAN9} } @@ -544,7 +542,6 @@ func tempDir() string { dir = "/tmp" } return dir - } // Chdir changes the current working directory to the file, diff --git a/src/os/file_unix.go b/src/os/file_unix.go index 533a48404b..a527b23e4f 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -426,9 +426,7 @@ func Symlink(oldname, newname string) error { return nil } -// Readlink returns the destination of the named symbolic link. -// If there is an error, it will be of type *PathError. -func Readlink(name string) (string, error) { +func readlink(name string) (string, error) { for len := 128; ; len *= 2 { b := make([]byte, len) var ( diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 63d53a1df8..8b04ed6e47 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -406,7 +406,7 @@ func normaliseLinkPath(path string) (string, error) { return "", errors.New("GetFinalPathNameByHandle returned unexpected path: " + s) } -func readlink(path string) (string, error) { +func readReparseLink(path string) (string, error) { h, err := openSymlink(path) if err != nil { return "", err @@ -438,10 +438,8 @@ func readlink(path string) (string, error) { } } -// Readlink returns the destination of the named symbolic link. -// If there is an error, it will be of type *PathError. -func Readlink(name string) (string, error) { - s, err := readlink(fixLongPath(name)) +func readlink(name string) (string, error) { + s, err := readReparseLink(fixLongPath(name)) if err != nil { return "", &PathError{Op: "readlink", Path: name, Err: err} } diff --git a/src/os/os_test.go b/src/os/os_test.go index ae12b9ce1b..2f5b117bd9 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -1620,8 +1620,17 @@ func TestFileChdir(t *testing.T) { if err != nil { t.Fatalf("Getwd: %s", err) } - if !equal(wdNew, wd) { - t.Fatalf("fd.Chdir failed, got %s, want %s", wdNew, wd) + + wdInfo, err := fd.Stat() + if err != nil { + t.Fatal(err) + } + newInfo, err := Stat(wdNew) + if err != nil { + t.Fatal(err) + } + if !SameFile(wdInfo, newInfo) { + t.Fatalf("fd.Chdir failed: got %s, want %s", wdNew, wd) } } @@ -3321,3 +3330,27 @@ func TestPipeCloseRace(t *testing.T) { t.Errorf("got nils %d errs %d, want 2 2", nils, errs) } } + +func TestRandomLen(t *testing.T) { + for range 5 { + dir, err := MkdirTemp(t.TempDir(), "*") + if err != nil { + t.Fatal(err) + } + base := filepath.Base(dir) + if len(base) > 10 { + t.Errorf("MkdirTemp returned len %d: %s", len(base), base) + } + } + for range 5 { + f, err := CreateTemp(t.TempDir(), "*") + if err != nil { + t.Fatal(err) + } + base := filepath.Base(f.Name()) + f.Close() + if len(base) > 10 { + t.Errorf("CreateTemp returned len %d: %s", len(base), base) + } + } +} diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index f8edaeb876..7436b9a969 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -581,7 +581,7 @@ func TestStatLxSymLink(t *testing.T) { } if m := fi.Mode(); m&fs.ModeSymlink != 0 { // This can happen depending on newer WSL versions when running as admin or in developer mode. - t.Skip("skipping: WSL created reparse tag IO_REPARSE_TAG_SYMLINK instead of a IO_REPARSE_TAG_LX_SYMLINK") + t.Skip("skipping: WSL created reparse tag IO_REPARSE_TAG_SYMLINK instead of an IO_REPARSE_TAG_LX_SYMLINK") } // Stat'ing a IO_REPARSE_TAG_LX_SYMLINK from outside WSL always return ERROR_CANT_ACCESS_FILE. // We check this condition to validate that os.Stat has tried to follow the link. diff --git a/src/os/tempfile.go b/src/os/tempfile.go index 315f65ad9c..66c65e6c78 100644 --- a/src/os/tempfile.go +++ b/src/os/tempfile.go @@ -8,16 +8,18 @@ import ( "errors" "internal/bytealg" "internal/itoa" + _ "unsafe" // for go:linkname ) -// fastrand provided by runtime. +// random number source provided by runtime. // We generate random temporary file names so that there's a good // chance the file doesn't exist yet - keeps the number of tries in // TempFile to a minimum. -func fastrand() uint32 +//go:linkname runtime_rand runtime.rand +func runtime_rand() uint64 func nextRandom() string { - return itoa.Uitoa(uint(fastrand())) + return itoa.Uitoa(uint(uint32(runtime_rand()))) } // CreateTemp creates a new temporary file in the directory dir, diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go index b5cc4b8cf3..12f0bfa7d3 100644 --- a/src/path/filepath/match.go +++ b/src/path/filepath/match.go @@ -35,7 +35,7 @@ var ErrBadPattern = errors.New("syntax error in pattern") // lo '-' hi matches character c for lo <= c <= hi // // Match requires pattern to match all of name, not just a substring. -// The only possible returned error is ErrBadPattern, when pattern +// The only possible returned error is [ErrBadPattern], when pattern // is malformed. // // On Windows, escaping is disabled. Instead, '\\' is treated as @@ -233,11 +233,11 @@ func getEsc(chunk string) (r rune, nchunk string, err error) { // Glob returns the names of all files matching pattern or nil // if there is no matching file. The syntax of patterns is the same -// as in Match. The pattern may describe hierarchical names such as -// /usr/*/bin/ed (assuming the Separator is '/'). +// as in [Match]. The pattern may describe hierarchical names such as +// /usr/*/bin/ed (assuming the [Separator] is '/'). // // Glob ignores file system errors such as I/O errors reading directories. -// The only possible returned error is ErrBadPattern, when pattern +// The only possible returned error is [ErrBadPattern], when pattern // is malformed. func Glob(pattern string) (matches []string, err error) { return globWithLimit(pattern, 0) diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go index 3d693f840a..2af0f5b04c 100644 --- a/src/path/filepath/path.go +++ b/src/path/filepath/path.go @@ -73,7 +73,7 @@ const ( // by purely lexical processing. It applies the following rules // iteratively until no further processing can be done: // -// 1. Replace multiple Separator elements with a single one. +// 1. Replace multiple [Separator] elements with a single one. // 2. Eliminate each . path name element (the current directory). // 3. Eliminate each inner .. path name element (the parent directory) // along with the non-.. element that precedes it. @@ -231,7 +231,7 @@ func FromSlash(path string) string { return strings.ReplaceAll(path, "/", string(Separator)) } -// SplitList splits a list of paths joined by the OS-specific ListSeparator, +// SplitList splits a list of paths joined by the OS-specific [ListSeparator], // usually found in PATH or GOPATH environment variables. // Unlike strings.Split, SplitList returns an empty slice when passed an empty // string. @@ -239,7 +239,7 @@ func SplitList(path string) []string { return splitList(path) } -// Split splits path immediately following the final Separator, +// Split splits path immediately following the final [Separator], // separating it into a directory and file name component. // If there is no Separator in path, Split returns an empty dir // and file set to path. @@ -254,7 +254,7 @@ func Split(path string) (dir, file string) { } // Join joins any number of path elements into a single path, -// separating them with an OS specific Separator. Empty elements +// separating them with an OS specific [Separator]. Empty elements // are ignored. The result is Cleaned. However, if the argument // list is empty or all its elements are empty, Join returns // an empty string. @@ -281,7 +281,7 @@ func Ext(path string) string { // links. // If path is relative the result will be relative to the current directory, // unless one of the components is an absolute symbolic link. -// EvalSymlinks calls Clean on the result. +// EvalSymlinks calls [Clean] on the result. func EvalSymlinks(path string) (string, error) { return evalSymlinks(path) } @@ -290,7 +290,7 @@ func EvalSymlinks(path string) (string, error) { // If the path is not absolute it will be joined with the current // working directory to turn it into an absolute path. The absolute // path name for a given file is not guaranteed to be unique. -// Abs calls Clean on the result. +// Abs calls [Clean] on the result. func Abs(path string) (string, error) { return abs(path) } @@ -308,12 +308,12 @@ func unixAbs(path string) (string, error) { // Rel returns a relative path that is lexically equivalent to targpath when // joined to basepath with an intervening separator. That is, -// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself. +// [Join](basepath, Rel(basepath, targpath)) is equivalent to targpath itself. // On success, the returned path will always be relative to basepath, // even if basepath and targpath share no elements. // An error is returned if targpath can't be made relative to basepath or if // knowing the current working directory would be necessary to compute it. -// Rel calls Clean on the result. +// Rel calls [Clean] on the result. func Rel(basepath, targpath string) (string, error) { baseVol := VolumeName(basepath) targVol := VolumeName(targpath) @@ -386,17 +386,17 @@ func Rel(basepath, targpath string) (string, error) { return targ[t0:], nil } -// SkipDir is used as a return value from WalkFuncs to indicate that +// SkipDir is used as a return value from [WalkFunc] to indicate that // the directory named in the call is to be skipped. It is not returned // as an error by any function. var SkipDir error = fs.SkipDir -// SkipAll is used as a return value from WalkFuncs to indicate that +// SkipAll is used as a return value from [WalkFunc] to indicate that // all remaining files and directories are to be skipped. It is not returned // as an error by any function. var SkipAll error = fs.SkipAll -// WalkFunc is the type of the function called by Walk to visit each +// WalkFunc is the type of the function called by [Walk] to visit each // file or directory. // // The path argument contains the argument to Walk as a prefix. @@ -412,9 +412,9 @@ var SkipAll error = fs.SkipAll // The info argument is the fs.FileInfo for the named path. // // The error result returned by the function controls how Walk continues. -// If the function returns the special value SkipDir, Walk skips the +// If the function returns the special value [SkipDir], Walk skips the // current directory (path if info.IsDir() is true, otherwise path's -// parent directory). If the function returns the special value SkipAll, +// parent directory). If the function returns the special value [SkipAll], // Walk skips all remaining files and directories. Otherwise, if the function // returns a non-nil error, Walk stops entirely and returns that error. // @@ -425,14 +425,14 @@ var SkipAll error = fs.SkipAll // // Walk calls the function with a non-nil err argument in two cases. // -// First, if an os.Lstat on the root directory or any directory or file +// First, if an [os.Lstat] on the root directory or any directory or file // in the tree fails, Walk calls the function with path set to that // directory or file's path, info set to nil, and err set to the error // from os.Lstat. // // Second, if a directory's Readdirnames method fails, Walk calls the // function with path set to the directory's path, info, set to an -// fs.FileInfo describing the directory, and err set to the error from +// [fs.FileInfo] describing the directory, and err set to the error from // Readdirnames. type WalkFunc func(path string, info fs.FileInfo, err error) error @@ -514,7 +514,7 @@ func walk(path string, info fs.FileInfo, walkFn WalkFunc) error { // directory in the tree, including root. // // All errors that arise visiting files and directories are filtered by fn: -// see the fs.WalkDirFunc documentation for details. +// see the [fs.WalkDirFunc] documentation for details. // // The files are walked in lexical order, which makes the output deterministic // but requires WalkDir to read an entire directory into memory before proceeding @@ -542,7 +542,7 @@ func WalkDir(root string, fn fs.WalkDirFunc) error { // directory in the tree, including root. // // All errors that arise visiting files and directories are filtered by fn: -// see the WalkFunc documentation for details. +// see the [WalkFunc] documentation for details. // // The files are walked in lexical order, which makes the output deterministic // but requires Walk to read an entire directory into memory before proceeding @@ -550,7 +550,7 @@ func WalkDir(root string, fn fs.WalkDirFunc) error { // // Walk does not follow symbolic links. // -// Walk is less efficient than WalkDir, introduced in Go 1.16, +// Walk is less efficient than [WalkDir], introduced in Go 1.16, // which avoids calling os.Lstat on every visited file or directory. func Walk(root string, fn WalkFunc) error { info, err := os.Lstat(root) @@ -611,7 +611,7 @@ func Base(path string) string { } // Dir returns all but the last element of path, typically the path's directory. -// After dropping the final element, Dir calls Clean on the path and trailing +// After dropping the final element, Dir calls [Clean] on the path and trailing // slashes are removed. // If the path is empty, Dir returns ".". // If the path consists entirely of separators, Dir returns a single separator. diff --git a/src/path/match.go b/src/path/match.go index 673bbc7ff6..d8b6809568 100644 --- a/src/path/match.go +++ b/src/path/match.go @@ -32,7 +32,7 @@ var ErrBadPattern = errors.New("syntax error in pattern") // lo '-' hi matches character c for lo <= c <= hi // // Match requires pattern to match all of name, not just a substring. -// The only possible returned error is ErrBadPattern, when pattern +// The only possible returned error is [ErrBadPattern], when pattern // is malformed. func Match(pattern, name string) (matched bool, err error) { Pattern: diff --git a/src/path/path.go b/src/path/path.go index 50065ac731..5149a92c4f 100644 --- a/src/path/path.go +++ b/src/path/path.go @@ -214,7 +214,7 @@ func IsAbs(path string) bool { } // Dir returns all but the last element of path, typically the path's directory. -// After dropping the final element using Split, the path is Cleaned and trailing +// After dropping the final element using [Split], the path is Cleaned and trailing // slashes are removed. // If the path is empty, Dir returns ".". // If the path consists entirely of slashes followed by non-slash bytes, Dir diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index a28f2a4bed..e77537c9a5 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -4755,7 +4755,7 @@ func TestConvertSlice2Array(t *testing.T) { // Converting a slice to non-empty array needs to return // a non-addressable copy of the original memory. if v.CanAddr() { - t.Fatalf("convert slice to non-empty array returns a addressable copy array") + t.Fatalf("convert slice to non-empty array returns an addressable copy array") } for i := range s { ov.Index(i).Set(ValueOf(i + 1)) diff --git a/src/reflect/asm_loong64.s b/src/reflect/asm_loong64.s index 341a6d55c1..520f0afdd5 100644 --- a/src/reflect/asm_loong64.s +++ b/src/reflect/asm_loong64.s @@ -7,34 +7,83 @@ #define REGCTXT R29 +// 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) + 8 (bool + 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 + ADDV $LOCAL_REGARGS, R3, R25 // spillArgs using R25 + JAL runtime·spillArgs(SB) + MOVV REGCTXT, 32(R3) // save REGCTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS + +#ifdef GOEXPERIMENT_regabiargs + MOVV REGCTXT, R4 + MOVV R25, R5 +#else MOVV REGCTXT, 8(R3) - MOVV $argframe+0(FP), R19 - MOVV R19, 16(R3) - MOVB R0, 40(R3) - ADDV $40, R3, R19 - MOVV R19, 24(R3) - MOVV R0, 32(R3) + MOVV R25, 16(R3) +#endif + JAL ·moveMakeFuncArgPtrs(SB) + MOVV 32(R3), REGCTXT // restore REGCTXT + + MOVV REGCTXT, 8(R3) + MOVV $argframe+0(FP), R20 + MOVV R20, 16(R3) + MOVV R0, LOCAL_RETVALID(R3) + ADDV $LOCAL_RETVALID, R3, R20 + MOVV R20, 24(R3) + ADDV $LOCAL_REGARGS, R3, R20 + MOVV R20, 32(R3) JAL ·callReflect(SB) + ADDV $LOCAL_REGARGS, R3, R25 //unspillArgs using R25 + JAL 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 + ADDV $LOCAL_REGARGS, R3, R25 // spillArgs using R25 + JAL runtime·spillArgs(SB) + MOVV REGCTXT, 32(R3) // save REGCTXT > args of moveMakeFuncArgPtrs < LOCAL_REGARGS +#ifdef GOEXPERIMENT_regabiargs + MOVV REGCTXT, R4 + MOVV R25, R5 +#else MOVV REGCTXT, 8(R3) - MOVV $argframe+0(FP), R19 - MOVV R19, 16(R3) - MOVB R0, 40(R3) - ADDV $40, R3, R19 - MOVV R19, 24(R3) - MOVV R0, 32(R3) + MOVV R25, 16(R3) +#endif + JAL ·moveMakeFuncArgPtrs(SB) + MOVV 32(R3), REGCTXT // restore REGCTXT + MOVV REGCTXT, 8(R3) + MOVV $argframe+0(FP), R20 + MOVV R20, 16(R3) + MOVB R0, LOCAL_RETVALID(R3) + ADDV $LOCAL_RETVALID, R3, R20 + MOVV R20, 24(R3) + ADDV $LOCAL_REGARGS, R3, R20 + MOVV R20, 32(R3) // frame size to 32+SP as callreflect args) JAL ·callMethod(SB) + ADDV $LOCAL_REGARGS, R3, R25 // unspillArgs using R25 + JAL runtime·unspillArgs(SB) RET diff --git a/src/reflect/type.go b/src/reflect/type.go index a35898547a..89c5015530 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -1914,7 +1914,7 @@ func needKeyUpdate(t *abi.Type) bool { case Float32, Float64, Complex64, Complex128, Interface, String: // Float keys can be updated from +0 to -0. // String keys can be updated to use a smaller backing store. - // Interfaces might have floats of strings in them. + // Interfaces might have floats or strings in them. return true case Array: tt := (*arrayType)(unsafe.Pointer(t)) diff --git a/src/reflect/value.go b/src/reflect/value.go index 2bd41f37fd..06f22f7428 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1598,24 +1598,23 @@ func (v Value) IsZero() bool { case Complex64, Complex128: return v.Complex() == 0 case Array: - array := (*abi.ArrayType)(unsafe.Pointer(v.typ())) - // Avoid performance degradation of small benchmarks. + if v.flag&flagIndir == 0 { + return v.ptr == nil + } + typ := (*abi.ArrayType)(unsafe.Pointer(v.typ())) // If the type is comparable, then compare directly with zero. - if array.Equal != nil && array.Size() <= maxZero { - if v.flag&flagIndir == 0 { - return v.ptr == nil - } + if typ.Equal != nil && typ.Size() <= abi.ZeroValSize { // v.ptr doesn't escape, as Equal functions are compiler generated // and never escape. The escape analysis doesn't know, as it is a // function pointer call. - return array.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0])) + return typ.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0])) } - if array.TFlag&abi.TFlagRegularMemory != 0 { + if typ.TFlag&abi.TFlagRegularMemory != 0 { // For some types where the zero value is a value where all bits of this type are 0 // optimize it. - return isZero(unsafe.Slice(((*byte)(v.ptr)), array.Size())) + return isZero(unsafe.Slice(((*byte)(v.ptr)), typ.Size())) } - n := int(array.Len) + n := int(typ.Len) for i := 0; i < n; i++ { if !v.Index(i).IsZero() { return false @@ -1632,7 +1631,7 @@ func (v Value) IsZero() bool { } typ := (*abi.StructType)(unsafe.Pointer(v.typ())) // If the type is comparable, then compare directly with zero. - if typ.Equal != nil && typ.Size() <= maxZero { + if typ.Equal != nil && typ.Size() <= abi.ZeroValSize { // See noescape justification above. return typ.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0])) } @@ -1663,7 +1662,7 @@ func isZero(b []byte) bool { return true } const n = 32 - // Align memory addresses to 8 bytes + // Align memory addresses to 8 bytes. for uintptr(unsafe.Pointer(&b[0]))%8 != 0 { if b[0] != 0 { return false @@ -1690,7 +1689,14 @@ func isZero(b []byte) bool { w = w[1:] } for len(w) >= n { - if w[0] != 0 || w[1] != 0 || w[2] != 0 || w[3] != 0 || w[4] != 0 || w[5] != 0 || w[6] != 0 || w[7] != 0 || w[8] != 0 || w[9] != 0 || w[10] != 0 || w[11] != 0 || w[12] != 0 || w[13] != 0 || w[14] != 0 || w[15] != 0 || w[16] != 0 || w[17] != 0 || w[18] != 0 || w[19] != 0 || w[20] != 0 || w[21] != 0 || w[22] != 0 || w[23] != 0 || w[24] != 0 || w[25] != 0 || w[26] != 0 || w[27] != 0 || w[28] != 0 || w[29] != 0 || w[30] != 0 || w[31] != 0 { + if w[0] != 0 || w[1] != 0 || w[2] != 0 || w[3] != 0 || + w[4] != 0 || w[5] != 0 || w[6] != 0 || w[7] != 0 || + w[8] != 0 || w[9] != 0 || w[10] != 0 || w[11] != 0 || + w[12] != 0 || w[13] != 0 || w[14] != 0 || w[15] != 0 || + w[16] != 0 || w[17] != 0 || w[18] != 0 || w[19] != 0 || + w[20] != 0 || w[21] != 0 || w[22] != 0 || w[23] != 0 || + w[24] != 0 || w[25] != 0 || w[26] != 0 || w[27] != 0 || + w[28] != 0 || w[29] != 0 || w[30] != 0 || w[31] != 0 { return false } w = w[n:] @@ -3271,7 +3277,7 @@ func Zero(typ Type) Value { fl := flag(t.Kind()) if t.IfaceIndir() { var p unsafe.Pointer - if t.Size() <= maxZero { + if t.Size() <= abi.ZeroValSize { p = unsafe.Pointer(&zeroVal[0]) } else { p = unsafe_New(t) @@ -3281,11 +3287,8 @@ func Zero(typ Type) Value { return Value{t, nil, fl} } -// must match declarations in runtime/map.go. -const maxZero = 1024 - //go:linkname zeroVal runtime.zeroVal -var zeroVal [maxZero]byte +var zeroVal [abi.ZeroValSize]byte // New returns a Value representing a pointer to a new zero value // for the specified type. That is, the returned Value's Type is PointerTo(typ). diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 336058d159..eaf9c91490 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -66,7 +66,7 @@ func f32hash(p unsafe.Pointer, h uintptr) uintptr { case f == 0: return c1 * (c0 ^ h) // +0, -0 case f != f: - return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN + return c1 * (c0 ^ h ^ uintptr(rand())) // any kind of NaN default: return memhash(p, h, 4) } @@ -78,7 +78,7 @@ func f64hash(p unsafe.Pointer, h uintptr) uintptr { case f == 0: return c1 * (c0 ^ h) // +0, -0 case f != f: - return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN + return c1 * (c0 ^ h ^ uintptr(rand())) // any kind of NaN default: return memhash(p, h, 8) } @@ -390,17 +390,18 @@ func alginit() { initAlgAES() return } - getRandomData((*[len(hashkey) * goarch.PtrSize]byte)(unsafe.Pointer(&hashkey))[:]) - hashkey[0] |= 1 // make sure these numbers are odd - hashkey[1] |= 1 - hashkey[2] |= 1 - hashkey[3] |= 1 + for i := range hashkey { + hashkey[i] = uintptr(rand()) | 1 // make sure these numbers are odd + } } func initAlgAES() { useAeshash = true // Initialize with random data so hash collisions will be hard to engineer. - getRandomData(aeskeysched[:]) + key := (*[hashRandomBytes / 8]uint64)(unsafe.Pointer(&aeskeysched)) + for i := range key { + key[i] = bootstrapRand() + } } // Note: These routines perform the read with a native endianness. diff --git a/src/runtime/asan0.go b/src/runtime/asan0.go index 0948786200..bcfd96f1ab 100644 --- a/src/runtime/asan0.go +++ b/src/runtime/asan0.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Go Authors. All rights reserved. +// 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. diff --git a/src/runtime/asan_amd64.s b/src/runtime/asan_amd64.s index ac09ec1105..195faf4e6d 100644 --- a/src/runtime/asan_amd64.s +++ b/src/runtime/asan_amd64.s @@ -28,7 +28,7 @@ // func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) TEXT runtime·doasanread(SB), NOSPLIT, $0-32 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 MOVQ sp+16(FP), RARG2 MOVQ pc+24(FP), RARG3 // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc); @@ -38,7 +38,7 @@ TEXT runtime·doasanread(SB), NOSPLIT, $0-32 // func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 MOVQ sp+16(FP), RARG2 MOVQ pc+24(FP), RARG3 // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc); @@ -48,7 +48,7 @@ TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 // func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr) TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 // void __asan_unpoison_go(void *addr, uintptr_t sz); MOVQ $__asan_unpoison_go(SB), AX JMP asancall<>(SB) @@ -56,7 +56,7 @@ TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 // func runtime·asanpoison(addr unsafe.Pointer, sz uintptr) TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 // void __asan_poison_go(void *addr, uintptr_t sz); MOVQ $__asan_poison_go(SB), AX JMP asancall<>(SB) @@ -64,7 +64,7 @@ TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 // func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ n+8(FP), RARG1 // void __asan_register_globals_go(void *addr, uintptr_t n); MOVQ $__asan_register_globals_go(SB), AX JMP asancall<>(SB) diff --git a/src/runtime/asan_arm64.s b/src/runtime/asan_arm64.s index 697c98206e..dfa3f81bf2 100644 --- a/src/runtime/asan_arm64.s +++ b/src/runtime/asan_arm64.s @@ -17,7 +17,7 @@ // func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) TEXT runtime·doasanread(SB), NOSPLIT, $0-32 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 MOVD sp+16(FP), RARG2 MOVD pc+24(FP), RARG3 // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc); @@ -27,7 +27,7 @@ TEXT runtime·doasanread(SB), NOSPLIT, $0-32 // func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 MOVD sp+16(FP), RARG2 MOVD pc+24(FP), RARG3 // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc); @@ -37,7 +37,7 @@ TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 // func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr) TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 // void __asan_unpoison_go(void *addr, uintptr_t sz); MOVD $__asan_unpoison_go(SB), FARG JMP asancall<>(SB) @@ -45,7 +45,7 @@ TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 // func runtime·asanpoison(addr unsafe.Pointer, sz uintptr) TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 // void __asan_poison_go(void *addr, uintptr_t sz); MOVD $__asan_poison_go(SB), FARG JMP asancall<>(SB) @@ -53,7 +53,7 @@ TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 // func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD n+8(FP), RARG1 // void __asan_register_globals_go(void *addr, uintptr_t n); MOVD $__asan_register_globals_go(SB), FARG JMP asancall<>(SB) diff --git a/src/runtime/asan_loong64.s b/src/runtime/asan_loong64.s index 70386fcb6e..0034a31687 100644 --- a/src/runtime/asan_loong64.s +++ b/src/runtime/asan_loong64.s @@ -17,7 +17,7 @@ // func runtime·doasanread(addr unsafe.Pointer, sz, sp, pc uintptr) TEXT runtime·doasanread(SB), NOSPLIT, $0-32 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 MOVV sp+16(FP), RARG2 MOVV pc+24(FP), RARG3 // void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc); @@ -27,7 +27,7 @@ TEXT runtime·doasanread(SB), NOSPLIT, $0-32 // func runtime·doasanwrite(addr unsafe.Pointer, sz, sp, pc uintptr) TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 MOVV sp+16(FP), RARG2 MOVV pc+24(FP), RARG3 // void __asan_write_go(void *addr, uintptr_t sz, void *sp, void *pc); @@ -37,7 +37,7 @@ TEXT runtime·doasanwrite(SB), NOSPLIT, $0-32 // func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr) TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 // void __asan_unpoison_go(void *addr, uintptr_t sz); MOVV $__asan_unpoison_go(SB), FARG JMP asancall<>(SB) @@ -45,7 +45,7 @@ TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16 // func runtime·asanpoison(addr unsafe.Pointer, sz uintptr) TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 // void __asan_poison_go(void *addr, uintptr_t sz); MOVV $__asan_poison_go(SB), FARG JMP asancall<>(SB) @@ -53,7 +53,7 @@ TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 // func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV n+8(FP), RARG1 // void __asan_register_globals_go(void *addr, uintptr_t n); MOVV $__asan_register_globals_go(SB), FARG JMP asancall<>(SB) diff --git a/src/runtime/asm.s b/src/runtime/asm.s index 45ba467806..24cd0c95db 100644 --- a/src/runtime/asm.s +++ b/src/runtime/asm.s @@ -20,6 +20,7 @@ TEXT ·mapinitnoop(SB),NOSPLIT,$0-0 #ifndef GOARCH_ppc64 #ifndef GOARCH_ppc64le #ifndef GOARCH_riscv64 +#ifndef GOARCH_wasm // stub to appease shared build mode. TEXT ·switchToCrashStack0(SB),NOSPLIT,$0-0 UNDEF @@ -30,3 +31,4 @@ TEXT ·switchToCrashStack0(SB),NOSPLIT,$0-0 #endif #endif #endif +#endif diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index e3206a1d27..31a0584fb5 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -39,10 +39,10 @@ TEXT _rt0_arm_lib(SB),NOSPLIT,$104 MOVW g, 32(R13) MOVW R11, 36(R13) - // Skip floating point registers on GOARM < 6. - MOVB runtime·goarm(SB), R11 - CMP $6, R11 - BLT skipfpsave + // Skip floating point registers on goarmsoftfp != 0. + MOVB runtime·goarmsoftfp(SB), R11 + CMP $0, R11 + BNE skipfpsave MOVD F8, (40+8*0)(R13) MOVD F9, (40+8*1)(R13) MOVD F10, (40+8*2)(R13) @@ -77,9 +77,9 @@ nocgo: BL runtime·newosproc0(SB) rr: // Restore callee-save registers and return. - MOVB runtime·goarm(SB), R11 - CMP $6, R11 - BLT skipfprest + MOVB runtime·goarmsoftfp(SB), R11 + CMP $0, R11 + BNE skipfprest MOVD (40+8*0)(R13), F8 MOVD (40+8*1)(R13), F9 MOVD (40+8*2)(R13), F10 @@ -197,10 +197,10 @@ TEXT runtime·breakpoint(SB),NOSPLIT,$0-0 RET TEXT runtime·asminit(SB),NOSPLIT,$0-0 - // disable runfast (flush-to-zero) mode of vfp if runtime.goarm > 5 - MOVB runtime·goarm(SB), R11 - CMP $5, R11 - BLE 4(PC) + // disable runfast (flush-to-zero) mode of vfp if runtime.goarmsoftfp == 0 + MOVB runtime·goarmsoftfp(SB), R11 + CMP $0, R11 + BNE 4(PC) WORD $0xeef1ba10 // vmrs r11, fpscr BIC $(1<<24), R11 WORD $0xeee1ba10 // vmsr fpscr, r11 diff --git a/src/runtime/asm_loong64.s b/src/runtime/asm_loong64.s index 6ffa1392c4..586bd23ed4 100644 --- a/src/runtime/asm_loong64.s +++ b/src/runtime/asm_loong64.s @@ -72,7 +72,7 @@ nocgo: MOVV R0, 1(R0) RET -DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 @@ -123,26 +123,31 @@ TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0 // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) // to keep running g. -TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +TEXT runtime·mcall(SB), NOSPLIT|NOFRAME, $0-8 +#ifdef GOEXPERIMENT_regabiargs + MOVV R4, REGCTXT +#else + MOVV fn+0(FP), REGCTXT +#endif + // Save caller state in g->sched MOVV R3, (g_sched+gobuf_sp)(g) MOVV R1, (g_sched+gobuf_pc)(g) MOVV R0, (g_sched+gobuf_lr)(g) // Switch to m->g0 & its stack, call fn. - MOVV g, R19 - MOVV g_m(g), R4 - MOVV m_g0(R4), g + MOVV g, R4 // arg = g + MOVV g_m(g), R20 + MOVV m_g0(R20), g JAL runtime·save_g(SB) - BNE g, R19, 2(PC) + BNE g, R4, 2(PC) JMP runtime·badmcall(SB) - MOVV fn+0(FP), REGCTXT // context - MOVV 0(REGCTXT), R5 // code pointer + MOVV 0(REGCTXT), R20 // code pointer MOVV (g_sched+gobuf_sp)(g), R3 // sp = m->g0->sched.sp ADDV $-16, R3 - MOVV R19, 8(R3) + MOVV R4, 8(R3) MOVV R0, 0(R3) - JAL (R5) + JAL (R20) JMP runtime·badmcall2(SB) // systemstack_switch is a dummy routine that systemstack leaves at the bottom @@ -214,7 +219,7 @@ noswitch: // Called during function prolog when more stack is needed. // Caller has already loaded: -// loong64: R5: LR +// loong64: R31: LR // // The traceback routines see morestack on a g0 as being // the top of a stack (for example, morestack calling newstack @@ -238,12 +243,12 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 // Set g->sched to context in f. MOVV R3, (g_sched+gobuf_sp)(g) MOVV R1, (g_sched+gobuf_pc)(g) - MOVV R5, (g_sched+gobuf_lr)(g) + MOVV R31, (g_sched+gobuf_lr)(g) MOVV REGCTXT, (g_sched+gobuf_ctxt)(g) // Called from f. // Set m->morebuf to f's caller. - MOVV R5, (m_morebuf+gobuf_pc)(R7) // f's caller's PC + MOVV R31, (m_morebuf+gobuf_pc)(R7) // f's caller's PC MOVV R3, (m_morebuf+gobuf_sp)(R7) // f's caller's SP MOVV g, (m_morebuf+gobuf_g)(R7) @@ -272,7 +277,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 JMP runtime·morestack(SB) // reflectcall: call a function with the given argument list -// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32). +// func call(stackArgsType *_type, f *FuncVal, stackArgs *byte, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs). // we don't have variable-sized frames, so we use a small number // of constant-sized-frame functions to encode a few bits of size in the pc. // Caution: ugly multiline assembly macros in your future! @@ -286,7 +291,7 @@ TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 // Note: can't just "BR NAME(SB)" - bad inlining results. TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48 - MOVWU stackArgsSize+24(FP), R19 + MOVWU frameSize+32(FP), R19 DISPATCH(runtime·call32, 32) DISPATCH(runtime·call64, 64) DISPATCH(runtime·call128, 128) @@ -317,7 +322,7 @@ TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-48 JMP (R4) #define CALLFN(NAME,MAXSIZE) \ -TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ +TEXT NAME(SB), WRAPPER, $MAXSIZE-48; \ NO_LOCAL_POINTERS; \ /* copy arguments to stack */ \ MOVV arg+16(FP), R4; \ @@ -331,12 +336,17 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ MOVBU R6, (R12); \ ADDV $1, R12; \ JMP -5(PC); \ + /* set up argument registers */ \ + MOVV regArgs+40(FP), R25; \ + JAL ·unspillArgs(SB); \ /* call function */ \ MOVV f+8(FP), REGCTXT; \ - MOVV (REGCTXT), R6; \ + MOVV (REGCTXT), R25; \ PCDATA $PCDATA_StackMapIndex, $0; \ - JAL (R6); \ + JAL (R25); \ /* copy return values back */ \ + MOVV regArgs+40(FP), R25; \ + JAL ·spillArgs(SB); \ MOVV argtype+0(FP), R7; \ MOVV arg+16(FP), R4; \ MOVWU n+24(FP), R5; \ @@ -352,11 +362,13 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ // separate function so it can allocate stack space for the arguments // to reflectcallmove. It does not follow the Go ABI; it expects its // arguments in registers. -TEXT callRet<>(SB), NOSPLIT, $32-0 +TEXT callRet<>(SB), NOSPLIT, $40-0 + NO_LOCAL_POINTERS MOVV R7, 8(R3) MOVV R4, 16(R3) MOVV R12, 24(R3) MOVV R5, 32(R3) + MOVV R25, 40(R3) JAL runtime·reflectcallmove(SB) RET @@ -567,7 +579,7 @@ havem: // If the m on entry wasn't nil, // 1. the thread might be a Go thread, // 2. or it wasn't the first call from a C thread on pthread platforms, - // since then we skip dropm to reuse the m in the first call. + // since then we skip dropm to resue the m in the first call. MOVV savedm-8(SP), R12 BNE R12, droppedm @@ -604,14 +616,14 @@ TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0 UNDEF // AES hashing not implemented for loong64 -TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 - JMP runtime·memhashFallback(SB) -TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·strhashFallback(SB) -TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·memhash32Fallback(SB) -TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 - JMP runtime·memhash64Fallback(SB) +TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 + JMP runtime·memhashFallback(SB) +TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·strhashFallback(SB) +TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash32Fallback(SB) +TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 + JMP runtime·memhash64Fallback(SB) TEXT runtime·return0(SB), NOSPLIT, $0 MOVW $0, R19 @@ -642,11 +654,102 @@ TEXT runtime·goexit(SB),NOSPLIT|NOFRAME|TOPFRAME,$0-0 // traceback from goexit1 must hit code range of goexit NOOP +// This is called from .init_array and follows the platform, not Go, ABI. +TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0 + ADDV $-0x10, R3 + MOVV R30, 8(R3) // The access to global variables below implicitly uses R30, which is callee-save + MOVV runtime·lastmoduledatap(SB), R12 + MOVV R4, moduledata_next(R12) + MOVV R4, runtime·lastmoduledatap(SB) + MOVV 8(R3), R30 + ADDV $0x10, R3 + RET + TEXT ·checkASM(SB),NOSPLIT,$0-1 MOVW $1, R19 MOVB R19, ret+0(FP) RET +#ifdef GOEXPERIMENT_regabiargs +// spillArgs stores return values from registers to a *internal/abi.RegArgs in R25. +TEXT ·spillArgs(SB),NOSPLIT,$0-0 + MOVV R4, (0*8)(R25) + MOVV R5, (1*8)(R25) + MOVV R6, (2*8)(R25) + MOVV R7, (3*8)(R25) + MOVV R8, (4*8)(R25) + MOVV R9, (5*8)(R25) + MOVV R10, (6*8)(R25) + MOVV R11, (7*8)(R25) + MOVV R12, (8*8)(R25) + MOVV R13, (9*8)(R25) + MOVV R14, (10*8)(R25) + MOVV R15, (11*8)(R25) + MOVV R16, (12*8)(R25) + MOVV R17, (13*8)(R25) + MOVV R18, (14*8)(R25) + MOVV R19, (15*8)(R25) + MOVD F0, (16*8)(R25) + MOVD F1, (17*8)(R25) + MOVD F2, (18*8)(R25) + MOVD F3, (19*8)(R25) + MOVD F4, (20*8)(R25) + MOVD F5, (21*8)(R25) + MOVD F6, (22*8)(R25) + MOVD F7, (23*8)(R25) + MOVD F8, (24*8)(R25) + MOVD F9, (25*8)(R25) + MOVD F10, (26*8)(R25) + MOVD F11, (27*8)(R25) + MOVD F12, (28*8)(R25) + MOVD F13, (29*8)(R25) + MOVD F14, (30*8)(R25) + MOVD F15, (31*8)(R25) + RET + +// unspillArgs loads args into registers from a *internal/abi.RegArgs in R25. +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 + MOVV (0*8)(R25), R4 + MOVV (1*8)(R25), R5 + MOVV (2*8)(R25), R6 + MOVV (3*8)(R25), R7 + MOVV (4*8)(R25), R8 + MOVV (5*8)(R25), R9 + MOVV (6*8)(R25), R10 + MOVV (7*8)(R25), R11 + MOVV (8*8)(R25), R12 + MOVV (9*8)(R25), R13 + MOVV (10*8)(R25), R14 + MOVV (11*8)(R25), R15 + MOVV (12*8)(R25), R16 + MOVV (13*8)(R25), R17 + MOVV (14*8)(R25), R18 + MOVV (15*8)(R25), R19 + MOVD (16*8)(R25), F0 + MOVD (17*8)(R25), F1 + MOVD (18*8)(R25), F2 + MOVD (19*8)(R25), F3 + MOVD (20*8)(R25), F4 + MOVD (21*8)(R25), F5 + MOVD (22*8)(R25), F6 + MOVD (23*8)(R25), F7 + MOVD (24*8)(R25), F8 + MOVD (25*8)(R25), F9 + MOVD (26*8)(R25), F10 + MOVD (27*8)(R25), F11 + MOVD (28*8)(R25), F12 + MOVD (29*8)(R25), F13 + MOVD (30*8)(R25), F14 + MOVD (31*8)(R25), F15 + RET +#else +TEXT ·spillArgs(SB),NOSPLIT,$0-0 + RET + +TEXT ·unspillArgs(SB),NOSPLIT,$0-0 + RET +#endif + // gcWriteBarrier informs the GC about heap pointer writes. // // gcWriteBarrier does NOT follow the Go ABI. It accepts the @@ -774,71 +877,156 @@ TEXT runtime·gcWriteBarrier8(SB),NOSPLIT,$0 // 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 - MOVV R19, x+0(FP) - MOVV R18, y+8(FP) - JMP runtime·goPanicIndex(SB) -TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 - MOVV R19, x+0(FP) - MOVV R18, y+8(FP) - JMP runtime·goPanicIndexU(SB) -TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 - MOVV R18, x+0(FP) - MOVV R17, y+8(FP) - JMP runtime·goPanicSliceAlen(SB) -TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 - MOVV R18, x+0(FP) - MOVV R17, y+8(FP) - JMP runtime·goPanicSliceAlenU(SB) -TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 - MOVV R18, x+0(FP) - MOVV R17, y+8(FP) - JMP runtime·goPanicSliceAcap(SB) -TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 - MOVV R18, x+0(FP) - MOVV R17, y+8(FP) - JMP runtime·goPanicSliceAcapU(SB) -TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 - MOVV R19, x+0(FP) - MOVV R18, y+8(FP) - JMP runtime·goPanicSliceB(SB) -TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 - MOVV R19, x+0(FP) - MOVV R18, y+8(FP) - JMP runtime·goPanicSliceBU(SB) -TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 - MOVV R17, x+0(FP) - MOVV R4, y+8(FP) - JMP runtime·goPanicSlice3Alen(SB) -TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 - MOVV R17, x+0(FP) - MOVV R4, y+8(FP) - JMP runtime·goPanicSlice3AlenU(SB) -TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 - MOVV R17, x+0(FP) - MOVV R4, y+8(FP) - JMP runtime·goPanicSlice3Acap(SB) -TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 - MOVV R17, x+0(FP) - MOVV R4, y+8(FP) - JMP runtime·goPanicSlice3AcapU(SB) -TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 - MOVV R18, x+0(FP) - MOVV R17, y+8(FP) - JMP runtime·goPanicSlice3B(SB) -TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 - MOVV R18, x+0(FP) - MOVV R17, y+8(FP) - JMP runtime·goPanicSlice3BU(SB) -TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 - MOVV R19, x+0(FP) - MOVV R18, y+8(FP) - JMP runtime·goPanicSlice3C(SB) -TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 - MOVV R19, x+0(FP) - MOVV R18, y+8(FP) - JMP runtime·goPanicSlice3CU(SB) -TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 - MOVV R17, x+0(FP) - MOVV R4, y+8(FP) - JMP runtime·goPanicSliceConvert(SB) +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R20, R4 + MOVV R21, R5 +#else + MOVV R20, x+0(FP) + MOVV R21, y+8(FP) +#endif + JMP runtime·goPanicIndex(SB) +TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R20, R4 + MOVV R21, R5 +#else + MOVV R20, x+0(FP) + MOVV R21, y+8(FP) +#endif + JMP runtime·goPanicIndexU(SB) +TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R21, R4 + MOVV R23, R5 +#else + MOVV R21, x+0(FP) + MOVV R23, y+8(FP) +#endif + JMP runtime·goPanicSliceAlen(SB) +TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R21, R4 + MOVV R23, R5 +#else + MOVV R21, x+0(FP) + MOVV R23, y+8(FP) +#endif + JMP runtime·goPanicSliceAlenU(SB) +TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R21, R4 + MOVV R23, R5 +#else + MOVV R21, x+0(FP) + MOVV R23, y+8(FP) +#endif + JMP runtime·goPanicSliceAcap(SB) +TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R21, R4 + MOVV R23, R5 +#else + MOVV R21, x+0(FP) + MOVV R23, y+8(FP) +#endif + JMP runtime·goPanicSliceAcapU(SB) +TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R20, R4 + MOVV R21, R5 +#else + MOVV R20, x+0(FP) + MOVV R21, y+8(FP) +#endif + JMP runtime·goPanicSliceB(SB) +TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R20, R4 + MOVV R21, R5 +#else + MOVV R20, x+0(FP) + MOVV R21, y+8(FP) +#endif + JMP runtime·goPanicSliceBU(SB) +TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R23, R4 + MOVV R24, R5 +#else + MOVV R23, x+0(FP) + MOVV R24, y+8(FP) +#endif + JMP runtime·goPanicSlice3Alen(SB) +TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R23, R4 + MOVV R24, R5 +#else + MOVV R23, x+0(FP) + MOVV R24, y+8(FP) +#endif + JMP runtime·goPanicSlice3AlenU(SB) +TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R23, R4 + MOVV R24, R5 +#else + MOVV R23, x+0(FP) + MOVV R24, y+8(FP) +#endif + JMP runtime·goPanicSlice3Acap(SB) +TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R23, R4 + MOVV R24, R5 +#else + MOVV R23, x+0(FP) + MOVV R24, y+8(FP) +#endif + JMP runtime·goPanicSlice3AcapU(SB) +TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R21, R4 + MOVV R23, R5 +#else + MOVV R21, x+0(FP) + MOVV R23, y+8(FP) +#endif + JMP runtime·goPanicSlice3B(SB) +TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R21, R4 + MOVV R23, R5 +#else + MOVV R21, x+0(FP) + MOVV R23, y+8(FP) +#endif + JMP runtime·goPanicSlice3BU(SB) +TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R20, R4 + MOVV R21, R5 +#else + MOVV R20, x+0(FP) + MOVV R21, y+8(FP) +#endif + JMP runtime·goPanicSlice3C(SB) +TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R20, R4 + MOVV R21, R5 +#else + MOVV R20, x+0(FP) + MOVV R21, y+8(FP) +#endif + JMP runtime·goPanicSlice3CU(SB) +TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16 +#ifdef GOEXPERIMENT_regabiargs + MOVV R23, R4 + MOVV R24, R5 +#else + MOVV R23, x+0(FP) + MOVV R24, y+8(FP) +#endif + JMP runtime·goPanicSliceConvert(SB) diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index a96115b02c..b44a4f7dd4 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -140,6 +140,7 @@ TEXT runtime·systemstack(SB), NOSPLIT, $0-8 I64Ne If CALLNORESUME runtime·badsystemstack(SB) + CALLNORESUME runtime·abort(SB) End // switch: @@ -181,6 +182,9 @@ TEXT runtime·systemstack(SB), NOSPLIT, $0-8 TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0 RET +TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0 + UNDEF + // AES hashing not implemented for wasm TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 JMP runtime·memhashFallback(SB) @@ -208,6 +212,33 @@ TEXT runtime·procyield(SB), NOSPLIT, $0-0 // FIXME TEXT runtime·breakpoint(SB), NOSPLIT, $0-0 UNDEF +// func switchToCrashStack0(fn func()) +TEXT runtime·switchToCrashStack0(SB), NOSPLIT, $0-8 + MOVD fn+0(FP), CTXT // context register + MOVD g_m(g), R2 // curm + + // set g to gcrash + MOVD $runtime·gcrash(SB), g // g = &gcrash + MOVD R2, g_m(g) // g.m = curm + MOVD g, m_g0(R2) // curm.g0 = g + + // switch to crashstack + I64Load (g_stack+stack_hi)(g) + I64Const $(-4*8) + I64Add + I32WrapI64 + Set SP + + // call target function + Get CTXT + I32WrapI64 + I64Load $0 + CALL + + // should never return + CALL runtime·abort(SB) + UNDEF + // Called during function prolog when more stack is needed. // // The traceback routines see morestack on a g0 as being @@ -221,12 +252,19 @@ TEXT runtime·morestack(SB), NOSPLIT, $0-0 // R2 = g0 MOVD m_g0(R1), R2 + // Set g->sched to context in f. + NOP SP // tell vet SP changed - stop checking offsets + MOVD 0(SP), g_sched+gobuf_pc(g) + MOVD $8(SP), g_sched+gobuf_sp(g) // f's SP + MOVD CTXT, g_sched+gobuf_ctxt(g) + // Cannot grow scheduler stack (m->g0). Get g Get R2 I64Eq If CALLNORESUME runtime·badmorestackg0(SB) + CALLNORESUME runtime·abort(SB) End // Cannot grow signal stack (m->gsignal). @@ -235,20 +273,15 @@ TEXT runtime·morestack(SB), NOSPLIT, $0-0 I64Eq If CALLNORESUME runtime·badmorestackgsignal(SB) + CALLNORESUME runtime·abort(SB) End // Called from f. // Set m->morebuf to f's caller. - NOP SP // tell vet SP changed - stop checking offsets MOVD 8(SP), m_morebuf+gobuf_pc(R1) MOVD $16(SP), m_morebuf+gobuf_sp(R1) // f's caller's SP MOVD g, m_morebuf+gobuf_g(R1) - // Set g->sched to context in f. - MOVD 0(SP), g_sched+gobuf_pc(g) - MOVD $8(SP), g_sched+gobuf_sp(g) // f's SP - MOVD CTXT, g_sched+gobuf_ctxt(g) - // Call newstack on m->g0's stack. MOVD R2, g MOVD g_sched+gobuf_sp(R2), SP diff --git a/src/runtime/cgo/asm_arm.s b/src/runtime/cgo/asm_arm.s index 095e9c06c9..425899ebe5 100644 --- a/src/runtime/cgo/asm_arm.s +++ b/src/runtime/cgo/asm_arm.s @@ -32,10 +32,10 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 // starting at 4(R13). MOVW.W R14, -4(R13) - // Skip floating point registers on GOARM < 6. - MOVB runtime·goarm(SB), R11 - CMP $6, R11 - BLT skipfpsave + // Skip floating point registers if goarmsoftfp!=0. + MOVB runtime·goarmsoftfp(SB), R11 + CMP $0, R11 + BNE skipfpsave MOVD F8, (13*4+8*1)(R13) MOVD F9, (13*4+8*2)(R13) MOVD F10, (13*4+8*3)(R13) @@ -50,9 +50,9 @@ skipfpsave: // We set up the arguments to cgocallback when saving registers above. BL runtime·cgocallback(SB) - MOVB runtime·goarm(SB), R11 - CMP $6, R11 - BLT skipfprest + MOVB runtime·goarmsoftfp(SB), R11 + CMP $0, R11 + BNE skipfprest MOVD (13*4+8*1)(R13), F8 MOVD (13*4+8*2)(R13), F9 MOVD (13*4+8*3)(R13), F10 diff --git a/src/runtime/coro.go b/src/runtime/coro.go new file mode 100644 index 0000000000..0d6666e343 --- /dev/null +++ b/src/runtime/coro.go @@ -0,0 +1,165 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import "unsafe" + +// A coro represents extra concurrency without extra parallelism, +// as would be needed for a coroutine implementation. +// The coro does not represent a specific coroutine, only the ability +// to do coroutine-style control transfers. +// It can be thought of as like a special channel that always has +// a goroutine blocked on it. If another goroutine calls coroswitch(c), +// the caller becomes the goroutine blocked in c, and the goroutine +// formerly blocked in c starts running. +// These switches continue until a call to coroexit(c), +// which ends the use of the coro by releasing the blocked +// goroutine in c and exiting the current goroutine. +// +// Coros are heap allocated and garbage collected, so that user code +// can hold a pointer to a coro without causing potential dangling +// pointer errors. +type coro struct { + gp guintptr + f func(*coro) +} + +//go:linkname newcoro + +// newcoro creates a new coro containing a +// goroutine blocked waiting to run f +// and returns that coro. +func newcoro(f func(*coro)) *coro { + c := new(coro) + c.f = f + pc := getcallerpc() + gp := getg() + systemstack(func() { + start := corostart + startfv := *(**funcval)(unsafe.Pointer(&start)) + gp = newproc1(startfv, gp, pc) + }) + gp.coroarg = c + gp.waitreason = waitReasonCoroutine + casgstatus(gp, _Grunnable, _Gwaiting) + c.gp.set(gp) + return c +} + +//go:linkname corostart + +// corostart is the entry func for a new coroutine. +// It runs the coroutine user function f passed to corostart +// and then calls coroexit to remove the extra concurrency. +func corostart() { + gp := getg() + c := gp.coroarg + gp.coroarg = nil + + c.f(c) + coroexit(c) +} + +// coroexit is like coroswitch but closes the coro +// and exits the current goroutine +func coroexit(c *coro) { + gp := getg() + gp.coroarg = c + gp.coroexit = true + mcall(coroswitch_m) +} + +//go:linkname coroswitch + +// coroswitch switches to the goroutine blocked on c +// and then blocks the current goroutine on c. +func coroswitch(c *coro) { + gp := getg() + gp.coroarg = c + mcall(coroswitch_m) +} + +// coroswitch_m is the implementation of coroswitch +// that runs on the m stack. +// +// Note: Coroutine switches are expected to happen at +// an order of magnitude (or more) higher frequency +// than regular goroutine switches, so this path is heavily +// optimized to remove unnecessary work. +// The fast path here is three CAS: the one at the top on gp.atomicstatus, +// the one in the middle to choose the next g, +// and the one at the bottom on gnext.atomicstatus. +// It is important not to add more atomic operations or other +// expensive operations to the fast path. +func coroswitch_m(gp *g) { + // TODO(rsc,mknyszek): add tracing support in a lightweight manner. + // Probably the tracer will need a global bool (set and cleared during STW) + // that this code can check to decide whether to use trace.gen.Load(); + // we do not want to do the atomic load all the time, especially when + // tracer use is relatively rare. + c := gp.coroarg + gp.coroarg = nil + exit := gp.coroexit + gp.coroexit = false + mp := gp.m + + if exit { + gdestroy(gp) + gp = nil + } else { + // If we can CAS ourselves directly from running to waiting, so do, + // keeping the control transfer as lightweight as possible. + gp.waitreason = waitReasonCoroutine + if !gp.atomicstatus.CompareAndSwap(_Grunning, _Gwaiting) { + // The CAS failed: use casgstatus, which will take care of + // coordinating with the garbage collector about the state change. + casgstatus(gp, _Grunning, _Gwaiting) + } + + // Clear gp.m. + setMNoWB(&gp.m, nil) + } + + // The goroutine stored in c is the one to run next. + // Swap it with ourselves. + var gnext *g + for { + // Note: this is a racy load, but it will eventually + // get the right value, and if it gets the wrong value, + // the c.gp.cas will fail, so no harm done other than + // a wasted loop iteration. + // The cas will also sync c.gp's + // memory enough that the next iteration of the racy load + // should see the correct value. + // We are avoiding the atomic load to keep this path + // as lightweight as absolutely possible. + // (The atomic load is free on x86 but not free elsewhere.) + next := c.gp + if next.ptr() == nil { + throw("coroswitch on exited coro") + } + var self guintptr + self.set(gp) + if c.gp.cas(next, self) { + gnext = next.ptr() + break + } + } + + // Start running next, without heavy scheduling machinery. + // Set mp.curg and gnext.m and then update scheduling state + // directly if possible. + setGNoWB(&mp.curg, gnext) + setMNoWB(&gnext.m, mp) + if !gnext.atomicstatus.CompareAndSwap(_Gwaiting, _Grunning) { + // The CAS failed: use casgstatus, which will take care of + // coordinating with the garbage collector about the state change. + casgstatus(gnext, _Gwaiting, _Grunnable) + casgstatus(gnext, _Grunnable, _Grunning) + } + + // Switch to gnext. Does not return. + gogo(&gnext.sched) +} diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index ffd99f3a87..2ed0fd8f07 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -795,14 +795,12 @@ func TestG0StackOverflow(t *testing.T) { if runtime.GOOS == "ios" { testenv.SkipFlaky(t, 62671) } - if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" { - testenv.SkipFlaky(t, 63938) // TODO(cherry): fix and unskip - } if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" { cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestG0StackOverflow$", "-test.v")) cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1") out, err := cmd.CombinedOutput() + t.Logf("output:\n%s", out) // Don't check err since it's expected to crash. if n := strings.Count(string(out), "morestack on g0\n"); n != 1 { t.Fatalf("%s\n(exit status %v)", out, err) diff --git a/src/runtime/duff_loong64.s b/src/runtime/duff_loong64.s index 63fa3bcca1..b05502d91d 100644 --- a/src/runtime/duff_loong64.s +++ b/src/runtime/duff_loong64.s @@ -4,904 +4,904 @@ #include "textflag.h" -TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 - MOVV R0, (R19) - ADDV $8, R19 +TEXT runtime·duffzero(SB), NOSPLIT|NOFRAME, $0-0 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 + MOVV R0, (R20) + ADDV $8, R20 RET -TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) +TEXT runtime·duffcopy(SB), NOSPLIT|NOFRAME, $0-0 + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 - MOVV (R19), R30 - ADDV $8, R19 - MOVV R30, (R20) + MOVV (R20), R30 ADDV $8, R20 + MOVV R30, (R21) + ADDV $8, R21 RET diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 5f0e24d4f1..e25f748ed4 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -31,6 +31,8 @@ var Exitsyscall = exitsyscall var LockedOSThread = lockedOSThread var Xadduintptr = atomic.Xadduintptr +var ReadRandomFailed = &readRandomFailed + var Fastlog2 = fastlog2 var Atoi = atoi @@ -398,9 +400,9 @@ func CountPagesInUse() (pagesInUse, counted uintptr) { return } -func Fastrand() uint32 { return fastrand() } -func Fastrand64() uint64 { return fastrand64() } -func Fastrandn(n uint32) uint32 { return fastrandn(n) } +func Fastrand() uint32 { return uint32(rand()) } +func Fastrand64() uint64 { return rand() } +func Fastrandn(n uint32) uint32 { return randn(n) } type ProfBuf profBuf @@ -464,6 +466,8 @@ func ReadMetricsSlow(memStats *MemStats, samplesp unsafe.Pointer, len, cap int) startTheWorld(stw) } +var DoubleCheckReadMemStats = &doubleCheckReadMemStats + // ReadMemStatsSlow returns both the runtime-computed MemStats and // MemStats accumulated by scanning the heap. func ReadMemStatsSlow() (base, slow MemStats) { @@ -582,6 +586,10 @@ type RWMutex struct { rw rwmutex } +func (rw *RWMutex) Init() { + rw.rw.init(lockRankTestR, lockRankTestRInternal, lockRankTestW) +} + func (rw *RWMutex) RLock() { rw.rw.rlock() } @@ -1345,16 +1353,7 @@ type Mutex = mutex var Lock = lock var Unlock = unlock -func MutexContended(l *mutex) bool { - switch atomic.Loaduintptr(&l.key) { - case 0: // unlocked - return false - case 1: // locked - return false - default: // an M is sleeping - return true - } -} +var MutexContended = mutexContended func SemRootLock(addr *uint32) *mutex { root := semtable.rootFor(addr) @@ -1941,24 +1940,8 @@ func UserArenaClone[T any](s T) T { var AlignUp = alignUp -// BlockUntilEmptyFinalizerQueue blocks until either the finalizer -// queue is emptied (and the finalizers have executed) or the timeout -// is reached. Returns true if the finalizer queue was emptied. func BlockUntilEmptyFinalizerQueue(timeout int64) bool { - start := nanotime() - for nanotime()-start < timeout { - lock(&finlock) - // We know the queue has been drained when both finq is nil - // and the finalizer g has stopped executing. - empty := finq == nil - empty = empty && readgstatus(fing) == _Gwaiting && fing.waitreason == waitReasonFinalizerWait - unlock(&finlock) - if empty { - return true - } - Gosched() - } - return false + return blockUntilEmptyFinalizerQueue(timeout) } func FrameStartLine(f *Frame) int { diff --git a/src/runtime/extern.go b/src/runtime/extern.go index d199720b9b..b7bf0a505b 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -55,6 +55,13 @@ It is a comma-separated list of name=val pairs setting these named variables: cgocheck mode can be enabled using GOEXPERIMENT (which requires a rebuild), see https://pkg.go.dev/internal/goexperiment for details. + disablethp: setting disablethp=1 on Linux disables transparent huge pages for the heap. + It has no effect on other platforms. disablethp is meant for compatibility with versions + of Go before 1.21, which stopped working around a Linux kernel default that can result + in significant memory overuse. See https://go.dev/issue/64332. This setting will be + removed in a future release, so operators should tweak their Linux configuration to suit + their needs before then. See https://go.dev/doc/gc-guide#Linux_transparent_huge_pages. + dontfreezetheworld: by default, the start of a fatal panic or throw "freezes the world", preempting all threads to stop all running goroutines, which makes it possible to traceback all goroutines, and @@ -145,13 +152,14 @@ It is a comma-separated list of name=val pairs setting these named variables: risk in that scenario. Currently not supported on Windows, plan9 or js/wasm. Setting this option for some applications can produce large traces, so use with care. - profileruntimelocks: setting profileruntimelocks=1 includes call stacks related to - contention on runtime-internal locks in the "mutex" profile, subject to the - MutexProfileFraction setting. The call stacks will correspond to the unlock call that - released the lock. But instead of the value corresponding to the amount of contention that - call stack caused, it corresponds to the amount of time the caller of unlock had to wait - in its original call to lock. A future release is expected to align those and remove this - setting. + runtimecontentionstacks: setting runtimecontentionstacks=1 enables inclusion of call stacks + related to contention on runtime-internal locks in the "mutex" profile, subject to the + MutexProfileFraction setting. When runtimecontentionstacks=0, contention on + runtime-internal locks will report as "runtime._LostContendedRuntimeLock". When + runtimecontentionstacks=1, the call stacks will correspond to the unlock call that released + the lock. But instead of the value corresponding to the amount of contention that call + stack caused, it corresponds to the amount of time the caller of unlock had to wait in its + original call to lock. A future release is expected to align those and remove this setting. invalidptr: invalidptr=1 (the default) causes the garbage collector and stack copier to crash the program if an invalid pointer value (for example, 1) diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index b80396aa11..c6759a172c 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -577,6 +577,11 @@ func TestPageAccounting(t *testing.T) { } } +func init() { + // Enable ReadMemStats' double-check mode. + *runtime.DoubleCheckReadMemStats = true +} + func TestReadMemStats(t *testing.T) { base, slow := runtime.ReadMemStatsSlow() if base != slow { diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 4563809a9d..bad49a346e 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -440,14 +440,14 @@ func typeAssert(s *abi.TypeAssert, t *_type) *itab { // Maybe update the cache, so the next time the generated code // doesn't need to call into the runtime. - if fastrand()&1023 != 0 { + if cheaprand()&1023 != 0 { // Only bother updating the cache ~1 in 1000 times. return tab } // Load the current cache. oldC := (*abi.TypeAssertCache)(atomic.Loadp(unsafe.Pointer(&s.Cache))) - if fastrand()&uint32(oldC.Mask) != 0 { + if cheaprand()&uint32(oldC.Mask) != 0 { // As cache gets larger, choose to update it less often // so we can amortize the cost of building a new cache. return tab @@ -540,7 +540,7 @@ func interfaceSwitch(s *abi.InterfaceSwitch, t *_type) (int, *itab) { // Maybe update the cache, so the next time the generated code // doesn't need to call into the runtime. - if fastrand()&1023 != 0 { + if cheaprand()&1023 != 0 { // Only bother updating the cache ~1 in 1000 times. // This ensures we don't waste memory on switches, or // switch arguments, that only happen a few times. @@ -549,7 +549,7 @@ func interfaceSwitch(s *abi.InterfaceSwitch, t *_type) (int, *itab) { // Load the current cache. oldC := (*abi.InterfaceSwitchCache)(atomic.Loadp(unsafe.Pointer(&s.Cache))) - if fastrand()&uint32(oldC.Mask) != 0 { + if cheaprand()&uint32(oldC.Mask) != 0 { // As cache gets larger, choose to update it less often // so we can amortize the cost of building a new cache // (that cost is linear in oldc.Mask). @@ -567,7 +567,7 @@ func interfaceSwitch(s *abi.InterfaceSwitch, t *_type) (int, *itab) { return case_, tab } -// buildInterfaceSwitchCache constructs a interface switch cache +// buildInterfaceSwitchCache constructs an interface switch cache // containing all the entries from oldC plus the new entry // (typ,case_,tab). func buildInterfaceSwitchCache(oldC *abi.InterfaceSwitchCache, typ *_type, case_ int, tab *itab) *abi.InterfaceSwitchCache { diff --git a/src/runtime/internal/atomic/atomic_andor_generic.go b/src/runtime/internal/atomic/atomic_andor_generic.go new file mode 100644 index 0000000000..00b402681e --- /dev/null +++ b/src/runtime/internal/atomic/atomic_andor_generic.go @@ -0,0 +1,67 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build arm || s390x || loong64 || mips || mipsle || mips64 || mips64le || wasm + +package atomic + +//go:nosplit +func And32(ptr *uint32, val uint32) uint32 { + for { + old := *ptr + if Cas(ptr, old, old&val) { + return old + } + } +} + +//go:nosplit +func Or32(ptr *uint32, val uint32) uint32 { + for { + old := *ptr + if Cas(ptr, old, old|val) { + return old + } + } +} + +//go:nosplit +func And64(ptr *uint64, val uint64) uint64 { + for { + old := *ptr + if Cas64(ptr, old, old&val) { + return old + } + } +} + +//go:nosplit +func Or64(ptr *uint64, val uint64) uint64 { + for { + old := *ptr + if Cas64(ptr, old, old|val) { + return old + } + } +} + +//go:nosplit +func Anduintptr(ptr *uintptr, val uintptr) uintptr { + for { + old := *ptr + if Casuintptr(ptr, old, old&val) { + return old + } + } +} + +//go:nosplit +func Oruintptr(ptr *uintptr, val uintptr) uintptr { + for { + old := *ptr + if Casuintptr(ptr, old, old|val) { + return old + } + } +} diff --git a/src/runtime/internal/atomic/atomic_andor_test.go b/src/runtime/internal/atomic/atomic_andor_test.go index 9dd8b60ae4..a2f3b6f3a9 100644 --- a/src/runtime/internal/atomic/atomic_andor_test.go +++ b/src/runtime/internal/atomic/atomic_andor_test.go @@ -1,6 +1,3 @@ -//go:build 386 || amd64 || arm || arm64 || ppc64 || ppc64le || riscv64 || wasm - -// // Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go index ae609cf4db..567e951244 100644 --- a/src/runtime/internal/atomic/atomic_arm.go +++ b/src/runtime/internal/atomic/atomic_arm.go @@ -208,66 +208,6 @@ func And(addr *uint32, v uint32) { } } -//go:nosplit -func Or32(addr *uint32, v uint32) uint32 { - for { - old := *addr - if Cas(addr, old, old|v) { - return old - } - } -} - -//go:nosplit -func And32(addr *uint32, v uint32) uint32 { - for { - old := *addr - if Cas(addr, old, old&v) { - return old - } - } -} - -//go:nosplit -func Or64(addr *uint64, v uint64) uint64 { - for { - old := *addr - if Cas64(addr, old, old|v) { - return old - } - } -} - -//go:nosplit -func And64(addr *uint64, v uint64) uint64 { - for { - old := *addr - if Cas64(addr, old, old&v) { - return old - } - } -} - -//go:nosplit -func Oruintptr(addr *uintptr, v uintptr) uintptr { - for { - old := *addr - if Casuintptr(addr, old, old|v) { - return old - } - } -} - -//go:nosplit -func Anduintptr(addr *uintptr, v uintptr) uintptr { - for { - old := *addr - if Casuintptr(addr, old, old&v) { - return old - } - } -} - //go:nosplit func armcas(ptr *uint32, old, new uint32) bool diff --git a/src/runtime/internal/atomic/atomic_wasm.go b/src/runtime/internal/atomic/atomic_wasm.go index d1ca994205..835fc43ccf 100644 --- a/src/runtime/internal/atomic/atomic_wasm.go +++ b/src/runtime/internal/atomic/atomic_wasm.go @@ -339,51 +339,3 @@ func Xaddint64(ptr *int64, delta int64) int64 { *ptr = new return new } - -//go:nosplit -//go:noinline -func And32(ptr *uint32, val uint32) uint32 { - old := *ptr - *ptr = old & val - return old -} - -//go:nosplit -//go:noinline -func And64(ptr *uint64, val uint64) uint64 { - old := *ptr - *ptr = old & val - return old -} - -//go:nosplit -//go:noinline -func Anduintptr(ptr *uintptr, val uintptr) uintptr { - old := *ptr - *ptr = old & val - return old -} - -//go:nosplit -//go:noinline -func Or32(ptr *uint32, val uint32) uint32 { - old := *ptr - *ptr = old | val - return old -} - -//go:nosplit -//go:noinline -func Or64(ptr *uint64, val uint64) uint64 { - old := *ptr - *ptr = old | val - return old -} - -//go:nosplit -//go:noinline -func Oruintptr(ptr *uintptr, val uintptr) uintptr { - old := *ptr - *ptr = old | val - return old -} diff --git a/src/runtime/internal/syscall/asm_linux_loong64.s b/src/runtime/internal/syscall/asm_linux_loong64.s index d6a33f90a7..11c5bc2468 100644 --- a/src/runtime/internal/syscall/asm_linux_loong64.s +++ b/src/runtime/internal/syscall/asm_linux_loong64.s @@ -5,7 +5,32 @@ #include "textflag.h" // func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) -TEXT ·Syscall6(SB),NOSPLIT,$0-80 +// +// We need to convert to the syscall ABI. +// +// arg | ABIInternal | Syscall +// --------------------------- +// num | R4 | R11 +// a1 | R5 | R4 +// a2 | R6 | R5 +// a3 | R7 | R6 +// a4 | R8 | R7 +// a5 | R9 | R8 +// a6 | R10 | R9 +// +// r1 | R4 | R4 +// r2 | R5 | R5 +// err | R6 | part of R4 +TEXT ·Syscall6(SB),NOSPLIT,$0-80 +#ifdef GOEXPERIMENT_regabiargs + MOVV R4, R11 // syscall entry + MOVV R5, R4 + MOVV R6, R5 + MOVV R7, R6 + MOVV R8, R7 + MOVV R9, R8 + MOVV R10, R9 +#else MOVV num+0(FP), R11 // syscall entry MOVV a1+8(FP), R4 MOVV a2+16(FP), R5 @@ -13,7 +38,15 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 MOVV a4+32(FP), R7 MOVV a5+40(FP), R8 MOVV a6+48(FP), R9 +#endif SYSCALL +#ifdef GOEXPERIMENT_regabiargs + MOVV R0, R5 // r2 is not used. Always set to 0. + MOVW $-4096, R12 + BGEU R12, R4, ok + SUBVU R4, R0, R6 // errno + MOVV $-1, R4 // r1 +#else MOVW $-4096, R12 BGEU R12, R4, ok MOVV $-1, R12 @@ -21,9 +54,15 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80 MOVV R0, r2+64(FP) SUBVU R4, R0, R4 MOVV R4, errno+72(FP) +#endif RET ok: +#ifdef GOEXPERIMENT_regabiargs + // r1 already in R4 + MOVV R0, R6 // errno +#else MOVV R4, r1+56(FP) MOVV R0, r2+64(FP) // r2 is not used. Always set to 0. MOVV R0, errno+72(FP) +#endif RET diff --git a/src/runtime/lock_futex.go b/src/runtime/lock_futex.go index b4f57d5259..867e2b34d0 100644 --- a/src/runtime/lock_futex.go +++ b/src/runtime/lock_futex.go @@ -44,6 +44,10 @@ func key32(p *uintptr) *uint32 { return (*uint32)(unsafe.Pointer(p)) } +func mutexContended(l *mutex) bool { + return atomic.Load(key32(&l.key)) > mutex_locked +} + func lock(l *mutex) { lockWithRank(l, getLockRank(l)) } diff --git a/src/runtime/lock_js.go b/src/runtime/lock_js.go index 91ad7be317..b6ee5ec7af 100644 --- a/src/runtime/lock_js.go +++ b/src/runtime/lock_js.go @@ -23,6 +23,10 @@ const ( passive_spin = 1 ) +func mutexContended(l *mutex) bool { + return false +} + func lock(l *mutex) { lockWithRank(l, getLockRank(l)) } diff --git a/src/runtime/lock_sema.go b/src/runtime/lock_sema.go index 84cd344db8..073e7d410e 100644 --- a/src/runtime/lock_sema.go +++ b/src/runtime/lock_sema.go @@ -31,6 +31,10 @@ const ( passive_spin = 1 ) +func mutexContended(l *mutex) bool { + return atomic.Loaduintptr(&l.key) > locked +} + func lock(l *mutex) { lockWithRank(l, getLockRank(l)) } diff --git a/src/runtime/lock_wasip1.go b/src/runtime/lock_wasip1.go index c4fc59f6cc..acfc62acb4 100644 --- a/src/runtime/lock_wasip1.go +++ b/src/runtime/lock_wasip1.go @@ -19,6 +19,10 @@ const ( active_spin_cnt = 30 ) +func mutexContended(l *mutex) bool { + return false +} + func lock(l *mutex) { lockWithRank(l, getLockRank(l)) } diff --git a/src/runtime/lockrank.go b/src/runtime/lockrank.go index 103131df5e..b27e6c5606 100644 --- a/src/runtime/lockrank.go +++ b/src/runtime/lockrank.go @@ -18,19 +18,24 @@ const ( lockRankSweepWaiters lockRankAssistQueue lockRankSweep - lockRankPollDesc + lockRankTestR + lockRankTestW + lockRankAllocmW + lockRankExecW lockRankCpuprof + lockRankPollDesc + lockRankWakeableSleep + // SCHED + lockRankAllocmR + lockRankExecR lockRankSched lockRankAllg lockRankAllp lockRankTimers - lockRankWakeableSleep lockRankNetpollInit lockRankHchan lockRankNotifyList lockRankSudog - lockRankRwmutexW - lockRankRwmutexR lockRankRoot lockRankItab lockRankReflectOffs @@ -64,6 +69,9 @@ const ( lockRankPanic lockRankDeadlock lockRankRaceFini + lockRankAllocmRInternal + lockRankExecRInternal + lockRankTestRInternal ) // lockRankLeafRank is the rank of lock that does not have a declared rank, @@ -72,53 +80,60 @@ const lockRankLeafRank lockRank = 1000 // lockNames gives the names associated with each of the above ranks. var lockNames = []string{ - lockRankSysmon: "sysmon", - lockRankScavenge: "scavenge", - lockRankForcegc: "forcegc", - lockRankDefer: "defer", - lockRankSweepWaiters: "sweepWaiters", - lockRankAssistQueue: "assistQueue", - lockRankSweep: "sweep", - lockRankPollDesc: "pollDesc", - lockRankCpuprof: "cpuprof", - lockRankSched: "sched", - lockRankAllg: "allg", - lockRankAllp: "allp", - lockRankTimers: "timers", - lockRankWakeableSleep: "wakeableSleep", - lockRankNetpollInit: "netpollInit", - lockRankHchan: "hchan", - lockRankNotifyList: "notifyList", - lockRankSudog: "sudog", - lockRankRwmutexW: "rwmutexW", - lockRankRwmutexR: "rwmutexR", - lockRankRoot: "root", - lockRankItab: "itab", - lockRankReflectOffs: "reflectOffs", - lockRankUserArenaState: "userArenaState", - lockRankTraceBuf: "traceBuf", - lockRankTraceStrings: "traceStrings", - lockRankFin: "fin", - lockRankSpanSetSpine: "spanSetSpine", - lockRankMspanSpecial: "mspanSpecial", - lockRankGcBitsArenas: "gcBitsArenas", - lockRankProfInsert: "profInsert", - lockRankProfBlock: "profBlock", - lockRankProfMemActive: "profMemActive", - lockRankProfMemFuture: "profMemFuture", - lockRankGscan: "gscan", - lockRankStackpool: "stackpool", - lockRankStackLarge: "stackLarge", - lockRankHchanLeaf: "hchanLeaf", - lockRankWbufSpans: "wbufSpans", - lockRankMheap: "mheap", - lockRankMheapSpecial: "mheapSpecial", - lockRankGlobalAlloc: "globalAlloc", - lockRankTrace: "trace", - lockRankTraceStackTab: "traceStackTab", - lockRankPanic: "panic", - lockRankDeadlock: "deadlock", - lockRankRaceFini: "raceFini", + lockRankSysmon: "sysmon", + lockRankScavenge: "scavenge", + lockRankForcegc: "forcegc", + lockRankDefer: "defer", + lockRankSweepWaiters: "sweepWaiters", + lockRankAssistQueue: "assistQueue", + lockRankSweep: "sweep", + lockRankTestR: "testR", + lockRankTestW: "testW", + lockRankAllocmW: "allocmW", + lockRankExecW: "execW", + lockRankCpuprof: "cpuprof", + lockRankPollDesc: "pollDesc", + lockRankWakeableSleep: "wakeableSleep", + lockRankAllocmR: "allocmR", + lockRankExecR: "execR", + lockRankSched: "sched", + lockRankAllg: "allg", + lockRankAllp: "allp", + lockRankTimers: "timers", + lockRankNetpollInit: "netpollInit", + lockRankHchan: "hchan", + lockRankNotifyList: "notifyList", + lockRankSudog: "sudog", + lockRankRoot: "root", + lockRankItab: "itab", + lockRankReflectOffs: "reflectOffs", + lockRankUserArenaState: "userArenaState", + lockRankTraceBuf: "traceBuf", + lockRankTraceStrings: "traceStrings", + lockRankFin: "fin", + lockRankSpanSetSpine: "spanSetSpine", + lockRankMspanSpecial: "mspanSpecial", + lockRankGcBitsArenas: "gcBitsArenas", + lockRankProfInsert: "profInsert", + lockRankProfBlock: "profBlock", + lockRankProfMemActive: "profMemActive", + lockRankProfMemFuture: "profMemFuture", + lockRankGscan: "gscan", + lockRankStackpool: "stackpool", + lockRankStackLarge: "stackLarge", + lockRankHchanLeaf: "hchanLeaf", + lockRankWbufSpans: "wbufSpans", + lockRankMheap: "mheap", + lockRankMheapSpecial: "mheapSpecial", + lockRankGlobalAlloc: "globalAlloc", + lockRankTrace: "trace", + lockRankTraceStackTab: "traceStackTab", + lockRankPanic: "panic", + lockRankDeadlock: "deadlock", + lockRankRaceFini: "raceFini", + lockRankAllocmRInternal: "allocmRInternal", + lockRankExecRInternal: "execRInternal", + lockRankTestRInternal: "testRInternal", } func (rank lockRank) String() string { @@ -140,51 +155,58 @@ func (rank lockRank) String() string { // // Lock ranks that allow self-cycles list themselves. var lockPartialOrder [][]lockRank = [][]lockRank{ - lockRankSysmon: {}, - lockRankScavenge: {lockRankSysmon}, - lockRankForcegc: {lockRankSysmon}, - lockRankDefer: {}, - lockRankSweepWaiters: {}, - lockRankAssistQueue: {}, - lockRankSweep: {}, - lockRankPollDesc: {}, - lockRankCpuprof: {}, - lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof}, - lockRankAllg: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched}, - lockRankAllp: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched}, - lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllp, lockRankTimers}, - lockRankWakeableSleep: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllp, lockRankTimers}, - lockRankNetpollInit: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllp, lockRankTimers}, - lockRankHchan: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankHchan}, - lockRankNotifyList: {}, - lockRankSudog: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankHchan, lockRankNotifyList}, - lockRankRwmutexW: {}, - lockRankRwmutexR: {lockRankSysmon, lockRankRwmutexW}, - lockRankRoot: {}, - lockRankItab: {}, - lockRankReflectOffs: {lockRankItab}, - lockRankUserArenaState: {}, - lockRankTraceBuf: {lockRankSysmon, lockRankScavenge}, - lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf}, - lockRankFin: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, - lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, - lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, - lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankMspanSpecial}, - lockRankProfInsert: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, - lockRankProfBlock: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, - lockRankProfMemActive: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, - lockRankProfMemFuture: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankProfMemActive}, - lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture}, - lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, - lockRankStackLarge: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, - lockRankHchanLeaf: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankHchanLeaf}, - lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, - lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans}, - lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap}, - lockRankGlobalAlloc: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankMheapSpecial}, - lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap}, - lockRankTraceStackTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankPollDesc, lockRankCpuprof, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRwmutexW, lockRankRwmutexR, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankTrace}, - lockRankPanic: {}, - lockRankDeadlock: {lockRankPanic, lockRankDeadlock}, - lockRankRaceFini: {lockRankPanic}, + lockRankSysmon: {}, + lockRankScavenge: {lockRankSysmon}, + lockRankForcegc: {lockRankSysmon}, + lockRankDefer: {}, + lockRankSweepWaiters: {}, + lockRankAssistQueue: {}, + lockRankSweep: {}, + lockRankTestR: {}, + lockRankTestW: {}, + lockRankAllocmW: {}, + lockRankExecW: {}, + lockRankCpuprof: {}, + lockRankPollDesc: {}, + lockRankWakeableSleep: {}, + lockRankAllocmR: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep}, + lockRankExecR: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep}, + lockRankSched: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR}, + lockRankAllg: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched}, + lockRankAllp: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched}, + lockRankTimers: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllp, lockRankTimers}, + lockRankNetpollInit: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllp, lockRankTimers}, + lockRankHchan: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankWakeableSleep, lockRankHchan}, + lockRankNotifyList: {}, + lockRankSudog: {lockRankSysmon, lockRankScavenge, lockRankSweep, lockRankTestR, lockRankWakeableSleep, lockRankHchan, lockRankNotifyList}, + lockRankRoot: {}, + lockRankItab: {}, + lockRankReflectOffs: {lockRankItab}, + lockRankUserArenaState: {}, + lockRankTraceBuf: {lockRankSysmon, lockRankScavenge}, + lockRankTraceStrings: {lockRankSysmon, lockRankScavenge, lockRankTraceBuf}, + lockRankFin: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, + lockRankSpanSetSpine: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, + lockRankMspanSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, + lockRankGcBitsArenas: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankMspanSpecial}, + lockRankProfInsert: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProfBlock: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProfMemActive: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings}, + lockRankProfMemFuture: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankHchan, lockRankNotifyList, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankProfMemActive}, + lockRankGscan: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture}, + lockRankStackpool: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, + lockRankStackLarge: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, + lockRankHchanLeaf: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankHchanLeaf}, + lockRankWbufSpans: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan}, + lockRankMheap: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans}, + lockRankMheapSpecial: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap}, + lockRankGlobalAlloc: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankMheapSpecial}, + lockRankTrace: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap}, + lockRankTraceStackTab: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankDefer, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR, lockRankExecR, lockRankSched, lockRankAllg, lockRankAllp, lockRankTimers, lockRankNetpollInit, lockRankHchan, lockRankNotifyList, lockRankSudog, lockRankRoot, lockRankItab, lockRankReflectOffs, lockRankUserArenaState, lockRankTraceBuf, lockRankTraceStrings, lockRankFin, lockRankSpanSetSpine, lockRankMspanSpecial, lockRankGcBitsArenas, lockRankProfInsert, lockRankProfBlock, lockRankProfMemActive, lockRankProfMemFuture, lockRankGscan, lockRankStackpool, lockRankStackLarge, lockRankWbufSpans, lockRankMheap, lockRankTrace}, + lockRankPanic: {}, + lockRankDeadlock: {lockRankPanic, lockRankDeadlock}, + lockRankRaceFini: {lockRankPanic}, + lockRankAllocmRInternal: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankAllocmW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankAllocmR}, + lockRankExecRInternal: {lockRankSysmon, lockRankScavenge, lockRankForcegc, lockRankSweepWaiters, lockRankAssistQueue, lockRankSweep, lockRankTestR, lockRankExecW, lockRankCpuprof, lockRankPollDesc, lockRankWakeableSleep, lockRankExecR}, + lockRankTestRInternal: {lockRankTestR, lockRankTestW}, } diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index ce03114edc..e2cb2e456e 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -1472,7 +1472,7 @@ func fastexprand(mean int) int32 { // x = -log_e(q) * mean // x = log_2(q) * (-log_e(2)) * mean ; Using log_2 for efficiency const randomBitCount = 26 - q := fastrandn(1< 0 { qlog = 0 @@ -1490,7 +1490,7 @@ func nextSampleNoFP() uintptr { rate = 0x3fffffff } if rate != 0 { - return uintptr(fastrandn(uint32(2 * rate))) + return uintptr(cheaprandn(uint32(2 * rate))) } return 0 } diff --git a/src/runtime/map.go b/src/runtime/map.go index 6f5623b102..cd3f838fa1 100644 --- a/src/runtime/map.go +++ b/src/runtime/map.go @@ -238,8 +238,8 @@ func (h *hmap) incrnoverflow() { // as many overflow buckets as buckets. mask := uint32(1)<<(h.B-15) - 1 // Example: if h.B == 18, then mask == 7, - // and fastrand & 7 == 0 with probability 1/8. - if fastrand()&mask == 0 { + // and rand() & 7 == 0 with probability 1/8. + if uint32(rand())&mask == 0 { h.noverflow++ } } @@ -293,7 +293,7 @@ func makemap64(t *maptype, hint int64, h *hmap) *hmap { // at compile time and the map needs to be allocated on the heap. func makemap_small() *hmap { h := new(hmap) - h.hash0 = fastrand() + h.hash0 = uint32(rand()) return h } @@ -312,7 +312,7 @@ func makemap(t *maptype, hint int, h *hmap) *hmap { if h == nil { h = new(hmap) } - h.hash0 = fastrand() + h.hash0 = uint32(rand()) // Find the size parameter B which will hold the requested # of elements. // For hint < 0 overLoadFactor returns false since hint < bucketCnt. @@ -797,7 +797,7 @@ search: // Reset the hash seed to make it more difficult for attackers to // repeatedly trigger hash collisions. See issue 25237. if h.count == 0 { - h.hash0 = fastrand() + h.hash0 = uint32(rand()) } break search } @@ -843,12 +843,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) { } // decide where to start - var r uintptr - if h.B > 31-bucketCntBits { - r = uintptr(fastrand64()) - } else { - r = uintptr(fastrand()) - } + r := uintptr(rand()) it.startBucket = r & bucketMask(h.B) it.offset = uint8(r >> h.B & (bucketCnt - 1)) @@ -1032,7 +1027,7 @@ func mapclear(t *maptype, h *hmap) { // Reset the hash seed to make it more difficult for attackers to // repeatedly trigger hash collisions. See issue 25237. - h.hash0 = fastrand() + h.hash0 = uint32(rand()) // Keep the mapextra allocation but clear any extra information. if h.extra != nil { @@ -1436,8 +1431,7 @@ func reflectlite_maplen(h *hmap) int { return h.count } -const maxZero = 1024 // must match value in reflect/value.go:maxZero cmd/compile/internal/gc/walk.go:zeroValSize -var zeroVal [maxZero]byte +var zeroVal [abi.ZeroValSize]byte // mapinitnoop is a no-op function known the Go linker; if a given global // map (of the right size) is determined to be dead, the linker will @@ -1481,12 +1475,24 @@ func moveToBmap(t *maptype, h *hmap, dst *bmap, pos int, src *bmap) (*bmap, int) dst.tophash[pos] = src.tophash[i] if t.IndirectKey() { - *(*unsafe.Pointer)(dstK) = *(*unsafe.Pointer)(srcK) + srcK = *(*unsafe.Pointer)(srcK) + if t.NeedKeyUpdate() { + kStore := newobject(t.Key) + typedmemmove(t.Key, kStore, srcK) + srcK = kStore + } + // Note: if NeedKeyUpdate is false, then the memory + // used to store the key is immutable, so we can share + // it between the original map and its clone. + *(*unsafe.Pointer)(dstK) = srcK } else { typedmemmove(t.Key, dstK, srcK) } if t.IndirectElem() { - *(*unsafe.Pointer)(dstEle) = *(*unsafe.Pointer)(srcEle) + srcEle = *(*unsafe.Pointer)(srcEle) + eStore := newobject(t.Elem) + typedmemmove(t.Elem, eStore, srcEle) + *(*unsafe.Pointer)(dstEle) = eStore } else { typedmemmove(t.Elem, dstEle, srcEle) } @@ -1510,14 +1516,14 @@ func mapclone2(t *maptype, src *hmap) *hmap { fatal("concurrent map clone and map write") } - if src.B == 0 { + if src.B == 0 && !(t.IndirectKey() && t.NeedKeyUpdate()) && !t.IndirectElem() { + // Quick copy for small maps. dst.buckets = newobject(t.Bucket) dst.count = src.count typedmemmove(t.Bucket, dst.buckets, src.buckets) return dst } - //src.B != 0 if dst.B == 0 { dst.buckets = newobject(t.Bucket) } @@ -1565,6 +1571,8 @@ func mapclone2(t *maptype, src *hmap) *hmap { continue } + // oldB < dst.B, so a single source bucket may go to multiple destination buckets. + // Process entries one at a time. for srcBmap != nil { // move from oldBlucket to new bucket for i := uintptr(0); i < bucketCnt; i++ { @@ -1606,7 +1614,7 @@ func keys(m any, p unsafe.Pointer) { return } s := (*slice)(p) - r := int(fastrand()) + r := int(rand()) offset := uint8(r >> h.B & (bucketCnt - 1)) if h.B == 0 { copyKeys(t, h, (*bmap)(h.buckets), s, offset) @@ -1669,7 +1677,7 @@ func values(m any, p unsafe.Pointer) { return } s := (*slice)(p) - r := int(fastrand()) + r := int(rand()) offset := uint8(r >> h.B & (bucketCnt - 1)) if h.B == 0 { copyValues(t, h, (*bmap)(h.buckets), s, offset) diff --git a/src/runtime/map_fast32.go b/src/runtime/map_fast32.go index d10dca3e91..e1dd495365 100644 --- a/src/runtime/map_fast32.go +++ b/src/runtime/map_fast32.go @@ -348,7 +348,7 @@ search: // Reset the hash seed to make it more difficult for attackers to // repeatedly trigger hash collisions. See issue 25237. if h.count == 0 { - h.hash0 = fastrand() + h.hash0 = uint32(rand()) } break search } diff --git a/src/runtime/map_fast64.go b/src/runtime/map_fast64.go index d771e0b747..7ca35ec6cb 100644 --- a/src/runtime/map_fast64.go +++ b/src/runtime/map_fast64.go @@ -350,7 +350,7 @@ search: // Reset the hash seed to make it more difficult for attackers to // repeatedly trigger hash collisions. See issue 25237. if h.count == 0 { - h.hash0 = fastrand() + h.hash0 = uint32(rand()) } break search } diff --git a/src/runtime/map_faststr.go b/src/runtime/map_faststr.go index ef71da859a..22e1f61f06 100644 --- a/src/runtime/map_faststr.go +++ b/src/runtime/map_faststr.go @@ -376,7 +376,7 @@ search: // Reset the hash seed to make it more difficult for attackers to // repeatedly trigger hash collisions. See issue 25237. if h.count == 0 { - h.hash0 = fastrand() + h.hash0 = uint32(rand()) } break search } diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index a0402d2933..cdd1c5fc3b 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -441,7 +441,7 @@ func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) { } // countAlloc returns the number of objects allocated in span s by -// scanning the allocation bitmap. +// scanning the mark bitmap. func (s *mspan) countAlloc() int { count := 0 bytes := divRoundUp(uintptr(s.nelems), 8) diff --git a/src/runtime/mbitmap_allocheaders.go b/src/runtime/mbitmap_allocheaders.go index 33535a515a..2151c12b85 100644 --- a/src/runtime/mbitmap_allocheaders.go +++ b/src/runtime/mbitmap_allocheaders.go @@ -907,14 +907,14 @@ func heapSetType(x, dataSize uintptr, typ *_type, header **_type, span *mspan) ( if header == nil { maxIterBytes = dataSize } - off := alignUp(uintptr(fastrand())%dataSize, goarch.PtrSize) + off := alignUp(uintptr(cheaprand())%dataSize, goarch.PtrSize) size := dataSize - off if size == 0 { off -= goarch.PtrSize size += goarch.PtrSize } interior := x + off - size -= alignDown(uintptr(fastrand())%size, goarch.PtrSize) + size -= alignDown(uintptr(cheaprand())%size, goarch.PtrSize) if size == 0 { size = goarch.PtrSize } diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go index c9823d3011..d63c38c209 100644 --- a/src/runtime/mem_linux.go +++ b/src/runtime/mem_linux.go @@ -170,4 +170,12 @@ func sysMapOS(v unsafe.Pointer, n uintptr) { print("runtime: mmap(", v, ", ", n, ") returned ", p, ", ", err, "\n") throw("runtime: cannot map pages in arena address space") } + + // Disable huge pages if the GODEBUG for it is set. + // + // Note that there are a few sysHugePage calls that can override this, but + // they're all for GC metadata. + if debug.disablethp != 0 { + sysNoHugePageOS(v, n) + } } diff --git a/src/runtime/memclr_loong64.s b/src/runtime/memclr_loong64.s index 7bb6f3dfc9..313e4d4f33 100644 --- a/src/runtime/memclr_loong64.s +++ b/src/runtime/memclr_loong64.s @@ -6,37 +6,39 @@ #include "textflag.h" // func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) -TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 - MOVV ptr+0(FP), R6 - MOVV n+8(FP), R7 - ADDV R6, R7, R4 +TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 +#ifndef GOEXPERIMENT_regabiargs + MOVV ptr+0(FP), R4 + MOVV n+8(FP), R5 +#endif + ADDV R4, R5, R6 // if less than 8 bytes, do one byte at a time - SGTU $8, R7, R8 + SGTU $8, R5, R8 BNE R8, out // do one byte at a time until 8-aligned - AND $7, R6, R8 + AND $7, R4, R8 BEQ R8, words - MOVB R0, (R6) - ADDV $1, R6 + MOVB R0, (R4) + ADDV $1, R4 JMP -4(PC) words: // do 8 bytes at a time if there is room - ADDV $-7, R4, R7 + ADDV $-7, R6, R5 PCALIGN $16 - SGTU R7, R6, R8 + SGTU R5, R4, R8 BEQ R8, out - MOVV R0, (R6) - ADDV $8, R6 + MOVV R0, (R4) + ADDV $8, R4 JMP -4(PC) out: - BEQ R6, R4, done - MOVB R0, (R6) - ADDV $1, R6 + BEQ R4, R6, done + MOVB R0, (R4) + ADDV $1, R4 JMP -3(PC) done: RET diff --git a/src/runtime/memmove_loong64.s b/src/runtime/memmove_loong64.s index 0f139bcc13..5b7aeba698 100644 --- a/src/runtime/memmove_loong64.s +++ b/src/runtime/memmove_loong64.s @@ -7,10 +7,12 @@ // See memmove Go doc for important implementation constraints. // func memmove(to, from unsafe.Pointer, n uintptr) -TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24 +TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24 +#ifndef GOEXPERIMENT_regabiargs MOVV to+0(FP), R4 MOVV from+8(FP), R5 MOVV n+16(FP), R6 +#endif BNE R6, check RET diff --git a/src/runtime/metrics/description.go b/src/runtime/metrics/description.go index abe7440f10..19a7dbf07a 100644 --- a/src/runtime/metrics/description.go +++ b/src/runtime/metrics/description.go @@ -441,7 +441,7 @@ var allDesc = []Description{ }, { Name: "/sync/mutex/wait/total:seconds", - Description: "Approximate cumulative time goroutines have spent blocked on a sync.Mutex or sync.RWMutex. This metric is useful for identifying global changes in lock contention. Collect a mutex or block profile using the runtime/pprof package for more detailed contention data.", + Description: "Approximate cumulative time goroutines have spent blocked on a sync.Mutex, sync.RWMutex, or runtime-internal lock. This metric is useful for identifying global changes in lock contention. Collect a mutex or block profile using the runtime/pprof package for more detailed contention data.", Kind: KindFloat64, Cumulative: true, }, diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index 5be6c32bfa..fb2f44da29 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -8,7 +8,7 @@ /* Package metrics provides a stable interface to access implementation-defined metrics exported by the Go runtime. This package is similar to existing functions -like [runtime.ReadMemStats] and [debug.ReadGCStats], but significantly more general. +like [runtime.ReadMemStats] and [runtime/debug.ReadGCStats], but significantly more general. The set of metrics defined by this package may evolve as the runtime itself evolves, and also enables variation across Go implementations, whose relevant @@ -28,7 +28,8 @@ encouraged to use build tags, and although metrics may be deprecated and removed users should consider this to be an exceptional and rare event, coinciding with a very large change in a particular Go implementation. -Each metric key also has a "kind" that describes the format of the metric's value. +Each metric key also has a "kind" (see [ValueKind]) that describes the format of the +metric's value. In the interest of not breaking users of this package, the "kind" for a given metric is guaranteed not to change. If it must change, then a new metric will be introduced with a new key and a new "kind." @@ -314,6 +315,10 @@ Below is the full list of supported metrics, ordered lexicographically. The number of non-default behaviors executed by the crypto/tls package due to a non-default GODEBUG=tlsrsakex=... setting. + /godebug/non-default-behavior/tlsunsafeekm:events + The number of non-default behaviors executed by the crypto/tls + package due to a non-default GODEBUG=tlsunsafeekm=... setting. + /godebug/non-default-behavior/x509sha1:events The number of non-default behaviors executed by the crypto/x509 package due to a non-default GODEBUG=x509sha1=... setting. @@ -323,6 +328,11 @@ Below is the full list of supported metrics, ordered lexicographically. package due to a non-default GODEBUG=x509usefallbackroots=... setting. + /godebug/non-default-behavior/x509usepolicies:events + The number of non-default behaviors executed by the crypto/x509 + package due to a non-default GODEBUG=x509usepolicies=... + setting. + /godebug/non-default-behavior/zipinsecurepath:events The number of non-default behaviors executed by the archive/zip package due to a non-default GODEBUG=zipinsecurepath=... @@ -443,10 +453,10 @@ Below is the full list of supported metrics, ordered lexicographically. monotonically. /sync/mutex/wait/total:seconds - Approximate cumulative time goroutines have spent blocked - on a sync.Mutex or sync.RWMutex. This metric is useful for - identifying global changes in lock contention. Collect a mutex - or block profile using the runtime/pprof package for more - detailed contention data. + Approximate cumulative time goroutines have spent blocked on a + sync.Mutex, sync.RWMutex, or runtime-internal lock. This metric + is useful for identifying global changes in lock contention. + Collect a mutex or block profile using the runtime/pprof package + for more detailed contention data. */ package metrics diff --git a/src/runtime/metrics_test.go b/src/runtime/metrics_test.go index 56f3340b16..d7f41334cd 100644 --- a/src/runtime/metrics_test.go +++ b/src/runtime/metrics_test.go @@ -9,6 +9,7 @@ import ( "fmt" "internal/goexperiment" "internal/profile" + "internal/testenv" "os" "reflect" "runtime" @@ -46,7 +47,7 @@ func TestReadMetrics(t *testing.T) { oldLimit := debug.SetMemoryLimit(limit) defer debug.SetMemoryLimit(oldLimit) - // Set an GC percent to check the metric for it + // Set a GC percent to check the metric for it gcPercent := 99 oldGCPercent := debug.SetGCPercent(gcPercent) defer debug.SetGCPercent(oldGCPercent) @@ -947,6 +948,8 @@ func TestSchedPauseMetrics(t *testing.T) { } func TestRuntimeLockMetricsAndProfile(t *testing.T) { + testenv.SkipFlaky(t, 64253) + old := runtime.SetMutexProfileFraction(0) // enabled during sub-tests defer runtime.SetMutexProfileFraction(old) if old != 0 { @@ -956,12 +959,18 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { { before := os.Getenv("GODEBUG") for _, s := range strings.Split(before, ",") { - if strings.HasPrefix(s, "profileruntimelocks=") { + if strings.HasPrefix(s, "runtimecontentionstacks=") { t.Logf("GODEBUG includes explicit setting %q", s) } } defer func() { os.Setenv("GODEBUG", before) }() - os.Setenv("GODEBUG", fmt.Sprintf("%s,profileruntimelocks=1", before)) + os.Setenv("GODEBUG", fmt.Sprintf("%s,runtimecontentionstacks=1", before)) + } + + t.Logf("NumCPU %d", runtime.NumCPU()) + t.Logf("GOMAXPROCS %d", runtime.GOMAXPROCS(0)) + if minCPU := 2; runtime.NumCPU() < minCPU { + t.Skipf("creating and observing contention on runtime-internal locks requires NumCPU >= %d", minCPU) } loadProfile := func(t *testing.T) *profile.Profile { @@ -1012,7 +1021,7 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { return metricGrowth, profileGrowth, p } - testcase := func(stk []string, workers int, fn func() bool) func(t *testing.T) (metricGrowth, profileGrowth float64, n, value int64) { + testcase := func(strictTiming bool, acceptStacks [][]string, workers int, fn func() bool) func(t *testing.T) (metricGrowth, profileGrowth float64, n, value int64) { return func(t *testing.T) (metricGrowth, profileGrowth float64, n, value int64) { metricGrowth, profileGrowth, p := measureDelta(t, func() { var started, stopped sync.WaitGroup @@ -1037,7 +1046,9 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { if profileGrowth == 0 { t.Errorf("no increase in mutex profile") } - if metricGrowth == 0 { + if metricGrowth == 0 && strictTiming { + // If the critical section is very short, systems with low timer + // resolution may be unable to measure it via nanotime. t.Errorf("no increase in /sync/mutex/wait/total:seconds metric") } // This comparison is possible because the time measurements in support of @@ -1048,19 +1059,24 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { t.Logf("lock contention growth in runtime/pprof's view (%fs)", profileGrowth) t.Logf("lock contention growth in runtime/metrics' view (%fs)", metricGrowth) - if goexperiment.StaticLockRanking { - if !slices.ContainsFunc(stk, func(s string) bool { - return s == "runtime.systemstack" || s == "runtime.mcall" || s == "runtime.mstart" - }) { - // stk is a call stack that is still on the user stack when - // it calls runtime.unlock. Add the extra function that - // we'll see, when the static lock ranking implementation of - // runtime.unlockWithRank switches to the system stack. - stk = append([]string{"runtime.unlockWithRank"}, stk...) + acceptStacks = append([][]string(nil), acceptStacks...) + for i, stk := range acceptStacks { + if goexperiment.StaticLockRanking { + if !slices.ContainsFunc(stk, func(s string) bool { + return s == "runtime.systemstack" || s == "runtime.mcall" || s == "runtime.mstart" + }) { + // stk is a call stack that is still on the user stack when + // it calls runtime.unlock. Add the extra function that + // we'll see, when the static lock ranking implementation of + // runtime.unlockWithRank switches to the system stack. + stk = append([]string{"runtime.unlockWithRank"}, stk...) + } } + acceptStacks[i] = stk } var stks [][]string + values := make([][2]int64, len(acceptStacks)) for _, s := range p.Sample { var have []string for _, loc := range s.Location { @@ -1069,18 +1085,26 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { } } stks = append(stks, have) - if slices.Equal(have, stk) { - n += s.Value[0] - value += s.Value[1] + for i, stk := range acceptStacks { + if slices.Equal(have, stk) { + values[i][0] += s.Value[0] + values[i][1] += s.Value[1] + } } } - t.Logf("stack %v has samples totaling n=%d value=%d", stk, n, value) + for i, stk := range acceptStacks { + n += values[i][0] + value += values[i][1] + t.Logf("stack %v has samples totaling n=%d value=%d", stk, values[i][0], values[i][1]) + } if n == 0 && value == 0 { t.Logf("profile:\n%s", p) for _, have := range stks { t.Logf("have stack %v", have) } - t.Errorf("want stack %v", stk) + for _, stk := range acceptStacks { + t.Errorf("want stack %v", stk) + } } return metricGrowth, profileGrowth, n, value @@ -1092,7 +1116,7 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { t.Run("runtime.lock", func(t *testing.T) { mus := make([]runtime.Mutex, 100) var needContention atomic.Int64 - delay := 10 * time.Microsecond + delay := 100 * time.Microsecond // large relative to system noise, for comparison between clocks delayMicros := delay.Microseconds() // The goroutine that acquires the lock will only proceed when it @@ -1132,18 +1156,18 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { return true } - stk := []string{ + stks := [][]string{{ "runtime.unlock", "runtime_test." + name + ".func5.1", "runtime_test.(*contentionWorker).run", - } + }} t.Run("sample-1", func(t *testing.T) { old := runtime.SetMutexProfileFraction(1) defer runtime.SetMutexProfileFraction(old) needContention.Store(int64(len(mus) - 1)) - metricGrowth, profileGrowth, n, _ := testcase(stk, workers, fn)(t) + metricGrowth, profileGrowth, n, _ := testcase(true, stks, workers, fn)(t) if have, want := metricGrowth, delay.Seconds()*float64(len(mus)); have < want { // The test imposes a delay with usleep, verified with calls to @@ -1167,7 +1191,7 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { defer runtime.SetMutexProfileFraction(old) needContention.Store(int64(len(mus) - 1)) - metricGrowth, profileGrowth, n, _ := testcase(stk, workers, fn)(t) + metricGrowth, profileGrowth, n, _ := testcase(true, stks, workers, fn)(t) // With 100 trials and profile fraction of 2, we expect to capture // 50 samples. Allow the test to pass if we get at least 20 samples; @@ -1203,12 +1227,18 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { } var sem uint32 = 1 + var tries atomic.Int32 + tries.Store(10_000_000) // prefer controlled failure to timeout var sawContention atomic.Int32 - var need int32 = 1000 // counteract low timer resolution by requiring more samples + var need int32 = 1 fn := func() bool { if sawContention.Load() >= need { return false } + if tries.Add(-1) < 0 { + return false + } + runtime.Semacquire(&sem) runtime.Semrelease1(&sem, false, 0) if runtime.MutexContended(runtime.SemRootLock(&sem)) { @@ -1217,19 +1247,32 @@ func TestRuntimeLockMetricsAndProfile(t *testing.T) { return true } - stk := []string{ - "runtime.unlock", - "runtime.semrelease1", - "runtime_test.TestRuntimeLockMetricsAndProfile.func6.1", - "runtime_test.(*contentionWorker).run", + stks := [][]string{ + { + "runtime.unlock", + "runtime.semrelease1", + "runtime_test.TestRuntimeLockMetricsAndProfile.func6.1", + "runtime_test.(*contentionWorker).run", + }, + { + "runtime.unlock", + "runtime.semacquire1", + "runtime.semacquire", + "runtime_test.TestRuntimeLockMetricsAndProfile.func6.1", + "runtime_test.(*contentionWorker).run", + }, } // Verify that we get call stack we expect, with anything more than zero - // nanoseconds / zero samples. The duration of each contention event is - // too small relative to the expected overhead for us to verify its - // value more directly. Leave that to the explicit lock/unlock test. + // cycles / zero samples. The duration of each contention event is too + // small relative to the expected overhead for us to verify its value + // more directly. Leave that to the explicit lock/unlock test. - testcase(stk, workers, fn)(t) + testcase(false, stks, workers, fn)(t) + + if remaining := tries.Load(); remaining >= 0 { + t.Logf("finished test early (%d tries remaining)", remaining) + } }) } diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index be501e6fca..7d9d547c0f 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -300,6 +300,27 @@ func isGoPointerWithoutSpan(p unsafe.Pointer) bool { return false } +// blockUntilEmptyFinalizerQueue blocks until either the finalizer +// queue is emptied (and the finalizers have executed) or the timeout +// is reached. Returns true if the finalizer queue was emptied. +// This is used by the runtime and sync tests. +func blockUntilEmptyFinalizerQueue(timeout int64) bool { + start := nanotime() + for nanotime()-start < timeout { + lock(&finlock) + // We know the queue has been drained when both finq is nil + // and the finalizer g has stopped executing. + empty := finq == nil + empty = empty && readgstatus(fing) == _Gwaiting && fing.waitreason == waitReasonFinalizerWait + unlock(&finlock) + if empty { + return true + } + Gosched() + } + return false +} + // SetFinalizer sets the finalizer associated with obj to the provided // finalizer function. When the garbage collector finds an unreachable block // with an associated finalizer, it clears the association and runs diff --git a/src/runtime/mgcpacer.go b/src/runtime/mgcpacer.go index 716e3efccc..e9af3d60cd 100644 --- a/src/runtime/mgcpacer.go +++ b/src/runtime/mgcpacer.go @@ -712,7 +712,7 @@ func (c *gcControllerState) enlistWorker() { } myID := gp.m.p.ptr().id for tries := 0; tries < 5; tries++ { - id := int32(fastrandn(uint32(gomaxprocs - 1))) + id := int32(cheaprandn(uint32(gomaxprocs - 1))) if id >= myID { id++ } @@ -1377,7 +1377,7 @@ func (c *gcControllerState) needIdleMarkWorker() bool { return n < max } -// removeIdleMarkWorker must be called when an new idle mark worker stops executing. +// removeIdleMarkWorker must be called when a new idle mark worker stops executing. func (c *gcControllerState) removeIdleMarkWorker() { for { old := c.idleMarkWorkers.Load() diff --git a/src/runtime/mgcscavenge.go b/src/runtime/mgcscavenge.go index e6725b4622..86c2103f18 100644 --- a/src/runtime/mgcscavenge.go +++ b/src/runtime/mgcscavenge.go @@ -307,7 +307,7 @@ type scavengerState struct { // See sleepRatio for more details. sleepController piController - // cooldown is the time left in nanoseconds during which we avoid + // controllerCooldown is the time left in nanoseconds during which we avoid // using the controller and we hold sleepRatio at a conservative // value. Used if the controller's assumptions fail to hold. controllerCooldown int64 diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go index 2d84c0d07c..0fbb23d573 100644 --- a/src/runtime/mgcsweep.go +++ b/src/runtime/mgcsweep.go @@ -796,7 +796,10 @@ func (sl *sweepLocked) sweep(preserve bool) bool { s := spanOf(uintptr(unsafe.Pointer(s.largeType))) mheap_.freeManual(s, spanAllocPtrScalarBits) }) - s.largeType = nil + // Make sure to zero this pointer without putting the old + // value in a write buffer, as the old value might be an + // invalid pointer. See arena.go:(*mheap).allocUserArenaChunk. + *(*uintptr)(unsafe.Pointer(&s.largeType)) = 0 } // Count the free in the consistent, external stats. diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 0bbda4aa3b..0069328346 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -399,7 +399,7 @@ type mspan struct { _ sys.NotInHeap next *mspan // next span in list, or nil if none prev *mspan // previous span in list, or nil if none - list *mSpanList // For debugging. TODO: Remove. + list *mSpanList // For debugging. startAddr uintptr // address of first byte of span aka s.base() npages uintptr // number of pages in span diff --git a/src/runtime/mkduff.go b/src/runtime/mkduff.go index e8d4fcc93e..b7f07b5087 100644 --- a/src/runtime/mkduff.go +++ b/src/runtime/mkduff.go @@ -181,21 +181,21 @@ func zeroLOONG64(w io.Writer) { // R0: always zero // R19 (aka REGRT1): ptr to memory to be zeroed // On return, R19 points to the last zeroed dword. - 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, "\tMOVV\tR0, (R19)") - fmt.Fprintln(w, "\tADDV\t$8, R19") + fmt.Fprintln(w, "\tMOVV\tR0, (R20)") + fmt.Fprintln(w, "\tADDV\t$8, R20") } fmt.Fprintln(w, "\tRET") } func copyLOONG64(w io.Writer) { - 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, "\tMOVV\t(R19), R30") - fmt.Fprintln(w, "\tADDV\t$8, R19") - fmt.Fprintln(w, "\tMOVV\tR30, (R20)") + fmt.Fprintln(w, "\tMOVV\t(R20), R30") fmt.Fprintln(w, "\tADDV\t$8, R20") + fmt.Fprintln(w, "\tMOVV\tR30, (R21)") + fmt.Fprintln(w, "\tADDV\t$8, R21") fmt.Fprintln(w) } fmt.Fprintln(w, "\tRET") diff --git a/src/runtime/mklockrank.go b/src/runtime/mklockrank.go index 4cb232b1ba..c8e34d8f00 100644 --- a/src/runtime/mklockrank.go +++ b/src/runtime/mklockrank.go @@ -52,30 +52,40 @@ NONE < assistQueue, sweep; +# Test only +NONE < testR, testW; + # Scheduler, timers, netpoll -NONE < pollDesc, cpuprof; +NONE < + allocmW, + execW, + cpuprof, + pollDesc, + wakeableSleep; assistQueue, cpuprof, forcegc, pollDesc, # pollDesc can interact with timers, which can lock sched. scavenge, sweep, - sweepWaiters + sweepWaiters, + testR, + wakeableSleep +# Above SCHED are things that can call into the scheduler. +< SCHED +# Below SCHED is the scheduler implementation. +< allocmR, + execR < sched; sched < allg, allp; -allp < timers; -timers < wakeableSleep; +allp, wakeableSleep < timers; timers < netpollInit; # Channels -scavenge, sweep < hchan; +scavenge, sweep, testR, wakeableSleep < hchan; NONE < notifyList; hchan, notifyList < sudog; -# RWMutex -NONE < rwmutexW; -rwmutexW, sysmon < rwmutexR; - # Semaphores NONE < root; @@ -100,6 +110,9 @@ traceBuf < traceStrings; # Malloc allg, + allocmR, + execR, # May grow stack + execW, # May allocate after BeforeFork hchan, notifyList, reflectOffs, @@ -136,7 +149,7 @@ gcBitsArenas, < STACKGROW # Below STACKGROW is the stack allocator/copying implementation. < gscan; -gscan, rwmutexR < stackpool; +gscan < stackpool; gscan < stackLarge; # Generally, hchan must be acquired before gscan. But in one case, # where we suspend a G and then shrink its stack, syncadjustsudogs @@ -189,6 +202,20 @@ NONE < panic; panic < deadlock; # raceFini is only held while exiting. panic < raceFini; + +# RWMutex internal read lock + +allocmR, + allocmW +< allocmRInternal; + +execR, + execW +< execRInternal; + +testR, + testW +< testRInternal; ` // cyclicRanks lists lock ranks that allow multiple locks of the same diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index a96ae59c15..17544d6b21 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -317,11 +317,11 @@ func genARM() { p("MOVW.W R14, -%d(R13)", lfp.stack) // allocate frame, save LR l.save() - p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp") // test goarm, and skip FP registers if goarm=5. + p("MOVB ·goarmsoftfp(SB), R0\nCMP $0, R0\nBNE nofp") // test goarmsoftfp, and skip FP registers if goarmsoftfp!=0. lfp.save() label("nofp:") p("CALL ·asyncPreempt2(SB)") - p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp2") // test goarm, and skip FP registers if goarm=5. + p("MOVB ·goarmsoftfp(SB), R0\nCMP $0, R0\nBNE nofp2") // test goarmsoftfp, and skip FP registers if goarmsoftfp!=0. lfp.restore() label("nofp2:") l.restore() diff --git a/src/runtime/mpagealloc_64bit.go b/src/runtime/mpagealloc_64bit.go index 1418831a50..36cd222360 100644 --- a/src/runtime/mpagealloc_64bit.go +++ b/src/runtime/mpagealloc_64bit.go @@ -209,23 +209,20 @@ func (s *scavengeIndex) sysGrow(base, limit uintptr, sysStat *sysMemStat) uintpt haveMax := s.max.Load() needMin := alignDown(uintptr(chunkIndex(base)), physPageSize/scSize) needMax := alignUp(uintptr(chunkIndex(limit)), physPageSize/scSize) - // Extend the range down to what we have, if there's no overlap. + + // We need a contiguous range, so extend the range if there's no overlap. if needMax < haveMin { needMax = haveMin } if haveMax != 0 && needMin > haveMax { needMin = haveMax } - have := makeAddrRange( - // Avoid a panic from indexing one past the last element. - uintptr(unsafe.Pointer(&s.chunks[0]))+haveMin*scSize, - uintptr(unsafe.Pointer(&s.chunks[0]))+haveMax*scSize, - ) - need := makeAddrRange( - // Avoid a panic from indexing one past the last element. - uintptr(unsafe.Pointer(&s.chunks[0]))+needMin*scSize, - uintptr(unsafe.Pointer(&s.chunks[0]))+needMax*scSize, - ) + + // Avoid a panic from indexing one past the last element. + chunksBase := uintptr(unsafe.Pointer(&s.chunks[0])) + have := makeAddrRange(chunksBase+haveMin*scSize, chunksBase+haveMax*scSize) + need := makeAddrRange(chunksBase+needMin*scSize, chunksBase+needMax*scSize) + // Subtract any overlap from rounding. We can't re-map memory because // it'll be zeroed. need = need.subtract(have) @@ -235,10 +232,10 @@ func (s *scavengeIndex) sysGrow(base, limit uintptr, sysStat *sysMemStat) uintpt sysMap(unsafe.Pointer(need.base.addr()), need.size(), sysStat) sysUsed(unsafe.Pointer(need.base.addr()), need.size(), need.size()) // Update the indices only after the new memory is valid. - if haveMin == 0 || needMin < haveMin { + if haveMax == 0 || needMin < haveMin { s.min.Store(needMin) } - if haveMax == 0 || needMax > haveMax { + if needMax > haveMax { s.max.Store(needMax) } } diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go index b1930b3020..e5c11c58c9 100644 --- a/src/runtime/mprof.go +++ b/src/runtime/mprof.go @@ -498,7 +498,7 @@ func blockevent(cycles int64, skip int) { // blocksampled returns true for all events where cycles >= rate. Shorter // events have a cycles/rate random chance of returning true. func blocksampled(cycles, rate int64) bool { - if rate <= 0 || (rate > cycles && int64(fastrand())%rate > cycles) { + if rate <= 0 || (rate > cycles && cheaprand64()%rate > cycles) { return false } return true @@ -552,7 +552,7 @@ func saveblockevent(cycles, rate int64, skip int, which bucketType) { // previous lock call took (like the user-space "block" profile). // // Thus, reporting the call stacks of runtime-internal lock contention is -// guarded by GODEBUG for now. Set GODEBUG=profileruntimelocks=1 to enable. +// guarded by GODEBUG for now. Set GODEBUG=runtimecontentionstacks=1 to enable. // // TODO(rhysh): plumb through the delay duration, remove GODEBUG, update comment // @@ -589,11 +589,11 @@ func (lt *lockTimer) begin() { if rate != 0 && rate < lt.timeRate { lt.timeRate = rate } - if int64(fastrand())%lt.timeRate == 0 { + if int64(cheaprand())%lt.timeRate == 0 { lt.timeStart = nanotime() } - if rate > 0 && int64(fastrand())%rate == 0 { + if rate > 0 && int64(cheaprand())%rate == 0 { lt.tickStart = cputicks() } } @@ -644,9 +644,9 @@ func (prof *mLockProfile) recordLock(cycles int64, l *mutex) { if prev := prof.cycles; prev > 0 { // We can only store one call stack for runtime-internal lock contention // on this M, and we've already got one. Decide which should stay, and - // add the other to the report for runtime._LostContendedLock. - prevScore := fastrand64() % uint64(prev) - thisScore := fastrand64() % uint64(cycles) + // add the other to the report for runtime._LostContendedRuntimeLock. + prevScore := uint64(cheaprand64()) % uint64(prev) + thisScore := uint64(cheaprand64()) % uint64(cycles) if prevScore > thisScore { prof.cyclesLost += cycles return @@ -690,8 +690,8 @@ func (prof *mLockProfile) captureStack() { } prof.pending = 0 - if debug.profileruntimelocks.Load() == 0 { - prof.stack[0] = abi.FuncPCABIInternal(_LostContendedLock) + sys.PCQuantum + if debug.runtimeContentionStacks.Load() == 0 { + prof.stack[0] = abi.FuncPCABIInternal(_LostContendedRuntimeLock) + sys.PCQuantum prof.stack[1] = 0 return } @@ -733,7 +733,7 @@ func (prof *mLockProfile) store() { saveBlockEventStack(cycles, rate, prof.stack[:nstk], mutexProfile) if lost > 0 { lostStk := [...]uintptr{ - abi.FuncPCABIInternal(_LostContendedLock) + sys.PCQuantum, + abi.FuncPCABIInternal(_LostContendedRuntimeLock) + sys.PCQuantum, } saveBlockEventStack(lost, rate, lostStk[:], mutexProfile) } @@ -790,7 +790,7 @@ func mutexevent(cycles int64, skip int) { cycles = 0 } rate := int64(atomic.Load64(&mutexprofilerate)) - if rate > 0 && int64(fastrand())%rate == 0 { + if rate > 0 && cheaprand64()%rate == 0 { saveblockevent(cycles, rate, skip+1, mutexProfile) } } diff --git a/src/runtime/msan_amd64.s b/src/runtime/msan_amd64.s index 89ed3048d0..a1dc388063 100644 --- a/src/runtime/msan_amd64.s +++ b/src/runtime/msan_amd64.s @@ -28,7 +28,7 @@ // Called from msanread. TEXT runtime·domsanread(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 // void __msan_read_go(void *addr, uintptr_t sz); MOVQ $__msan_read_go(SB), AX JMP msancall<>(SB) @@ -37,7 +37,7 @@ TEXT runtime·domsanread(SB), NOSPLIT, $0-16 // Called from instrumented code. TEXT runtime·msanwrite(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 // void __msan_write_go(void *addr, uintptr_t sz); MOVQ $__msan_write_go(SB), AX JMP msancall<>(SB) @@ -45,7 +45,7 @@ TEXT runtime·msanwrite(SB), NOSPLIT, $0-16 // func runtime·msanmalloc(addr unsafe.Pointer, sz uintptr) TEXT runtime·msanmalloc(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 // void __msan_malloc_go(void *addr, uintptr_t sz); MOVQ $__msan_malloc_go(SB), AX JMP msancall<>(SB) @@ -53,7 +53,7 @@ TEXT runtime·msanmalloc(SB), NOSPLIT, $0-16 // func runtime·msanfree(addr unsafe.Pointer, sz uintptr) TEXT runtime·msanfree(SB), NOSPLIT, $0-16 MOVQ addr+0(FP), RARG0 - MOVQ size+8(FP), RARG1 + MOVQ sz+8(FP), RARG1 // void __msan_free_go(void *addr, uintptr_t sz); MOVQ $__msan_free_go(SB), AX JMP msancall<>(SB) @@ -62,7 +62,7 @@ TEXT runtime·msanfree(SB), NOSPLIT, $0-16 TEXT runtime·msanmove(SB), NOSPLIT, $0-24 MOVQ dst+0(FP), RARG0 MOVQ src+8(FP), RARG1 - MOVQ size+16(FP), RARG2 + MOVQ sz+16(FP), RARG2 // void __msan_memmove(void *dst, void *src, uintptr_t sz); MOVQ $__msan_memmove(SB), AX JMP msancall<>(SB) diff --git a/src/runtime/msan_arm64.s b/src/runtime/msan_arm64.s index b9eff34ab6..ce475cf44a 100644 --- a/src/runtime/msan_arm64.s +++ b/src/runtime/msan_arm64.s @@ -16,7 +16,7 @@ // Called from msanread. TEXT runtime·domsanread(SB), NOSPLIT, $0-16 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 // void __msan_read_go(void *addr, uintptr_t sz); MOVD $__msan_read_go(SB), FARG JMP msancall<>(SB) @@ -25,7 +25,7 @@ TEXT runtime·domsanread(SB), NOSPLIT, $0-16 // Called from instrumented code. TEXT runtime·msanwrite(SB), NOSPLIT, $0-16 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 // void __msan_write_go(void *addr, uintptr_t sz); MOVD $__msan_write_go(SB), FARG JMP msancall<>(SB) @@ -33,7 +33,7 @@ TEXT runtime·msanwrite(SB), NOSPLIT, $0-16 // func runtime·msanmalloc(addr unsafe.Pointer, sz uintptr) TEXT runtime·msanmalloc(SB), NOSPLIT, $0-16 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 // void __msan_malloc_go(void *addr, uintptr_t sz); MOVD $__msan_malloc_go(SB), FARG JMP msancall<>(SB) @@ -41,7 +41,7 @@ TEXT runtime·msanmalloc(SB), NOSPLIT, $0-16 // func runtime·msanfree(addr unsafe.Pointer, sz uintptr) TEXT runtime·msanfree(SB), NOSPLIT, $0-16 MOVD addr+0(FP), RARG0 - MOVD size+8(FP), RARG1 + MOVD sz+8(FP), RARG1 // void __msan_free_go(void *addr, uintptr_t sz); MOVD $__msan_free_go(SB), FARG JMP msancall<>(SB) @@ -50,7 +50,7 @@ TEXT runtime·msanfree(SB), NOSPLIT, $0-16 TEXT runtime·msanmove(SB), NOSPLIT, $0-24 MOVD dst+0(FP), RARG0 MOVD src+8(FP), RARG1 - MOVD size+16(FP), RARG2 + MOVD sz+16(FP), RARG2 // void __msan_memmove(void *dst, void *src, uintptr_t sz); MOVD $__msan_memmove(SB), FARG JMP msancall<>(SB) diff --git a/src/runtime/msan_loong64.s b/src/runtime/msan_loong64.s index f69fb45454..b9fa5fd120 100644 --- a/src/runtime/msan_loong64.s +++ b/src/runtime/msan_loong64.s @@ -16,7 +16,7 @@ // Called from msanread. TEXT runtime·domsanread(SB), NOSPLIT, $0-16 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 // void __msan_read_go(void *addr, uintptr_t sz); MOVV $__msan_read_go(SB), FARG JMP msancall<>(SB) @@ -25,7 +25,7 @@ TEXT runtime·domsanread(SB), NOSPLIT, $0-16 // Called from instrumented code. TEXT runtime·msanwrite(SB), NOSPLIT, $0-16 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 // void __msan_write_go(void *addr, uintptr_t sz); MOVV $__msan_write_go(SB), FARG JMP msancall<>(SB) @@ -33,7 +33,7 @@ TEXT runtime·msanwrite(SB), NOSPLIT, $0-16 // func runtime·msanmalloc(addr unsafe.Pointer, sz uintptr) TEXT runtime·msanmalloc(SB), NOSPLIT, $0-16 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 // void __msan_malloc_go(void *addr, uintptr_t sz); MOVV $__msan_malloc_go(SB), FARG JMP msancall<>(SB) @@ -41,7 +41,7 @@ TEXT runtime·msanmalloc(SB), NOSPLIT, $0-16 // func runtime·msanfree(addr unsafe.Pointer, sz uintptr) TEXT runtime·msanfree(SB), NOSPLIT, $0-16 MOVV addr+0(FP), RARG0 - MOVV size+8(FP), RARG1 + MOVV sz+8(FP), RARG1 // void __msan_free_go(void *addr, uintptr_t sz); MOVV $__msan_free_go(SB), FARG JMP msancall<>(SB) @@ -50,7 +50,7 @@ TEXT runtime·msanfree(SB), NOSPLIT, $0-16 TEXT runtime·msanmove(SB), NOSPLIT, $0-24 MOVV dst+0(FP), RARG0 MOVV src+8(FP), RARG1 - MOVV size+16(FP), RARG2 + MOVV sz+16(FP), RARG2 // void __msan_memmove(void *dst, void *src, uintptr_t sz); MOVV $__msan_memmove(SB), FARG JMP msancall<>(SB) diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index 874b08ae3a..87afec47c8 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -361,6 +361,11 @@ func ReadMemStats(m *MemStats) { startTheWorld(stw) } +// doubleCheckReadMemStats controls a double-check mode for ReadMemStats that +// ensures consistency between the values that ReadMemStats is using and the +// runtime-internal stats. +var doubleCheckReadMemStats = false + // readmemstats_m populates stats for internal runtime values. // // The world must be stopped. @@ -435,56 +440,65 @@ func readmemstats_m(stats *MemStats) { heapGoal := gcController.heapGoal() - // The world is stopped, so the consistent stats (after aggregation) - // should be identical to some combination of memstats. In particular: - // - // * memstats.heapInUse == inHeap - // * memstats.heapReleased == released - // * memstats.heapInUse + memstats.heapFree == committed - inStacks - inWorkBufs - inPtrScalarBits - // * memstats.totalAlloc == totalAlloc - // * memstats.totalFree == totalFree - // - // 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 gcController.heapInUse.load() != uint64(consStats.inHeap) { - print("runtime: heapInUse=", gcController.heapInUse.load(), "\n") - print("runtime: consistent value=", consStats.inHeap, "\n") - throw("heapInUse and consistent stats are not equal") - } - if gcController.heapReleased.load() != uint64(consStats.released) { - print("runtime: heapReleased=", gcController.heapReleased.load(), "\n") - print("runtime: consistent value=", consStats.released, "\n") - throw("heapReleased and consistent stats are not equal") - } - heapRetained := gcController.heapInUse.load() + gcController.heapFree.load() - consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) - if heapRetained != consRetained { - print("runtime: global value=", heapRetained, "\n") - print("runtime: consistent value=", consRetained, "\n") - throw("measures of the retained heap are not equal") - } - if gcController.totalAlloc.Load() != totalAlloc { - print("runtime: totalAlloc=", gcController.totalAlloc.Load(), "\n") - print("runtime: consistent value=", totalAlloc, "\n") - throw("totalAlloc and consistent stats are not equal") - } - if gcController.totalFree.Load() != totalFree { - print("runtime: totalFree=", gcController.totalFree.Load(), "\n") - print("runtime: consistent value=", totalFree, "\n") - throw("totalFree and consistent stats are not equal") - } - // Also check that mappedReady lines up with totalMapped - released. - // This isn't really the same type of "make sure consistent stats line up" situation, - // but this is an opportune time to check. - if gcController.mappedReady.Load() != totalMapped-uint64(consStats.released) { - print("runtime: mappedReady=", gcController.mappedReady.Load(), "\n") - print("runtime: totalMapped=", totalMapped, "\n") - print("runtime: released=", uint64(consStats.released), "\n") - print("runtime: totalMapped-released=", totalMapped-uint64(consStats.released), "\n") - throw("mappedReady and other memstats are not equal") + if doubleCheckReadMemStats { + // Only check this if we're debugging. It would be bad to crash an application + // just because the debugging stats are wrong. We mostly rely on tests to catch + // these issues, and we enable the double check mode for tests. + // + // The world is stopped, so the consistent stats (after aggregation) + // should be identical to some combination of memstats. In particular: + // + // * memstats.heapInUse == inHeap + // * memstats.heapReleased == released + // * memstats.heapInUse + memstats.heapFree == committed - inStacks - inWorkBufs - inPtrScalarBits + // * memstats.totalAlloc == totalAlloc + // * memstats.totalFree == totalFree + // + // Check if that's actually true. + // + // Prevent sysmon and the tracer from skewing the stats since they can + // act without synchronizing with a STW. See #64401. + lock(&sched.sysmonlock) + lock(&trace.lock) + if gcController.heapInUse.load() != uint64(consStats.inHeap) { + print("runtime: heapInUse=", gcController.heapInUse.load(), "\n") + print("runtime: consistent value=", consStats.inHeap, "\n") + throw("heapInUse and consistent stats are not equal") + } + if gcController.heapReleased.load() != uint64(consStats.released) { + print("runtime: heapReleased=", gcController.heapReleased.load(), "\n") + print("runtime: consistent value=", consStats.released, "\n") + throw("heapReleased and consistent stats are not equal") + } + heapRetained := gcController.heapInUse.load() + gcController.heapFree.load() + consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits) + if heapRetained != consRetained { + print("runtime: global value=", heapRetained, "\n") + print("runtime: consistent value=", consRetained, "\n") + throw("measures of the retained heap are not equal") + } + if gcController.totalAlloc.Load() != totalAlloc { + print("runtime: totalAlloc=", gcController.totalAlloc.Load(), "\n") + print("runtime: consistent value=", totalAlloc, "\n") + throw("totalAlloc and consistent stats are not equal") + } + if gcController.totalFree.Load() != totalFree { + print("runtime: totalFree=", gcController.totalFree.Load(), "\n") + print("runtime: consistent value=", totalFree, "\n") + throw("totalFree and consistent stats are not equal") + } + // Also check that mappedReady lines up with totalMapped - released. + // This isn't really the same type of "make sure consistent stats line up" situation, + // but this is an opportune time to check. + if gcController.mappedReady.Load() != totalMapped-uint64(consStats.released) { + print("runtime: mappedReady=", gcController.mappedReady.Load(), "\n") + print("runtime: totalMapped=", totalMapped, "\n") + print("runtime: released=", uint64(consStats.released), "\n") + print("runtime: totalMapped-released=", totalMapped-uint64(consStats.released), "\n") + throw("mappedReady and other memstats are not equal") + } + unlock(&trace.lock) + unlock(&sched.sysmonlock) } // We've calculated all the values we need. Now, populate stats. diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go index 8af88d1832..0d20079242 100644 --- a/src/runtime/os2_aix.go +++ b/src/runtime/os2_aix.go @@ -428,7 +428,6 @@ func write1(fd uintptr, p unsafe.Pointer, n int32) int32 { } // Note that in this case we can't return a valid errno value. return write2(fd, uintptr(p), n) - } //go:nosplit @@ -641,7 +640,6 @@ func sysconf(name int32) uintptr { throw("syscall sysconf") } return r - } // pthread functions returns its error code in the main return value diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go index 81629f02a2..92daf13b1a 100644 --- a/src/runtime/os3_solaris.go +++ b/src/runtime/os3_solaris.go @@ -198,11 +198,11 @@ func exitThread(wait *atomic.Uint32) { var urandom_dev = []byte("/dev/urandom\x00") //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { diff --git a/src/runtime/os_aix.go b/src/runtime/os_aix.go index b26922c908..3a5078a64c 100644 --- a/src/runtime/os_aix.go +++ b/src/runtime/os_aix.go @@ -239,11 +239,11 @@ func exitThread(wait *atomic.Uint32) { var urandom_dev = []byte("/dev/urandom\x00") //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index ff33db084b..430d1865df 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -194,11 +194,11 @@ func getPageSize() uintptr { var urandom_dev = []byte("/dev/urandom\x00") //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { diff --git a/src/runtime/os_darwin_arm64.go b/src/runtime/os_darwin_arm64.go index b808150de0..ebc1b139a6 100644 --- a/src/runtime/os_darwin_arm64.go +++ b/src/runtime/os_darwin_arm64.go @@ -6,7 +6,6 @@ package runtime //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go index 80c1267765..2aeea17755 100644 --- a/src/runtime/os_dragonfly.go +++ b/src/runtime/os_dragonfly.go @@ -181,11 +181,11 @@ func osinit() { var urandom_dev = []byte("/dev/urandom\x00") //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go index c05e00f6ac..d0d6f14fa0 100644 --- a/src/runtime/os_freebsd.go +++ b/src/runtime/os_freebsd.go @@ -283,11 +283,11 @@ func osinit() { var urandom_dev = []byte("/dev/urandom\x00") //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { diff --git a/src/runtime/os_freebsd_arm.go b/src/runtime/os_freebsd_arm.go index df8c709b8f..5f6bf46798 100644 --- a/src/runtime/os_freebsd_arm.go +++ b/src/runtime/os_freebsd_arm.go @@ -15,14 +15,16 @@ const ( ) func checkgoarm() { - if goarm > 5 && cpu.HWCap&_HWCAP_VFP == 0 { + if cpu.HWCap&_HWCAP_VFP == 0 && goarmsoftfp == 0 { print("runtime: this CPU has no floating point hardware, so it cannot run\n") - print("this GOARM=", goarm, " binary. Recompile using GOARM=5.\n") + print("a binary compiled for hard floating point. Recompile adding ,softfloat\n") + print("to GOARM.\n") exit(1) } - if goarm > 6 && cpu.HWCap&_HWCAP_VFPv3 == 0 { + if goarm > 6 && cpu.HWCap&_HWCAP_VFPv3 == 0 && goarmsoftfp == 0 { print("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n") - print("this GOARM=", goarm, " binary. Recompile using GOARM=5 or GOARM=6.\n") + print("a binary compiled for VFPv3 hard floating point. Recompile adding ,softfloat\n") + print("to GOARM or changing GOARM to 6.\n") exit(1) } @@ -47,7 +49,6 @@ func archauxv(tag, val uintptr) { //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_freebsd_arm64.go b/src/runtime/os_freebsd_arm64.go index b5b25f0dc5..58bc5d34b7 100644 --- a/src/runtime/os_freebsd_arm64.go +++ b/src/runtime/os_freebsd_arm64.go @@ -6,7 +6,6 @@ package runtime //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed fastrand(). // nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_js.go b/src/runtime/os_js.go index 65fb499de6..099c5265a0 100644 --- a/src/runtime/os_js.go +++ b/src/runtime/os_js.go @@ -32,6 +32,11 @@ func usleep(usec uint32) { //go:noescape func getRandomData(r []byte) +func readRandom(r []byte) int { + getRandomData(r) + return len(r) +} + func goenvs() { goenvs_unix() } diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 6386b82a85..0ba607fe1f 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -288,10 +288,6 @@ func sysargs(argc int32, argv **byte) { auxv = auxvreadbuf[: pairs*2 : pairs*2] } -// startupRandomData holds random bytes initialized at startup. These come from -// the ELF AT_RANDOM auxiliary vector. -var startupRandomData []byte - // secureMode holds the value of AT_SECURE passed in the auxiliary vector. var secureMode bool @@ -303,7 +299,7 @@ func sysauxv(auxv []uintptr) (pairs int) { case _AT_RANDOM: // The kernel provides a pointer to 16-bytes // worth of random data. - startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:] + startupRand = (*[16]byte)(unsafe.Pointer(val))[:] case _AT_PAGESZ: physPageSize = val @@ -352,16 +348,11 @@ func osinit() { var urandom_dev = []byte("/dev/urandom\x00") -func getRandomData(r []byte) { - if startupRandomData != nil { - n := copy(r, startupRandomData) - extendRandom(r, n) - return - } +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { @@ -656,7 +647,7 @@ func setThreadCPUProfiler(hz int32) { // activates may do a couple milliseconds of GC-related work and nothing // else in the few seconds that the profiler observes. spec := new(itimerspec) - spec.it_value.setNsec(1 + int64(fastrandn(uint32(1e9/hz)))) + spec.it_value.setNsec(1 + int64(cheaprandn(uint32(1e9/hz)))) spec.it_interval.setNsec(1e9 / int64(hz)) var timerid int32 diff --git a/src/runtime/os_linux_arm.go b/src/runtime/os_linux_arm.go index 6e0c729855..5e1274ebab 100644 --- a/src/runtime/os_linux_arm.go +++ b/src/runtime/os_linux_arm.go @@ -23,14 +23,16 @@ func checkgoarm() { if GOOS == "android" { return } - if goarm > 5 && cpu.HWCap&_HWCAP_VFP == 0 { + if cpu.HWCap&_HWCAP_VFP == 0 && goarmsoftfp == 0 { print("runtime: this CPU has no floating point hardware, so it cannot run\n") - print("this GOARM=", goarm, " binary. Recompile using GOARM=5.\n") + print("a binary compiled for hard floating point. Recompile adding ,softfloat\n") + print("to GOARM.\n") exit(1) } - if goarm > 6 && cpu.HWCap&_HWCAP_VFPv3 == 0 { + if goarm > 6 && cpu.HWCap&_HWCAP_VFPv3 == 0 && goarmsoftfp == 0 { print("runtime: this CPU has no VFPv3 floating point hardware, so it cannot run\n") - print("this GOARM=", goarm, " binary. Recompile using GOARM=5 or GOARM=6.\n") + print("a binary compiled for VFPv3 hard floating point. Recompile adding ,softfloat\n") + print("to GOARM or changing GOARM to 6.\n") exit(1) } } @@ -50,7 +52,6 @@ func osArchInit() {} //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed fastrand(). // nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go index 2daa56fce7..62cead1d22 100644 --- a/src/runtime/os_linux_arm64.go +++ b/src/runtime/os_linux_arm64.go @@ -19,7 +19,6 @@ func osArchInit() {} //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed fastrand(). // nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_linux_mips64x.go b/src/runtime/os_linux_mips64x.go index 11d35bc020..770cc27ba7 100644 --- a/src/runtime/os_linux_mips64x.go +++ b/src/runtime/os_linux_mips64x.go @@ -19,7 +19,6 @@ func osArchInit() {} //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed fastrand(). // nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_linux_mipsx.go b/src/runtime/os_linux_mipsx.go index cdf83ff71d..3807e6d051 100644 --- a/src/runtime/os_linux_mipsx.go +++ b/src/runtime/os_linux_mipsx.go @@ -13,7 +13,6 @@ func osArchInit() {} //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed fastrand(). // nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go index 7cbba48194..8abb688aae 100644 --- a/src/runtime/os_netbsd.go +++ b/src/runtime/os_netbsd.go @@ -274,11 +274,11 @@ func osinit() { var urandom_dev = []byte("/dev/urandom\x00") //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { diff --git a/src/runtime/os_netbsd_arm.go b/src/runtime/os_netbsd_arm.go index 5fb4e08d66..7494a387e3 100644 --- a/src/runtime/os_netbsd_arm.go +++ b/src/runtime/os_netbsd_arm.go @@ -31,7 +31,6 @@ func checkgoarm() { //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_netbsd_arm64.go b/src/runtime/os_netbsd_arm64.go index 2dda9c9274..48841afdb6 100644 --- a/src/runtime/os_netbsd_arm64.go +++ b/src/runtime/os_netbsd_arm64.go @@ -20,7 +20,6 @@ func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintp //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index aa2ba859a8..856979910a 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -142,11 +142,11 @@ func osinit() { var urandom_dev = []byte("/dev/urandom\x00") //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) closefd(fd) - extendRandom(r, int(n)) + return int(n) } func goenvs() { diff --git a/src/runtime/os_openbsd_arm.go b/src/runtime/os_openbsd_arm.go index 0a2409676c..d5dc8cb479 100644 --- a/src/runtime/os_openbsd_arm.go +++ b/src/runtime/os_openbsd_arm.go @@ -17,7 +17,6 @@ func checkgoarm() { //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_openbsd_arm64.go b/src/runtime/os_openbsd_arm64.go index d71de7d196..4b2c6e3fe9 100644 --- a/src/runtime/os_openbsd_arm64.go +++ b/src/runtime/os_openbsd_arm64.go @@ -6,7 +6,6 @@ package runtime //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_openbsd_mips64.go b/src/runtime/os_openbsd_mips64.go index ae220cd683..e5eeb2dcd1 100644 --- a/src/runtime/os_openbsd_mips64.go +++ b/src/runtime/os_openbsd_mips64.go @@ -6,7 +6,6 @@ package runtime //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go index f4ff4d5f45..77446d09d3 100644 --- a/src/runtime/os_plan9.go +++ b/src/runtime/os_plan9.go @@ -327,24 +327,8 @@ func crash() { } //go:nosplit -func getRandomData(r []byte) { - // inspired by wyrand see hash32.go for detail - t := nanotime() - v := getg().m.procid ^ uint64(t) - - for len(r) > 0 { - v ^= 0xa0761d6478bd642f - v *= 0xe7037ed1a0b428db - size := 8 - if len(r) < 8 { - size = len(r) - } - for i := 0; i < size; i++ { - r[i] = byte(v >> (8 * i)) - } - r = r[size:] - v = v>>32 | v<<32 - } +func readRandom(r []byte) int { + return 0 } func initsig(preinit bool) { diff --git a/src/runtime/os_plan9_arm.go b/src/runtime/os_plan9_arm.go index f165a34151..cce6229323 100644 --- a/src/runtime/os_plan9_arm.go +++ b/src/runtime/os_plan9_arm.go @@ -10,7 +10,6 @@ func checkgoarm() { //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. return nanotime() } diff --git a/src/runtime/os_wasip1.go b/src/runtime/os_wasip1.go index 8811bb6178..acac2b3f7a 100644 --- a/src/runtime/os_wasip1.go +++ b/src/runtime/os_wasip1.go @@ -180,10 +180,11 @@ func usleep(usec uint32) { } } -func getRandomData(r []byte) { +func readRandom(r []byte) int { if random_get(unsafe.Pointer(&r[0]), size(len(r))) != 0 { - throw("random_get failed") + return 0 } + return len(r) } func goenvs() { diff --git a/src/runtime/os_wasm.go b/src/runtime/os_wasm.go index bf78dfb5f9..ce260de67e 100644 --- a/src/runtime/os_wasm.go +++ b/src/runtime/os_wasm.go @@ -122,9 +122,7 @@ func syscall_now() (sec int64, nsec int32) { //go:nosplit func cputicks() int64 { - // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand(). // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. - // TODO: need more entropy to better seed fastrand. return nanotime() } diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 3772a864b2..6533b64004 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -468,7 +468,10 @@ func initLongPathSupport() { // strictly necessary, but is a nice validity check for the near to // medium term, when this functionality is still relatively new in // Windows. - getRandomData(longFileName[len(longFileName)-33 : len(longFileName)-1]) + targ := longFileName[len(longFileName)-33 : len(longFileName)-1] + if readRandom(targ) != len(targ) { + readTimeRandom(targ) + } start := copy(longFileName[:], sysDirectory[:sysDirectoryLen]) const dig = "0123456789abcdef" for i := 0; i < 32; i++ { @@ -519,12 +522,12 @@ func osinit() { } //go:nosplit -func getRandomData(r []byte) { +func readRandom(r []byte) int { n := 0 if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 { n = len(r) } - extendRandom(r, n) + return n } func goenvs() { diff --git a/src/runtime/pagetrace_on.go b/src/runtime/pagetrace_on.go index 0e621cb6ca..f82521caad 100644 --- a/src/runtime/pagetrace_on.go +++ b/src/runtime/pagetrace_on.go @@ -317,7 +317,7 @@ func finishPageTrace() { pageTrace.enabled = false // Execute a ragged barrier, flushing each trace buffer. - forEachP(func(pp *p) { + forEachP(waitReasonPageTraceFlush, func(pp *p) { if pp.pageTraceBuf.buf != nil { pp.pageTraceBuf = pp.pageTraceBuf.flush(pp.id, nanotime()) } diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index d3a3c788b1..a4dcf33508 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -69,7 +69,7 @@ // all pprof commands. // // For more information about pprof, see -// https://github.com/google/pprof/blob/master/doc/README.md. +// https://github.com/google/pprof/blob/main/doc/README.md. package pprof import ( @@ -109,6 +109,12 @@ import ( // These predefined profiles maintain themselves and panic on an explicit // [Profile.Add] or [Profile.Remove] method call. // +// 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. +// +// # Heap profile +// // The heap profile reports statistics as of the most recently completed // garbage collection; it elides more recent allocation to avoid skewing // the profile away from live data and toward garbage. @@ -122,13 +128,47 @@ import ( // flags select which to display, defaulting to -inuse_space (live objects, // scaled by size). // +// # Allocs profile +// // The allocs profile is the same as the heap profile but changes the default // pprof display to -alloc_space, the total number of bytes allocated since // the program began (including garbage-collected bytes). // -// 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. +// # Block profile +// +// The block profile tracks time spent blocked on synchronization primitives, +// such as [sync.Mutex], [sync.RWMutex], [sync.WaitGroup], [sync.Cond], and +// channel send/receive/select. +// +// Stack traces correspond to the location that blocked (for example, +// [sync.Mutex.Lock]). +// +// Sample values correspond to cumulative time spent blocked at that stack +// trace, subject to time-based sampling specified by +// [runtime.SetBlockProfileRate]. +// +// # Mutex profile +// +// The mutex profile tracks contention on mutexes, such as [sync.Mutex], +// [sync.RWMutex], and runtime-internal locks. +// +// Stack traces correspond to the end of the critical section causing +// contention. For example, a lock held for a long time while other goroutines +// are waiting to acquire the lock will report contention when the lock is +// finally unlocked (that is, at [sync.Mutex.Unlock]). +// +// Sample values correspond to the approximate cumulative time other goroutines +// spent blocked waiting for the lock, subject to event-based sampling +// specified by [runtime.SetMutexProfileFraction]. For example, if a caller +// holds a lock for 1s while 5 other goroutines are waiting for the entire +// second to acquire the lock, its unlock call stack will report 5s of +// contention. +// +// Runtime-internal locks are always reported at the location +// "runtime._LostContendedRuntimeLock". More detailed stack traces for +// runtime-internal locks can be obtained by setting +// `GODEBUG=runtimecontentionstacks=1` (see package [runtime] docs for +// caveats). type Profile struct { name string mu sync.Mutex diff --git a/src/runtime/pprof/proto.go b/src/runtime/pprof/proto.go index cdc4bd7c80..5214374bd9 100644 --- a/src/runtime/pprof/proto.go +++ b/src/runtime/pprof/proto.go @@ -561,7 +561,7 @@ func (d *pcDeck) tryAdd(pc uintptr, frames []runtime.Frame, symbolizeResult symb if last.Entry != newFrame.Entry { // newFrame is for a different function. return false } - if last.Function == newFrame.Function { // maybe recursion. + if runtime_FrameSymbolName(&last) == runtime_FrameSymbolName(&newFrame) { // maybe recursion. return false } } @@ -611,13 +611,14 @@ func (b *profileBuilder) emitLocation() uint64 { b.pb.uint64Opt(tagLocation_Address, uint64(firstFrame.PC)) for _, frame := range b.deck.frames { // Write out each line in frame expansion. - funcID := uint64(b.funcs[frame.Function]) + funcName := runtime_FrameSymbolName(&frame) + funcID := uint64(b.funcs[funcName]) if funcID == 0 { funcID = uint64(len(b.funcs)) + 1 - b.funcs[frame.Function] = int(funcID) + b.funcs[funcName] = int(funcID) newFuncs = append(newFuncs, newFunc{ id: funcID, - name: runtime_FrameSymbolName(&frame), + name: funcName, file: frame.File, startLine: int64(runtime_FrameStartLine(&frame)), }) diff --git a/src/runtime/pprof/proto_test.go b/src/runtime/pprof/proto_test.go index e1a7f2306d..85cd066946 100644 --- a/src/runtime/pprof/proto_test.go +++ b/src/runtime/pprof/proto_test.go @@ -45,7 +45,7 @@ func fmtJSON(x any) string { return string(js) } -func TestConvertCPUProfileEmpty(t *testing.T) { +func TestConvertCPUProfileNoSamples(t *testing.T) { // A test server with mock cpu profile data. var buf bytes.Buffer @@ -64,9 +64,13 @@ func TestConvertCPUProfileEmpty(t *testing.T) { } // Expected PeriodType and SampleType. - sampleType := []*profile.ValueType{{}, {}} + periodType := &profile.ValueType{Type: "cpu", Unit: "nanoseconds"} + sampleType := []*profile.ValueType{ + {Type: "samples", Unit: "count"}, + {Type: "cpu", Unit: "nanoseconds"}, + } - checkProfile(t, p, 2000*1000, nil, sampleType, nil, "") + checkProfile(t, p, 2000*1000, periodType, sampleType, nil, "") } func f1() { f1() } diff --git a/src/runtime/pprof/protomem_test.go b/src/runtime/pprof/protomem_test.go index 156f6286a9..5fb67c53f6 100644 --- a/src/runtime/pprof/protomem_test.go +++ b/src/runtime/pprof/protomem_test.go @@ -6,8 +6,12 @@ package pprof import ( "bytes" + "fmt" "internal/profile" + "internal/testenv" "runtime" + "slices" + "strings" "testing" ) @@ -82,3 +86,138 @@ func TestConvertMemProfile(t *testing.T) { }) } } + +func genericAllocFunc[T interface{ uint32 | uint64 }](n int) []T { + return make([]T, n) +} + +func profileToStrings(p *profile.Profile) []string { + var res []string + for _, s := range p.Sample { + res = append(res, sampleToString(s)) + } + return res +} + +func sampleToString(s *profile.Sample) string { + var funcs []string + for i := len(s.Location) - 1; i >= 0; i-- { + loc := s.Location[i] + funcs = locationToStrings(loc, funcs) + } + return fmt.Sprintf("%s %v", strings.Join(funcs, ";"), s.Value) +} + +func locationToStrings(loc *profile.Location, funcs []string) []string { + for j := range loc.Line { + line := loc.Line[len(loc.Line)-1-j] + funcs = append(funcs, line.Function.Name) + } + return funcs +} + +// This is a regression test for https://go.dev/issue/64528 . +func TestGenericsHashKeyInPprofBuilder(t *testing.T) { + previousRate := runtime.MemProfileRate + runtime.MemProfileRate = 1 + defer func() { + runtime.MemProfileRate = previousRate + }() + for _, sz := range []int{128, 256} { + genericAllocFunc[uint32](sz / 4) + } + for _, sz := range []int{32, 64} { + genericAllocFunc[uint64](sz / 8) + } + + runtime.GC() + buf := bytes.NewBuffer(nil) + if err := WriteHeapProfile(buf); err != nil { + t.Fatalf("writing profile: %v", err) + } + p, err := profile.Parse(buf) + if err != nil { + t.Fatalf("profile.Parse: %v", err) + } + + actual := profileToStrings(p) + expected := []string{ + "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 128 0 0]", + "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint32] [1 256 0 0]", + "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 32 0 0]", + "testing.tRunner;runtime/pprof.TestGenericsHashKeyInPprofBuilder;runtime/pprof.genericAllocFunc[go.shape.uint64] [1 64 0 0]", + } + + for _, l := range expected { + if !slices.Contains(actual, l) { + t.Errorf("profile = %v\nwant = %v", strings.Join(actual, "\n"), l) + } + } +} + +type opAlloc struct { + buf [128]byte +} + +type opCall struct { +} + +var sink []byte + +func storeAlloc() { + sink = make([]byte, 16) +} + +func nonRecursiveGenericAllocFunction[CurrentOp any, OtherOp any](alloc bool) { + if alloc { + storeAlloc() + } else { + nonRecursiveGenericAllocFunction[OtherOp, CurrentOp](true) + } +} + +func TestGenericsInlineLocations(t *testing.T) { + if testenv.OptimizationOff() { + t.Skip("skipping test with optimizations disabled") + } + + previousRate := runtime.MemProfileRate + runtime.MemProfileRate = 1 + defer func() { + runtime.MemProfileRate = previousRate + sink = nil + }() + + nonRecursiveGenericAllocFunction[opAlloc, opCall](true) + nonRecursiveGenericAllocFunction[opCall, opAlloc](false) + + runtime.GC() + + buf := bytes.NewBuffer(nil) + if err := WriteHeapProfile(buf); err != nil { + t.Fatalf("writing profile: %v", err) + } + p, err := profile.Parse(buf) + if err != nil { + t.Fatalf("profile.Parse: %v", err) + } + + const expectedSample = "testing.tRunner;runtime/pprof.TestGenericsInlineLocations;runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct {},go.shape.struct { runtime/pprof.buf [128]uint8 }];runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct { runtime/pprof.buf [128]uint8 },go.shape.struct {}];runtime/pprof.storeAlloc [1 16 1 16]" + const expectedLocation = "runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct {},go.shape.struct { runtime/pprof.buf [128]uint8 }];runtime/pprof.nonRecursiveGenericAllocFunction[go.shape.struct { runtime/pprof.buf [128]uint8 },go.shape.struct {}];runtime/pprof.storeAlloc" + const expectedLocationNewInliner = "runtime/pprof.TestGenericsInlineLocations;" + expectedLocation + var s *profile.Sample + for _, sample := range p.Sample { + if sampleToString(sample) == expectedSample { + s = sample + break + } + } + if s == nil { + t.Fatalf("expected \n%s\ngot\n%s", expectedSample, strings.Join(profileToStrings(p), "\n")) + } + loc := s.Location[0] + actual := strings.Join(locationToStrings(loc, nil), ";") + if expectedLocation != actual && expectedLocationNewInliner != actual { + t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual) + } +} diff --git a/src/runtime/pprof/vminfo_darwin_test.go b/src/runtime/pprof/vminfo_darwin_test.go index 1a3b67a0bf..8749a13390 100644 --- a/src/runtime/pprof/vminfo_darwin_test.go +++ b/src/runtime/pprof/vminfo_darwin_test.go @@ -34,7 +34,10 @@ func TestVMInfo(t *testing.T) { // the go toolchain itself. first = false }) - lo, hi := useVMMap(t) + lo, hi, err := useVMMapWithRetry(t) + if err != nil { + t.Fatal(err) + } if got, want := begin, lo; got != want { t.Errorf("got %x, want %x", got, want) } @@ -53,7 +56,21 @@ func TestVMInfo(t *testing.T) { } } -func useVMMap(t *testing.T) (hi, lo uint64) { +func useVMMapWithRetry(t *testing.T) (hi, lo uint64, err error) { + var retryable bool + for { + hi, lo, retryable, err = useVMMap(t) + if err == nil { + return hi, lo, nil + } + if !retryable { + return 0, 0, err + } + t.Logf("retrying vmmap after error: %v", err) + } +} + +func useVMMap(t *testing.T) (hi, lo uint64, retryable bool, err error) { pid := strconv.Itoa(os.Getpid()) testenv.MustHaveExecPath(t, "vmmap") cmd := testenv.Command(t, "vmmap", pid) @@ -63,20 +80,24 @@ func useVMMap(t *testing.T) (hi, lo uint64) { if ee, ok := cmdErr.(*exec.ExitError); ok && len(ee.Stderr) > 0 { t.Logf("%v: %v\n%s", cmd, cmdErr, ee.Stderr) } + retryable = bytes.Contains(out, []byte("resource shortage")) t.Logf("%v: %v", cmd, cmdErr) + if retryable { + return 0, 0, true, cmdErr + } } // Always parse the output of vmmap since it may return an error // code even if it successfully reports the text segment information // required for this test. - hi, lo, err := parseVmmap(out) + hi, lo, err = parseVmmap(out) if err != nil { if cmdErr != nil { - t.Fatalf("failed to parse vmmap output, vmmap reported an error: %v", err) + return 0, 0, false, fmt.Errorf("failed to parse vmmap output, vmmap reported an error: %v", err) } t.Logf("vmmap output: %s", out) - t.Fatalf("failed to parse vmmap output, vmmap did not report an error: %v", err) + return 0, 0, false, fmt.Errorf("failed to parse vmmap output, vmmap did not report an error: %v", err) } - return hi, lo + return hi, lo, false, nil } // parseVmmap parses the output of vmmap and calls addMapping for the first r-x TEXT segment in the output. diff --git a/src/runtime/preempt_arm.s b/src/runtime/preempt_arm.s index 8f243c0dcd..b68df5d6b1 100644 --- a/src/runtime/preempt_arm.s +++ b/src/runtime/preempt_arm.s @@ -19,9 +19,9 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVW R12, 48(R13) MOVW CPSR, R0 MOVW R0, 52(R13) - MOVB ·goarm(SB), R0 - CMP $6, R0 - BLT nofp + MOVB ·goarmsoftfp(SB), R0 + CMP $0, R0 + BNE nofp MOVW FPCR, R0 MOVW R0, 56(R13) MOVD F0, 60(R13) @@ -42,9 +42,9 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD F15, 180(R13) nofp: CALL ·asyncPreempt2(SB) - MOVB ·goarm(SB), R0 - CMP $6, R0 - BLT nofp2 + MOVB ·goarmsoftfp(SB), R0 + CMP $0, R0 + BNE nofp2 MOVD 180(R13), F15 MOVD 172(R13), F14 MOVD 164(R13), F13 diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 66d7da887e..c487da652e 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -576,7 +576,10 @@ func switchToCrashStack(fn func()) { abort() } -const crashStackImplemented = GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "mips64" || GOARCH == "mips64le" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64" +// Disable crash stack on Windows for now. Apparently, throwing an exception +// on a non-system-allocated crash stack causes EXCEPTION_STACK_OVERFLOW and +// hangs the process (see issue 63938). +const crashStackImplemented = (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "mips64" || GOARCH == "mips64le" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64" || GOARCH == "wasm") && GOOS != "windows" //go:noescape func switchToCrashStack0(fn func()) // in assembly @@ -756,6 +759,8 @@ func schedinit() { lockInit(&reflectOffs.lock, lockRankReflectOffs) lockInit(&finlock, lockRankFin) lockInit(&cpuprof.lock, lockRankCpuprof) + allocmLock.init(lockRankAllocmR, lockRankAllocmRInternal, lockRankAllocmW) + execLock.init(lockRankExecR, lockRankExecRInternal, lockRankExecW) traceLockInit() // Enforce that this lock is always a leaf lock. // All of this lock's critical sections should be @@ -781,8 +786,8 @@ func schedinit() { godebug := getGodebugEarly() initPageTrace(godebug) // must run after mallocinit but before anything allocates cpuinit(godebug) // must run before alginit - alginit() // maps, hash, fastrand must not be used before this call - fastrandinit() // must run before mcommoninit + randinit() // must run before alginit, mcommoninit + alginit() // maps, hash, rand must not be used before this call mcommoninit(gp.m, -1) modulesinit() // provides activeModules typelinksinit() // uses maps, activeModules @@ -897,18 +902,7 @@ func mcommoninit(mp *m, id int64) { mp.id = mReserveID() } - lo := uint32(int64Hash(uint64(mp.id), fastrandseed)) - hi := uint32(int64Hash(uint64(cputicks()), ^fastrandseed)) - if lo|hi == 0 { - hi = 1 - } - // Same behavior as for 1.17. - // TODO: Simplify this. - if goarch.BigEndian { - mp.fastrand = uint64(lo)<<32 | uint64(hi) - } else { - mp.fastrand = uint64(hi)<<32 | uint64(lo) - } + mrandinit(mp) mpreinit(mp) if mp.gsignal != nil { @@ -954,13 +948,6 @@ const ( osHasLowResClock = osHasLowResClockInt > 0 ) -var fastrandseed uintptr - -func fastrandinit() { - s := (*[unsafe.Sizeof(fastrandseed)]byte)(unsafe.Pointer(&fastrandseed))[:] - getRandomData(s) -} - // Mark gp ready to run. func ready(gp *g, traceskip int, next bool) { status := readgstatus(gp) @@ -2425,8 +2412,20 @@ func dropm() { // Flush all the M's buffers. This is necessary because the M might // be used on a different thread with a different procid, so we have // to make sure we don't write into the same buffer. - if traceEnabled() || traceShuttingDown() { + // + // N.B. traceThreadDestroy is a no-op in the old tracer, so avoid the + // unnecessary acquire/release of the lock. + if goexperiment.ExecTracer2 && (traceEnabled() || traceShuttingDown()) { + // Acquire sched.lock across thread destruction. One of the invariants of the tracer + // is that a thread cannot disappear from the tracer's view (allm or freem) without + // it noticing, so it requires that sched.lock be held over traceThreadDestroy. + // + // This isn't strictly necessary in this case, because this thread never leaves allm, + // but the critical section is short and dropm is rare on pthread platforms, so just + // take the lock and play it safe. traceThreadDestroy also asserts that the lock is held. + lock(&sched.lock) traceThreadDestroy(mp) + unlock(&sched.lock) } mp.isExtraInSig = false @@ -3551,7 +3550,7 @@ func stealWork(now int64) (gp *g, inheritTime bool, rnow, pollUntil int64, newWo for i := 0; i < stealTries; i++ { stealTimersOrRunNextG := i == stealTries-1 - for enum := stealOrder.start(fastrand()); !enum.done(); enum.next() { + for enum := stealOrder.start(cheaprand()); !enum.done(); enum.next() { if sched.gcwaiting.Load() { // GC work may be available. return nil, false, now, pollUntil, true @@ -4178,6 +4177,11 @@ func goexit1() { // goexit continuation on g0. func goexit0(gp *g) { + gdestroy(gp) + schedule() +} + +func gdestroy(gp *g) { mp := getg().m pp := mp.p.ptr() @@ -4214,7 +4218,7 @@ func goexit0(gp *g) { if GOARCH == "wasm" { // no threads yet on wasm gfput(pp, gp) - schedule() // never returns + return } if mp.lockedInt != 0 { @@ -4237,7 +4241,6 @@ func goexit0(gp *g) { mp.lockedExt = 0 } } - schedule() } // save updates getg().sched to refer to pc and sp so that a following @@ -4404,10 +4407,21 @@ func entersyscall_gcwait() { if sched.stopwait > 0 && atomic.Cas(&pp.status, _Psyscall, _Pgcstop) { trace := traceAcquire() if trace.ok() { - trace.GoSysBlock(pp) - // N.B. ProcSteal not necessary because if we succeed we're - // always stopping the P we just put into the syscall status. - trace.ProcStop(pp) + if goexperiment.ExecTracer2 { + // This is a steal in the new tracer. While it's very likely + // that we were the ones to put this P into _Psyscall, between + // then and now it's totally possible it had been stolen and + // then put back into _Psyscall for us to acquire here. In such + // case ProcStop would be incorrect. + // + // TODO(mknyszek): Consider emitting a ProcStop instead when + // gp.m.syscalltick == pp.syscalltick, since then we know we never + // lost the P. + trace.ProcSteal(pp, true) + } else { + trace.GoSysBlock(pp) + trace.ProcStop(pp) + } traceRelease(trace) } pp.syscalltick++ @@ -4929,7 +4943,7 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g { } } // Track initial transition? - newg.trackingSeq = uint8(fastrand()) + newg.trackingSeq = uint8(cheaprand()) if newg.trackingSeq%gTrackingPeriod == 0 { newg.tracking = true } @@ -5271,7 +5285,7 @@ func _ExternalCode() { _ExternalCode() } func _LostExternalCode() { _LostExternalCode() } func _GC() { _GC() } func _LostSIGPROFDuringAtomic64() { _LostSIGPROFDuringAtomic64() } -func _LostContendedLock() { _LostContendedLock() } +func _LostContendedRuntimeLock() { _LostContendedRuntimeLock() } func _VDSO() { _VDSO() } // Called if we receive a SIGPROF signal. @@ -5743,15 +5757,23 @@ func wirep(pp *p) { gp := getg() if gp.m.p != 0 { - throw("wirep: already in go") + // Call on the systemstack to avoid a nosplit overflow build failure + // on some platforms when built with -N -l. See #64113. + systemstack(func() { + throw("wirep: already in go") + }) } if pp.m != 0 || pp.status != _Pidle { - id := int64(0) - if pp.m != 0 { - id = pp.m.ptr().id - } - print("wirep: p->m=", pp.m, "(", id, ") p->status=", pp.status, "\n") - throw("wirep: invalid p state") + // Call on the systemstack to avoid a nosplit overflow build failure + // on some platforms when built with -N -l. See #64113. + systemstack(func() { + id := int64(0) + if pp.m != 0 { + id = pp.m.ptr().id + } + print("wirep: p->m=", pp.m, "(", id, ") p->status=", pp.status, "\n") + throw("wirep: invalid p state") + }) } gp.m.p.set(pp) pp.m.set(gp.m) @@ -6602,7 +6624,7 @@ const randomizeScheduler = raceenabled // If the run queue is full, runnext puts g on the global queue. // Executed only by the owner P. func runqput(pp *p, gp *g, next bool) { - if randomizeScheduler && next && fastrandn(2) == 0 { + if randomizeScheduler && next && randn(2) == 0 { next = false } @@ -6655,7 +6677,7 @@ func runqputslow(pp *p, gp *g, h, t uint32) bool { if randomizeScheduler { for i := uint32(1); i <= n; i++ { - j := fastrandn(i + 1) + j := cheaprandn(i + 1) batch[i], batch[j] = batch[j], batch[i] } } @@ -6696,7 +6718,7 @@ func runqputbatch(pp *p, q *gQueue, qsize int) { return (pp.runqtail + o) % uint32(len(pp.runq)) } for i := uint32(1); i < n; i++ { - j := fastrandn(i + 1) + j := cheaprandn(i + 1) pp.runq[off(i)], pp.runq[off(j)] = pp.runq[off(j)], pp.runq[off(i)] } } diff --git a/src/runtime/profbuf.go b/src/runtime/profbuf.go index 5772a8020c..d3afbcd8c7 100644 --- a/src/runtime/profbuf.go +++ b/src/runtime/profbuf.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Go Authors. All rights reserved. +// Copyright 2017 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. diff --git a/src/runtime/race.go b/src/runtime/race.go index f9cbc1f54b..ca4f051979 100644 --- a/src/runtime/race.go +++ b/src/runtime/race.go @@ -179,7 +179,7 @@ func raceSymbolizeCode(ctx *symbolizeCodeContext) { // Ignore wrappers, unless we're at the outermost frame of u. // A non-inlined wrapper frame always means we have a physical // frame consisting entirely of wrappers, in which case we'll - // take a outermost wrapper over nothing. + // take an outermost wrapper over nothing. continue } diff --git a/src/runtime/race/README b/src/runtime/race/README index 75484f71eb..47c51ca9c1 100644 --- a/src/runtime/race/README +++ b/src/runtime/race/README @@ -13,5 +13,5 @@ internal/amd64v1/race_windows.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d internal/amd64v3/race_linux.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. race_darwin_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. race_linux_arm64.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. -race_linux_ppc64le.syso built with LLVM 41cb504b7c4b18ac15830107431a0c1eec73a6b2 and Go 851ecea4cc99ab276109493477b2c7e30c253ea8. +race_linux_ppc64le.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. race_linux_s390x.syso built with LLVM 51bfeff0e4b0757ff773da6882f4d538996c9b04 and Go e7d582b55dda36e76ce4d0ce770139ca0915b7c5. diff --git a/src/runtime/race/race_linux_ppc64le.syso b/src/runtime/race/race_linux_ppc64le.syso index 1939f29ac0..49824a9d18 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/rand.go b/src/runtime/rand.go new file mode 100644 index 0000000000..10cd116fad --- /dev/null +++ b/src/runtime/rand.go @@ -0,0 +1,247 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Random number generation + +package runtime + +import ( + "internal/chacha8rand" + "internal/goarch" + "runtime/internal/math" + "unsafe" + _ "unsafe" // for go:linkname +) + +// OS-specific startup can set startupRand if the OS passes +// random data to the process at startup time. +// For example Linux passes 16 bytes in the auxv vector. +var startupRand []byte + +// globalRand holds the global random state. +// It is only used at startup and for creating new m's. +// Otherwise the per-m random state should be used +// by calling goodrand. +var globalRand struct { + lock mutex + seed [32]byte + state chacha8rand.State + init bool +} + +var readRandomFailed bool + +// randinit initializes the global random state. +// It must be called before any use of grand. +func randinit() { + lock(&globalRand.lock) + if globalRand.init { + fatal("randinit twice") + } + + seed := &globalRand.seed + if startupRand != nil { + for i, c := range startupRand { + seed[i%len(seed)] ^= c + } + clear(startupRand) + startupRand = nil + } else { + if readRandom(seed[:]) != len(seed) { + // readRandom should never fail, but if it does we'd rather + // not make Go binaries completely unusable, so make up + // some random data based on the current time. + readRandomFailed = true + readTimeRandom(seed[:]) + } + } + globalRand.state.Init(*seed) + clear(seed[:]) + globalRand.init = true + unlock(&globalRand.lock) +} + +// readTimeRandom stretches any entropy in the current time +// into entropy the length of r and XORs it into r. +// This is a fallback for when readRandom does not read +// the full requested amount. +// Whatever entropy r already contained is preserved. +func readTimeRandom(r []byte) { + // Inspired by wyrand. + // An earlier version of this code used getg().m.procid as well, + // but note that this is called so early in startup that procid + // is not initialized yet. + v := uint64(nanotime()) + for len(r) > 0 { + v ^= 0xa0761d6478bd642f + v *= 0xe7037ed1a0b428db + size := 8 + if len(r) < 8 { + size = len(r) + } + for i := 0; i < size; i++ { + r[i] ^= byte(v >> (8 * i)) + } + r = r[size:] + v = v>>32 | v<<32 + } +} + +// bootstrapRand returns a random uint64 from the global random generator. +func bootstrapRand() uint64 { + lock(&globalRand.lock) + if !globalRand.init { + fatal("randinit missed") + } + for { + if x, ok := globalRand.state.Next(); ok { + unlock(&globalRand.lock) + return x + } + globalRand.state.Refill() + } +} + +// bootstrapRandReseed reseeds the bootstrap random number generator, +// clearing from memory any trace of previously returned random numbers. +func bootstrapRandReseed() { + lock(&globalRand.lock) + if !globalRand.init { + fatal("randinit missed") + } + globalRand.state.Reseed() + unlock(&globalRand.lock) +} + +// rand32 is uint32(rand()), called from compiler-generated code. +//go:nosplit +func rand32() uint32 { + return uint32(rand()) +} + +// rand returns a random uint64 from the per-m chacha8 state. +// Do not change signature: used via linkname from other packages. +//go:nosplit +//go:linkname rand +func rand() uint64 { + // Note: We avoid acquirem here so that in the fast path + // there is just a getg, an inlined c.Next, and a return. + // The performance difference on a 16-core AMD is + // 3.7ns/call this way versus 4.3ns/call with acquirem (+16%). + mp := getg().m + c := &mp.chacha8 + for { + // Note: c.Next is marked nosplit, + // so we don't need to use mp.locks + // on the fast path, which is that the + // first attempt succeeds. + x, ok := c.Next() + if ok { + return x + } + mp.locks++ // hold m even though c.Refill may do stack split checks + c.Refill() + mp.locks-- + } +} + +// mrandinit initializes the random state of an m. +func mrandinit(mp *m) { + var seed [4]uint64 + for i := range seed { + seed[i] = bootstrapRand() + } + bootstrapRandReseed() // erase key we just extracted + mp.chacha8.Init64(seed) + mp.cheaprand = rand() +} + +// randn is like rand() % n but faster. +// Do not change signature: used via linkname from other packages. +//go:nosplit +//go:linkname randn +func randn(n uint32) uint32 { + // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + return uint32((uint64(uint32(rand())) * uint64(n)) >> 32) +} + +// cheaprand is a non-cryptographic-quality 32-bit random generator +// suitable for calling at very high frequency (such as during scheduling decisions) +// and at sensitive moments in the runtime (such as during stack unwinding). +// it is "cheap" in the sense of both expense and quality. +// +// cheaprand must not be exported to other packages: +// the rule is that other packages using runtime-provided +// randomness must always use rand. +//go:nosplit +func cheaprand() uint32 { + mp := getg().m + // Implement wyrand: https://github.com/wangyi-fudan/wyhash + // Only the platform that math.Mul64 can be lowered + // by the compiler should be in this list. + if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64| + goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le| + goarch.IsS390x|goarch.IsRiscv64|goarch.IsLoong64 == 1 { + mp.cheaprand += 0xa0761d6478bd642f + hi, lo := math.Mul64(mp.cheaprand, mp.cheaprand^0xe7037ed1a0b428db) + return uint32(hi ^ lo) + } + + // Implement xorshift64+: 2 32-bit xorshift sequences added together. + // Shift triplet [17,7,16] was calculated as indicated in Marsaglia's + // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf + // This generator passes the SmallCrush suite, part of TestU01 framework: + // http://simul.iro.umontreal.ca/testu01/tu01.html + t := (*[2]uint32)(unsafe.Pointer(&mp.cheaprand)) + s1, s0 := t[0], t[1] + s1 ^= s1 << 17 + s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 + t[0], t[1] = s0, s1 + return s0 + s1 +} + +// cheaprand64 is a non-cryptographic-quality 63-bit random generator +// suitable for calling at very high frequency (such as during sampling decisions). +// it is "cheap" in the sense of both expense and quality. +// +// cheaprand64 must not be exported to other packages: +// the rule is that other packages using runtime-provided +// randomness must always use rand. +//go:nosplit +func cheaprand64() int64 { + return int64(cheaprand())<<31 ^ int64(cheaprand()) +} + +// cheaprandn is like cheaprand() % n but faster. +// +// cheaprandn must not be exported to other packages: +// the rule is that other packages using runtime-provided +// randomness must always use randn. +//go:nosplit +func cheaprandn(n uint32) uint32 { + // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + return uint32((uint64(cheaprand()) * uint64(n)) >> 32) +} + +// Too much legacy code has go:linkname references +// to runtime.fastrand and friends, so keep these around for now. +// Code should migrate to math/rand/v2.Uint64, +// which is just as fast, but that's only available in Go 1.22+. +// It would be reasonable to remove these in Go 1.24. +// Do not call these from package runtime. + +//go:linkname legacy_fastrand runtime.fastrand +func legacy_fastrand() uint32 { + return uint32(rand()) +} + +//go:linkname legacy_fastrandn runtime.fastrandn +func legacy_fastrandn(n uint32) uint32 { + return randn(n) +} + +//go:linkname legacy_fastrand64 runtime.fastrand64 +func legacy_fastrand64() uint64 { + return rand() +} diff --git a/src/runtime/rand_test.go b/src/runtime/rand_test.go index 92d07ebada..baecb6984d 100644 --- a/src/runtime/rand_test.go +++ b/src/runtime/rand_test.go @@ -8,8 +8,20 @@ import ( . "runtime" "strconv" "testing" + _ "unsafe" // for go:linkname ) +func TestReadRandom(t *testing.T) { + if *ReadRandomFailed { + switch GOOS { + default: + t.Fatalf("readRandom failed at startup") + case "plan9": + // ok + } + } +} + func BenchmarkFastrand(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { @@ -51,3 +63,35 @@ func BenchmarkFastrandn(b *testing.B) { }) } } + +//go:linkname fastrand runtime.fastrand +func fastrand() uint32 + +//go:linkname fastrandn runtime.fastrandn +func fastrandn(uint32) uint32 + +//go:linkname fastrand64 runtime.fastrand64 +func fastrand64() uint64 + +func TestLegacyFastrand(t *testing.T) { + // Testing mainly that the calls work at all, + // but check that all three don't return the same number (1 in 2^64 chance) + { + x, y, z := fastrand(), fastrand(), fastrand() + if x == y && y == z { + t.Fatalf("fastrand three times = %#x, %#x, %#x, want different numbers", x, y, z) + } + } + { + x, y, z := fastrandn(1e9), fastrandn(1e9), fastrandn(1e9) + if x == y && y == z { + t.Fatalf("fastrandn three times = %#x, %#x, %#x, want different numbers", x, y, z) + } + } + { + x, y, z := fastrand64(), fastrand64(), fastrand64() + if x == y && y == z { + t.Fatalf("fastrand64 three times = %#x, %#x, %#x, want different numbers", x, y, z) + } + } +} diff --git a/src/runtime/runtime-gdb_unix_test.go b/src/runtime/runtime-gdb_unix_test.go index 5413306f77..a1f2401a6e 100644 --- a/src/runtime/runtime-gdb_unix_test.go +++ b/src/runtime/runtime-gdb_unix_test.go @@ -20,6 +20,43 @@ import ( "testing" ) +func canGenerateCore(t *testing.T) bool { + // Ensure there is enough RLIMIT_CORE available to generate a full core. + var lim syscall.Rlimit + err := syscall.Getrlimit(syscall.RLIMIT_CORE, &lim) + if err != nil { + t.Fatalf("error getting rlimit: %v", err) + } + // Minimum RLIMIT_CORE max to allow. This is a conservative estimate. + // Most systems allow infinity. + const minRlimitCore = 100 << 20 // 100 MB + if lim.Max < minRlimitCore { + t.Skipf("RLIMIT_CORE max too low: %#+v", lim) + } + + // Make sure core pattern will send core to the current directory. + b, err := os.ReadFile("/proc/sys/kernel/core_pattern") + if err != nil { + t.Fatalf("error reading core_pattern: %v", err) + } + if string(b) != "core\n" { + t.Skipf("Unexpected core pattern %q", string(b)) + } + + coreUsesPID := false + b, err = os.ReadFile("/proc/sys/kernel/core_uses_pid") + if err == nil { + switch string(bytes.TrimSpace(b)) { + case "0": + case "1": + coreUsesPID = true + default: + t.Skipf("unexpected core_uses_pid value %q", string(b)) + } + } + return coreUsesPID +} + const coreSignalSource = ` package main @@ -81,45 +118,12 @@ func TestGdbCoreSignalBacktrace(t *testing.T) { t.Parallel() checkGdbVersion(t) - // Ensure there is enough RLIMIT_CORE available to generate a full core. - var lim syscall.Rlimit - err := syscall.Getrlimit(syscall.RLIMIT_CORE, &lim) - if err != nil { - t.Fatalf("error getting rlimit: %v", err) - } - // Minimum RLIMIT_CORE max to allow. This is a conservative estimate. - // Most systems allow infinity. - const minRlimitCore = 100 << 20 // 100 MB - if lim.Max < minRlimitCore { - t.Skipf("RLIMIT_CORE max too low: %#+v", lim) - } - - // Make sure core pattern will send core to the current directory. - b, err := os.ReadFile("/proc/sys/kernel/core_pattern") - if err != nil { - t.Fatalf("error reading core_pattern: %v", err) - } - if string(b) != "core\n" { - t.Skipf("Unexpected core pattern %q", string(b)) - } - - coreUsesPID := false - b, err = os.ReadFile("/proc/sys/kernel/core_uses_pid") - if err == nil { - switch string(bytes.TrimSpace(b)) { - case "0": - case "1": - coreUsesPID = true - default: - t.Skipf("unexpected core_uses_pid value %q", string(b)) - } - } - - dir := t.TempDir() + coreUsesPID := canGenerateCore(t) // Build the source code. + dir := t.TempDir() src := filepath.Join(dir, "main.go") - err = os.WriteFile(src, []byte(coreSignalSource), 0644) + err := os.WriteFile(src, []byte(coreSignalSource), 0644) if err != nil { t.Fatalf("failed to create file: %v", err) } @@ -230,3 +234,144 @@ func TestGdbCoreSignalBacktrace(t *testing.T) { t.Fatalf("could not find runtime symbol in backtrace after signal handler:\n%s", rest) } } + +const coreCrashThreadSource = ` +package main + +/* +#cgo CFLAGS: -g -O0 +#include +#include +void trigger_crash() +{ + int* ptr = NULL; + *ptr = 1024; +} +*/ +import "C" +import ( + "flag" + "fmt" + "os" + "runtime/debug" + "syscall" +) + +func enableCore() { + debug.SetTraceback("crash") + + var lim syscall.Rlimit + err := syscall.Getrlimit(syscall.RLIMIT_CORE, &lim) + if err != nil { + panic(fmt.Sprintf("error getting rlimit: %v", err)) + } + lim.Cur = lim.Max + fmt.Fprintf(os.Stderr, "Setting RLIMIT_CORE = %+#v\n", lim) + err = syscall.Setrlimit(syscall.RLIMIT_CORE, &lim) + if err != nil { + panic(fmt.Sprintf("error setting rlimit: %v", err)) + } +} + +func main() { + flag.Parse() + + enableCore() + + C.trigger_crash() +} +` + +// TestGdbCoreCrashThreadBacktrace tests that runtime could let the fault thread to crash process +// and make fault thread as number one thread while gdb in a core file +func TestGdbCoreCrashThreadBacktrace(t *testing.T) { + if runtime.GOOS != "linux" { + // N.B. This test isn't fundamentally Linux-only, but it needs + // to know how to enable/find core files on each OS. + t.Skip("Test only supported on Linux") + } + if runtime.GOARCH != "386" && runtime.GOARCH != "amd64" { + // TODO(go.dev/issue/25218): Other architectures use sigreturn + // via VDSO, which we somehow don't handle correctly. + t.Skip("Backtrace through signal handler only works on 386 and amd64") + } + + testenv.MustHaveCGO(t) + checkGdbEnvironment(t) + t.Parallel() + checkGdbVersion(t) + + coreUsesPID := canGenerateCore(t) + + // Build the source code. + dir := t.TempDir() + src := filepath.Join(dir, "main.go") + err := os.WriteFile(src, []byte(coreCrashThreadSource), 0644) + if err != nil { + t.Fatalf("failed to create file: %v", err) + } + cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe", "main.go") + cmd.Dir = dir + out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() + if err != nil { + t.Fatalf("building source %v\n%s", err, out) + } + + // Start the test binary. + cmd = testenv.Command(t, "./a.exe") + cmd.Dir = dir + var output bytes.Buffer + cmd.Stdout = &output // for test logging + cmd.Stderr = &output + + if err := cmd.Start(); err != nil { + t.Fatalf("error starting test binary: %v", err) + } + + pid := cmd.Process.Pid + + err = cmd.Wait() + t.Logf("child output:\n%s", output.String()) + if err == nil { + t.Fatalf("Wait succeeded, want SIGABRT") + } + ee, ok := err.(*exec.ExitError) + if !ok { + t.Fatalf("Wait err got %T %v, want exec.ExitError", ee, ee) + } + ws, ok := ee.Sys().(syscall.WaitStatus) + if !ok { + t.Fatalf("Sys got %T %v, want syscall.WaitStatus", ee.Sys(), ee.Sys()) + } + if ws.Signal() != syscall.SIGABRT { + t.Fatalf("Signal got %d want SIGABRT", ws.Signal()) + } + if !ws.CoreDump() { + t.Fatalf("CoreDump got %v want true", ws.CoreDump()) + } + + coreFile := "core" + if coreUsesPID { + coreFile += fmt.Sprintf(".%d", pid) + } + + // Execute gdb commands. + args := []string{"-nx", "-batch", + "-iex", "add-auto-load-safe-path " + filepath.Join(testenv.GOROOT(t), "src", "runtime"), + "-ex", "backtrace", + filepath.Join(dir, "a.exe"), + filepath.Join(dir, coreFile), + } + cmd = testenv.Command(t, "gdb", args...) + + got, err := cmd.CombinedOutput() + t.Logf("gdb output:\n%s", got) + if err != nil { + t.Fatalf("gdb exited with error: %v", err) + } + + re := regexp.MustCompile(`#.* trigger_crash`) + if found := re.Find(got) != nil; !found { + t.Fatalf("could not find trigger_crash in backtrace") + } +} diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 0829a84e43..c70a76e409 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -167,12 +167,17 @@ func (g *godebugInc) IncNonDefault() { if newInc == nil { return } - // If other goroutines are racing here, no big deal. One will win, - // and all the inc functions will be using the same underlying - // *godebug.Setting. inc = new(func()) *inc = (*newInc)(g.name) - g.inc.Store(inc) + if raceenabled { + racereleasemerge(unsafe.Pointer(&g.inc)) + } + if !g.inc.CompareAndSwap(nil, inc) { + inc = g.inc.Load() + } + } + if raceenabled { + raceacquire(unsafe.Pointer(&g.inc)) } (*inc)() } diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index 877d94eef2..afe1bdd298 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -307,27 +307,28 @@ type dbgVar struct { // existing int var for that value, which may // already have an initial value. var debug struct { - cgocheck int32 - clobberfree int32 - dontfreezetheworld int32 - efence int32 - gccheckmark int32 - gcpacertrace int32 - gcshrinkstackoff int32 - gcstoptheworld int32 - gctrace int32 - invalidptr int32 - madvdontneed int32 // for Linux; issue 28466 - profileruntimelocks atomic.Int32 - scavtrace int32 - scheddetail int32 - schedtrace int32 - tracebackancestors int32 - asyncpreemptoff int32 - harddecommit int32 - adaptivestackstart int32 - tracefpunwindoff int32 - traceadvanceperiod int32 + cgocheck int32 + clobberfree int32 + disablethp int32 + dontfreezetheworld int32 + efence int32 + gccheckmark int32 + gcpacertrace int32 + gcshrinkstackoff int32 + gcstoptheworld int32 + gctrace int32 + invalidptr int32 + madvdontneed int32 // for Linux; issue 28466 + runtimeContentionStacks atomic.Int32 + scavtrace int32 + scheddetail int32 + schedtrace int32 + tracebackancestors int32 + asyncpreemptoff int32 + harddecommit int32 + adaptivestackstart int32 + tracefpunwindoff int32 + traceadvanceperiod int32 // debug.malloc is used as a combined debug check // in the malloc function and should be set @@ -344,6 +345,7 @@ var dbgvars = []*dbgVar{ {name: "allocfreetrace", value: &debug.allocfreetrace}, {name: "clobberfree", value: &debug.clobberfree}, {name: "cgocheck", value: &debug.cgocheck}, + {name: "disablethp", value: &debug.disablethp}, {name: "dontfreezetheworld", value: &debug.dontfreezetheworld}, {name: "efence", value: &debug.efence}, {name: "gccheckmark", value: &debug.gccheckmark}, @@ -353,7 +355,7 @@ var dbgvars = []*dbgVar{ {name: "gctrace", value: &debug.gctrace}, {name: "invalidptr", value: &debug.invalidptr}, {name: "madvdontneed", value: &debug.madvdontneed}, - {name: "profileruntimelocks", atomic: &debug.profileruntimelocks}, + {name: "runtimecontentionstacks", atomic: &debug.runtimeContentionStacks}, {name: "sbrk", value: &debug.sbrk}, {name: "scavtrace", value: &debug.scavtrace}, {name: "scheddetail", value: &debug.scheddetail}, @@ -621,7 +623,6 @@ func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { //go:linkname reflect_resolveTextOff reflect.resolveTextOff func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { return toRType((*_type)(rtype)).textOff(textOff(off)) - } // reflectlite_resolveNameOff resolves a name offset from a base pointer. diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 6bdd66766d..63320d4a8a 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -6,6 +6,7 @@ package runtime import ( "internal/abi" + "internal/chacha8rand" "internal/goarch" "runtime/internal/atomic" "runtime/internal/sys" @@ -482,6 +483,7 @@ type g struct { // inMarkAssist indicates whether the goroutine is in mark assist. // Used by the execution tracer. inMarkAssist bool + coroexit bool // argument to coroswitch_m raceignore int8 // ignore race detection events nocgocallback bool // whether disable callback from C @@ -506,6 +508,8 @@ type g struct { timer *timer // cached timer for time.Sleep selectDone atomic.Uint32 // are we participating in a select and did someone win the race? + coroarg *coro // argument during coroutine transfers + // goroutineProfiled indicates the status of this goroutine's stack for the // current in-progress goroutine profile goroutineProfiled goroutineProfileStateHolder @@ -577,7 +581,6 @@ type m struct { isExtraInC bool // m is an extra m that is not executing Go code isExtraInSig bool // m is an extra m in a signal handler freeWait atomic.Uint32 // Whether it is safe to free g0 and delete m (one of freeMRef, freeMStack, freeMWait) - fastrand uint64 needextram bool traceback uint8 ncgocall uint64 // number of cgo calls in total @@ -632,6 +635,9 @@ type m struct { mOS + chacha8 chacha8rand.State + cheaprand uint64 + // Up to 10 locks held by this m, maintained by the lock ranking code. locksHeldLen int locksHeld [10]heldLockInfo @@ -857,7 +863,7 @@ type schedt struct { sysmonwait atomic.Bool sysmonnote note - // safepointFn should be called on each P at the next GC + // safePointFn should be called on each P at the next GC // safepoint if p.runSafePointFn is set. safePointFn func(*p) safePointWait int32 @@ -1009,27 +1015,6 @@ type forcegcstate struct { idle atomic.Bool } -// extendRandom extends the random numbers in r[:n] to the whole slice r. -// Treats n<0 as n==0. -func extendRandom(r []byte, n int) { - if n < 0 { - n = 0 - } - for n < len(r) { - // Extend random bits using hash function & time seed - w := n - if w > 16 { - w = 16 - } - h := memhash(unsafe.Pointer(&r[n-w]), uintptr(nanotime()), uintptr(w)) - for i := 0; i < goarch.PtrSize && n < len(r); i++ { - r[n] = byte(h) - n++ - h >>= 8 - } - } -} - // A _defer holds an entry on the list of deferred calls. // If you add a field here, add code to clear it in deferProcStack. // This struct must match the code in cmd/compile/internal/ssagen/ssa.go:deferstruct @@ -1142,6 +1127,8 @@ const ( waitReasonFlushProcCaches // "flushing proc caches" waitReasonTraceGoroutineStatus // "trace goroutine status" waitReasonTraceProcStatus // "trace proc status" + waitReasonPageTraceFlush // "page trace flush" + waitReasonCoroutine // "coroutine" ) var waitReasonStrings = [...]string{ @@ -1180,6 +1167,8 @@ var waitReasonStrings = [...]string{ waitReasonFlushProcCaches: "flushing proc caches", waitReasonTraceGoroutineStatus: "trace goroutine status", waitReasonTraceProcStatus: "trace proc status", + waitReasonPageTraceFlush: "page trace flush", + waitReasonCoroutine: "coroutine", } func (w waitReason) String() string { @@ -1238,7 +1227,9 @@ var ( processorVersionInfo uint32 isIntel bool - goarm uint8 // set by cmd/link on arm systems + // set by cmd/link on arm systems + goarm uint8 + goarmsoftfp uint8 ) // Set by the linker so the runtime can determine the buildmode. diff --git a/src/runtime/rwmutex.go b/src/runtime/rwmutex.go index ede3d13599..34d8f675c1 100644 --- a/src/runtime/rwmutex.go +++ b/src/runtime/rwmutex.go @@ -25,6 +25,43 @@ type rwmutex struct { readerCount atomic.Int32 // number of pending readers readerWait atomic.Int32 // number of departing readers + + readRank lockRank // semantic lock rank for read locking +} + +// Lock ranking an rwmutex has two aspects: +// +// Semantic ranking: this rwmutex represents some higher level lock that +// protects some resource (e.g., allocmLock protects creation of new Ms). The +// read and write locks of that resource need to be represented in the lock +// rank. +// +// Internal ranking: as an implementation detail, rwmutex uses two mutexes: +// rLock and wLock. These have lock order requirements: wLock must be locked +// before rLock. This also needs to be represented in the lock rank. +// +// Semantic ranking is represented by acquiring readRank during read lock and +// writeRank during write lock. +// +// wLock is held for the duration of a write lock, so it uses writeRank +// directly, both for semantic and internal ranking. rLock is only held +// temporarily inside the rlock/lock methods, so it uses readRankInternal to +// represent internal ranking. Semantic ranking is represented by a separate +// acquire of readRank for the duration of a read lock. +// +// The lock ranking must document this ordering: +// - readRankInternal is a leaf lock. +// - readRank is taken before readRankInternal. +// - writeRank is taken before readRankInternal. +// - readRank is placed in the lock order wherever a read lock of this rwmutex +// belongs. +// - writeRank is placed in the lock order wherever a write lock of this +// rwmutex belongs. +func (rw *rwmutex) init(readRank, readRankInternal, writeRank lockRank) { + rw.readRank = readRank + + lockInit(&rw.rLock, readRankInternal) + lockInit(&rw.wLock, writeRank) } const rwmutexMaxReaders = 1 << 30 @@ -36,10 +73,14 @@ func (rw *rwmutex) rlock() { // deadlock (issue #20903). Alternatively, we could drop the P // while sleeping. acquirem() + + acquireLockRank(rw.readRank) + lockWithRankMayAcquire(&rw.rLock, getLockRank(&rw.rLock)) + if rw.readerCount.Add(1) < 0 { // A writer is pending. Park on the reader queue. systemstack(func() { - lockWithRank(&rw.rLock, lockRankRwmutexR) + lock(&rw.rLock) if rw.readerPass > 0 { // Writer finished. rw.readerPass -= 1 @@ -67,7 +108,7 @@ func (rw *rwmutex) runlock() { // A writer is pending. if rw.readerWait.Add(-1) == 0 { // The last reader unblocks the writer. - lockWithRank(&rw.rLock, lockRankRwmutexR) + lock(&rw.rLock) w := rw.writer.ptr() if w != nil { notewakeup(&w.park) @@ -75,18 +116,19 @@ func (rw *rwmutex) runlock() { unlock(&rw.rLock) } } + releaseLockRank(rw.readRank) releasem(getg().m) } // lock locks rw for writing. func (rw *rwmutex) lock() { // Resolve competition with other writers and stick to our P. - lockWithRank(&rw.wLock, lockRankRwmutexW) + lock(&rw.wLock) m := getg().m // Announce that there is a pending writer. r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders // Wait for any active readers to complete. - lockWithRank(&rw.rLock, lockRankRwmutexR) + lock(&rw.rLock) if r != 0 && rw.readerWait.Add(r) != 0 { // Wait for reader to wake us up. systemstack(func() { @@ -108,7 +150,7 @@ func (rw *rwmutex) unlock() { throw("unlock of unlocked rwmutex") } // Unblock blocked readers. - lockWithRank(&rw.rLock, lockRankRwmutexR) + lock(&rw.rLock) for rw.readers.ptr() != nil { reader := rw.readers.ptr() rw.readers = reader.schedlink diff --git a/src/runtime/rwmutex_test.go b/src/runtime/rwmutex_test.go index ddb16aead4..bdeb9c4901 100644 --- a/src/runtime/rwmutex_test.go +++ b/src/runtime/rwmutex_test.go @@ -29,6 +29,7 @@ func parallelReader(m *RWMutex, clocked chan bool, cunlock *atomic.Bool, cdone c func doTestParallelReaders(numReaders int) { GOMAXPROCS(numReaders + 1) var m RWMutex + m.Init() clocked := make(chan bool, numReaders) var cunlock atomic.Bool cdone := make(chan bool) @@ -100,6 +101,7 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) { // Number of active readers + 10000 * number of active writers. var activity int32 var rwm RWMutex + rwm.Init() cdone := make(chan bool) go writer(&rwm, num_iterations, &activity, cdone) var i int @@ -141,6 +143,7 @@ func BenchmarkRWMutexUncontended(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { var rwm PaddedRWMutex + rwm.Init() for pb.Next() { rwm.RLock() rwm.RLock() @@ -154,6 +157,7 @@ func BenchmarkRWMutexUncontended(b *testing.B) { func benchmarkRWMutex(b *testing.B, localWork, writeRatio int) { var rwm RWMutex + rwm.Init() b.RunParallel(func(pb *testing.PB) { foo := 0 for pb.Next() { diff --git a/src/runtime/select.go b/src/runtime/select.go index 34c06375c2..b3a3085cb0 100644 --- a/src/runtime/select.go +++ b/src/runtime/select.go @@ -173,7 +173,7 @@ func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, blo continue } - j := fastrandn(uint32(norder + 1)) + j := cheaprandn(uint32(norder + 1)) pollorder[norder] = pollorder[j] pollorder[j] = uint16(i) norder++ diff --git a/src/runtime/sema.go b/src/runtime/sema.go index 3b6874ca11..c87fc7658e 100644 --- a/src/runtime/sema.go +++ b/src/runtime/sema.go @@ -338,7 +338,7 @@ func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) { // // s.ticket compared with zero in couple of places, therefore set lowest bit. // It will not affect treap's quality noticeably. - s.ticket = fastrand() | 1 + s.ticket = cheaprand() | 1 s.parent = last *pt = s diff --git a/src/runtime/signal_aix_ppc64.go b/src/runtime/signal_aix_ppc64.go index c6cb91a0a2..8ae0f749ed 100644 --- a/src/runtime/signal_aix_ppc64.go +++ b/src/runtime/signal_aix_ppc64.go @@ -1,4 +1,4 @@ -/// Copyright 2018 The Go Authors. All rights reserved. +// 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. diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go index cd9fd5d796..84391d58ed 100644 --- a/src/runtime/signal_unix.go +++ b/src/runtime/signal_unix.go @@ -597,7 +597,7 @@ func adjustSignalStack(sig uint32, mp *m, gsigStack *gsignalStack) bool { // crashing is the number of m's we have waited for when implementing // GOTRACEBACK=crash when a signal is received. -var crashing int32 +var crashing atomic.Int32 // testSigtrap and testSigusr1 are used by the runtime tests. If // non-nil, it is called on SIGTRAP/SIGUSR1. If it returns true, the @@ -730,7 +730,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { mp.throwing = throwTypeRuntime mp.caughtsig.set(gp) - if crashing == 0 { + if crashing.Load() == 0 { startpanic_m() } @@ -740,11 +740,11 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { if level > 0 { goroutineheader(gp) tracebacktrap(c.sigpc(), c.sigsp(), c.siglr(), gp) - if crashing > 0 && gp != mp.curg && mp.curg != nil && readgstatus(mp.curg)&^_Gscan == _Grunning { + if crashing.Load() > 0 && gp != mp.curg && mp.curg != nil && readgstatus(mp.curg)&^_Gscan == _Grunning { // tracebackothers on original m skipped this one; trace it now. goroutineheader(mp.curg) traceback(^uintptr(0), ^uintptr(0), 0, mp.curg) - } else if crashing == 0 { + } else if crashing.Load() == 0 { tracebackothers(gp) print("\n") } @@ -752,20 +752,35 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { } if docrash { - crashing++ - if crashing < mcount()-int32(extraMLength.Load()) { + isCrashThread := false + if crashing.CompareAndSwap(0, 1) { + isCrashThread = true + } else { + crashing.Add(1) + } + if crashing.Load() < mcount()-int32(extraMLength.Load()) { // There are other m's that need to dump their stacks. // Relay SIGQUIT to the next m by sending it to the current process. // All m's that have already received SIGQUIT have signal masks blocking // receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet. - // When the last m receives the SIGQUIT, it will fall through to the call to - // crash below. Just in case the relaying gets botched, each m involved in + // The first m will wait until all ms received the SIGQUIT, then crash/exit. + // Just in case the relaying gets botched, each m involved in // the relay sleeps for 5 seconds and then does the crash/exit itself. - // In expected operation, the last m has received the SIGQUIT and run - // crash/exit and the process is gone, all long before any of the - // 5-second sleeps have finished. + // The faulting m is crashing first so it is the faulting thread in the core dump (see issue #63277): + // in expected operation, the first m will wait until the last m has received the SIGQUIT, + // and then run crash/exit and the process is gone. + // However, if it spends more than 5 seconds to send SIGQUIT to all ms, + // any of ms may crash/exit the process after waiting for 5 seconds. print("\n-----\n\n") raiseproc(_SIGQUIT) + } + if isCrashThread { + i := 0 + for (crashing.Load() < mcount()-int32(extraMLength.Load())) && i < 10 { + i++ + usleep(500 * 1000) + } + } else { usleep(5 * 1000 * 1000) } printDebugLog() diff --git a/src/runtime/sizeof_test.go b/src/runtime/sizeof_test.go index ccc0864ca9..aa8caaadda 100644 --- a/src/runtime/sizeof_test.go +++ b/src/runtime/sizeof_test.go @@ -17,9 +17,9 @@ import ( func TestSizeof(t *testing.T) { const _64bit = unsafe.Sizeof(uintptr(0)) == 8 - g32bit := uintptr(252) + g32bit := uintptr(256) if goexperiment.ExecTracer2 { - g32bit = uintptr(256) + g32bit = uintptr(260) } var tests = []struct { @@ -27,7 +27,7 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {runtime.G{}, g32bit, 408}, // g, but exported for testing + {runtime.G{}, g32bit, 424}, // g, but exported for testing {runtime.Sudog{}, 56, 88}, // sudog, but exported for testing } diff --git a/src/runtime/stkframe.go b/src/runtime/stkframe.go index a2f40c92d5..becb729e59 100644 --- a/src/runtime/stkframe.go +++ b/src/runtime/stkframe.go @@ -234,7 +234,7 @@ func (frame *stkframe) getStackMap(debug bool) (locals, args bitvector, objs []s } // stack objects. - if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") && + if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "loong64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") && unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect { // For reflect.makeFuncStub and reflect.methodValueCall, // we need to fake the stack object record. diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go index cf856e135f..34984d86ff 100644 --- a/src/runtime/stubs.go +++ b/src/runtime/stubs.go @@ -6,8 +6,6 @@ package runtime import ( "internal/abi" - "internal/goarch" - "runtime/internal/math" "unsafe" ) @@ -120,94 +118,6 @@ func reflect_memmove(to, from unsafe.Pointer, n uintptr) { // exported value for testing const hashLoad = float32(loadFactorNum) / float32(loadFactorDen) -//go:nosplit -func fastrand() uint32 { - mp := getg().m - // Implement wyrand: https://github.com/wangyi-fudan/wyhash - // Only the platform that math.Mul64 can be lowered - // by the compiler should be in this list. - if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64| - goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le| - goarch.IsS390x|goarch.IsRiscv64|goarch.IsLoong64 == 1 { - mp.fastrand += 0xa0761d6478bd642f - hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db) - return uint32(hi ^ lo) - } - - // Implement xorshift64+: 2 32-bit xorshift sequences added together. - // Shift triplet [17,7,16] was calculated as indicated in Marsaglia's - // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf - // This generator passes the SmallCrush suite, part of TestU01 framework: - // http://simul.iro.umontreal.ca/testu01/tu01.html - t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand)) - s1, s0 := t[0], t[1] - s1 ^= s1 << 17 - s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 - t[0], t[1] = s0, s1 - return s0 + s1 -} - -//go:nosplit -func fastrandn(n uint32) uint32 { - // This is similar to fastrand() % n, but faster. - // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ - return uint32(uint64(fastrand()) * uint64(n) >> 32) -} - -func fastrand64() uint64 { - mp := getg().m - // Implement wyrand: https://github.com/wangyi-fudan/wyhash - // Only the platform that math.Mul64 can be lowered - // by the compiler should be in this list. - if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64| - goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le| - goarch.IsS390x|goarch.IsRiscv64 == 1 { - mp.fastrand += 0xa0761d6478bd642f - hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db) - return hi ^ lo - } - - // Implement xorshift64+: 2 32-bit xorshift sequences added together. - // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf - // This generator passes the SmallCrush suite, part of TestU01 framework: - // http://simul.iro.umontreal.ca/testu01/tu01.html - t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand)) - s1, s0 := t[0], t[1] - s1 ^= s1 << 17 - s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 - r := uint64(s0 + s1) - - s0, s1 = s1, s0 - s1 ^= s1 << 17 - s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16 - r += uint64(s0+s1) << 32 - - t[0], t[1] = s0, s1 - return r -} - -func fastrandu() uint { - if goarch.PtrSize == 4 { - return uint(fastrand()) - } - return uint(fastrand64()) -} - -//go:linkname rand_fastrand64 math/rand.fastrand64 -func rand_fastrand64() uint64 { return fastrand64() } - -//go:linkname rand2_fastrand64 math/rand/v2.fastrand64 -func rand2_fastrand64() uint64 { return fastrand64() } - -//go:linkname sync_fastrandn sync.fastrandn -func sync_fastrandn(n uint32) uint32 { return fastrandn(n) } - -//go:linkname net_fastrandu net.fastrandu -func net_fastrandu() uint { return fastrandu() } - -//go:linkname os_fastrand os.fastrand -func os_fastrand() uint32 { return fastrand() } - // in internal/bytealg/equal_*.s // //go:noescape diff --git a/src/runtime/stubs_loong64.go b/src/runtime/stubs_loong64.go index 556983cad1..4576089b0b 100644 --- a/src/runtime/stubs_loong64.go +++ b/src/runtime/stubs_loong64.go @@ -10,6 +10,13 @@ package runtime 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() + // getfp returns the frame pointer register of its caller or 0 if not implemented. // TODO: Make this a compiler intrinsic func getfp() uintptr { return 0 } diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 87b687a196..8b878525d0 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -931,7 +931,7 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, strict bool) (int32, uint cache.inUse++ if cache.inUse == 1 { e := &cache.entries[ck] - ci := fastrandn(uint32(len(cache.entries[ck]))) + ci := cheaprandn(uint32(len(cache.entries[ck]))) e[ci] = e[0] e[0] = pcvalueCacheEnt{ targetpc: targetpc, diff --git a/src/runtime/syscall_aix.go b/src/runtime/syscall_aix.go index e87d4d6d7a..7738fca602 100644 --- a/src/runtime/syscall_aix.go +++ b/src/runtime/syscall_aix.go @@ -164,7 +164,6 @@ func syscall_exit(code uintptr) { func syscall_fcntl1(fd, cmd, arg uintptr) (val, err uintptr) { val, err = syscall3(&libc_fcntl, fd, cmd, arg) return - } //go:linkname syscall_forkx syscall.forkx diff --git a/src/runtime/trace2.go b/src/runtime/trace2.go index 1a58015989..c2a1c1ca1e 100644 --- a/src/runtime/trace2.go +++ b/src/runtime/trace2.go @@ -443,6 +443,7 @@ func traceAdvance(stopTrace bool) { // held, we can be certain that when there are no writers there are // also no stale generation values left. Therefore, it's safe to flush // any buffers that remain in that generation's slot. + const debugDeadlock = false systemstack(func() { // Track iterations for some rudimentary deadlock detection. i := 0 @@ -479,16 +480,18 @@ func traceAdvance(stopTrace bool) { osyield() } - // Try to detect a deadlock. We probably shouldn't loop here - // this many times. - if i > 100000 && !detectedDeadlock { - detectedDeadlock = true - println("runtime: failing to flush") - for mp := mToFlush; mp != nil; mp = mp.trace.link { - print("runtime: m=", mp.id, "\n") + if debugDeadlock { + // Try to detect a deadlock. We probably shouldn't loop here + // this many times. + if i > 100000 && !detectedDeadlock { + detectedDeadlock = true + println("runtime: failing to flush") + for mp := mToFlush; mp != nil; mp = mp.trace.link { + print("runtime: m=", mp.id, "\n") + } } + i++ } - i++ } }) @@ -761,6 +764,8 @@ func readTrace0() (buf []byte, park bool) { // can continue to advance. if trace.flushedGen.Load() == gen { if trace.shutdown.Load() { + unlock(&trace.lock) + // Wake up anyone waiting for us to be done with this generation. // // Do this after reading trace.shutdown, because the thread we're @@ -775,13 +780,13 @@ func readTrace0() (buf []byte, park bool) { // We're shutting down, and the last generation is fully // read. We're done. - unlock(&trace.lock) return nil, false } // The previous gen has had all of its buffers flushed, and // there's nothing else for us to read. Advance the generation // we're reading from and try again. trace.readerGen.Store(trace.gen.Load()) + unlock(&trace.lock) // Wake up anyone waiting for us to be done with this generation. // @@ -792,6 +797,9 @@ func readTrace0() (buf []byte, park bool) { racerelease(unsafe.Pointer(&trace.doneSema[gen%2])) } semrelease(&trace.doneSema[gen%2]) + + // Reacquire the lock and go back to the top of the loop. + lock(&trace.lock) continue } // Wait for new data. diff --git a/src/runtime/trace2runtime.go b/src/runtime/trace2runtime.go index b6837d0360..a9c8d8a590 100644 --- a/src/runtime/trace2runtime.go +++ b/src/runtime/trace2runtime.go @@ -493,10 +493,10 @@ func (tl traceLocker) GoSysExit(lostP bool) { // ProcSteal indicates that our current M stole a P from another M. // -// forMe indicates that the caller is stealing pp to wire it up to itself. +// inSyscall indicates that we're stealing the P from a syscall context. // // The caller must have ownership of pp. -func (tl traceLocker) ProcSteal(pp *p, forMe bool) { +func (tl traceLocker) ProcSteal(pp *p, inSyscall bool) { // Grab the M ID we stole from. mStolenFrom := pp.trace.mSyscallID pp.trace.mSyscallID = -1 @@ -506,17 +506,20 @@ func (tl traceLocker) ProcSteal(pp *p, forMe bool) { // the P just to get its attention (e.g. STW or sysmon retake) or we're trying to steal a P for // ourselves specifically to keep running. The two contexts look different, but can be summarized // fairly succinctly. In the former, we're a regular running goroutine and proc, if we have either. - // In the latter, we're a goroutine in a syscall, + // In the latter, we're a goroutine in a syscall. goStatus := traceGoRunning procStatus := traceProcRunning - if forMe { + if inSyscall { goStatus = traceGoSyscall procStatus = traceProcSyscallAbandoned } w := tl.eventWriter(goStatus, procStatus) - // Emit the status of the P we're stealing. We may have *just* done this, but we may not have, - // even if forMe is true, depending on whether we wired the P to ourselves already. + // Emit the status of the P we're stealing. We may have *just* done this when creating the event + // writer but it's not guaranteed, even if inSyscall is true. Although it might seem like from a + // syscall context we're always stealing a P for ourselves, we may have not wired it up yet (so + // it wouldn't be visible to eventWriter) or we may not even intend to wire it up to ourselves + // at all (e.g. entersyscall_gcwait). if !pp.trace.statusWasTraced(tl.gen) && pp.trace.acquireStatus(tl.gen) { // Careful: don't use the event writer. We never want status or in-progress events // to trigger more in-progress events. diff --git a/src/runtime/trace2stack.go b/src/runtime/trace2stack.go index ebfe7c57f0..af6638fa8f 100644 --- a/src/runtime/trace2stack.go +++ b/src/runtime/trace2stack.go @@ -97,7 +97,8 @@ func (t *traceStackTable) put(pcs []uintptr) uint64 { } // dump writes all previously cached stacks to trace buffers, -// releases all memory and resets state. +// releases all memory and resets state. It must only be called once the caller +// can guarantee that there are no more writers to the table. // // This must run on the system stack because it flushes buffers and thus // may acquire trace.lock. @@ -107,7 +108,15 @@ func (t *traceStackTable) dump(gen uintptr) { w := unsafeTraceWriter(gen, nil) // Iterate over the table. - lock(&t.tab.lock) + // + // Do not acquire t.tab.lock. There's a conceptual lock cycle between acquiring this lock + // here and allocation-related locks. Specifically, this lock may be acquired when an event + // is emitted in allocation paths. Simultaneously, we might allocate here with the lock held, + // creating a cycle. In practice, this cycle is never exercised. Because the table is only + // dumped once there are no more writers, it's not possible for the cycle to occur. However + // the lockrank mode is not sophisticated enough to identify this, and if it's not possible + // for that cycle to happen, then it's also not possible for this to race with writers to + // the table. for i := range t.tab.tab { stk := t.tab.bucket(i) for ; stk != nil; stk = stk.next() { @@ -144,6 +153,9 @@ func (t *traceStackTable) dump(gen uintptr) { } } } + // Still, hold the lock over reset. The callee expects it, even though it's + // not strictly necessary. + lock(&t.tab.lock) t.tab.reset() unlock(&t.tab.lock) diff --git a/src/runtime/trace2status.go b/src/runtime/trace2status.go index 0f64452c3e..5016e08656 100644 --- a/src/runtime/trace2status.go +++ b/src/runtime/trace2status.go @@ -82,8 +82,17 @@ func (w traceWriter) writeProcStatusForP(pp *p, inSTW bool) traceWriter { // in _Pgcstop, but we model it as running in the tracer. status = traceProcRunning } - case _Prunning, _Psyscall: + case _Prunning: status = traceProcRunning + // There's a short window wherein the goroutine may have entered _Gsyscall + // but it still owns the P (it's not in _Psyscall yet). The goroutine entering + // _Gsyscall is the tracer's signal that the P its bound to is also in a syscall, + // so we need to emit a status that matches. See #64318. + if w.mp.p.ptr() == pp && w.mp.curg != nil && readgstatus(w.mp.curg)&^_Gscan == _Gsyscall { + status = traceProcSyscall + } + case _Psyscall: + status = traceProcSyscall default: throw("attempt to trace invalid or unsupported P status") } @@ -138,6 +147,7 @@ func goStatusToTraceGoStatus(status uint32, wr waitReason) traceGoStatus { wr == waitReasonGCMarkTermination || wr == waitReasonGarbageCollection || wr == waitReasonTraceProcStatus || + wr == waitReasonPageTraceFlush || wr == waitReasonGCWorkerActive { tgs = traceGoRunning } diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 66a1cc85ee..1e5afc6bdd 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -1322,7 +1322,7 @@ func isSystemGoroutine(gp *g, fixed bool) bool { if !f.valid() { return false } - if f.funcID == abi.FuncID_runtime_main || f.funcID == abi.FuncID_handleAsyncEvent { + if f.funcID == abi.FuncID_runtime_main || f.funcID == abi.FuncID_corostart || f.funcID == abi.FuncID_handleAsyncEvent { return false } if f.funcID == abi.FuncID_runfinq { diff --git a/src/runtime/traceback_test.go b/src/runtime/traceback_test.go index 204b4f5316..8cbccac673 100644 --- a/src/runtime/traceback_test.go +++ b/src/runtime/traceback_test.go @@ -419,6 +419,17 @@ func TestTracebackArgs(t *testing.T) { "testTracebackArgs11b(0xffffffff?, 0xffffffff?, 0x3?, 0x4)", "testTracebackArgs11b(0x1, 0x2, 0x3, 0x4)"), }, + // Make sure spilled slice data pointers are spilled to the right location + // to ensure we see it listed without a ?. + // See issue 64414. + { + func() int { + poisonStack() + return testTracebackArgsSlice(testTracebackArgsSliceBackingStore[:]) + }, + // Note: capacity of the slice might be junk, as it is not used. + fmt.Sprintf("testTracebackArgsSlice({%p, 0x2, ", &testTracebackArgsSliceBackingStore[0]), + }, } for _, test := range tests { n := test.fn() @@ -450,7 +461,6 @@ func testTracebackArgs2(a bool, b struct { return b.a + b.b + b.c + b.x[0] + b.x[1] + int(d[0]) + int(d[1]) + int(d[2]) } return n - } //go:noinline @@ -667,6 +677,19 @@ func testTracebackArgs11b(a, b, c, d int32) int { return runtime.Stack(testTracebackArgsBuf[:], false) } +// norace to avoid race instrumentation changing spill locations. +// nosplit to avoid preemption or morestack spilling registers. +// +//go:norace +//go:nosplit +//go:noinline +func testTracebackArgsSlice(a []int) int { + n := runtime.Stack(testTracebackArgsBuf[:], false) + return a[1] + n +} + +var testTracebackArgsSliceBackingStore [2]int + // Poison the arg area with deterministic values. // //go:noinline diff --git a/src/slices/slices_test.go b/src/slices/slices_test.go index ab25bd8dfd..31d59abe17 100644 --- a/src/slices/slices_test.go +++ b/src/slices/slices_test.go @@ -557,7 +557,7 @@ func TestInsertPanics(t *testing.T) { {"with out-of-bounds index and = cap", a[:1:2], 2, b[:]}, {"with out-of-bounds index and < cap", a[:1:3], 2, b[:]}, } { - if !panics(func() { Insert(test.s, test.i, test.v...) }) { + if !panics(func() { _ = Insert(test.s, test.i, test.v...) }) { t.Errorf("Insert %s: got no panic, want panic", test.name) } } @@ -684,7 +684,7 @@ func TestDeletePanics(t *testing.T) { {"s[i:j] is valid and j > len(s)", s, 0, 4}, {"s[i:j] is valid and i == j > len(s)", s, 3, 3}, } { - if !panics(func() { Delete(test.s, test.i, test.j) }) { + if !panics(func() { _ = Delete(test.s, test.i, test.j) }) { t.Errorf("Delete %s: got no panic, want panic", test.name) } } @@ -906,10 +906,10 @@ func TestGrow(t *testing.T) { } // Test number of allocations. - if n := testing.AllocsPerRun(100, func() { Grow(s2, cap(s2)-len(s2)) }); n != 0 { + if n := testing.AllocsPerRun(100, func() { _ = Grow(s2, cap(s2)-len(s2)) }); n != 0 { t.Errorf("Grow should not allocate when given sufficient capacity; allocated %v times", n) } - if n := testing.AllocsPerRun(100, func() { Grow(s2, cap(s2)-len(s2)+1) }); n != 1 { + if n := testing.AllocsPerRun(100, func() { _ = Grow(s2, cap(s2)-len(s2)+1) }); n != 1 { errorf := t.Errorf if race.Enabled || testenv.OptimizationOff() { errorf = t.Logf // this allocates multiple times in race detector mode @@ -921,7 +921,7 @@ func TestGrow(t *testing.T) { var gotPanic bool func() { defer func() { gotPanic = recover() != nil }() - Grow(s1, -1) + _ = Grow(s1, -1) }() if !gotPanic { t.Errorf("Grow(-1) did not panic; expected a panic") @@ -1037,7 +1037,7 @@ func TestReplacePanics(t *testing.T) { {"s[i:j] is valid and j > len(s)", s, nil, 0, 4}, } { ss, vv := Clone(test.s), Clone(test.v) - if !panics(func() { Replace(ss, test.i, test.j, vv...) }) { + if !panics(func() { _ = Replace(ss, test.i, test.j, vv...) }) { t.Errorf("Replace %s: should have panicked", test.name) } } diff --git a/src/sort/search.go b/src/sort/search.go index 874e40813d..ccf76dba24 100644 --- a/src/sort/search.go +++ b/src/sort/search.go @@ -117,7 +117,7 @@ func Find(n int, cmp func(int) int) (i int, found bool) { // Convenience wrappers for common cases. // SearchInts searches for x in a sorted slice of ints and returns the index -// as specified by Search. The return value is the index to insert x if x is +// 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 { @@ -125,7 +125,7 @@ func SearchInts(a []int, x int) int { } // SearchFloat64s searches for x in a sorted slice of float64s and returns the index -// as specified by Search. The return value is the index to insert x if x is not +// 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 { @@ -140,11 +140,11 @@ func SearchStrings(a []string, x string) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } -// Search returns the result of applying SearchInts to the receiver and x. +// Search returns the result of applying [SearchInts] to the receiver and x. func (p IntSlice) Search(x int) int { return SearchInts(p, x) } -// Search returns the result of applying SearchFloat64s to the receiver and x. +// Search returns the result of applying [SearchFloat64s] to the receiver and x. func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) } -// Search returns the result of applying SearchStrings to the receiver and x. +// Search returns the result of applying [SearchStrings] to the receiver and x. func (p StringSlice) Search(x string) int { return SearchStrings(p, x) } diff --git a/src/sort/slice.go b/src/sort/slice.go index 73ba548a47..bc9dd84ed2 100644 --- a/src/sort/slice.go +++ b/src/sort/slice.go @@ -14,12 +14,12 @@ import ( // // The sort is not guaranteed to be stable: equal elements // may be reversed from their original order. -// For a stable sort, use SliceStable. +// For a stable sort, use [SliceStable]. // // The less function must satisfy the same requirements as // the Interface type's Less method. // -// Note: in many situations, the newer slices.SortFunc function is more +// Note: in many situations, the newer [slices.SortFunc] function is more // ergonomic and runs faster. func Slice(x any, less func(i, j int) bool) { rv := reflectlite.ValueOf(x) @@ -36,7 +36,7 @@ func Slice(x any, less func(i, j int) bool) { // The less function must satisfy the same requirements as // the Interface type's Less method. // -// Note: in many situations, the newer slices.SortStableFunc function is more +// Note: in many situations, the newer [slices.SortStableFunc] function is more // ergonomic and runs faster. func SliceStable(x any, less func(i, j int) bool) { rv := reflectlite.ValueOf(x) @@ -47,7 +47,7 @@ func SliceStable(x any, less func(i, j int) bool) { // SliceIsSorted reports whether the slice x is sorted according to the provided less function. // It panics if x is not a slice. // -// Note: in many situations, the newer slices.IsSortedFunc function is more +// Note: in many situations, the newer [slices.IsSortedFunc] function is more // ergonomic and runs faster. func SliceIsSorted(x any, less func(i, j int) bool) bool { rv := reflectlite.ValueOf(x) diff --git a/src/sort/sort.go b/src/sort/sort.go index 8ea62a5e6a..6db161f0c0 100644 --- a/src/sort/sort.go +++ b/src/sort/sort.go @@ -40,7 +40,7 @@ type Interface interface { // It makes one call to data.Len to determine n and O(n*log(n)) calls to // data.Less and data.Swap. The sort is not guaranteed to be stable. // -// Note: in many situations, the newer slices.SortFunc function is more +// Note: in many situations, the newer [slices.SortFunc] function is more // ergonomic and runs faster. func Sort(data Interface) { n := data.Len() @@ -100,7 +100,7 @@ func Reverse(data Interface) Interface { // IsSorted reports whether data is sorted. // -// Note: in many situations, the newer slices.IsSortedFunc function is more +// Note: in many situations, the newer [slices.IsSortedFunc] function is more // ergonomic and runs faster. func IsSorted(data Interface) bool { n := data.Len() @@ -161,34 +161,34 @@ func (x StringSlice) Sort() { Sort(x) } // Ints sorts a slice of ints in increasing order. // -// Note: as of Go 1.22, this function simply calls slices.Sort. +// Note: as of Go 1.22, this function simply calls [slices.Sort]. func Ints(x []int) { intsImpl(x) } // Float64s sorts a slice of float64s in increasing order. // Not-a-number (NaN) values are ordered before other values. // -// Note: as of Go 1.22, this function simply calls slices.Sort. +// Note: as of Go 1.22, this function simply calls [slices.Sort]. func Float64s(x []float64) { float64sImpl(x) } // Strings sorts a slice of strings in increasing order. // -// Note: as of Go 1.22, this function simply calls slices.Sort. +// Note: as of Go 1.22, this function simply calls [slices.Sort]. func Strings(x []string) { stringsImpl(x) } // IntsAreSorted reports whether the slice x is sorted in increasing order. // -// Note: as of Go 1.22, this function simply calls slices.IsSorted. +// Note: as of Go 1.22, this function simply calls [slices.IsSorted]. func IntsAreSorted(x []int) bool { return intsAreSortedImpl(x) } // Float64sAreSorted reports whether the slice x is sorted in increasing order, // with not-a-number (NaN) values before any other values. // -// Note: as of Go 1.22, this function simply calls slices.IsSorted. +// Note: as of Go 1.22, this function simply calls [slices.IsSorted]. func Float64sAreSorted(x []float64) bool { return float64sAreSortedImpl(x) } // StringsAreSorted reports whether the slice x is sorted in increasing order. // -// Note: as of Go 1.22, this function simply calls slices.IsSorted. +// Note: as of Go 1.22, this function simply calls [slices.IsSorted]. func StringsAreSorted(x []string) bool { return stringsAreSortedImpl(x) } // Notes on stable sorting: diff --git a/src/strings/strings.go b/src/strings/strings.go index ce79bccf8c..f3f0723721 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -521,7 +521,7 @@ func Map(mapping func(rune) rune, s string) string { if r < utf8.RuneSelf { b.WriteByte(byte(r)) } else { - // r is not a ASCII rune. + // r is not an ASCII rune. b.WriteRune(r) } } diff --git a/src/sync/map.go b/src/sync/map.go index 00b2446153..7a9eebdce3 100644 --- a/src/sync/map.go +++ b/src/sync/map.go @@ -8,7 +8,7 @@ import ( "sync/atomic" ) -// Map is like a Go map[interface{}]interface{} but is safe for concurrent use +// Map is like a Go map[any]any but is safe for concurrent use // by multiple goroutines without additional locking or coordination. // Loads, stores, and deletes run in amortized constant time. // diff --git a/src/sync/map_test.go b/src/sync/map_test.go index 20872f3b72..316f87bacc 100644 --- a/src/sync/map_test.go +++ b/src/sync/map_test.go @@ -278,7 +278,7 @@ func TestCompareAndSwap_NonExistingKey(t *testing.T) { m := &sync.Map{} if m.CompareAndSwap(m, nil, 42) { // See https://go.dev/issue/51972#issuecomment-1126408637. - t.Fatalf("CompareAndSwap on an non-existing key succeeded") + t.Fatalf("CompareAndSwap on a non-existing key succeeded") } } diff --git a/src/sync/oncefunc.go b/src/sync/oncefunc.go index 9ef8344132..db286283d1 100644 --- a/src/sync/oncefunc.go +++ b/src/sync/oncefunc.go @@ -25,7 +25,8 @@ func OnceFunc(f func()) func() { } }() f() - valid = true // Set only if f does not panic + f = nil // Do not keep f alive after invoking it. + valid = true // Set only if f does not panic. } return func() { once.Do(g) @@ -54,6 +55,7 @@ func OnceValue[T any](f func() T) func() T { } }() result = f() + f = nil valid = true } return func() T { @@ -85,6 +87,7 @@ func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { } }() r1, r2 = f() + f = nil valid = true } return func() (T1, T2) { diff --git a/src/sync/oncefunc_test.go b/src/sync/oncefunc_test.go index 3c523a5b62..5f0d564063 100644 --- a/src/sync/oncefunc_test.go +++ b/src/sync/oncefunc_test.go @@ -6,10 +6,13 @@ package sync_test import ( "bytes" + "math" "runtime" "runtime/debug" "sync" + "sync/atomic" "testing" + _ "unsafe" ) // We assume that the Once.Do tests have already covered parallelism. @@ -182,6 +185,53 @@ func onceFuncPanic() { panic("x") } +func TestOnceXGC(t *testing.T) { + fns := map[string]func([]byte) func(){ + "OnceFunc": func(buf []byte) func() { + return sync.OnceFunc(func() { buf[0] = 1 }) + }, + "OnceValue": func(buf []byte) func() { + f := sync.OnceValue(func() any { buf[0] = 1; return nil }) + return func() { f() } + }, + "OnceValues": func(buf []byte) func() { + f := sync.OnceValues(func() (any, any) { buf[0] = 1; return nil, nil }) + return func() { f() } + }, + } + for n, fn := range fns { + t.Run(n, func(t *testing.T) { + buf := make([]byte, 1024) + var gc atomic.Bool + runtime.SetFinalizer(&buf[0], func(_ *byte) { + gc.Store(true) + }) + f := fn(buf) + gcwaitfin() + if gc.Load() != false { + t.Fatal("wrapped function garbage collected too early") + } + f() + gcwaitfin() + if gc.Load() != true { + // Even if f is still alive, the function passed to Once(Func|Value|Values) + // is not kept alive after the first call to f. + t.Fatal("wrapped function should be garbage collected, but still live") + } + f() + }) + } +} + +// gcwaitfin performs garbage collection and waits for all finalizers to run. +func gcwaitfin() { + runtime.GC() + runtime_blockUntilEmptyFinalizerQueue(math.MaxInt64) +} + +//go:linkname runtime_blockUntilEmptyFinalizerQueue runtime.blockUntilEmptyFinalizerQueue +func runtime_blockUntilEmptyFinalizerQueue(int64) bool + var ( onceFunc = sync.OnceFunc(func() {}) diff --git a/src/sync/pool.go b/src/sync/pool.go index ffab67bf19..3359aba57b 100644 --- a/src/sync/pool.go +++ b/src/sync/pool.go @@ -76,7 +76,8 @@ type poolLocal struct { } // from runtime -func fastrandn(n uint32) uint32 +//go:linkname runtime_randn runtime.randn +func runtime_randn(n uint32) uint32 var poolRaceHash [128]uint64 @@ -97,7 +98,7 @@ func (p *Pool) Put(x any) { return } if race.Enabled { - if fastrandn(4) == 0 { + if runtime_randn(4) == 0 { // Randomly drop x on floor. return } diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index e1c71b5a34..e6d6343ed8 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -133,7 +133,7 @@ func runtime_AfterForkInChild() func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Set up and fork. This returns immediately in the parent or // if there's an error. - upid, err, mapPipe, locked := forkAndExecInChild1(argv0, argv, envv, chroot, dir, attr, sys, pipe) + upid, pidfd, err, mapPipe, locked := forkAndExecInChild1(argv0, argv, envv, chroot, dir, attr, sys, pipe) if locked { runtime_AfterFork() } @@ -143,6 +143,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // parent; return PID pid = int(upid) + if sys.PidFD != nil { + *sys.PidFD = int(pidfd) + } if sys.UidMappings != nil || sys.GidMappings != nil { Close(mapPipe[0]) @@ -210,7 +213,7 @@ type cloneArgs struct { //go:noinline //go:norace //go:nocheckptr -func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid uintptr, err1 Errno, mapPipe [2]int, locked bool) { +func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid uintptr, pidfd int32, err1 Errno, mapPipe [2]int, locked bool) { // Defined in linux/prctl.h starting with Linux 4.3. const ( PR_CAP_AMBIENT = 0x2f @@ -241,12 +244,12 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att uidmap, setgroups, gidmap []byte clone3 *cloneArgs pgrp int32 - pidfd _C_int = -1 dirfd int cred *Credential ngroups, groups uintptr c uintptr ) + pidfd = -1 rlim := origRlimitNofile.Load() @@ -341,10 +344,6 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att // Fork succeeded, now in child. - if sys.PidFD != nil { - *sys.PidFD = int(pidfd) - } - // Enable the "keep capabilities" flag to set ambient capabilities later. if len(sys.AmbientCaps) > 0 { _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_KEEPCAPS, 1, 0, 0, 0, 0) diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go index f255930aa8..68ec6fe3f8 100644 --- a/src/syscall/exec_linux_test.go +++ b/src/syscall/exec_linux_test.go @@ -12,6 +12,7 @@ import ( "flag" "fmt" "internal/platform" + "internal/syscall/unix" "internal/testenv" "io" "os" @@ -522,7 +523,7 @@ func TestCloneTimeNamespace(t *testing.T) { } } -func testPidFD(t *testing.T) error { +func testPidFD(t *testing.T, userns bool) error { testenv.MustHaveExec(t) if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { @@ -541,6 +542,9 @@ func testPidFD(t *testing.T) error { cmd.SysProcAttr = &syscall.SysProcAttr{ PidFD: &pidfd, } + if userns { + cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER + } if err := cmd.Start(); err != nil { return err } @@ -557,11 +561,11 @@ func testPidFD(t *testing.T) error { // Use pidfd to send a signal to the child. sig := syscall.SIGINT - if _, _, e := syscall.Syscall(syscall.Sys_pidfd_send_signal, uintptr(pidfd), uintptr(sig), 0); e != 0 { - if e != syscall.EINVAL && testenv.SyscallIsNotSupported(e) { - t.Skip("pidfd_send_signal syscall not supported:", e) + if err := unix.PidFDSendSignal(uintptr(pidfd), sig); err != nil { + if err != syscall.EINVAL && testenv.SyscallIsNotSupported(err) { + t.Skip("pidfd_send_signal syscall not supported:", err) } - t.Fatal("pidfd_send_signal syscall failed:", e) + t.Fatal("pidfd_send_signal syscall failed:", err) } // Check if the child received our signal. err = cmd.Wait() @@ -572,7 +576,16 @@ func testPidFD(t *testing.T) error { } func TestPidFD(t *testing.T) { - if err := testPidFD(t); err != nil { + if err := testPidFD(t, false); err != nil { + t.Fatal("can't start a process:", err) + } +} + +func TestPidFDWithUserNS(t *testing.T) { + if err := testPidFD(t, true); err != nil { + if testenv.SyscallIsNotSupported(err) { + t.Skip("userns not supported:", err) + } t.Fatal("can't start a process:", err) } } @@ -581,7 +594,7 @@ func TestPidFDClone3(t *testing.T) { *syscall.ForceClone3 = true defer func() { *syscall.ForceClone3 = false }() - if err := testPidFD(t); err != nil { + if err := testPidFD(t, false); err != nil { if testenv.SyscallIsNotSupported(err) { t.Skip("clone3 not supported:", err) } diff --git a/src/syscall/export_linux_test.go b/src/syscall/export_linux_test.go index a09db60753..3aa877cfe3 100644 --- a/src/syscall/export_linux_test.go +++ b/src/syscall/export_linux_test.go @@ -10,6 +10,5 @@ var ( ) const ( - Sys_GETEUID = sys_GETEUID - Sys_pidfd_send_signal = _SYS_pidfd_send_signal + Sys_GETEUID = sys_GETEUID ) diff --git a/src/syscall/mksyscall.pl b/src/syscall/mksyscall.pl index 73d4b0f6e3..47efbffcbc 100755 --- a/src/syscall/mksyscall.pl +++ b/src/syscall/mksyscall.pl @@ -85,6 +85,9 @@ if($ARGV[0] =~ /^-/) { if($libc) { $extraimports = 'import "internal/abi"'; } +if($darwin) { + $extraimports .= "\nimport \"runtime\""; +} sub parseparamlist($) { my ($list) = @_; @@ -137,7 +140,7 @@ while(<>) { # without reading the header. $text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"; - if (($darwin && $func =~ /^ptrace1(Ptr)?$/) || (($openbsd && $libc) && $func =~ /^ptrace(Ptr)?$/)) { + if ((($darwin || ($openbsd && $libc)) && $func =~ /^ptrace(Ptr)?$/)) { # The ptrace function is called from forkAndExecInChild where stack # growth is forbidden. $text .= "//go:nosplit\n" @@ -147,6 +150,13 @@ while(<>) { my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : ""; $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl; + # Disable ptrace on iOS. + if ($darwin && $func =~ /^ptrace(Ptr)?$/) { + $text .= "\tif runtime.GOOS == \"ios\" {\n"; + $text .= "\t\tpanic(\"unimplemented\")\n"; + $text .= "\t}\n"; + } + # Check if err return available my $errvar = ""; foreach my $p (@out) { diff --git a/src/syscall/ptrace_darwin.go b/src/syscall/ptrace_darwin.go deleted file mode 100644 index 519e451c73..0000000000 --- a/src/syscall/ptrace_darwin.go +++ /dev/null @@ -1,14 +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. - -//go:build !ios - -package syscall - -// Nosplit because it is called from forkAndExecInChild. -// -//go:nosplit -func ptrace(request int, pid int, addr uintptr, data uintptr) error { - return ptrace1(request, pid, addr, data) -} diff --git a/src/syscall/ptrace_ios.go b/src/syscall/ptrace_ios.go deleted file mode 100644 index fa8d000715..0000000000 --- a/src/syscall/ptrace_ios.go +++ /dev/null @@ -1,14 +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. - -//go:build ios - -package syscall - -// Nosplit because it is called from forkAndExecInChild. -// -//go:nosplit -func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { - panic("unimplemented") -} diff --git a/src/syscall/syscall_darwin_amd64.go b/src/syscall/syscall_darwin_amd64.go index ef3c1998aa..64e54ad730 100644 --- a/src/syscall/syscall_darwin_amd64.go +++ b/src/syscall/syscall_darwin_amd64.go @@ -24,7 +24,7 @@ func setTimeval(sec, usec int64) Timeval { //sys Stat(path string, stat *Stat_t) (err error) = SYS_stat64 //sys Statfs(path string, stat *Statfs_t) (err error) = SYS_statfs64 //sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_fstatat64 -//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace +//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Ident = uint64(fd) diff --git a/src/syscall/syscall_darwin_arm64.go b/src/syscall/syscall_darwin_arm64.go index cea42772bb..913c748374 100644 --- a/src/syscall/syscall_darwin_arm64.go +++ b/src/syscall/syscall_darwin_arm64.go @@ -24,7 +24,7 @@ func setTimeval(sec, usec int64) Timeval { //sys Stat(path string, stat *Stat_t) (err error) //sys Statfs(path string, stat *Statfs_t) (err error) //sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error) -//sys ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) = SYS_ptrace +//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Ident = uint64(fd) diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go index 1ab6c5e148..a559f7e288 100644 --- a/src/syscall/syscall_linux_386.go +++ b/src/syscall/syscall_linux_386.go @@ -7,11 +7,10 @@ package syscall import "unsafe" const ( - _SYS_setgroups = SYS_SETGROUPS32 - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS32 + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) func setTimespec(sec, nsec int64) Timespec { diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go index 1083a507c7..ec52f8a4bd 100644 --- a/src/syscall/syscall_linux_amd64.go +++ b/src/syscall/syscall_linux_amd64.go @@ -9,11 +9,10 @@ import ( ) const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go index 2641cd2868..a6d92cea13 100644 --- a/src/syscall/syscall_linux_arm.go +++ b/src/syscall/syscall_linux_arm.go @@ -7,11 +7,10 @@ package syscall import "unsafe" const ( - _SYS_setgroups = SYS_SETGROUPS32 - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS32 + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) func setTimespec(sec, nsec int64) Timespec { diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go index 74d6e3a958..b87b51c0c0 100644 --- a/src/syscall/syscall_linux_arm64.go +++ b/src/syscall/syscall_linux_arm64.go @@ -7,11 +7,10 @@ package syscall import "unsafe" const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT diff --git a/src/syscall/syscall_linux_loong64.go b/src/syscall/syscall_linux_loong64.go index eb275bc717..634cf30cf2 100644 --- a/src/syscall/syscall_linux_loong64.go +++ b/src/syscall/syscall_linux_loong64.go @@ -7,11 +7,10 @@ package syscall import "unsafe" const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT diff --git a/src/syscall/syscall_linux_mips64x.go b/src/syscall/syscall_linux_mips64x.go index 3bdee928ed..41106ed81f 100644 --- a/src/syscall/syscall_linux_mips64x.go +++ b/src/syscall/syscall_linux_mips64x.go @@ -11,11 +11,10 @@ import ( ) const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 5435 - _SYS_faccessat2 = 5439 - _SYS_pidfd_send_signal = 5424 - _SYS_fchmodat2 = 5452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 5435 + _SYS_faccessat2 = 5439 + _SYS_fchmodat2 = 5452 ) //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_mipsx.go b/src/syscall/syscall_linux_mipsx.go index 7253c648e7..7d4f8f2264 100644 --- a/src/syscall/syscall_linux_mipsx.go +++ b/src/syscall/syscall_linux_mipsx.go @@ -9,11 +9,10 @@ package syscall import "unsafe" const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 4435 - _SYS_faccessat2 = 4439 - _SYS_pidfd_send_signal = 4424 - _SYS_fchmodat2 = 4452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 4435 + _SYS_faccessat2 = 4439 + _SYS_fchmodat2 = 4452 ) func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go index 9cfe2dc695..13c184c44f 100644 --- a/src/syscall/syscall_linux_ppc64x.go +++ b/src/syscall/syscall_linux_ppc64x.go @@ -11,11 +11,10 @@ import ( ) const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_riscv64.go b/src/syscall/syscall_linux_riscv64.go index 61fb4c1668..00872a74fb 100644 --- a/src/syscall/syscall_linux_riscv64.go +++ b/src/syscall/syscall_linux_riscv64.go @@ -7,11 +7,10 @@ package syscall import "unsafe" const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) //sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT diff --git a/src/syscall/syscall_linux_s390x.go b/src/syscall/syscall_linux_s390x.go index 3a0afc404a..ea667ec1da 100644 --- a/src/syscall/syscall_linux_s390x.go +++ b/src/syscall/syscall_linux_s390x.go @@ -7,11 +7,10 @@ package syscall import "unsafe" const ( - _SYS_setgroups = SYS_SETGROUPS - _SYS_clone3 = 435 - _SYS_faccessat2 = 439 - _SYS_pidfd_send_signal = 424 - _SYS_fchmodat2 = 452 + _SYS_setgroups = SYS_SETGROUPS + _SYS_clone3 = 435 + _SYS_faccessat2 = 439 + _SYS_fchmodat2 = 452 ) //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 33876c7fe0..d13acc5c44 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -409,6 +409,10 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) { // Necessary for opening directory handles. attrs |= FILE_FLAG_BACKUP_SEMANTICS } + if mode&O_SYNC != 0 { + const _FILE_FLAG_WRITE_THROUGH = 0x80000000 + attrs |= _FILE_FLAG_WRITE_THROUGH + } return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0) } diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go index 3ad9bad076..8812fb12cd 100644 --- a/src/syscall/zsyscall_darwin_amd64.go +++ b/src/syscall/zsyscall_darwin_amd64.go @@ -7,6 +7,7 @@ package syscall import "unsafe" import "internal/abi" +import "runtime" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2011,7 +2012,10 @@ func libc_fstatat64_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT //go:nosplit -func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) { +func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { + if runtime.GOOS == "ios" { + panic("unimplemented") + } _, _, e1 := syscall6(abi.FuncPCABI0(libc_ptrace_trampoline), uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) if e1 != 0 { err = errnoErr(e1) diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go index c2502c7842..22b096349d 100644 --- a/src/syscall/zsyscall_darwin_arm64.go +++ b/src/syscall/zsyscall_darwin_arm64.go @@ -7,6 +7,7 @@ package syscall import "unsafe" import "internal/abi" +import "runtime" // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT @@ -2011,7 +2012,10 @@ func libc_fstatat_trampoline() // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT //go:nosplit -func ptrace1(request int, pid int, addr uintptr, data uintptr) (err error) { +func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { + if runtime.GOOS == "ios" { + panic("unimplemented") + } _, _, e1 := syscall6(abi.FuncPCABI0(libc_ptrace_trampoline), uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) if e1 != 0 { err = errnoErr(e1) diff --git a/src/testing/helper_test.go b/src/testing/helper_test.go index 6e8986a2ab..da5622f85f 100644 --- a/src/testing/helper_test.go +++ b/src/testing/helper_test.go @@ -2,98 +2,107 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package testing +package testing_test import ( + "internal/testenv" + "os" "regexp" "strings" + "testing" ) -func TestTBHelper(t *T) { - var buf strings.Builder - ctx := newTestContext(1, allMatcher()) - t1 := &T{ - common: common{ - signal: make(chan bool), - w: &buf, - }, - context: ctx, - } - t1.Run("Test", testHelper) +func TestTBHelper(t *testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + testTestHelper(t) - want := `--- FAIL: Test (?s) -helperfuncs_test.go:12: 0 -helperfuncs_test.go:40: 1 -helperfuncs_test.go:21: 2 -helperfuncs_test.go:42: 3 -helperfuncs_test.go:49: 4 ---- FAIL: Test/sub (?s) -helperfuncs_test.go:52: 5 -helperfuncs_test.go:21: 6 -helperfuncs_test.go:51: 7 -helperfuncs_test.go:63: 8 ---- FAIL: Test/sub2 (?s) -helperfuncs_test.go:78: 11 -helperfuncs_test.go:82: recover 12 -helperfuncs_test.go:84: GenericFloat64 -helperfuncs_test.go:85: GenericInt -helperfuncs_test.go:71: 9 -helperfuncs_test.go:67: 10 + // Check that calling Helper from inside a top-level test function + // has no effect. + t.Helper() + t.Error("8") + return + } + + testenv.MustHaveExec(t) + t.Parallel() + + exe, err := os.Executable() + if err != nil { + t.Fatal(err) + } + + cmd := testenv.Command(t, exe, "-test.run=^TestTBHelper$") + cmd = testenv.CleanCmdEnv(cmd) + cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1") + out, _ := cmd.CombinedOutput() + + want := `--- FAIL: TestTBHelper \([^)]+\) + helperfuncs_test.go:15: 0 + helperfuncs_test.go:47: 1 + helperfuncs_test.go:24: 2 + helperfuncs_test.go:49: 3 + helperfuncs_test.go:56: 4 + --- FAIL: TestTBHelper/sub \([^)]+\) + helperfuncs_test.go:59: 5 + helperfuncs_test.go:24: 6 + helperfuncs_test.go:58: 7 + --- FAIL: TestTBHelper/sub2 \([^)]+\) + helperfuncs_test.go:80: 11 + helperfuncs_test.go:84: recover 12 + helperfuncs_test.go:86: GenericFloat64 + helperfuncs_test.go:87: GenericInt + helper_test.go:22: 8 + helperfuncs_test.go:73: 9 + helperfuncs_test.go:69: 10 ` - lines := strings.Split(buf.String(), "\n") - durationRE := regexp.MustCompile(`\(.*\)$`) - for i, line := range lines { - line = strings.TrimSpace(line) - line = durationRE.ReplaceAllString(line, "(?s)") - lines[i] = line - } - got := strings.Join(lines, "\n") - if got != want { - t.Errorf("got output:\n\n%s\nwant:\n\n%s", got, want) + if !regexp.MustCompile(want).Match(out) { + t.Errorf("got output:\n\n%s\nwant matching:\n\n%s", out, want) } } -func TestTBHelperParallel(t *T) { - var buf strings.Builder - ctx := newTestContext(1, newMatcher(regexp.MatchString, "", "", "")) - t1 := &T{ - common: common{ - signal: make(chan bool), - w: &buf, - }, - context: ctx, +func TestTBHelperParallel(t *testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + parallelTestHelper(t) + return } - t1.Run("Test", parallelTestHelper) - lines := strings.Split(strings.TrimSpace(buf.String()), "\n") - if len(lines) != 6 { - t.Fatalf("parallelTestHelper gave %d lines of output; want 6", len(lines)) + testenv.MustHaveExec(t) + t.Parallel() + + exe, err := os.Executable() + if err != nil { + t.Fatal(err) } - want := "helperfuncs_test.go:21: parallel" + + cmd := testenv.Command(t, exe, "-test.run=^TestTBHelperParallel$") + cmd = testenv.CleanCmdEnv(cmd) + cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1") + out, _ := cmd.CombinedOutput() + + t.Logf("output:\n%s", out) + + lines := strings.Split(strings.TrimSpace(string(out)), "\n") + + // We expect to see one "--- FAIL" line at the start + // of the log, five lines of "parallel" logging, + // and a final "FAIL" line at the end of the test. + const wantLines = 7 + + if len(lines) != wantLines { + t.Fatalf("parallelTestHelper gave %d lines of output; want %d", len(lines), wantLines) + } + want := "helperfuncs_test.go:24: parallel" if got := strings.TrimSpace(lines[1]); got != want { - t.Errorf("got output line %q; want %q", got, want) + t.Errorf("got second output line %q; want %q", got, want) } } -type noopWriter int - -func (nw *noopWriter) Write(b []byte) (int, error) { return len(b), nil } - -func BenchmarkTBHelper(b *B) { - w := noopWriter(0) - ctx := newTestContext(1, allMatcher()) - t1 := &T{ - common: common{ - signal: make(chan bool), - w: &w, - }, - context: ctx, - } +func BenchmarkTBHelper(b *testing.B) { f1 := func() { - t1.Helper() + b.Helper() } f2 := func() { - t1.Helper() + b.Helper() } b.ResetTimer() b.ReportAllocs() diff --git a/src/testing/helperfuncs_test.go b/src/testing/helperfuncs_test.go index b63bc91ac2..f0295f35df 100644 --- a/src/testing/helperfuncs_test.go +++ b/src/testing/helperfuncs_test.go @@ -2,38 +2,45 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package testing +package testing_test -import "sync" +import ( + "sync" + "testing" +) // The line numbering of this file is important for TestTBHelper. -func notHelper(t *T, msg string) { +func notHelper(t *testing.T, msg string) { t.Error(msg) } -func helper(t *T, msg string) { +func helper(t *testing.T, msg string) { t.Helper() t.Error(msg) } -func notHelperCallingHelper(t *T, msg string) { +func notHelperCallingHelper(t *testing.T, msg string) { helper(t, msg) } -func helperCallingHelper(t *T, msg string) { +func helperCallingHelper(t *testing.T, msg string) { t.Helper() helper(t, msg) } -func genericHelper[G any](t *T, msg string) { +func genericHelper[G any](t *testing.T, msg string) { t.Helper() t.Error(msg) } var genericIntHelper = genericHelper[int] -func testHelper(t *T) { +func testTestHelper(t *testing.T) { + testHelper(t) +} + +func testHelper(t *testing.T) { // Check combinations of directly and indirectly // calling helper functions. notHelper(t, "0") @@ -48,7 +55,7 @@ func testHelper(t *T) { } fn("4") - t.Run("sub", func(t *T) { + t.Run("sub", func(t *testing.T) { helper(t, "5") notHelperCallingHelper(t, "6") // Check that calling Helper from inside a subtest entry function @@ -57,11 +64,6 @@ func testHelper(t *T) { t.Error("7") }) - // Check that calling Helper from inside a top-level test function - // has no effect. - t.Helper() - t.Error("8") - // Check that right caller is reported for func passed to Cleanup when // multiple cleanup functions have been registered. t.Cleanup(func() { @@ -85,7 +87,7 @@ func testHelper(t *T) { genericIntHelper(t, "GenericInt") } -func parallelTestHelper(t *T) { +func parallelTestHelper(t *testing.T) { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) @@ -97,15 +99,15 @@ func parallelTestHelper(t *T) { wg.Wait() } -func helperSubCallingHelper(t *T, msg string) { +func helperSubCallingHelper(t *testing.T, msg string) { t.Helper() - t.Run("sub2", func(t *T) { + t.Run("sub2", func(t *testing.T) { t.Helper() t.Fatal(msg) }) } -func recoverHelper(t *T, msg string) { +func recoverHelper(t *testing.T, msg string) { t.Helper() defer func() { t.Helper() @@ -116,7 +118,7 @@ func recoverHelper(t *T, msg string) { doPanic(t, msg) } -func doPanic(t *T, msg string) { +func doPanic(t *testing.T, msg string) { t.Helper() panic(msg) } diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 55b14c3795..1c23d054a0 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -767,22 +767,6 @@ func TestBenchmarkReadMemStatsBeforeFirstRun(t *T) { }) } -func TestParallelSub(t *T) { - c := make(chan int) - block := make(chan int) - for i := 0; i < 10; i++ { - go func(i int) { - <-block - t.Run(fmt.Sprint(i), func(t *T) {}) - c <- 1 - }(i) - } - close(block) - for i := 0; i < 10; i++ { - <-c - } -} - type funcWriter struct { write func([]byte) (int, error) } @@ -910,18 +894,22 @@ func TestCleanup(t *T) { func TestConcurrentCleanup(t *T) { cleanups := 0 t.Run("test", func(t *T) { - done := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(2) for i := 0; i < 2; i++ { i := i go func() { t.Cleanup(func() { + // Although the calls to Cleanup are concurrent, the functions passed + // to Cleanup should be called sequentially, in some nondeterministic + // order based on when the Cleanup calls happened to be scheduled. + // So these assignments to the cleanups variable should not race. cleanups |= 1 << i }) - done <- struct{}{} + wg.Done() }() } - <-done - <-done + wg.Wait() }) if cleanups != 1|2 { t.Errorf("unexpected cleanup; got %d want 3", cleanups) diff --git a/src/testing/testing.go b/src/testing/testing.go index ed8b3630f1..5c06aea5f8 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -1638,15 +1638,22 @@ func tRunner(t *T, fn func(t *T)) { if len(t.sub) > 0 { // Run parallel subtests. - // Decrease the running count for this test. + + // Decrease the running count for this test and mark it as no longer running. t.context.release() + running.Delete(t.name) + // Release the parallel subtests. close(t.barrier) // Wait for subtests to complete. for _, sub := range t.sub { <-sub.signal } + + // Run any cleanup callbacks, marking the test as running + // in case the cleanup hangs. cleanupStart := time.Now() + running.Store(t.name, cleanupStart) err := t.runCleanup(recoverAndReturnPanic) t.duration += time.Since(cleanupStart) if err != nil { @@ -1733,11 +1740,19 @@ func (t *T) Run(name string, f func(t *T)) bool { // without being preempted, even when their parent is a parallel test. This // may especially reduce surprises if *parallel == 1. go tRunner(t, f) + + // The parent goroutine will block until the subtest either finishes or calls + // Parallel, but in general we don't know whether the parent goroutine is the + // top-level test function or some other goroutine it has spawned. + // To avoid confusing false-negatives, we leave the parent in the running map + // even though in the typical case it is blocked. + if !<-t.signal { // At this point, it is likely that FailNow was called on one of the // parent tests by one of the subtests. Continue aborting up the chain. runtime.Goexit() } + if t.chatty != nil && t.chatty.json { t.chatty.Updatef(t.parent.name, "=== NAME %s\n", t.parent.name) } diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go index 91c6ccf21d..d3822dfd57 100644 --- a/src/testing/testing_test.go +++ b/src/testing/testing_test.go @@ -6,13 +6,18 @@ package testing_test import ( "bytes" + "fmt" "internal/race" "internal/testenv" "os" + "os/exec" "path/filepath" "regexp" + "slices" + "strings" "sync" "testing" + "time" ) // This is exactly what a test would do without a TestMain. @@ -636,3 +641,174 @@ func BenchmarkSubRacy(b *testing.B) { doRace() // should be reported separately } + +func TestRunningTests(t *testing.T) { + t.Parallel() + + // Regression test for https://go.dev/issue/64404: + // on timeout, the "running tests" message should not include + // tests that are waiting on parked subtests. + + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + for i := 0; i < 2; i++ { + t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) { + t.Parallel() + for j := 0; j < 2; j++ { + t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) { + t.Parallel() + for { + time.Sleep(1 * time.Millisecond) + } + }) + } + }) + } + } + + timeout := 10 * time.Millisecond + for { + cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String(), "-test.parallel=4") + cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1") + out, err := cmd.CombinedOutput() + t.Logf("%v:\n%s", cmd, out) + if _, ok := err.(*exec.ExitError); !ok { + t.Fatal(err) + } + + // Because the outer subtests (and TestRunningTests itself) are marked as + // parallel, their test functions return (and are no longer “running”) + // before the inner subtests are released to run and hang. + // Only those inner subtests should be reported as running. + want := []string{ + "TestRunningTests/outer0/inner0", + "TestRunningTests/outer0/inner1", + "TestRunningTests/outer1/inner0", + "TestRunningTests/outer1/inner1", + } + + got, ok := parseRunningTests(out) + if slices.Equal(got, want) { + break + } + if ok { + t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n")) + } else { + t.Logf("no running tests found") + } + t.Logf("retrying with longer timeout") + timeout *= 2 + } +} + +func TestRunningTestsInCleanup(t *testing.T) { + t.Parallel() + + if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { + for i := 0; i < 2; i++ { + t.Run(fmt.Sprintf("outer%d", i), func(t *testing.T) { + // Not parallel: we expect to see only one outer test, + // stuck in cleanup after its subtest finishes. + + t.Cleanup(func() { + for { + time.Sleep(1 * time.Millisecond) + } + }) + + for j := 0; j < 2; j++ { + t.Run(fmt.Sprintf("inner%d", j), func(t *testing.T) { + t.Parallel() + }) + } + }) + } + } + + timeout := 10 * time.Millisecond + for { + cmd := testenv.Command(t, os.Args[0], "-test.run=^"+t.Name()+"$", "-test.timeout="+timeout.String()) + cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1") + out, err := cmd.CombinedOutput() + t.Logf("%v:\n%s", cmd, out) + if _, ok := err.(*exec.ExitError); !ok { + t.Fatal(err) + } + + // TestRunningTestsInCleanup is blocked in the call to t.Run, + // but its test function has not yet returned so it should still + // be considered to be running. + // outer1 hasn't even started yet, so only outer0 and the top-level + // test function should be reported as running. + want := []string{ + "TestRunningTestsInCleanup", + "TestRunningTestsInCleanup/outer0", + } + + got, ok := parseRunningTests(out) + if slices.Equal(got, want) { + break + } + if ok { + t.Logf("found running tests:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n")) + } else { + t.Logf("no running tests found") + } + t.Logf("retrying with longer timeout") + timeout *= 2 + } +} + +func parseRunningTests(out []byte) (runningTests []string, ok bool) { + inRunningTests := false + for _, line := range strings.Split(string(out), "\n") { + if inRunningTests { + if trimmed, ok := strings.CutPrefix(line, "\t"); ok { + if name, _, ok := strings.Cut(trimmed, " "); ok { + runningTests = append(runningTests, name) + continue + } + } + + // This line is not the name of a running test. + return runningTests, true + } + + if strings.TrimSpace(line) == "running tests:" { + inRunningTests = true + } + } + + return nil, false +} + +func TestConcurrentRun(t *testing.T) { + // Regression test for https://go.dev/issue/64402: + // this deadlocked after https://go.dev/cl/506755. + + block := make(chan struct{}) + var ready, done sync.WaitGroup + for i := 0; i < 2; i++ { + ready.Add(1) + done.Add(1) + go t.Run("", func(*testing.T) { + ready.Done() + <-block + done.Done() + }) + } + ready.Wait() + close(block) + done.Wait() +} + +func TestParentRun(t1 *testing.T) { + // Regression test for https://go.dev/issue/64402: + // this deadlocked after https://go.dev/cl/506755. + + t1.Run("outer", func(t2 *testing.T) { + t2.Log("Hello outer!") + t1.Run("not_inner", func(t3 *testing.T) { // Note: this is t1.Run, not t2.Run. + t3.Log("Hello inner!") + }) + }) +} diff --git a/src/text/template/doc.go b/src/text/template/doc.go index 4c01b05ebf..032784bc3f 100644 --- a/src/text/template/doc.go +++ b/src/text/template/doc.go @@ -438,13 +438,13 @@ produce the text By construction, a template may reside in only one association. If it's necessary to have a template addressable from multiple associations, the template definition must be parsed multiple times to create distinct *Template -values, or must be copied with the Clone or AddParseTree method. +values, or must be copied with [Template.Clone] or [Template.AddParseTree]. Parse may be called multiple times to assemble the various associated templates; -see the ParseFiles and ParseGlob functions and methods for simple ways to parse -related templates stored in files. +see [ParseFiles], [ParseGlob], [Template.ParseFiles] and [Template.ParseGlob] +for simple ways to parse related templates stored in files. -A template may be executed directly or through ExecuteTemplate, which executes +A template may be executed directly or through [Template.ExecuteTemplate], which executes an associated template identified by name. To invoke our example above, we might write, diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go index 47268225c8..c36688825c 100644 --- a/src/text/template/parse/node.go +++ b/src/text/template/parse/node.go @@ -284,7 +284,6 @@ func (a *ActionNode) tree() *Tree { func (a *ActionNode) Copy() Node { return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) - } // CommandNode holds a command (a pipeline inside an evaluating action). diff --git a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s index 541d696b67..731d2ac6db 100644 --- a/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s +++ b/src/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s @@ -183,11 +183,31 @@ GLOBL ·andMask<>(SB), (NOPTR+RODATA), $240 #define shiftD1Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xd2; BYTE $0x04 // PALIGNR $4, X10, X10 #define shiftD2Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x04 // PALIGNR $4, X11, X11 #define shiftD3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x04 // PALIGNR $4, X15, X15 + // Some macros + +// ROL rotates the uint32s in register R left by N bits, using temporary T. +#define ROL(N, R, T) \ + MOVO R, T; PSLLL $(N), T; PSRLL $(32-(N)), R; PXOR T, R + +// ROL16 rotates the uint32s in register R left by 16, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL16(R, T) PSHUFB ·rol16<>(SB), R +#else +#define ROL16(R, T) ROL(16, R, T) +#endif + +// ROL8 rotates the uint32s in register R left by 8, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL8(R, T) PSHUFB ·rol8<>(SB), R +#else +#define ROL8(R, T) ROL(8, R, T) +#endif + #define chachaQR(A, B, C, D, T) \ - PADDD B, A; PXOR A, D; PSHUFB ·rol16<>(SB), D \ + PADDD B, A; PXOR A, D; ROL16(D, T) \ PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $12, T; PSRLL $20, B; PXOR T, B \ - PADDD B, A; PXOR A, D; PSHUFB ·rol8<>(SB), D \ + PADDD B, A; PXOR A, D; ROL8(D, T) \ PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $7, T; PSRLL $25, B; PXOR T, B #define chachaQR_AVX2(A, B, C, D, T) \ diff --git a/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go index 6fc2838a3f..2492f796af 100644 --- a/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go +++ b/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -733,13 +733,14 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag return true } -// ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or, -// if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue. -// It reports whether the operation was successful. -func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool { +// ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN +// explicitly tagged with tag into out and advances. If no element with a +// matching tag is present, it sets "out" to defaultValue instead. It reports +// whether the read was successful. +func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue bool) bool { var present bool var child String - if !s.ReadOptionalASN1(&child, &present, asn1.BOOLEAN) { + if !s.ReadOptionalASN1(&child, &present, tag) { return false } @@ -748,7 +749,7 @@ func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool { return true } - return s.ReadASN1Boolean(out) + return child.ReadASN1Boolean(out) } func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool { diff --git a/src/vendor/golang.org/x/crypto/hkdf/hkdf.go b/src/vendor/golang.org/x/crypto/hkdf/hkdf.go index dda3f143be..f4ded5fee2 100644 --- a/src/vendor/golang.org/x/crypto/hkdf/hkdf.go +++ b/src/vendor/golang.org/x/crypto/hkdf/hkdf.go @@ -56,7 +56,9 @@ func (f *hkdf) Read(p []byte) (int, error) { // Fill the rest of the buffer for len(p) > 0 { - f.expander.Reset() + if f.counter > 1 { + f.expander.Reset() + } f.expander.Write(f.prev) f.expander.Write(f.info) f.expander.Write([]byte{f.counter}) diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index 6d61189796..338c496bf9 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -1,4 +1,4 @@ -# golang.org/x/crypto v0.14.1-0.20231011220222-8779cbd1c995 +# golang.org/x/crypto v0.16.1-0.20231129163542-152cdb1503eb ## explicit; go 1.18 golang.org/x/crypto/chacha20 golang.org/x/crypto/chacha20poly1305 @@ -7,7 +7,7 @@ golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/hkdf golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 -# golang.org/x/net v0.17.1-0.20231025180729-4c7a5b64f145 +# golang.org/x/net v0.19.0 ## explicit; go 1.18 golang.org/x/net/dns/dnsmessage golang.org/x/net/http/httpguts @@ -17,10 +17,10 @@ golang.org/x/net/idna golang.org/x/net/lif golang.org/x/net/nettest golang.org/x/net/route -# golang.org/x/sys v0.13.1-0.20231011215430-1bfbee0e20e3 +# golang.org/x/sys v0.15.0 ## explicit; go 1.18 golang.org/x/sys/cpu -# golang.org/x/text v0.13.1-0.20231011215848-6c97a165dd66 +# golang.org/x/text v0.14.0 ## explicit; go 1.18 golang.org/x/text/secure/bidirule golang.org/x/text/transform diff --git a/test/codegen/bits.go b/test/codegen/bits.go index 67daf12d62..4b6c8b94b8 100644 --- a/test/codegen/bits.go +++ b/test/codegen/bits.go @@ -382,7 +382,6 @@ func signextendAndMask8to64(a int8) (s, z uint64) { // ppc64x: -"MOVB", "ANDCC\t[$]247," z = uint64(uint8(a)) & 0x3F7 return - } // Verify zero-extended values are not sign-extended under a bit mask (#61297) @@ -392,7 +391,6 @@ func zeroextendAndMask8to64(a int8, b int16) (x, y uint64) { // ppc64x: -"MOVH\t", -"ANDCC", "MOVHZ" y = uint64(b) & 0xFFFF return - } // Verify rotate and mask instructions, and further simplified instructions for small types diff --git a/test/codegen/issue63332.go b/test/codegen/issue63332.go new file mode 100644 index 0000000000..dbe671d247 --- /dev/null +++ b/test/codegen/issue63332.go @@ -0,0 +1,14 @@ +// asmcheck + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package codegen + +func issue63332(c chan int) { + x := 0 + // amd64:-`MOVQ` + x += 2 + c <- x +} diff --git a/test/codegen/memcombine.go b/test/codegen/memcombine.go index 1b8abc348a..6d6c33d947 100644 --- a/test/codegen/memcombine.go +++ b/test/codegen/memcombine.go @@ -348,7 +348,6 @@ func reassoc_load_uint32(b []byte) uint32 { func extrashift_load_uint32(b []byte) uint32 { // amd64:`MOVL\s\([A-Z]+\)`,`SHLL\s[$]2`,-`MOV[BW]`,-`OR` return uint32(b[0])<<2 | uint32(b[1])<<10 | uint32(b[2])<<18 | uint32(b[3])<<26 - } func outoforder_load_uint32(b []byte) uint32 { @@ -882,3 +881,40 @@ func wideStore2(p *[8]uint64, x, y uint64) { // s390x:-"STMG",-"MOVD" p[1] = y } + +func store32le(p *struct{ a, b uint32 }, x uint64) { + // amd64:"MOVQ",-"MOVL",-"SHRQ" + // arm64:"MOVD",-"MOVW",-"LSR" + // ppc64le:"MOVD",-"MOVW",-"SRD" + p.a = uint32(x) + // amd64:-"MOVL",-"SHRQ" + // arm64:-"MOVW",-"LSR" + // ppc64le:-"MOVW",-"SRD" + p.b = uint32(x >> 32) +} +func store32be(p *struct{ a, b uint32 }, x uint64) { + // ppc64:"MOVD",-"MOVW",-"SRD" + // s390x:"MOVD",-"MOVW",-"SRD" + p.a = uint32(x >> 32) + // ppc64:-"MOVW",-"SRD" + // s390x:-"MOVW",-"SRD" + p.b = uint32(x) +} +func store16le(p *struct{ a, b uint16 }, x uint32) { + // amd64:"MOVL",-"MOVW",-"SHRL" + // arm64:"MOVW",-"MOVH",-"UBFX" + // ppc64le:"MOVW",-"MOVH",-"SRW" + p.a = uint16(x) + // amd64:-"MOVW",-"SHRL" + // arm64:-"MOVH",-"UBFX" + // ppc64le:-"MOVH",-"SRW" + p.b = uint16(x >> 16) +} +func store16be(p *struct{ a, b uint16 }, x uint32) { + // ppc64:"MOVW",-"MOVH",-"SRW" + // s390x:"MOVW",-"MOVH",-"SRW" + p.a = uint16(x >> 16) + // ppc64:-"MOVH",-"SRW" + // s390x:-"MOVH",-"SRW" + p.b = uint16(x) +} diff --git a/test/codegen/noextend.go b/test/codegen/noextend.go index 98ad0ece89..193f75b092 100644 --- a/test/codegen/noextend.go +++ b/test/codegen/noextend.go @@ -277,7 +277,6 @@ func shouldSignEXT(x int) int64 { ret += int64(int8(x & 0x1100000000000011)) return ret - } func noIntermediateExtension(a, b, c uint32) uint32 { diff --git a/test/codegen/shift.go b/test/codegen/shift.go index 32cfaffae0..50d60426d0 100644 --- a/test/codegen/shift.go +++ b/test/codegen/shift.go @@ -22,12 +22,42 @@ func rshConst64Ux64(v uint64) uint64 { return v >> uint64(33) } +func rshConst64Ux64Overflow32(v uint32) uint64 { + // riscv64:"MOV\t\\$0,",-"SRL" + return uint64(v) >> 32 +} + +func rshConst64Ux64Overflow16(v uint16) uint64 { + // riscv64:"MOV\t\\$0,",-"SRL" + return uint64(v) >> 16 +} + +func rshConst64Ux64Overflow8(v uint8) uint64 { + // riscv64:"MOV\t\\$0,",-"SRL" + return uint64(v) >> 8 +} + func rshConst64x64(v int64) int64 { // ppc64x:"SRAD" // riscv64:"SRAI\t",-"OR",-"SLTIU" return v >> uint64(33) } +func rshConst64x64Overflow32(v int32) int64 { + // riscv64:"SRAIW",-"SLLI",-"SRAI\t" + return int64(v) >> 32 +} + +func rshConst64x64Overflow16(v int16) int64 { + // riscv64:"SLLI","SRAI",-"SRAIW" + return int64(v) >> 16 +} + +func rshConst64x64Overflow8(v int8) int64 { + // riscv64:"SLLI","SRAI",-"SRAIW" + return int64(v) >> 8 +} + func lshConst32x64(v int32) int32 { // ppc64x:"SLW" // riscv64:"SLLI",-"AND",-"SLTIU", -"MOVW" diff --git a/test/escape2.go b/test/escape2.go index 99f85914a3..3e5d11f88e 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -397,7 +397,6 @@ func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" return nil } return nil - } // assigning to an array element is like assigning to the array diff --git a/test/escape2n.go b/test/escape2n.go index 350be65202..2613152150 100644 --- a/test/escape2n.go +++ b/test/escape2n.go @@ -397,7 +397,6 @@ func exprsw(i *int) *int { // ERROR "leaking param: i to result ~r0 level=0$" return nil } return nil - } // assigning to an array element is like assigning to the array diff --git a/test/escape_calls.go b/test/escape_calls.go index aa7c7f516c..5424c006ee 100644 --- a/test/escape_calls.go +++ b/test/escape_calls.go @@ -52,3 +52,10 @@ func bar() { s := "string" f([]string{s}) // ERROR "\[\]string{...} escapes to heap" } + +func strmin(a, b, c string) string { // ERROR "leaking param: a to result ~r0 level=0" "leaking param: b to result ~r0 level=0" "leaking param: c to result ~r0 level=0" + return min(a, b, c) +} +func strmax(a, b, c string) string { // ERROR "leaking param: a to result ~r0 level=0" "leaking param: b to result ~r0 level=0" "leaking param: c to result ~r0 level=0" + return max(a, b, c) +} diff --git a/test/fixedbugs/bug398.go b/test/fixedbugs/bug398.go index db3e43c7f9..2b00f6074d 100644 --- a/test/fixedbugs/bug398.go +++ b/test/fixedbugs/bug398.go @@ -1,4 +1,4 @@ -// compile -d=interfacecycles +// errorcheck // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,11 +11,11 @@ package p // exported interfaces -type I1 interface { +type I1 interface { // ERROR "invalid recursive type: anonymous interface refers to itself" F() interface{I1} } -type I2 interface { +type I2 interface { // ERROR "invalid recursive type: anonymous interface refers to itself" F() interface{I2} } @@ -28,11 +28,11 @@ func F() bool { // non-exported interfaces -type i1 interface { +type i1 interface { // ERROR "invalid recursive type: anonymous interface refers to itself" F() interface{i1} } -type i2 interface { +type i2 interface { // ERROR "invalid recursive type: anonymous interface refers to itself" F() interface{i2} } diff --git a/test/fixedbugs/issue16369.go b/test/fixedbugs/issue16369.go index 3a7bb7eaed..86d0ce645d 100644 --- a/test/fixedbugs/issue16369.go +++ b/test/fixedbugs/issue16369.go @@ -1,4 +1,4 @@ -// compile -d=interfacecycles +// errorcheck // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -6,7 +6,7 @@ package p -type T interface { +type T interface { // ERROR "invalid recursive type: anonymous interface refers to itself" M(interface { T }) diff --git a/test/fixedbugs/issue32680b.go b/test/fixedbugs/issue32680b.go index 61e531701a..09bc3a9ce0 100644 --- a/test/fixedbugs/issue32680b.go +++ b/test/fixedbugs/issue32680b.go @@ -12,5 +12,4 @@ func hashBytesRaw(b0, b1, b2, b3, b7 byte) uint64 { func doStuff(data []byte) uint64 { return hashBytesRaw(data[0], data[1], data[2], data[3], data[7]) - } diff --git a/test/fixedbugs/issue35073.go b/test/fixedbugs/issue35073a.go similarity index 100% rename from test/fixedbugs/issue35073.go rename to test/fixedbugs/issue35073a.go diff --git a/test/fixedbugs/issue35073b.go b/test/fixedbugs/issue35073b.go new file mode 100644 index 0000000000..8cdc6c184d --- /dev/null +++ b/test/fixedbugs/issue35073b.go @@ -0,0 +1,23 @@ +// errorcheck -0 -d=checkptr -m + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that we can inline the receiver arguments for +// reflect.Value.UnsafeAddr/Pointer, even in checkptr mode. + +package main + +import ( + "reflect" + "unsafe" +) + +func main() { + n := 10 // ERROR "moved to heap: n" + m := make(map[string]string) // ERROR "moved to heap: m" "make\(map\[string\]string\) escapes to heap" + + _ = unsafe.Pointer(reflect.ValueOf(&n).Elem().UnsafeAddr()) // ERROR "inlining call" + _ = unsafe.Pointer(reflect.ValueOf(&m).Elem().Pointer()) // ERROR "inlining call" +} diff --git a/test/fixedbugs/issue42284.dir/a.go b/test/fixedbugs/issue42284.dir/a.go index f7fd80bd20..ccf54fad54 100644 --- a/test/fixedbugs/issue42284.dir/a.go +++ b/test/fixedbugs/issue42284.dir/a.go @@ -20,7 +20,7 @@ func F(i I) I { // ERROR "can inline F" "leaking param: i to result ~r0 level=0" func g() { h := E() // ERROR "inlining call to E" "T\(0\) does not escape" - h.M() // ERROR "devirtualizing h.M to T" + h.M() // ERROR "devirtualizing h.M to T" "inlining call to T.M" // BAD: T(0) could be stack allocated. i := F(T(0)) // ERROR "inlining call to F" "T\(0\) escapes to heap" diff --git a/test/fixedbugs/issue42284.dir/b.go b/test/fixedbugs/issue42284.dir/b.go index 8cd93b8db4..559de59184 100644 --- a/test/fixedbugs/issue42284.dir/b.go +++ b/test/fixedbugs/issue42284.dir/b.go @@ -8,7 +8,7 @@ import "./a" func g() { h := a.E() // ERROR "inlining call to a.E" "T\(0\) does not escape" - h.M() // ERROR "devirtualizing h.M to a.T" + h.M() // ERROR "devirtualizing h.M to a.T" "inlining call to a.T.M" // BAD: T(0) could be stack allocated. i := a.F(a.T(0)) // ERROR "inlining call to a.F" "a.T\(0\) escapes to heap" diff --git a/test/fixedbugs/issue52193.go b/test/fixedbugs/issue52193.go new file mode 100644 index 0000000000..40e6dcb33b --- /dev/null +++ b/test/fixedbugs/issue52193.go @@ -0,0 +1,46 @@ +// errorcheck -0 -m + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import ( + "crypto/ecdh" + "crypto/rand" +) + +func F(peerShare []byte) ([]byte, error) { // ERROR "leaking param: peerShare" + p256 := ecdh.P256() // ERROR "inlining call to ecdh.P256" + + ourKey, err := p256.GenerateKey(rand.Reader) // ERROR "devirtualizing p256.GenerateKey" "inlining call to ecdh.*GenerateKey" + if err != nil { + return nil, err + } + + peerPublic, err := p256.NewPublicKey(peerShare) // ERROR "devirtualizing p256.NewPublicKey" "inlining call to ecdh.*NewPublicKey" + if err != nil { + return nil, err + } + + return ourKey.ECDH(peerPublic) +} + +// Test that inlining doesn't break if devirtualization exposes a new +// inlinable callee. + +func f() { // ERROR "can inline f" + var i interface{ m() } = T(0) // ERROR "T\(0\) does not escape" + i.m() // ERROR "devirtualizing i.m" +} + +type T int + +func (T) m() { // ERROR "can inline T.m" + if never { + f() // ERROR "inlining call to f" "devirtualizing i.m" "T\(0\) does not escape" + } +} + +var never bool diff --git a/test/fixedbugs/issue56923.go b/test/fixedbugs/issue56923.go index 700a1046a9..7cc9494b76 100644 --- a/test/fixedbugs/issue56923.go +++ b/test/fixedbugs/issue56923.go @@ -18,7 +18,6 @@ func (r EqFunc[T]) Eqv(a, b T) bool { func New[T any](f func(a, b T) bool) Eq[T] { return EqFunc[T](f) - } func Equal(a, b []byte) bool { diff --git a/test/fixedbugs/issue64565.go b/test/fixedbugs/issue64565.go new file mode 100644 index 0000000000..634025ce3e --- /dev/null +++ b/test/fixedbugs/issue64565.go @@ -0,0 +1,15 @@ +// run + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + m := "0" + for _, c := range "321" { + m = max(string(c), m) + println(m) + } +} diff --git a/test/fixedbugs/issue64565.out b/test/fixedbugs/issue64565.out new file mode 100644 index 0000000000..1f242fa6f0 --- /dev/null +++ b/test/fixedbugs/issue64565.out @@ -0,0 +1,3 @@ +3 +3 +3 diff --git a/test/fixedbugs/issue64606.go b/test/fixedbugs/issue64606.go new file mode 100644 index 0000000000..9b53c1041f --- /dev/null +++ b/test/fixedbugs/issue64606.go @@ -0,0 +1,32 @@ +// build -race + +//go:build race + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + var o any = uint64(5) + switch o.(type) { + case int: + goto ret + case int8: + goto ret + case int16: + goto ret + case int32: + goto ret + case int64: + goto ret + case float32: + goto ret + case float64: + goto ret + default: + goto ret + } +ret: +} diff --git a/test/fixedbugs/issue64715.go b/test/fixedbugs/issue64715.go new file mode 100644 index 0000000000..bf117165b7 --- /dev/null +++ b/test/fixedbugs/issue64715.go @@ -0,0 +1,25 @@ +// run + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func boolInt32(b bool) int32 { + if b { + return 1 + } + + return 0 +} + +func f(left uint16, right int32) (r uint16) { + return left >> right +} + +var n = uint16(65535) + +func main() { + println(f(n, boolInt32(int64(n^n) > 1))) +} diff --git a/test/fixedbugs/issue64715.out b/test/fixedbugs/issue64715.out new file mode 100644 index 0000000000..7a53b35687 --- /dev/null +++ b/test/fixedbugs/issue64715.out @@ -0,0 +1 @@ +65535 diff --git a/test/fixedbugs/issue64826.go b/test/fixedbugs/issue64826.go new file mode 100644 index 0000000000..864c474a64 --- /dev/null +++ b/test/fixedbugs/issue64826.go @@ -0,0 +1,38 @@ +// build + +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + f(g(false)) +} +func g(b bool) string { + if b { + return "z" + } + return "q" +} +func f(x string) int { + switch len(x) { + case 4: + return 4 + case 5: + return 5 + case 6: + return 6 + case 7: + return 7 + case 8: + return 8 + case 9: + return 9 + case 10: + return 10 + case 11: + return 11 + } + return 0 +} diff --git a/test/live.go b/test/live.go index 6badb011b0..5658c8ba06 100644 --- a/test/live.go +++ b/test/live.go @@ -667,7 +667,7 @@ func bad40() { func good40() { ret := T40{} // ERROR "stack object ret T40$" - ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$" + ret.m = make(map[int]int) // ERROR "live at call to rand32: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$" t := &ret printnl() // ERROR "live at call to printnl: ret$" // Note: ret is live at the printnl because the compiler moves &ret diff --git a/test/live_regabi.go b/test/live_regabi.go index 80a9cc1002..a335126b3f 100644 --- a/test/live_regabi.go +++ b/test/live_regabi.go @@ -664,7 +664,7 @@ func bad40() { func good40() { ret := T40{} // ERROR "stack object ret T40$" - ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$" + ret.m = make(map[int]int) // ERROR "live at call to rand32: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$" t := &ret printnl() // ERROR "live at call to printnl: ret$" // Note: ret is live at the printnl because the compiler moves &ret diff --git a/test/range3.go b/test/range3.go index 4f770a2b70..f58a398f94 100644 --- a/test/range3.go +++ b/test/range3.go @@ -74,9 +74,17 @@ func testint4() { } } +// Issue #64471. +func testint5() { + for i := range 'a' { + var _ *rune = &i // ensure i has type rune + } +} + func main() { testint1() testint2() testint3() testint4() + testint5() } diff --git a/test/range4.go b/test/range4.go index 696b205ab7..0b051f6d3c 100644 --- a/test/range4.go +++ b/test/range4.go @@ -311,6 +311,30 @@ func testcalls() { } } +type iter3YieldFunc func(int, int) bool + +func iter3(list ...int) func(iter3YieldFunc) { + return func(yield iter3YieldFunc) { + for k, v := range list { + if !yield(k, v) { + return + } + } + } +} + +func testcalls1() { + ncalls := 0 + for k, v := range iter3(1, 2, 3) { + _, _ = k, v + ncalls++ + } + if ncalls != 3 { + println("wrong number of calls:", ncalls, "!= 3") + panic("fail") + } +} + func main() { testfunc0() testfunc1() @@ -323,4 +347,5 @@ func main() { testfunc8() testfunc9() testcalls() + testcalls1() } diff --git a/test/rangegen.go b/test/rangegen.go index bdcf099862..8231c64db7 100644 --- a/test/rangegen.go +++ b/test/rangegen.go @@ -25,6 +25,7 @@ import ( "bytes" "fmt" "log" + "math/bits" "os" "os/exec" "strings" @@ -37,6 +38,13 @@ func main() { log.SetFlags(0) log.SetPrefix("rangegen: ") + if !long && bits.UintSize == 32 { + // Skip this test on 32-bit platforms, where it seems to + // cause timeouts and build problems. + skip() + return + } + b := new(bytes.Buffer) tests := "" flush := func(force bool) { @@ -331,3 +339,12 @@ func run(f func(*output, int)int, i int) *output { } ` + +func skip() { + const code = ` +package main +func main() { +} +` + fmt.Printf("%s\n", code) +} diff --git a/test/typeparam/issue50485.dir/a.go b/test/typeparam/issue50485.dir/a.go index 3a7c71a711..97cf4d2549 100644 --- a/test/typeparam/issue50485.dir/a.go +++ b/test/typeparam/issue50485.dir/a.go @@ -219,7 +219,6 @@ func (r ApplicativeFunctor2[H, HT, A1, A2, R]) ApOption(a Option[A1]) Applicativ func (r ApplicativeFunctor2[H, HT, A1, A2, R]) Ap(a A1) ApplicativeFunctor1[Cons[A1, H], A1, A2, R] { return r.ApOption(Some(a)) - } func Applicative2[A1, A2, R any](fn Func2[A1, A2, R]) ApplicativeFunctor2[Nil, Nil, A1, A2, R] { diff --git a/test/typeparam/issue51232.go b/test/typeparam/issue51232.go index 0d25e1863d..f4728f6e7c 100644 --- a/test/typeparam/issue51232.go +++ b/test/typeparam/issue51232.go @@ -13,19 +13,19 @@ type RC[RG any] interface { type Fn[RCT RC[RG], RG any] func(RCT) type F[RCT RC[RG], RG any] interface { - Fn() Fn[RCT] // ERROR "got 1 arguments" + Fn() Fn[RCT] // ERROR "not enough type arguments for type Fn: have 1, want 2" } type concreteF[RCT RC[RG], RG any] struct { - makeFn func() Fn[RCT] // ERROR "got 1 arguments" + makeFn func() Fn[RCT] // ERROR "not enough type arguments for type Fn: have 1, want 2" } -func (c *concreteF[RCT, RG]) Fn() Fn[RCT] { // ERROR "got 1 arguments" +func (c *concreteF[RCT, RG]) Fn() Fn[RCT] { // ERROR "not enough type arguments for type Fn: have 1, want 2" return c.makeFn() } -func NewConcrete[RCT RC[RG], RG any](Rc RCT) F[RCT] { // ERROR "got 1 arguments" - return &concreteF[RCT]{ // ERROR "cannot use" "got 1 arguments" +func NewConcrete[RCT RC[RG], RG any](Rc RCT) F[RCT] { // ERROR "not enough type arguments for type F: have 1, want 2" + return &concreteF[RCT]{ // ERROR "cannot use" "not enough type arguments for type concreteF: have 1, want 2" makeFn: nil, } } diff --git a/test/typeparam/issue51233.go b/test/typeparam/issue51233.go index 96a25ddb9c..5f2a045d84 100644 --- a/test/typeparam/issue51233.go +++ b/test/typeparam/issue51233.go @@ -13,16 +13,16 @@ type RC[RG any] interface { type Fn[RCT RC[RG], RG any] func(RCT) -type FFn[RCT RC[RG], RG any] func() Fn[RCT] // ERROR "got 1 arguments" +type FFn[RCT RC[RG], RG any] func() Fn[RCT] // ERROR "not enough type arguments for type Fn: have 1, want 2" type F[RCT RC[RG], RG any] interface { - Fn() Fn[RCT] // ERROR "got 1 arguments" + Fn() Fn[RCT] // ERROR "not enough type arguments for type Fn: have 1, want 2" } type concreteF[RCT RC[RG], RG any] struct { - makeFn FFn[RCT] // ERROR "got 1 arguments" + makeFn FFn[RCT] // ERROR "not enough type arguments for type FFn: have 1, want 2" } -func (c *concreteF[RCT, RG]) Fn() Fn[RCT] { // ERROR "got 1 arguments" +func (c *concreteF[RCT, RG]) Fn() Fn[RCT] { // ERROR "not enough type arguments for type Fn: have 1, want 2" return c.makeFn() } diff --git a/test/typeparam/issue51925.go b/test/typeparam/issue51925.go index 0a385acd17..abebf67766 100644 --- a/test/typeparam/issue51925.go +++ b/test/typeparam/issue51925.go @@ -25,7 +25,6 @@ func min[T IntLike](x, y T) T { return x } return y - } // Min returns the minimum element of `nums`.