mirror of https://github.com/golang/go.git
Merge branch 'master' into hotfix/net-http-fileserver-dir-file
This commit is contained in:
commit
754c9a1167
|
|
@ -1,45 +0,0 @@
|
|||
---
|
||||
name: Bugs
|
||||
about: The go command, standard library, or anything else
|
||||
title: "affected/package: "
|
||||
---
|
||||
|
||||
<!--
|
||||
Please answer these questions before submitting your issue. Thanks!
|
||||
-->
|
||||
|
||||
### What version of Go are you using (`go version`)?
|
||||
|
||||
<pre>
|
||||
$ go version
|
||||
|
||||
</pre>
|
||||
|
||||
### Does this issue reproduce with the latest release?
|
||||
|
||||
|
||||
|
||||
### What operating system and processor architecture are you using (`go env`)?
|
||||
|
||||
<details><summary><code>go env</code> Output</summary><br><pre>
|
||||
$ go env
|
||||
|
||||
</pre></details>
|
||||
|
||||
### What did you do?
|
||||
|
||||
<!--
|
||||
If possible, provide a recipe for reproducing the error.
|
||||
A complete runnable program is good.
|
||||
A link on go.dev/play is best.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
---
|
||||
|
||||
<!--
|
||||
Please answer these questions before submitting your issue. Thanks!
|
||||
-->
|
||||
|
||||
### What is the URL of the page with the issue?
|
||||
|
||||
|
||||
|
||||
### What is your user agent?
|
||||
|
||||
<!--
|
||||
You can find your user agent here:
|
||||
https://www.google.com/search?q=what+is+my+user+agent
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### Screenshot
|
||||
|
||||
<!--
|
||||
Please paste a screenshot of the page.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### What did you do?
|
||||
|
||||
<!--
|
||||
If possible, provide a recipe for reproducing the error.
|
||||
|
||||
Starting with a Private/Incognito tab/window may help rule out problematic browser extensions.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
---
|
||||
|
||||
<!--
|
||||
Please answer these questions before submitting your issue. Thanks!
|
||||
-->
|
||||
|
||||
### What is the path of the package that you would like to have removed?
|
||||
|
||||
<!---
|
||||
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.
|
||||
--->
|
||||
|
||||
|
||||
|
||||
### Are you the owner of this package?
|
||||
|
||||
<!---
|
||||
Only the package owners can request to have their packages removed from pkg.go.dev.
|
||||
--->
|
||||
|
||||
|
||||
|
||||
### What is the reason that you could not retract this package instead?
|
||||
|
||||
<!---
|
||||
If you would like to have your module removed from pkg.go.dev, we recommend that you retract them, so that they can be removed from the go command and proxy.golang.org as well.
|
||||
|
||||
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.
|
||||
--->
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
---
|
||||
|
||||
<!--
|
||||
Please answer these questions before submitting your issue. Thanks!
|
||||
-->
|
||||
|
||||
### gopls version
|
||||
|
||||
<!--
|
||||
Output of `gopls -v version` on the command line
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### go env
|
||||
|
||||
<!--
|
||||
Output of `go env` on the command line in your workspace directory
|
||||
-->
|
||||
|
||||
|
||||
### What did you do?
|
||||
|
||||
<!--
|
||||
If possible, provide a recipe for reproducing the error.
|
||||
A complete runnable program is good.
|
||||
A link on go.dev/play is better.
|
||||
A failing unit test is the best.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
|
||||
### Editor and settings
|
||||
|
||||
<!--
|
||||
Your editor and any settings you have configured (for example, your VSCode settings.json file)
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### Logs
|
||||
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
---
|
||||
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
### What version of Go are you using (`go version`)?
|
||||
|
||||
<pre>
|
||||
$ go version
|
||||
|
||||
</pre>
|
||||
|
||||
### Does this issue reproduce at the latest version of golang.org/x/vuln?
|
||||
|
||||
|
||||
|
||||
### What operating system and processor architecture are you using (`go env`)?
|
||||
|
||||
<details><summary><code>go env</code> Output</summary><br><pre>
|
||||
$ go env
|
||||
|
||||
</pre></details>
|
||||
|
||||
### What did you do?
|
||||
|
||||
<!--
|
||||
If possible, provide a recipe for reproducing the error.
|
||||
A complete runnable program is good.
|
||||
A link on go.dev/play is best.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
name: Proposals
|
||||
about: New external API or other notable changes
|
||||
title: "proposal: affected/package: "
|
||||
labels: Proposal
|
||||
---
|
||||
|
||||
<!--
|
||||
Our proposal process is documented here:
|
||||
https://go.dev/s/proposal-process
|
||||
-->
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
---
|
||||
name: Language Change Proposals
|
||||
about: Changes to the language
|
||||
title: "proposal: Go 2: "
|
||||
labels:
|
||||
- Proposal
|
||||
- v2
|
||||
- LanguageChange
|
||||
---
|
||||
|
||||
<!--
|
||||
Our process for evaluating language changes can be found here:
|
||||
https://go.googlesource.com/proposal/+/refs/heads/master#language-changes
|
||||
-->
|
||||
|
||||
### 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.)**
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg math/big, method (*Rat) FloatPrec() (int, bool) #50489
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg archive/zip, method (*Writer) AddFS(fs.FS) error #54898
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg slices, func Concat[$0 interface{ ~[]$1 }, $1 interface{}](...$0) $0 #56353
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg crypto/x509, method (*CertPool) AddCertWithConstraint(*Certificate, func([]*Certificate) error) #57178
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg archive/tar, method (*Writer) AddFS(fs.FS) error #58000
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
pkg net, method (*TCPConn) WriteTo(io.Writer) (int64, error) #58808
|
||||
pkg os, method (*File) WriteTo(io.Writer) (int64, error) #58808
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg reflect, func PtrTo //deprecated #59599
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg go/ast, func Unparen(Expr) Expr #60061
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg reflect, func TypeFor[$0 interface{}]() Type #60088
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg cmp, func Or[$0 comparable](...$0) $0 #60204
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
pkg net/http, method (*Request) PathValue(string) string #61410
|
||||
pkg net/http, method (*Request) SetPathValue(string, string) #61410
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg html/template, const ErrJSTemplate //deprecated #61619
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
pkg net/netip, method (AddrPort) Compare(AddrPort) int #61642
|
||||
pkg net/netip, method (Prefix) Compare(Prefix) int #61642
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg testing/slogtest, func Run(*testing.T, func(*testing.T) slog.Handler, func(*testing.T) map[string]interface{}) #61758
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg io, method (*SectionReader) Outer() (ReaderAt, int64, int64) #61870
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
pkg debug/elf, const R_MIPS_PC32 = 248 #61974
|
||||
pkg debug/elf, const R_MIPS_PC32 R_MIPS #61974
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
pkg go/types, method (*Info) PkgNameOf(*ast.ImportSpec) *PkgName #62037
|
||||
pkg go/types, method (Checker) PkgNameOf(*ast.ImportSpec) *PkgName #62037
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg log/slog, func SetLogLoggerLevel(Level) Level #62418
|
||||
|
|
@ -1 +0,0 @@
|
|||
pkg go/types, type Info struct, FileVersions map[*ast.File]string #62605
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
17
doc/asm.html
17
doc/asm.html
|
|
@ -464,6 +464,23 @@ Function is the outermost frame of the call stack. Traceback should stop at this
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="special-instructions">Special instructions</h3>
|
||||
|
||||
<p>
|
||||
The <code>PCALIGN</code> pseudo-instruction is used to indicate that the next instruction should be aligned
|
||||
to a specified boundary by padding with no-op instructions.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It is currently supported on arm64, amd64, ppc64, loong64 and riscv64.
|
||||
|
||||
For example, the start of the <code>MOVD</code> instruction below is aligned to 32 bytes:
|
||||
<pre>
|
||||
PCALIGN $32
|
||||
MOVD $2, R0
|
||||
</pre>
|
||||
</p>
|
||||
|
||||
<h3 id="data-offsets">Interacting with Go types and constants</h3>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,11 @@
|
|||
<h2 id="Introduction">Introduction</h2>
|
||||
|
||||
<p>
|
||||
This is a reference manual for the Go programming language. For
|
||||
more information and other documents, see <a href="/">golang.org</a>.
|
||||
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 <a href="/doc/go_spec.html">here</a>.
|
||||
For more information and other documents, see <a href="/">go.dev</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
938
doc/go1.22.html
938
doc/go1.22.html
|
|
@ -26,41 +26,390 @@ Do not send CLs removing the interior tags from such phrases.
|
|||
<h2 id="language">Changes to the language</h2>
|
||||
|
||||
<p>
|
||||
TODO: complete this section
|
||||
<!-- loop variable scope --><!-- range over int -->
|
||||
Go 1.22 makes two changes to "for" loops.
|
||||
<ul>
|
||||
<li>
|
||||
Previously, the variables declared by a "for" loop were created once and updated by each iteration. In Go 1.22, each iteration of the loop creates new variables, to avoid accidental sharing bugs.
|
||||
The <a href="https://go.dev/wiki/LoopvarExperiment#my-test-fails-with-the-change-how-can-i-debug-it">transition support tooling</a>
|
||||
described in the proposal continues to work in the same way it did in Go 1.21.
|
||||
</li>
|
||||
<li>
|
||||
"For" loops may now range over integers.
|
||||
For <a href="https://go.dev/play/p/ky02zZxgk_r?v=gotip">example</a>:
|
||||
<pre>
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
for i := range 10 {
|
||||
fmt.Println(10 - i)
|
||||
}
|
||||
fmt.Println("go1.22 has lift-off!")
|
||||
}
|
||||
</pre>
|
||||
See the spec for <a href="/ref/spec#For_range">details</a>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- range over func GOEXPERIMENT; https://go.dev/issue/61405, https://go.dev/issue/61897, CLs 510541,539277,540263,543319 -->
|
||||
</p>
|
||||
<p>
|
||||
Go 1.22 includes a preview of a language change we are considering
|
||||
for a future version of Go: <a href="https://go.dev/wiki/RangefuncExperiment">range-over-function iterators</a>.
|
||||
Building with <code>GOEXPERIMENT=rangefunc</code> enables this feature.
|
||||
</p>
|
||||
|
||||
<h2 id="tools">Tools</h2>
|
||||
|
||||
<h3 id="go-command">Go command</h3>
|
||||
|
||||
<!-- https://go.dev/issue/60056 -->
|
||||
<p>
|
||||
TODO: complete this section, or delete if not needed
|
||||
Commands in <a href="https://go.dev/ref/mod#workspaces">workspaces</a> can now
|
||||
use a <code>vendor</code> directory containing the dependencies of the
|
||||
workspace. The directory is created by
|
||||
<a href="/pkg/cmd/go#hdr-Make_vendored_copy_of_dependencies"><code>go</code> <code>work</code> <code>vendor</code></a>,
|
||||
and used by build commands when the <code>-mod</code> flag is set to
|
||||
<code>vendor</code>, which is the default when a workspace <code>vendor</code>
|
||||
directory is present.
|
||||
</p>
|
||||
<p>
|
||||
Note that the <code>vendor</code> 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 <code>vendor</code> directory
|
||||
can contain the dependencies of either the workspace or of the module,
|
||||
but not both.
|
||||
</p>
|
||||
|
||||
<h3 id="cgo">Cgo</h3>
|
||||
<!-- CL 518775, https://go.dev/issue/60915 -->
|
||||
<p>
|
||||
<code>go</code> <code>get</code> is no longer supported outside of a module in the
|
||||
legacy <code>GOPATH</code> mode (that is, with <code>GO111MODULE=off</code>).
|
||||
Other build commands, such as <code>go</code> <code>build</code> and
|
||||
<code>go</code> <code>test</code>, will continue to work indefinitely
|
||||
for legacy <code>GOPATH</code> programs.
|
||||
</p>
|
||||
|
||||
<!-- CL 497837 reverted -->
|
||||
<!-- CL 518776 -->
|
||||
<p>
|
||||
<code>go</code> <code>mod</code> <code>init</code> no longer attempts to import
|
||||
module requirements from configuration files for other vendoring tools
|
||||
(such as <code>Gopkg.lock</code>).
|
||||
</p>
|
||||
|
||||
<!-- CL 495447 -->
|
||||
<p>
|
||||
<code>go</code> <code>test</code> <code>-cover</code> now prints coverage summaries for covered
|
||||
packages that do not have their own test files. Prior to Go 1.22 a
|
||||
<code>go</code> <code>test</code> <code>-cover</code> run for such a package would report
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<code>? mymod/mypack [no test files]</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
and now with Go 1.22, functions in the package are treated as uncovered:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<code>mymod/mypack coverage: 0.0% of statements</code>
|
||||
</p>
|
||||
|
||||
<h3 id="trace">Trace</h3>
|
||||
|
||||
<!-- https://go.dev/issue/63960 -->
|
||||
<p>
|
||||
The <code>trace</code> 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.
|
||||
<br />
|
||||
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.
|
||||
</p>
|
||||
|
||||
<h3 id="vet">Vet</h3>
|
||||
|
||||
<h4 id="vet-loopclosure">References to loop variables</h4>
|
||||
|
||||
<p><!-- CL 539016, https://go.dev/issue/63888: cmd/vet: do not report variable capture for loop variables with the new lifetime rules -->
|
||||
The behavior of the <code>vet</code> 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),
|
||||
<code>vet</code>code> 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.
|
||||
</p>
|
||||
|
||||
<h4 id="vet-appends">New warnings for missing values after append</h4>
|
||||
|
||||
<p><!-- CL 498416, https://go.dev/issue/60448: add a new analyzer for check missing values after append -->
|
||||
The <code>vet</code> tool now reports calls to
|
||||
<a href="/pkg/builtin/#append"><code>append</code></a> that pass
|
||||
no values to be appended to the slice, such as <code>slice = append(slice)</code>.
|
||||
Such a statement has no effect, and experience has shown that is nearly always a mistake.
|
||||
</p>
|
||||
|
||||
<h4 id="vet-defers">New warnings for deferring <code>time.Since</code></h4>
|
||||
|
||||
<p><!-- CL 527095, https://go.dev/issue/60048: time.Since should not be used in defer statement -->
|
||||
The vet tool now reports a non-deferred call to
|
||||
<a href="/pkg/time/#Since"><code>time.Since(t)</code></a> within a <code>defer</code> statement.
|
||||
This is equivalent to calling <code>time.Now().Sub(t)</code> before the <code>defer</code> statement,
|
||||
not when the deferred function is called. In nearly all cases, the correct code
|
||||
requires deferring the <code>time.Since</code> call. For example:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
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
|
||||
}()
|
||||
</pre>
|
||||
|
||||
<h4 id="vet-slog">New warnings for mismatched key-value pairs in <code>log/slog</code> calls</h4>
|
||||
|
||||
<p><!-- CL 496156, https://go.dev/issue/59407: log/slog: add vet checks for variadic ...any inputs -->
|
||||
The vet tool now reports invalid arguments in calls to functions and methods
|
||||
in the structured logging package, <a href="/pkg/log/slog"><code>log/slog</code></a>,
|
||||
that accept alternating key/value pairs.
|
||||
It reports calls where an argument in a key position is neither a
|
||||
<code>string</code> nor a <code>slog.Attr</code>, and where a final key is missing its value.
|
||||
</p>
|
||||
|
||||
<h2 id="runtime">Runtime</h2>
|
||||
|
||||
<p><!-- CL 543255 -->
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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 <code>GOEXPERIMENT=noallocheaders</code> 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.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 525475 -->
|
||||
On the <code>windows/amd64 port</code>, programs linking or loading Go libraries built with
|
||||
<code>-buildmode=c-archive</code> or <code>-buildmode=c-shared</code> can now use
|
||||
the <code>SetUnhandledExceptionFilter</code> Win32 function to catch exceptions not handled
|
||||
by the Go runtime. Note that this was already supported on the <code>windows/386</code> port.
|
||||
</p>
|
||||
|
||||
<h2 id="compiler">Compiler</h2>
|
||||
|
||||
<p>
|
||||
TODO: complete this section, or delete if not needed
|
||||
<p><!-- https://go.dev/issue/61577 -->
|
||||
<a href="https://go.dev/doc/pgo">Profile-guided Optimization (PGO)</a> 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.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/cl/528321 -->
|
||||
The compiler now interleaves devirtualization and inlining, so interface
|
||||
method calls are better optimized.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/61502 -->
|
||||
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 <code>GOEXPERIMENT=newinliner</code> enables the new call-site
|
||||
heuristics; see <a href="https://go.dev/issue/61502">issue #61502</a> for
|
||||
more info and to provide feedback.
|
||||
</p>
|
||||
|
||||
<h2 id="linker">Linker</h2>
|
||||
|
||||
<p><!-- CL 493136 -->
|
||||
The linker's <code>-s</code> and <code>-w</code> flags are now behave more
|
||||
consistently across all platforms.
|
||||
The <code>-w</code> flag suppresses DWARF debug information generation.
|
||||
The <code>-s</code> flag suppresses symbol table generation.
|
||||
The <code>-s</code> flag also implies the <code>-w</code> flag, which can be
|
||||
negated with <code>-w=0</code>.
|
||||
That is, <code>-s</code> <code>-w=0</code> will generate a binary with DWARF
|
||||
debug information generation but without the symbol table.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 511475 -->
|
||||
On ELF platforms, the <code>-B</code> linker flag now accepts a special form:
|
||||
with <code>-B</code> <code>gobuildid</code>, the linker will generate a GNU
|
||||
build ID (the ELF <code>NT_GNU_BUILD_ID</code> note) derived from the Go
|
||||
build ID.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 534555 -->
|
||||
On Windows, when building with <code>-linkmode=internal</code>, the linker now
|
||||
preserves SEH information from C object files by copying the <code>.pdata</code>
|
||||
and <code>.xdata</code> 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.
|
||||
<code>-linkmode=external</code> is not affected by this change, as external linkers
|
||||
already preserve SEH information.
|
||||
</p>
|
||||
|
||||
<h2 id="bootstrap">Bootstrap</h2>
|
||||
|
||||
<p>
|
||||
TODO: complete this section, or delete if not needed
|
||||
As mentioned in the <a href="/doc/go1.20#bootstrap">Go 1.20 release notes</a>, 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.
|
||||
</p>
|
||||
|
||||
<h2 id="library">Core library</h2>
|
||||
|
||||
<h3 id="math_rand_v2">New math/rand/v2 package</h3>
|
||||
|
||||
<!-- CL 502495 -->
|
||||
<!-- CL 502497 -->
|
||||
<!-- CL 502498 -->
|
||||
<!-- CL 502499 -->
|
||||
<!-- CL 502500 -->
|
||||
<!-- CL 502505 -->
|
||||
<!-- CL 502506 -->
|
||||
<!-- CL 516857 -->
|
||||
<!-- CL 516859 -->
|
||||
|
||||
<p>
|
||||
Go 1.22 includes the first “v2” package in the standard library,
|
||||
<a href="/pkg/math/rand/v2/"><code>math/rand/v2</code></a>.
|
||||
The changes compared to <a href="/pkg/math/rand/"><code>math/rand</code></a> are
|
||||
detailed in <a href="/issue/61716">proposal #61716</a>. The most important changes are:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>The <code>Read</code> method, deprecated in <code>math/rand</code>,
|
||||
was not carried forward for <code>math/rand/v2</code>.
|
||||
(It remains available in <code>math/rand</code>.)
|
||||
The vast majority of calls to <code>Read</code> should use
|
||||
<a href="/pkg/crypto/rand/#Read"><code>crypto/rand</code>’s <code>Read</code></a> instead.
|
||||
Otherwise a custom <code>Read</code> can be constructed using the <code>Uint64</code> method.
|
||||
|
||||
<li>The global generator accessed by top-level functions is unconditionally randomly seeded.
|
||||
Because the API guarantees no fixed sequence of results,
|
||||
optimizations like per-thread random generator states are now possible.
|
||||
|
||||
<li>The <a href="/pkg/math/rand/v2/#Source"><code>Source</code></a>
|
||||
interface now has a single <code>Uint64</code> method;
|
||||
there is no <code>Source64</code> interface.
|
||||
|
||||
<li>Many methods now use faster algorithms that were not possible to adopt in <code>math/rand</code>
|
||||
because they changed the output streams.
|
||||
|
||||
<li>The
|
||||
<code>Intn</code>,
|
||||
<code>Int31</code>,
|
||||
<code>Int31n</code>,
|
||||
<code>Int63</code>,
|
||||
and
|
||||
<code>Int64n</code>
|
||||
top-level functions and methods from <code>math/rand</code>
|
||||
are spelled more idiomatically in <code>math/rand/v2</code>:
|
||||
<code>IntN</code>,
|
||||
<code>Int32</code>,
|
||||
<code>Int32N</code>,
|
||||
<code>Int64</code>,
|
||||
and
|
||||
<code>Int64N</code>.
|
||||
There are also new top-level functions and methods
|
||||
<code>Uint32</code>,
|
||||
<code>Uint32N</code>,
|
||||
<code>Uint64</code>,
|
||||
<code>Uint64N</code>,
|
||||
<code>Uint</code>,
|
||||
and
|
||||
<code>UintN</code>.
|
||||
|
||||
<li>The
|
||||
new generic function <a href="/pkg/math/rand/v2/#N"><code>N</code></a>
|
||||
is like
|
||||
<a href="/pkg/math/rand/v2/#Int64N"><code>Int64N</code></a> or
|
||||
<a href="/pkg/math/rand/v2/#Uint64N"><code>Uint64N</code></a>
|
||||
but works for any integer type.
|
||||
For example a random duration from 0 up to 5 minutes is
|
||||
<code>rand.N(5*time.Minute)</code>.
|
||||
|
||||
<li>The Mitchell & Reeds LFSR generator provided by
|
||||
<a href="/pkg/math/rand/#Source"><code>math/rand</code>’s <code>Source</code></a>
|
||||
has been replaced by two more modern pseudo-random generator sources:
|
||||
<a href="/pkg/math/rand/v2/#ChaCha8"><code>ChaCha8</code></a>
|
||||
<a href="/pkg/math/rand/v2/#PCG"><code>PCG</code></a>.
|
||||
ChaCha8 is a new, cryptographically strong random number generator
|
||||
roughly similar to PCG in efficiency.
|
||||
ChaCha8 is the algorithm used for the top-level functions in <code>math/rand/v2</code>.
|
||||
As of Go 1.22, <code>math/rand</code>'s top-level functions (when not explicitly seeded)
|
||||
and the Go runtime also use ChaCha8 for randomness.
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
We plan to include an API migration tool in a future release, likely Go 1.23.
|
||||
</p>
|
||||
|
||||
<h3 id="enhanced_routing_patterns">Enhanced routing patterns</h3>
|
||||
|
||||
<p><!-- https://go.dev/issue/61410 -->
|
||||
HTTP routing in the standard library is now more expressive.
|
||||
The patterns used by <a href="/pkg/net/http#ServeMux"><code>net/http.ServeMux</code></a> have been enhanced to accept methods and wildcards.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Registering a handler with a method, like <code>"POST /items/create"</code>, 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 <code> "GET"</code> also registers it with <code>"HEAD"</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Wildcards in patterns, like <code>/items/{id}</code>, match segments of the URL path.
|
||||
The actual segment value may be accessed by calling the <a href="/pkg/net/http#Request.PathValue"><code>Request.PathValue</code></a> method.
|
||||
A wildcard ending in "...", like <code>/files/{path...}</code>, must occur at the end of a pattern and matches all the remaining segments.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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 <code>{$}</code>,
|
||||
as in <code>/exact/match/{$}</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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 <a href="/doc/godebug"><code>GODEBUG</code></a> field named <code>httpmuxgo121</code>.
|
||||
Set <code>httpmuxgo121=1</code> to restore the old behavior.
|
||||
</p>
|
||||
|
||||
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
||||
|
||||
<p>
|
||||
|
|
@ -70,9 +419,95 @@ Do not send CLs removing the interior tags from such phrases.
|
|||
There are also various performance improvements, not enumerated here.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
TODO: complete this section
|
||||
</p>
|
||||
<!-- <p> -->
|
||||
<!-- TODO: complete this section -->
|
||||
<!-- </p> -->
|
||||
|
||||
<dl id="archive/tar"><dt><a href="/pkg/archive/tar/">archive/tar</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/58000, CL 513316 -->
|
||||
The new method <a href="/pkg/archive/tar#Writer.AddFS"><code>Writer.AddFS</code></a> adds all of the files from an <a href="/pkg/io/fs#FS"><code>fs.FS</code></a> to the archive.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/50102, CL 514235 -->
|
||||
If the argument to <a href="/pkg/archive/tar#FileInfoHeader"><code>FileInfoHeader</code></a> implements the new <a href="/pkg/archive/tar#FileInfoNames"><code>FileInfoNames</code></a> 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.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- archive/tar -->
|
||||
|
||||
<dl id="archive/zip"><dt><a href="/pkg/archive/zip/">archive/zip</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/54898, CL 513438 -->
|
||||
The new method <a href="/pkg/archive/zip#Writer.AddFS"><code>Writer.AddFS</code></a> adds all of the files from an <a href="/pkg/io/fs#FS"><code>fs.FS</code></a> to the archive.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- archive/zip -->
|
||||
|
||||
<dl id="bufio"><dt><a href="/pkg/bufio/">bufio</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/56381, CL 498117 -->
|
||||
When a <a href="/pkg/bufio#SplitFunc"><code>SplitFunc</code></a> returns <a href="/pkg/bufio#ErrFinalToken"><code>ErrFinalToken</code></a> with a <code>nil</code> token, <a href="/pkg/bufio#Scanner"><code>Scanner</code></a> 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 <code>[]byte{}</code> rather than <code>nil</code>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- bufio -->
|
||||
|
||||
<dl id="cmp"><dt><a href="/pkg/cmp/">cmp</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/60204 --><!-- CL 504883 -->
|
||||
The new function <code>Or</code> returns the first in a sequence of values that is not the zero value.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- cmp -->
|
||||
|
||||
<dl id="crypto/tls"><dt><a href="/pkg/crypto/tls/">crypto/tls</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/43922, CL 544155 -->
|
||||
<a href="/pkg/crypto/tls#ConnectionState.ExportKeyingMaterial"><code>ConnectionState.ExportKeyingMaterial</code></a> will now
|
||||
return an error unless TLS 1.3 is in use, or the <code>extended_master_secret</code> extension is supported by both the server and
|
||||
client. <code>crypto/tls</code> has supported this extension since Go 1.20. This can be disabled with the
|
||||
<code>tlsunsafeekm=1</code> GODEBUG setting.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/62459, CL 541516 -->
|
||||
By default, the minimum version offered by <code>crypto/tls</code> servers is now TLS 1.2 if not specified with
|
||||
<a href="/pkg/crypto/tls#Config.MinimumVersion"><code>config.MinimumVersion</code></a>, matching the behavior of <code>crypto/tls</code>
|
||||
clients. This change can be reverted with the <code>tls10server=1</code> GODEBUG setting.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/63413, CL 541517 -->
|
||||
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 <code>tlsrsakex=1</code> GODEBUG setting.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- crypto/tls -->
|
||||
|
||||
<dl id="crypto/x509"><dt><a href="/pkg/crypto/x509/">crypto/x509</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/57178 -->
|
||||
The new <a href="/pkg/crypto/x509#CertPool.AddCertWithConstraint"><code>CertPool.AddCertWithConstraint</code></a>
|
||||
method can be used to add customized constraints to root certificates to be applied during chain building.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/58922, CL 519315-->
|
||||
On Android, root certificates will now be loaded from <code>/data/misc/keychain/certs-added</code> as well as <code>/system/etc/security/cacerts</code>.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/60665, CL 520535 -->
|
||||
A new type, <a href="/pkg/crypto/x509#OID"><code>OID</code></a>, supports ASN.1 Object Identifiers with individual
|
||||
components larger than 31 bits. A new field which uses this type, <a href="/pkg/crypto/x509#Certificate.Policies"><code>Policies</code></a>,
|
||||
is added to the <code>Certificate</code> struct, and is now populated during parsing. Any OIDs which cannot be represented
|
||||
using a <a href="/pkg/encoding/asn1#ObjectIdentifier"><code>asn1.ObjectIdentifier</code></a> will appear in <code>Policies</code>,
|
||||
but not in the old <code>PolicyIdentifiers</code> field.
|
||||
|
||||
When calling <a href="/pkg/crypto/x509#CreateCertificate"><code>CreateCertificate</code></a>, the <code>Policies</code> field is ignored, and
|
||||
policies are taken from the <code>PolicyIdentifiers</code> field. Using the <code>x509usepolicies=1</code> GODEBUG setting inverts this,
|
||||
populating certificate policies from the <code>Policies</code> field, and ignoring the <code>PolicyIdentifiers</code> field. We may change the
|
||||
default value of <code>x509usepolicies</code> in Go 1.23, making <code>Policies</code> the default field for marshaling.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- crypto/x509 -->
|
||||
|
||||
<dl id="database/sql"><dt><a href="/pkg/database/sql/">database/sql</a></dt>
|
||||
<dd>
|
||||
|
|
@ -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.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- database/sql -->
|
||||
|
||||
<dl id="debug/elf"><dt><a href="/pkg/debug/elf/">debug/elf</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/61974, CL 469395 -->
|
||||
Constant <code>R_MIPS_PC32</code> is defined for use with MIPS64 systems.
|
||||
</p>
|
||||
</dd>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/63725, CL 537615 -->
|
||||
Additional <code>R_LARCH_*</code> constants are defined for use with LoongArch systems.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- debug/elf -->
|
||||
|
||||
<dl id="encoding"><dt><a href="/pkg/encoding/">encoding</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/53693, https://go.dev/cl/504884 -->
|
||||
The new methods <code>AppendEncode</code> and <code>AppendDecode</code> added to
|
||||
each of the <code>Encoding</code> types in the packages
|
||||
<a href="/pkg/encoding/base32"><code>encoding/base32</code></a>,
|
||||
<a href="/pkg/encoding/base64"><code>encoding/base64</code></a>, and
|
||||
<a href="/pkg/encoding/hex"><code>encoding/hex</code></a>
|
||||
simplify encoding and decoding from and to byte slices by taking care of byte slice buffer management.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/cl/505236 -->
|
||||
The methods
|
||||
<a href="/pkg/encoding/base32#Encoding.WithPadding"><code>base32.Encoding.WithPadding</code></a> and
|
||||
<a href="/pkg/encoding/base64#Encoding.WithPadding"><code>base64.Encoding.WithPadding</code></a>
|
||||
now panic if the <code>padding</code> argument is a negative value other than
|
||||
<code>NoPadding</code>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- encoding -->
|
||||
|
||||
<dl id="encoding/json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/cl/521675 -->
|
||||
Marshaling and encoding functionality now escapes
|
||||
<code>'\b'</code> and <code>'\f'</code> characters as
|
||||
<code>\b</code> and <code>\f</code> instead of
|
||||
<code>\u0008</code> and <code>\u000c</code>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- encoding/json -->
|
||||
|
||||
<dl id="go/ast"><dt><a href="/pkg/go/ast/">go/ast</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/52463, https://go/dev/cl/504915 -->
|
||||
The following declarations related to
|
||||
<a href='https://pkg.go.dev/go/ast#Object'>syntactic identifier resolution</a>
|
||||
are now <a href="https://go.dev/issue/52463">deprecated</a>:
|
||||
<code>Ident.Obj</code>,
|
||||
<code>Object</code>,
|
||||
<code>Scope</code>,
|
||||
<code>File.Scope</code>,
|
||||
<code>File.Unresolved</code>,
|
||||
<code>Importer</code>,
|
||||
<code>Package</code>,
|
||||
<code>NewPackage</code>.
|
||||
|
||||
In general, identifiers cannot be accurately resolved without type information.
|
||||
Consider, for example, the identifier <code>K</code>
|
||||
in <code>T{K: ""}</code>: 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 <a href='/pkg/go/types'>go/types</a>
|
||||
package to resolve identifiers; see
|
||||
<a href='https://pkg.go.dev/go/types#Object'><code>Object</code></a>,
|
||||
<a href='https://pkg.go.dev/go/types#Info.Uses'><code>Info.Uses</code></a>, and
|
||||
<a href='https://pkg.go.dev/go/types#Info.Defs'><code>Info.Defs</code></a> for details.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/60061 -->
|
||||
The new <a href='https://pkg.go.dev/go/ast#Unparen'><code>ast.Unparen</code></a>
|
||||
function removes any enclosing
|
||||
<a href='https://pkg.go.dev/go/ast#ParenExpr'>parentheses</a> from
|
||||
an <a href='https://pkg.go.dev/go/ast#Expr'>expression</a>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- go/ast -->
|
||||
|
||||
<dl id="go/types"><dt><a href="/pkg/go/types/">go/types</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/63223, CL 521956, CL 541737 -->
|
||||
The new <a href="/pkg/go/types#Alias"><code>Alias</code></a> 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 <code>Alias</code>.
|
||||
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, <code>Alias</code> types will also carry <a href="https://go.dev/issue/46477">type parameter information</a>.
|
||||
The new function <a href="/pkg/go/types#Unalias"><code>Unalias</code></a> returns the actual type denoted by an
|
||||
<code>Alias</code> type (or any other <a href="/pkg/go/types#Type"><code>Type</code></a> for that matter).
|
||||
</p>
|
||||
<p>
|
||||
Because <code>Alias</code> types may break existing type switches that do not know to check for them,
|
||||
this functionality is controlled by a <a href="/doc/godebug"><code>GODEBUG</code></a> field named <code>gotypesalias</code>.
|
||||
With <code>gotypesalias=0</code>, everything behaves as before, and <code>Alias</code> types are never created.
|
||||
With <code>gotypesalias=1</code>, <code>Alias</code> types are created and clients must expect them.
|
||||
The default is <code>gotypesalias=0</code>.
|
||||
In a future release, the default will be changed to <code>gotypesalias=1</code>.
|
||||
<em>Clients of <a href="/pkg/go/types"><code>go/types</code></a> are urged to adjust their code as soon as possible
|
||||
to work with <code>gotypesalias=1</code> to eliminate problems early.</em>
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/62605, CL 540056 -->
|
||||
The <a href="/pkg/go/types#Info"><code>Info</code></a> struct now exports the
|
||||
<a href="/pkg/go/types#Info.FileVersions"><code>FileVersions</code></a> map
|
||||
which provides per-file Go version information.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/62037, CL 541575 -->
|
||||
The new helper method <a href="/pkg/go/types#Info.PkgNameOf"><code>PkgNameOf</code></a> returns the local package name
|
||||
for the given import declaration.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/61035, multiple CLs, see issue for details -->
|
||||
The implementation of <a href="/pkg/go/types#SizesFor"><code>SizesFor</code></a> has been adjusted to compute
|
||||
the same type sizes as the compiler when the compiler argument for <code>SizesFor</code> is <code>"gc"</code>.
|
||||
The default <a href="/pkg/go/types#Sizes"><code>Sizes</code></a> implementation used by the type checker is now
|
||||
<code>types.SizesFor("gc", "amd64")</code>.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/64295, CL 544035 -->
|
||||
The start position (<a href="/pkg/go/types#Scope.Pos"><code>Pos</code></a>)
|
||||
of the lexical environment block (<a href="/pkg/go/types#Scope"><code>Scope</code></a>)
|
||||
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 <code>func</code> token.
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl id="go/version"><dt><a href="/pkg/go/version/">go/version</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/62039, https://go.dev/cl/538895 -->
|
||||
The new <a href="/pkg/go/version/"><code>go/version</code></a> package implements functions
|
||||
for validating and comparing Go version strings.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- go/version -->
|
||||
|
||||
<dl id="html/template"><dt><a href="/pkg/html/template/">html/template</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/61619, CL 507995 -->
|
||||
Javascript template literals may now contain Go template actions, and parsing a template containing one will
|
||||
no longer return <code>ErrJSTemplate</code>. Similarly the GODEBUG setting <code>jstmpllitinterp</code> no
|
||||
longer has any effect.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- html/template -->
|
||||
|
||||
<dl id="io"><dt><a href="/pkg/io/">io</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/61870, CL 526855 -->
|
||||
The new <a href="/pkg/io#SectionReader.Outer"><code>SectionReader.Outer</code></a> method returns the <a href="/pkg/io#ReaderAt"><code>ReaderAt</code></a>, offset, and size passed to <a href="/pkg/io#NewSectionReader"><code>NewSectionReader</code></a>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- io -->
|
||||
|
||||
<dl id="log/slog"><dt><a href="/pkg/log/slog/">log/slog</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/62418 -->
|
||||
The new <a href="/pkg/log/slog#SetLogLoggerLevel"><code>SetLogLoggerLevel</code></a> 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`.
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl id="math/big"><dt><a href="/pkg/math/big/">math/big</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/50489, CL 539299 -->
|
||||
The new method <a href="/pkg/math/big#Rat.FloatPrec">Rat.FloatPrec</a> 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.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- math/big -->
|
||||
|
||||
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/58808 -->
|
||||
When <a href="/pkg/io#Copy"><code>io.Copy</code></a> copies
|
||||
from a <code>TCPConn</code> to a <code>UnixConn</code>,
|
||||
it will now use Linux's <code>splice(2)</code> system call if possible,
|
||||
using the new method <a href="/pkg/net#TCPConn.WriteTo"><code>TCPConn.WriteTo</code></a>.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 467335 -->
|
||||
The Go DNS Resolver, used when building with "-tags=netgo",
|
||||
now searches for a matching name in the Windows hosts file,
|
||||
located at <code>%SystemRoot%\System32\drivers\etc\hosts</code>,
|
||||
before making a DNS query.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- net -->
|
||||
|
||||
<dl id="net/http"><dt><a href="/pkg/net/http/">net/http</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/51971 -->
|
||||
The new functions
|
||||
<a href="/pkg/net/http#ServeFileFS"><code>ServeFileFS</code></a>,
|
||||
<a href="/pkg/net/http#FileServerFS"><code>FileServerFS</code></a>, and
|
||||
<a href="/pkg/net/http#NewFileTransportFS"><code>NewFileTransportFS</code></a>
|
||||
are versions of the existing
|
||||
<code>ServeFile</code>, <code>FileServer</code>, and <code>NewFileTransport</code>,
|
||||
operating on an <code>fs.FS</code>.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/61679 -->
|
||||
The HTTP server and client now reject requests and responses containing
|
||||
an invalid empty <code>Content-Length</code> header.
|
||||
The previous behavior may be restored by setting
|
||||
<a href="/doc/godebug"><code>GODEBUG</code></a> field <code>httplaxcontentlength=1</code>.
|
||||
</p>
|
||||
|
||||
<!-- <p><\!-- CL 528355 -\-> -->
|
||||
<!-- TODO: <a href="https://go.dev/cl/528355">https://go.dev/cl/528355</a>: net/http: implement path value methods on Request; modified api/next/61410.txt -->
|
||||
<!-- </p> -->
|
||||
</dd>
|
||||
</dl><!-- net/http -->
|
||||
|
||||
<!-- <dl id="net/http/cgi"><dt><a href="/pkg/net/http/cgi/">net/http/cgi</a></dt> -->
|
||||
<!-- <dd> -->
|
||||
<!-- <p><\!-- CL 539615 -\-> -->
|
||||
<!-- TODO: <a href="https://go.dev/cl/539615">https://go.dev/cl/539615</a>: net/http/cgi: the PATH_INFO should be empty or start with a slash -->
|
||||
<!-- </p> -->
|
||||
<!-- </dd> -->
|
||||
<!-- </dl><\!-- net/http/cgi -\-> -->
|
||||
|
||||
<!-- <dl id="net/netip"><dt><a href="/pkg/net/netip/">net/netip</a></dt> -->
|
||||
<!-- <dd> -->
|
||||
<!-- <p><\!-- https://go.dev/issue/61642 -\-> -->
|
||||
<!-- TODO: <a href="https://go.dev/issue/61642">https://go.dev/issue/61642</a>: add Prefix.Compare and AddrPort.Compare -->
|
||||
<!-- </p> -->
|
||||
|
||||
<!-- <p><\!-- CL 524616 -\-> -->
|
||||
<!-- TODO: <a href="https://go.dev/cl/524616">https://go.dev/cl/524616</a>: net/netip: add AddrPort.Compare and Prefix.Compare; modified api/next/61642.txt -->
|
||||
<!-- </p> -->
|
||||
<!-- </dd> -->
|
||||
<!-- </dl><\!-- net/netip -\-> -->
|
||||
|
||||
<dl id="os"><dt><a href="/pkg/os/">os</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 516555 -->
|
||||
On Windows, the <a href="/pkg/os#Stat"><code>Stat</code></a> function now follows all reparse points
|
||||
that link to another named entity in the system.
|
||||
It was previously only following <code>IO_REPARSE_TAG_SYMLINK</code> and
|
||||
<code>IO_REPARSE_TAG_MOUNT_POINT</code> reparse points.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 541015 -->
|
||||
On Windows, passing <a href="/pkg/os#O_SYNC"><code>O_SYNC</code></a> to <a href="/pkg/os#OpenFile"><code>OpenFile</code></a> now causes write operations to go directly to disk, equivalent to <code>O_SYNC</code> on Unix platforms.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 452995 -->
|
||||
On Windows, the <a href="/pkg/os#ReadDir"><code>ReadDir</code></a>,
|
||||
<a href="/pkg/os#File.ReadDir"><code>File.ReadDir</code></a>,
|
||||
<a href="/pkg/os#File.Readdir"><code>File.Readdir</code></a>,
|
||||
and <a href="/pkg/os#File.Readdirnames"><code>File.Readdirnames</code></a> functions
|
||||
now read directory entries in batches to reduce the number of system calls,
|
||||
improving performance up to 30%.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/58808 -->
|
||||
When <a href="/pkg/io#Copy"><code>io.Copy</code></a> copies
|
||||
from a <code>File</code> to a <code>net.UnixConn</code>,
|
||||
it will now use Linux's <code>sendfile(2)</code> system call if possible,
|
||||
using the new method <a href="/pkg/os#File.WriteTo"><code>File.WriteTo</code></a>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- os -->
|
||||
|
||||
<dl id="os/exec"><dt><a href="/pkg/os/exec/">os/exec</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 528037 -->
|
||||
On Windows, <a href="/pkg/os/exec#LookPath"><code>LookPath</code></a> now
|
||||
ignores empty entries in <code>%PATH%</code>, and returns
|
||||
<code>ErrNotFound</code> (instead of <code>ErrNotExist</code>) if
|
||||
no executable file extension is found to resolve an otherwise-unambiguous
|
||||
name.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 528038, CL 527820 -->
|
||||
On Windows, <a href="/pkg/os/exec#Command"><code>Command</code></a> and
|
||||
<a href="/pkg/os/exec#Cmd.Start"><code>Cmd.Start</code></a> no
|
||||
longer call <code>LookPath</code> if the path to the executable is already
|
||||
absolute and has an executable file extension. In addition,
|
||||
<code>Cmd.Start</code> no longer writes the resolved extension back to
|
||||
the <a href="/pkg/os/exec#Cmd.Path"><code>Path</code></a> field,
|
||||
so it is now safe to call the <code>String</code> method concurrently
|
||||
with a call to <code>Start</code>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- os/exec -->
|
||||
|
||||
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/61827, CL 517777 -->
|
||||
|
|
@ -94,12 +827,189 @@ Do not send CLs removing the interior tags from such phrases.
|
|||
These changes make <code>IsZero</code> consistent with comparing
|
||||
a value to zero using the language <code>==</code> operator.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/59599, CL 511035 -->
|
||||
The <a href="/pkg/reflect/#PtrTo"><code>PtrTo</code></a> function is deprecated,
|
||||
in favor of <a href="/pkg/reflect/#PointerTo"><code>PointerTo</code></a>.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/60088, CL 513478 -->
|
||||
The new function <a href="/pkg/reflect/#TypeFor"><code>TypeFor</code></a>
|
||||
returns the <a href="/pkg/reflect/#Type"><code>Type</code></a> that represents
|
||||
the type argument T.
|
||||
Previously, to get the <code>reflect.Type</code> value for a type, one had to use
|
||||
<code>reflect.TypeOf((*T)(nil)).Elem()</code>.
|
||||
This may now be written as <code>reflect.TypeFor[T]()</code>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dl><!-- reflect -->
|
||||
|
||||
<dl id="runtime/metrics"><dt><a href="/pkg/runtime/metrics/">runtime/metrics</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/63340 -->
|
||||
Four new histogram metrics
|
||||
<code>/sched/pauses/stopping/gc:seconds</code>,
|
||||
<code>/sched/pauses/stopping/other:seconds</code>,
|
||||
<code>/sched/pauses/total/gc:seconds</code>, and
|
||||
<code>/sched/pauses/total/other:seconds</code> 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.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/63340 -->
|
||||
The <code>/gc/pauses:seconds</code> metric is deprecated, as it is
|
||||
equivalent to the new <code>/sched/pauses/total/gc:seconds</code> metric.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/57071 -->
|
||||
<code>/sync/mutex/wait/total:seconds</code> now includes contention on
|
||||
runtime-internal locks in addition to
|
||||
<a href="/pkg/sync#Mutex"><code>sync.Mutex</code></a> and
|
||||
<a href="/pkg/sync#RWMutex"><code>sync.RWMutex</code></a>.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- runtime/metrics -->
|
||||
|
||||
<dl id="runtime/pprof"><dt><a href="/pkg/runtime/pprof/">runtime/pprof</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/61015 -->
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/57071 -->
|
||||
Mutex profiles also now include contention on runtime-internal locks in addition to
|
||||
<a href="/pkg/sync#Mutex"><code>sync.Mutex</code></a> and
|
||||
<a href="/pkg/sync#RWMutex"><code>sync.RWMutex</code></a>.
|
||||
Contention on runtime-internal locks is always reported at <code>runtime._LostContendedRuntimeLock</code>.
|
||||
A future release will add complete stack traces in these cases.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/50891 -->
|
||||
CPU profiles on Darwin platforms now contain the process's memory map, enabling the disassembly
|
||||
view in the pprof tool.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- runtime/pprof -->
|
||||
|
||||
<dl id="runtime/trace"><dt><a href="/pkg/runtime/trace/">runtime/trace</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/60773 -->
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
To allow Go developers to take advantage of these improvements, an experimental
|
||||
trace reading package is available at <a href="/pkg/golang.org/x/exp/trace">golang.org/x/exp/trace</a>.
|
||||
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
|
||||
<a href="https://github.com/golang/go/issues/62627">the corresponding proposal issue</a>.
|
||||
</p>
|
||||
<p>
|
||||
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 <code>GOEXPERIMENT=noexectracer2</code>.
|
||||
If you do, please file an issue, otherwise this option will be removed in a future release.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- runtime/trace -->
|
||||
|
||||
<dl id="slices"><dt><a href="/pkg/slices/">slices</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/56353 --><!-- CL 504882 -->
|
||||
The new function <code>Concat</code> concatenates multiple slices.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/63393 --><!-- CL 543335 -->
|
||||
Functions that shrink the size of a slice (<code>Delete</code>, <code>DeleteFunc</code>, <code>Compact</code>, <code>CompactFunc</code>, and <code>Replace</code>) now zero the elements between the new length and the old length.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/63913 --><!-- CL 540155 -->
|
||||
<code>Insert</code> now always panics if the argument <code>i</code> is out of range. Previously it did not panic in this situation if there were no elements to be inserted.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- slices -->
|
||||
|
||||
<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/60797 -->
|
||||
The <code>syscall</code> package has been <a href="https://golang.org/s/go1.4-syscall">frozen</a> 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 <code>syscall</code> package, such as the <a href="/pkg/os/exec#Cmd"><code>os/exec.Cmd.SysProcAttr</code></a> field.
|
||||
To avoid unnecessary complaints on such code, the <code>syscall</code> package is no longer marked as deprecated.
|
||||
The package remains frozen to most new functionality, and new code remains encouraged to use <a href="/pkg/golang.org/x/sys/unix"><code>golang.org/x/sys/unix</code></a> or <a href="/pkg/golang.org/x/sys/windows"><code>golang.org/x/sys/windows</code></a> where possible.
|
||||
</p>
|
||||
|
||||
<p><!-- https://go.dev/issue/51246, CL 520266 -->
|
||||
On Linux, the new <a href="/pkg/syscall#SysProcAttr"><code>SysProcAttr.PidFD</code></a> field allows obtaining a PID FD when starting a child process via <a href="/pkg/syscall#StartProcess"><code>StartProcess</code></a> or <a href="/pkg/os/exec"><code>os/exec</code></a>.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 541015 -->
|
||||
On Windows, passing <a href="/pkg/syscall#O_SYNC"><code>O_SYNC</code></a> to <a href="/pkg/syscall#Open"><code>Open</code></a> now causes write operations to go directly to disk, equivalent to <code>O_SYNC</code> on Unix platforms.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- syscall -->
|
||||
|
||||
<dl id="testing/slogtest"><dt><a href="/pkg/testing/slogtest/">testing/slogtest</a></dt>
|
||||
<dd>
|
||||
<p><!-- https://go.dev/issue/61758 -->
|
||||
The new <a href="/pkg/testing/slogtest#Run"><code>Run</code></a> function uses sub-tests to run test cases,
|
||||
providing finer-grained control.
|
||||
</p>
|
||||
</dd>
|
||||
</dl><!-- testing/slogtest -->
|
||||
|
||||
<h2 id="ports">Ports</h2>
|
||||
|
||||
<p>
|
||||
TODO: complete this section, or delete if not needed
|
||||
<h3 id="darwin">Darwin</h3>
|
||||
<p><!-- CL 461697 -->
|
||||
On macOS on 64-bit x86 architecture (the <code>darwin/amd64</code> port),
|
||||
the Go toolchain now generates position-independent executables (PIE) by default.
|
||||
Non-PIE binaries can be generated by specifying the <code>-buildmode=exe</code>
|
||||
build flag.
|
||||
On 64-bit ARM-based macOS (the <code>darwin/arm64</code> port),
|
||||
the Go toolchain already generates PIE by default.
|
||||
</p>
|
||||
<p><!-- go.dev/issue/64207 -->
|
||||
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.
|
||||
</p>
|
||||
|
||||
<h3 id="arm">Arm</h3>
|
||||
<p><!-- CL 514907 -->
|
||||
The <code>GOARM</code> environment variable now allows you to select whether to use software or hardware floating point.
|
||||
Previously, valid <code>GOARM</code> values were <code>5</code>, <code>6</code>, or <code>7</code>. Now those same values can
|
||||
be optionally followed by <code>,softfloat</code> or <code>,hardfloat</code> to select the floating-point implementation.
|
||||
</p>
|
||||
<p>
|
||||
This new option defaults to <code>softfloat</code> for version <code>5</code> and <code>hardfloat</code> for versions
|
||||
<code>6</code> and <code>7</code>.
|
||||
</p>
|
||||
|
||||
<h3 id="loong64">Loong64</h3>
|
||||
<p><!-- CL 481315 -->
|
||||
The <code>loong64</code> port now supports passing function arguments and results using registers.
|
||||
</p>
|
||||
<p><!-- CL 481315,537615,480878 -->
|
||||
The <code>linux/loong64</code> port now supports the address sanitizer, memory sanitizer, new-style linker relocations, and the <code>plugin</code> build mode.
|
||||
</p>
|
||||
|
||||
<h3 id="openbsd">OpenBSD</h3>
|
||||
<p><!-- CL 517935 -->
|
||||
Go 1.22 adds an experimental port to OpenBSD on big-endian 64-bit PowerPC
|
||||
(<code>openbsd/ppc64</code>).
|
||||
</p>
|
||||
|
|
|
|||
280
doc/go_spec.html
280
doc/go_spec.html
|
|
@ -1,6 +1,6 @@
|
|||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of Nov 1, 2023",
|
||||
"Subtitle": "Version of Dec 27, 2023",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
This is the reference manual for the Go programming language.
|
||||
The pre-Go1.18 version, without generics, can be found
|
||||
<a href="/doc/go1.17_spec.html">here</a>.
|
||||
For more information and other documents, see <a href="/">golang.org</a>.
|
||||
For more information and other documents, see <a href="/">go.dev</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -70,6 +70,14 @@ enumerations or code snippets that are not further specified. The character <cod
|
|||
language.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A link of the form [<a href="#Language_versions">Go 1.xx</a>] 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 <a href="#Language_versions">linked section</a>
|
||||
in the <a href="#Appendix">appendix</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="Source_code_representation">Source code representation</h2>
|
||||
|
||||
<p>
|
||||
|
|
@ -263,7 +271,8 @@ continue for import return var
|
|||
|
||||
<p>
|
||||
The following character sequences represent <a href="#Operators">operators</a>
|
||||
(including <a href="#Assignment_statements">assignment operators</a>) and punctuation:
|
||||
(including <a href="#Assignment_statements">assignment operators</a>) and punctuation
|
||||
[<a href="#Go_1.18">Go 1.18</a>]:
|
||||
</p>
|
||||
<pre class="grammar">
|
||||
+ & += &= && == != ( )
|
||||
|
|
@ -281,7 +290,8 @@ An integer literal is a sequence of digits representing an
|
|||
<a href="#Constants">integer constant</a>.
|
||||
An optional prefix sets a non-decimal base: <code>0b</code> or <code>0B</code>
|
||||
for binary, <code>0</code>, <code>0o</code>, or <code>0O</code> for octal,
|
||||
and <code>0x</code> or <code>0X</code> for hexadecimal.
|
||||
and <code>0x</code> or <code>0X</code> for hexadecimal
|
||||
[<a href="#Go_1.13">Go 1.13</a>].
|
||||
A single <code>0</code> is considered a decimal zero.
|
||||
In hexadecimal literals, letters <code>a</code> through <code>f</code>
|
||||
and <code>A</code> through <code>F</code> 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 (<code>p</code> or <code>P</code> 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 2<sup>exp</sup>.
|
||||
An exponent value exp scales the mantissa (integer and fractional part) by 2<sup>exp</sup>
|
||||
[<a href="#Go_1.13">Go 1.13</a>].
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -411,7 +422,8 @@ It consists of an <a href="#Integer_literals">integer</a> or
|
|||
<a href="#Floating-point_literals">floating-point</a> literal
|
||||
followed by the lowercase letter <code>i</code>.
|
||||
The value of an imaginary literal is the value of the respective
|
||||
integer or floating-point literal multiplied by the imaginary unit <i>i</i>.
|
||||
integer or floating-point literal multiplied by the imaginary unit <i>i</i>
|
||||
[<a href="#Go_1.13">Go 1.13</a>]
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
|
|
@ -1340,6 +1352,7 @@ interface{}
|
|||
|
||||
<p>
|
||||
For convenience, the predeclared type <code>any</code> is an alias for the empty interface.
|
||||
[<a href="#Go_1.18">Go 1.18</a>]
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -1375,13 +1388,15 @@ as the <code>File</code> interface.
|
|||
In a slightly more general form
|
||||
an interface <code>T</code> may use a (possibly qualified) interface type
|
||||
name <code>E</code> as an interface element. This is called
|
||||
<i>embedding</i> interface <code>E</code> in <code>T</code>.
|
||||
<i>embedding</i> interface <code>E</code> in <code>T</code>
|
||||
[<a href="#Go_1.14">Go 1.14</a>].
|
||||
The type set of <code>T</code> is the <i>intersection</i> of the type sets
|
||||
defined by <code>T</code>'s explicitly declared methods and the type sets
|
||||
of <code>T</code>’s embedded interfaces.
|
||||
In other words, the type set of <code>T</code> is the set of all types that implement all the
|
||||
explicitly declared methods of <code>T</code> and also all the methods of
|
||||
<code>E</code>.
|
||||
<code>E</code>
|
||||
[<a href="#Go_1.18">Go 1.18</a>].
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
|
|
@ -1420,7 +1435,8 @@ type ReadCloser interface {
|
|||
<p>
|
||||
In their most general form, an interface element may also be an arbitrary type term
|
||||
<code>T</code>, or a term of the form <code>~T</code> specifying the underlying type <code>T</code>,
|
||||
or a union of terms <code>t<sub>1</sub>|t<sub>2</sub>|…|t<sub>n</sub></code>.
|
||||
or a union of terms <code>t<sub>1</sub>|t<sub>2</sub>|…|t<sub>n</sub></code>
|
||||
[<a href="#Go_1.18">Go 1.18</a>].
|
||||
Together with method specifications, these elements enable the precise
|
||||
definition of an interface's type set as follows:
|
||||
</p>
|
||||
|
|
@ -2303,7 +2319,9 @@ as an <a href="#Operands">operand</a>, and in <a href="#Assignment_statements">a
|
|||
|
||||
<p>
|
||||
The following identifiers are implicitly declared in the
|
||||
<a href="#Blocks">universe block</a>:
|
||||
<a href="#Blocks">universe block</a>
|
||||
[<a href="#Go_1.18">Go 1.18</a>]
|
||||
[<a href="#Go_1.21">Go 1.21</a>]:
|
||||
</p>
|
||||
<pre class="grammar">
|
||||
Types:
|
||||
|
|
@ -2487,7 +2505,8 @@ TypeSpec = AliasDecl | TypeDef .
|
|||
<h4 id="Alias_declarations">Alias declarations</h4>
|
||||
|
||||
<p>
|
||||
An alias declaration binds an identifier to the given type.
|
||||
An alias declaration binds an identifier to the given type
|
||||
[<a href="#Go_1.9">Go 1.9</a>].
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
|
|
@ -2636,7 +2655,8 @@ func (l *List[T]) Len() int { … }
|
|||
A type parameter list declares the <i>type parameters</i> of a generic function or type declaration.
|
||||
The type parameter list looks like an ordinary <a href="#Function_types">function parameter list</a>
|
||||
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
|
||||
[<a href="#Go_1.18">Go 1.18</a>].
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
|
|
@ -2719,7 +2739,8 @@ type T6[P int] struct{ f *T6[P] } // ok: reference to T6 is not in type para
|
|||
<p>
|
||||
A <i>type constraint</i> is an <a href="#Interface_types">interface</a> 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
|
||||
[<a href="#Go_1.18">Go 1.18</a>].
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
|
|
@ -2749,7 +2770,8 @@ other interfaces based on their type sets. But this should get us going for now.
|
|||
The <a href="#Predeclared_identifiers">predeclared</a>
|
||||
<a href="#Interface_types">interface type</a> <code>comparable</code>
|
||||
denotes the set of all non-interface types that are
|
||||
<a href="#Comparison_operators">strictly comparable</a>.
|
||||
<a href="#Comparison_operators">strictly comparable</a>
|
||||
[<a href="#Go_1.18">Go 1.18</a>].
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -2782,7 +2804,8 @@ if <code>T</code> is an element of the type set defined by <code>C</code>; i.e.,
|
|||
if <code>T</code> <a href="#Implementing_an_interface">implements</a> <code>C</code>.
|
||||
As an exception, a <a href="#Comparison_operators">strictly comparable</a>
|
||||
type constraint may also be satisfied by a <a href="#Comparison_operators">comparable</a>
|
||||
(not necessarily strictly comparable) type argument.
|
||||
(not necessarily strictly comparable) type argument
|
||||
[<a href="#Go_1.20">Go 1.20</a>].
|
||||
More precisely:
|
||||
</p>
|
||||
|
||||
|
|
@ -4306,7 +4329,7 @@ with the same underlying array.
|
|||
|
||||
<p>
|
||||
A generic function or type is <i>instantiated</i> by substituting <i>type arguments</i>
|
||||
for the type parameters.
|
||||
for the type parameters [<a href="#Go_1.18">Go 1.18</a>].
|
||||
Instantiation proceeds in two steps:
|
||||
</p>
|
||||
|
||||
|
|
@ -4759,6 +4782,7 @@ to the type of the other operand.
|
|||
|
||||
<p>
|
||||
The right operand in a shift expression must have <a href="#Numeric_types">integer type</a>
|
||||
[<a href="#Go_1.13">Go 1.13</a>]
|
||||
or be an untyped constant <a href="#Representability">representable</a> by a
|
||||
value of type <code>uint</code>.
|
||||
If the left operand of a non-constant shift expression is an untyped constant,
|
||||
|
|
@ -5426,7 +5450,8 @@ in any of these cases:
|
|||
<code>x</code> is a string and <code>T</code> is a slice of bytes or runes.
|
||||
</li>
|
||||
<li>
|
||||
<code>x</code> is a slice, <code>T</code> is an array or a pointer to an array,
|
||||
<code>x</code> is a slice, <code>T</code> is an array [<a href="#Go_1.20">Go 1.20</a>]
|
||||
or a pointer to an array [<a href="#Go_1.17">Go 1.17</a>],
|
||||
and the slice and array types have <a href="#Type_identity">identical</a> element types.
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -6516,7 +6541,6 @@ additionally it may specify an <i>init</i>
|
|||
and a <i>post</i> statement, such as an assignment,
|
||||
an increment or decrement statement. The init statement may be a
|
||||
<a href="#Short_variable_declarations">short variable declaration</a>, but the post statement must not.
|
||||
Variables declared by the init statement are re-used in each iteration.
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
|
|
@ -6548,12 +6572,54 @@ for cond { S() } is the same as for ; cond ; { S() }
|
|||
for { S() } is the same as for true { S() }
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Each iteration has its own separate declared variable (or variables)
|
||||
[<a href="#Go_1.22">Go 1.22</a>].
|
||||
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.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var prints []func()
|
||||
for i := 0; i < 5; i++ {
|
||||
prints = append(prints, func() { println(i) })
|
||||
i++
|
||||
}
|
||||
for _, p := range prints {
|
||||
p()
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
prints
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
1
|
||||
3
|
||||
5
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Prior to [<a href="#Go_1.22">Go 1.22</a>], iterations share one set of variables
|
||||
instead of having their own separate variables.
|
||||
In that case, the example above prints
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
6
|
||||
6
|
||||
6
|
||||
</pre>
|
||||
|
||||
<h4 id="For_range">For statements with <code>range</code> clause</h4>
|
||||
|
||||
<p>
|
||||
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 [<a href="#Go_1.22">Go 1.22</a>].
|
||||
For each entry it assigns <i>iteration values</i>
|
||||
to corresponding <i>iteration variables</i> if present and then executes the block.
|
||||
</p>
|
||||
|
|
@ -6652,9 +6718,10 @@ The iteration variables may be declared by the "range" clause using a form of
|
|||
<a href="#Short_variable_declarations">short variable declaration</a>
|
||||
(<code>:=</code>).
|
||||
In this case their types are set to the types of the respective iteration values
|
||||
and their <a href="#Declarations_and_scope">scope</a> 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 <a href="#Declarations_and_scope">scope</a> is the block of the "for" statement;
|
||||
each iteration has its own separate variables [<a href="#Go_1.22">Go 1.22</a>]
|
||||
(see also <a href="#For_clause">"for" statements with a ForClause</a>).
|
||||
If the iteration variables are declared outside the “for” statement,
|
||||
after execution their values will be those of the last iteration.
|
||||
</p>
|
||||
|
||||
|
|
@ -7249,7 +7316,8 @@ n3 := copy(b, "Hello, World!") // n3 == 5, b is []byte("Hello")
|
|||
<p>
|
||||
The built-in function <code>clear</code> takes an argument of <a href="#Map_types">map</a>,
|
||||
<a href="#Slice_types">slice</a>, or <a href="#Type_parameter_declarations">type parameter</a> type,
|
||||
and deletes or zeroes out all elements.
|
||||
and deletes or zeroes out all elements
|
||||
[<a href="#Go_1.21">Go 1.21</a>].
|
||||
</p>
|
||||
|
||||
<pre class="grammar">
|
||||
|
|
@ -7516,7 +7584,8 @@ The precise behavior is implementation-dependent.
|
|||
The built-in functions <code>min</code> and <code>max</code> compute the
|
||||
smallest—or largest, respectively—value of a fixed number of
|
||||
arguments of <a href="#Comparison_operators">ordered types</a>.
|
||||
There must be at least one argument.
|
||||
There must be at least one argument
|
||||
[<a href="#Go_1.21">Go 1.21</a>].
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -8232,8 +8301,8 @@ of if the general conversion rules take care of this.
|
|||
<p>
|
||||
A <code>Pointer</code> is a <a href="#Pointer_types">pointer type</a> but a <code>Pointer</code>
|
||||
value may not be <a href="#Address_operators">dereferenced</a>.
|
||||
Any pointer or value of <a href="#Underlying_types">underlying type</a> <code>uintptr</code> can be
|
||||
<a href="#Conversions">converted</a> to a type of underlying type <code>Pointer</code> and vice versa.
|
||||
Any pointer or value of <a href="#Core_types">core type</a> <code>uintptr</code> can be
|
||||
<a href="#Conversions">converted</a> to a type of core type <code>Pointer</code> and vice versa.
|
||||
The effect of converting between <code>Pointer</code> and <code>uintptr</code> is implementation-defined.
|
||||
</p>
|
||||
|
||||
|
|
@ -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
|
||||
</pre>
|
||||
|
||||
|
|
@ -8292,7 +8365,8 @@ of constant size.
|
|||
|
||||
<p>
|
||||
The function <code>Add</code> adds <code>len</code> to <code>ptr</code>
|
||||
and returns the updated pointer <code>unsafe.Pointer(uintptr(ptr) + uintptr(len))</code>.
|
||||
and returns the updated pointer <code>unsafe.Pointer(uintptr(ptr) + uintptr(len))</code>
|
||||
[<a href="#Go_1.17">Go 1.17</a>].
|
||||
The <code>len</code> argument must be of <a href="#Numeric_types">integer type</a> or an untyped <a href="#Constants">constant</a>.
|
||||
A constant <code>len</code> argument must be <a href="#Representability">representable</a> by a value of type <code>int</code>;
|
||||
if it is an untyped constant it is given type <code>int</code>.
|
||||
|
|
@ -8312,7 +8386,8 @@ and whose length and capacity are <code>len</code>.
|
|||
<p>
|
||||
except that, as a special case, if <code>ptr</code>
|
||||
is <code>nil</code> and <code>len</code> is zero,
|
||||
<code>Slice</code> returns <code>nil</code>.
|
||||
<code>Slice</code> returns <code>nil</code>
|
||||
[<a href="#Go_1.17">Go 1.17</a>].
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -8321,14 +8396,16 @@ A constant <code>len</code> argument must be non-negative and <a href="#Represen
|
|||
if it is an untyped constant it is given type <code>int</code>.
|
||||
At run time, if <code>len</code> is negative,
|
||||
or if <code>ptr</code> is <code>nil</code> and <code>len</code> is not zero,
|
||||
a <a href="#Run_time_panics">run-time panic</a> occurs.
|
||||
a <a href="#Run_time_panics">run-time panic</a> occurs
|
||||
[<a href="#Go_1.17">Go 1.17</a>].
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The function <code>SliceData</code> returns a pointer to the underlying array of the <code>slice</code> argument.
|
||||
If the slice's capacity <code>cap(slice)</code> is not zero, that pointer is <code>&slice[:1][0]</code>.
|
||||
If <code>slice</code> is <code>nil</code>, the result is <code>nil</code>.
|
||||
Otherwise it is a non-<code>nil</code> pointer to an unspecified memory address.
|
||||
Otherwise it is a non-<code>nil</code> pointer to an unspecified memory address
|
||||
[<a href="#Go_1.20">Go 1.20</a>].
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -8337,12 +8414,14 @@ The function <code>String</code> returns a <code>string</code> value whose under
|
|||
The same requirements apply to the <code>ptr</code> and <code>len</code> argument as in the function
|
||||
<code>Slice</code>. If <code>len</code> is zero, the result is the empty string <code>""</code>.
|
||||
Since Go strings are immutable, the bytes passed to <code>String</code> must not be modified afterwards.
|
||||
[<a href="#Go_1.20">Go 1.20</a>]
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The function <code>StringData</code> returns a pointer to the underlying bytes of the <code>str</code> argument.
|
||||
For an empty string the return value is unspecified, and may be <code>nil</code>.
|
||||
Since Go strings are immutable, the bytes returned by <code>StringData</code> must not be modified.
|
||||
Since Go strings are immutable, the bytes returned by <code>StringData</code> must not be modified
|
||||
[<a href="#Go_1.20">Go 1.20</a>].
|
||||
</p>
|
||||
|
||||
<h3 id="Size_and_alignment_guarantees">Size and alignment guarantees</h3>
|
||||
|
|
@ -8383,6 +8462,145 @@ A struct or array type has size zero if it contains no fields (or elements, resp
|
|||
|
||||
<h2 id="Appendix">Appendix</h2>
|
||||
|
||||
<h3 id="Language_versions">Language versions</h3>
|
||||
|
||||
<p>
|
||||
The <a href="/doc/go1compat">Go 1 compatibility guarantee</a> 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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For instance, the ability to use the prefix <code>0b</code> for binary
|
||||
integer literals was introduced with Go 1.13, indicated
|
||||
by [<a href="#Go_1.13">Go 1.13</a>] in the section on
|
||||
<a href="#Integer_literals">integer literals</a>.
|
||||
Source code containing an integer literal such as <code>0b1011</code>
|
||||
will be rejected if the implied or required language version used by
|
||||
the compiler is older than Go 1.13.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following table describes the minimum language version required for
|
||||
features introduced after Go 1.
|
||||
</p>
|
||||
|
||||
<h4 id="Go_1.9">Go 1.9</h4>
|
||||
<ul>
|
||||
<li>
|
||||
An <a href="#Alias_declarations">alias declaration</a> may be used to declare an alias name for a type.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="Go_1.13">Go 1.13</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#Integer_literals">Integer literals</a> may use the prefixes <code>0b</code>, <code>0B</code>, <code>0o</code>,
|
||||
and <code>0O</code> for binary, and octal literals, respectively.
|
||||
</li>
|
||||
<li>
|
||||
Hexadecimal <a href="#Floating-point_literals">floating-point literals</a> may be written using the prefixes
|
||||
<code>0x</code> and <code>0X</code>.
|
||||
</li>
|
||||
<li>
|
||||
The <a href="#Imaginary_literals">imaginary suffix</a> <code>i</code> may be used with any (binary, decimal, hexadecimal)
|
||||
integer or floating-point literal, not just decimal literals.
|
||||
</li>
|
||||
<li>
|
||||
The digits of any number literal may be <a href="#Integer_literals">separated</a> (grouped)
|
||||
using underscores <code>_</code>.
|
||||
</li>
|
||||
<li>
|
||||
The shift count in a <a href="#Operators">shift operation</a> may be a signed integer type.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="Go_1.14">Go 1.14</h4>
|
||||
<ul>
|
||||
<li>
|
||||
Emdedding a method more than once through different <a href="#Embedded_interfaces">embedded interfaces</a>
|
||||
is not an error.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="Go_1.17">Go 1.17</h4>
|
||||
<ul>
|
||||
<li>
|
||||
A slice may be <a href="#Conversions">converted</a> to an array pointer if the slice and array element
|
||||
types match, and the array is not longer than the slice.
|
||||
</li>
|
||||
<li>
|
||||
The built-in <a href="#Package_unsafe">package <code>unsafe</code></a> includes the new functions
|
||||
<code>Add</code> and <code>Slice</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="Go_1.18">Go 1.18</h4>
|
||||
<p>
|
||||
The 1.18 release adds polymorphic functions and types ("generics") to the language.
|
||||
Specifically:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
The set of <a href="#Operators_and_punctuation">operators and punctuation</a> includes the new token <code>~</code>.
|
||||
</li>
|
||||
<li>
|
||||
Function and type declarations may declare <a href="#Type_parameter_declarations">type parameters</a>.
|
||||
</li>
|
||||
<li>
|
||||
Interface types may <a href="#General_interfaces">embed arbitrary types</a> (not just type names of interfaces)
|
||||
as well as union and <code>~T</code> type elements.
|
||||
</li>
|
||||
<li>
|
||||
The set of <a href="#Predeclared_identifiers">predeclared</a> types includes the new types
|
||||
<code>any</code> and <code>comparable</code>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="Go_1.20">Go 1.20</h4>
|
||||
<ul>
|
||||
<li>
|
||||
A slice may be <a href="#Conversions">converted</a> to an array if the slice and array element
|
||||
types match and the array is not longer than the slice.
|
||||
</li>
|
||||
<li>
|
||||
The built-in <a href="#Package_unsafe">package <code>unsafe</code></a> includes the new functions
|
||||
<code>SliceData</code>, <code>String</code>, and <code>StringData</code>.
|
||||
</li>
|
||||
<li>
|
||||
<a href="#Comparison_operators">Comparable types</a> (such as ordinary interfaces) may satisfy
|
||||
<code>comparable</code> constraints, even if the type arguments are not strictly comparable.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="Go_1.21">Go 1.21</h4>
|
||||
<ul>
|
||||
<li>
|
||||
The set of <a href="#Predeclared_identifiers">predeclared</a> functions includes the new functions
|
||||
<code>min</code>, <code>max</code>, and <code>clear</code>.
|
||||
</li>
|
||||
<li>
|
||||
<a href="#Type_inference">Type inference</a> 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.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="Go_1.22">Go 1.22</h4>
|
||||
<ul>
|
||||
<li>
|
||||
In a <a href="#For_statements">"for" statement</a>, each iteration has its own set of iteration
|
||||
variables rather than sharing the same variables in each iteration.
|
||||
</li>
|
||||
<li>
|
||||
A "for" statement with <a href="#For_range">"range" clause</a> may iterate over
|
||||
integer values from zero to an upper limit.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="Type_unification_rules">Type unification rules</h3>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
package p
|
||||
|
||||
type BasicAlias = uint8
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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."`
|
||||
|
|
|
|||
|
|
@ -204,7 +204,6 @@ func NewHashDebug(ev, s string, file io.Writer) *HashDebug {
|
|||
i++
|
||||
}
|
||||
return hd
|
||||
|
||||
}
|
||||
|
||||
// TODO: Delete when we switch to bisect-only.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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] &&
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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 := <expr>").
|
||||
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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ func inline(j, k int) []*int {
|
|||
a = append(a, &private)
|
||||
}
|
||||
return a
|
||||
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue