Merge branch 'master' into hotfix/net-http-fileserver-dir-file

This commit is contained in:
Mauri de Souza Meneguzzo 2024-01-10 10:32:34 -03:00
commit 754c9a1167
780 changed files with 27628 additions and 7495 deletions

View File

@ -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?

94
.github/ISSUE_TEMPLATE/00-bug.yml vendored Normal file
View File

@ -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

View File

@ -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?

47
.github/ISSUE_TEMPLATE/01-pkgsite.yml vendored Normal file
View File

@ -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

View File

@ -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.
--->

View File

@ -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

View File

@ -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
-->

56
.github/ISSUE_TEMPLATE/03-gopls.yml vendored Normal file
View File

@ -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

View File

@ -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?

52
.github/ISSUE_TEMPLATE/04-vuln.yml vendored Normal file
View File

@ -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

View File

@ -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
-->

15
.github/ISSUE_TEMPLATE/10-proposal.yml vendored Normal file
View File

@ -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

View File

@ -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.)**

View File

@ -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

View File

@ -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:

View File

@ -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

144
api/go1.22.txt Normal file
View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@
pkg math/big, method (*Rat) FloatPrec() (int, bool) #50489

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@
pkg archive/zip, method (*Writer) AddFS(fs.FS) error #54898

View File

@ -1 +0,0 @@
pkg slices, func Concat[$0 interface{ ~[]$1 }, $1 interface{}](...$0) $0 #56353

View File

@ -1 +0,0 @@
pkg crypto/x509, method (*CertPool) AddCertWithConstraint(*Certificate, func([]*Certificate) error) #57178

View File

@ -1 +0,0 @@
pkg archive/tar, method (*Writer) AddFS(fs.FS) error #58000

View File

@ -1,2 +0,0 @@
pkg net, method (*TCPConn) WriteTo(io.Writer) (int64, error) #58808
pkg os, method (*File) WriteTo(io.Writer) (int64, error) #58808

View File

@ -1 +0,0 @@
pkg reflect, func PtrTo //deprecated #59599

View File

@ -1 +0,0 @@
pkg go/ast, func Unparen(Expr) Expr #60061

View File

@ -1 +0,0 @@
pkg reflect, func TypeFor[$0 interface{}]() Type #60088

View File

@ -1 +0,0 @@
pkg cmp, func Or[$0 comparable](...$0) $0 #60204

View File

@ -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

View File

@ -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

View File

@ -1,2 +0,0 @@
pkg net/http, method (*Request) PathValue(string) string #61410
pkg net/http, method (*Request) SetPathValue(string, string) #61410

View File

@ -1 +0,0 @@
pkg html/template, const ErrJSTemplate //deprecated #61619

View File

@ -1,2 +0,0 @@
pkg net/netip, method (AddrPort) Compare(AddrPort) int #61642
pkg net/netip, method (Prefix) Compare(Prefix) int #61642

View File

@ -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

View File

@ -1 +0,0 @@
pkg testing/slogtest, func Run(*testing.T, func(*testing.T) slog.Handler, func(*testing.T) map[string]interface{}) #61758

View File

@ -1 +0,0 @@
pkg io, method (*SectionReader) Outer() (ReaderAt, int64, int64) #61870

View File

@ -1,2 +0,0 @@
pkg debug/elf, const R_MIPS_PC32 = 248 #61974
pkg debug/elf, const R_MIPS_PC32 R_MIPS #61974

View File

@ -1,2 +0,0 @@
pkg go/types, method (*Info) PkgNameOf(*ast.ImportSpec) *PkgName #62037
pkg go/types, method (Checker) PkgNameOf(*ast.ImportSpec) *PkgName #62037

View File

@ -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

View File

@ -1 +0,0 @@
pkg log/slog, func SetLogLoggerLevel(Level) Level #62418

View File

@ -1 +0,0 @@
pkg go/types, type Info struct, FileVersions map[*ast.File]string #62605

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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&ndash;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&mdash;patterns with "{" and "}" behave differently&mdash;
and some less so&mdash;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>

View File

@ -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">
+ &amp; += &amp;= &amp;&amp; == != ( )
@ -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&mdash;or largest, respectively&mdash;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(&amp;f))
type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&amp;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>

View File

@ -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,

View File

@ -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.

View File

@ -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"

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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")

View File

@ -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)

View File

@ -0,0 +1,3 @@
package p
type BasicAlias = uint8

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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]"

View File

@ -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

View File

@ -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) {

View File

@ -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.

View 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.

View 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.

View 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)

View File

@ -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)

View File

@ -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 Gos 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.

View File

@ -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

View File

@ -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

View File

@ -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."`

View File

@ -204,7 +204,6 @@ func NewHashDebug(ev, s string, file io.Writer) *HashDebug {
i++
}
return hd
}
// TODO: Delete when we switch to bisect-only.

View File

@ -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.

View File

@ -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)
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -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] &&

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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()

View File

@ -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)
}
})
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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