mirror of https://github.com/golang/go.git
Merge remote-tracking branch 'origin/master' into feature/atomic-and-or
Change-Id: I4fa61dfec1b161a8b6820bd739928f688576d2f5
This commit is contained in:
commit
f174297007
|
|
@ -1 +1,2 @@
|
|||
pkg runtime/debug, func SetCrashOutput(*os.File) error #42888
|
||||
pkg runtime/debug, type CrashOptions struct #67182
|
||||
pkg runtime/debug, func SetCrashOutput(*os.File, CrashOptions) error #42888
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
pkg crypto/x509, func ParseOID(string) (OID, error) #66249
|
||||
pkg crypto/x509, method (*OID) UnmarshalBinary([]uint8) error #66249
|
||||
pkg crypto/x509, method (*OID) UnmarshalText([]uint8) error #66249
|
||||
pkg crypto/x509, method (OID) MarshalBinary() ([]uint8, error) #66249
|
||||
pkg crypto/x509, method (OID) MarshalText() ([]uint8, error) #66249
|
||||
|
|
@ -0,0 +1 @@
|
|||
pkg go/ast, func Preorder(Node) iter.Seq[Node] #66339
|
||||
|
|
@ -0,0 +1 @@
|
|||
pkg net/http, type Request struct, Pattern string #66405
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
pkg go/types, method (*Alias) Origin() *Alias #67143
|
||||
pkg go/types, method (*Alias) SetTypeParams([]*TypeParam) #67143
|
||||
pkg go/types, method (*Alias) TypeArgs() *TypeList #67143
|
||||
pkg go/types, method (*Alias) TypeParams() *TypeParamList #67143
|
||||
|
|
@ -27,22 +27,39 @@ should have a corresponding file named `doc/next/6-stdlib/99-minor/net/http/1234
|
|||
At a minimum, that file should contain either a full sentence or a TODO,
|
||||
ideally referring to a person with the responsibility to complete the note.
|
||||
|
||||
If your CL addresses an accepted proposal, mention the proposal issue number in
|
||||
your release note in the form `/issue/NUMBER`. A link to the issue in the text
|
||||
will have this form (see below). If you don't want to mention the issue in the
|
||||
text, add it as a comment:
|
||||
```
|
||||
<!-- go.dev/issue/12345 -->
|
||||
```
|
||||
If an accepted proposal is mentioned in a CL but not in the release notes, it will be
|
||||
flagged as a TODO by the automated tooling. That is true even for proposals that add API.
|
||||
|
||||
Use the following forms in your markdown:
|
||||
|
||||
[http.Request] # symbol documentation; auto-linked as in Go doc strings
|
||||
[Request] # short form, for symbols in the package being documented
|
||||
[#12345](/issue/12345) # GitHub issues
|
||||
[CL 6789](/cl/6789) # Gerrit changelists
|
||||
|
||||
## For the release team
|
||||
|
||||
At the start of a release development cycle, the contents of `next` should be deleted
|
||||
and replaced with those of `initial`. From the repo root:
|
||||
The `relnote` tool, at `golang.org/x/build/cmd/relnote`, operates on the files
|
||||
in `doc/next`.
|
||||
|
||||
As a release cycle nears completion, run `relnote todo` to get a list of
|
||||
unfinished release note work.
|
||||
|
||||
To prepare the release notes for a release, run `relnote generate`.
|
||||
That will merge the `.md` files in `next` into a single file.
|
||||
|
||||
To begin the next release development cycle, delete the contents of `next`
|
||||
and replace them with those of `initial`. From the repo root:
|
||||
|
||||
> cd doc
|
||||
> rm -r next/*
|
||||
> cp -r initial/* next
|
||||
|
||||
Then edit `next/1-intro.md` to refer to the next version.
|
||||
|
||||
To prepare the release notes for a release, run `golang.org/x/build/cmd/relnote generate`.
|
||||
That will merge the `.md` files in `next` into a single file.
|
||||
|
|
|
|||
|
|
@ -88,14 +88,38 @@ Because this method of setting GODEBUG defaults was introduced only in Go 1.21,
|
|||
programs listing versions of Go earlier than Go 1.20 are configured to match Go 1.20,
|
||||
not the older version.
|
||||
|
||||
To override these defaults, a main package's source files
|
||||
To override these defaults, starting in Go 1.23, the work module's `go.mod`
|
||||
or the workspace's `go.work` can list one or more `godebug` lines:
|
||||
|
||||
godebug (
|
||||
default=go1.21
|
||||
panicnil=1
|
||||
asynctimerchan=0
|
||||
)
|
||||
|
||||
The special key `default` indicates a Go version to take unspecified
|
||||
settings from. This allows setting the GODEBUG defaults separately
|
||||
from the Go language version in the module.
|
||||
In this example, the program is asking for Go 1.21 semantics and
|
||||
then asking for the old pre-Go 1.21 `panic(nil)` behavior and the
|
||||
new Go 1.23 `asynctimerchan=0` behavior.
|
||||
|
||||
Only the work module's `go.mod` is consulted for `godebug` directives.
|
||||
Any directives in required dependency modules are ignored.
|
||||
It is an error to list a `godebug` with an unrecognized setting.
|
||||
(Toolchains older than Go 1.23 reject all `godebug` lines, since they do not
|
||||
understand `godebug` at all.)
|
||||
|
||||
The defaults from the `go` and `godebug` lines apply to all main
|
||||
packages that are built. For more fine-grained control,
|
||||
starting in Go 1.21, a main package's source files
|
||||
can include one or more `//go:debug` directives at the top of the file
|
||||
(preceding the `package` statement).
|
||||
Continuing the `panicnil` example, if the module or workspace is updated
|
||||
to say `go` `1.21`, the program can opt back into the old `panic(nil)`
|
||||
behavior by including this directive:
|
||||
The `godebug` lines in the previous example would be written:
|
||||
|
||||
//go:debug default=go1.21
|
||||
//go:debug panicnil=1
|
||||
//go:debug asynctimerchan=0
|
||||
|
||||
Starting in Go 1.21, the Go toolchain treats a `//go:debug` directive
|
||||
with an unrecognized GODEBUG setting as an invalid program.
|
||||
|
|
@ -175,7 +199,7 @@ Whether the type checker produces `Alias` types or not is controlled by the
|
|||
[`gotypesalias` setting](/pkg/go/types#Alias).
|
||||
For Go 1.22 it defaults to `gotypesalias=0`.
|
||||
For Go 1.23, `gotypesalias=1` will become the default.
|
||||
This setting will be removed in a future release, Go 1.24 at the earliest.
|
||||
This setting will be removed in a future release, Go 1.27 at the earliest.
|
||||
|
||||
Go 1.22 changed the default minimum TLS version supported by both servers
|
||||
and clients to TLS 1.2. The default can be reverted to TLS 1.0 using the
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ Distributions that install the `go` command to a location other than
|
|||
`$GOROOT/bin/go` should install a symlink instead of relocating
|
||||
or copying the `go` binary.
|
||||
|
||||
The new go env `-changed` flag causes the command to print only
|
||||
those settings whose effective value differs from the default value
|
||||
that would be obtained in an empty environment with no prior uses of the `-w` flag.
|
||||
|
||||
### Vet {#vet}
|
||||
|
||||
The `go vet` subcommand now includes the
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ The build time overhead to building with [Profile Guided Optimization](/doc/pgo)
|
|||
Previously, large builds could see 100%+ build time increase from enabling PGO.
|
||||
In Go 1.23, overhead should be in the single digit percentages.
|
||||
|
||||
<!-- https://go.dev/issue/62737 , https://golang.org/cl/576681, https://golang.org/cl/577615 -->
|
||||
The compiler in Go 1.23 can now overlap the stack frame slots of local variables
|
||||
accessed in disjoint regions of a function, which reduces stack usage
|
||||
for Go applications.
|
||||
|
||||
## Assembler {#assembler}
|
||||
|
||||
## Linker {#linker}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
The new [ParseOID] function parses a dot-encoded ASN.1 Object Identifier string.
|
||||
The [OID] type now implements the [encoding.BinaryMarshaler],
|
||||
[encoding.BinaryUnmarshaler], [encoding.TextMarshaler], [encoding.TextUnmarshaler] interfaces.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
The new [Preorder] function returns a convenient iterator over all the
|
||||
nodes of a syntax tree.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
The methods [Alias.Origin], [Alias.SetTypeParams], [Alias.TypeParams],
|
||||
and [Alias.TypeArgs] have been added. They are needed for generic alias types.
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
The patterns used by [ServeMux] allow multiple spaces matching
|
||||
regexp '[ \t]+'.
|
||||
The patterns used by [ServeMux] now allow one or more spaces or tabs after the method name.
|
||||
Previously, only a single space was permitted.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
For inbound requests, the new [Request.Pattern] field contains the [ServeMux]
|
||||
pattern (if any) that matched the request. This field is not set when
|
||||
`GODEBUG=httpmuxgo121=1` is set.
|
||||
|
|
@ -0,0 +1 @@
|
|||
<!-- pacify TestCheckAPIFragments -->
|
||||
|
|
@ -699,9 +699,13 @@ func findSignatureInBlock(b []byte) int {
|
|||
if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 {
|
||||
// n is length of comment
|
||||
n := int(b[i+directoryEndLen-2]) | int(b[i+directoryEndLen-1])<<8
|
||||
if n+directoryEndLen+i <= len(b) {
|
||||
return i
|
||||
if n+directoryEndLen+i > len(b) {
|
||||
// Truncated comment.
|
||||
// Some parsers (such as Info-ZIP) ignore the truncated comment
|
||||
// rather than treating it as a hard error.
|
||||
return -1
|
||||
}
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
|
|
|
|||
|
|
@ -570,6 +570,14 @@ var tests = []ZipTest{
|
|||
},
|
||||
},
|
||||
},
|
||||
// Issue 66869: Don't skip over an EOCDR with a truncated comment.
|
||||
// The test file sneakily hides a second EOCDR before the first one;
|
||||
// previously we would extract one file ("file") from this archive,
|
||||
// while most other tools would reject the file or extract a different one ("FILE").
|
||||
{
|
||||
Name: "comment-truncated.zip",
|
||||
Error: ErrFormat,
|
||||
},
|
||||
}
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -28,6 +28,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"cmd/internal/objfile"
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
func printUsage(w *os.File) {
|
||||
|
|
@ -45,6 +46,7 @@ func usage() {
|
|||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("addr2line: ")
|
||||
telemetry.Start()
|
||||
|
||||
// pprof expects this behavior when checking for addr2line
|
||||
if len(os.Args) > 1 && os.Args[1] == "--help" {
|
||||
|
|
@ -54,6 +56,8 @@ func main() {
|
|||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
telemetry.Inc("addr2line/invocations")
|
||||
telemetry.CountFlags("addr2line/flag:", *flag.CommandLine)
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,13 +260,32 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
|
|||
XORIS $15, R3, R4 // 6c64000f
|
||||
XOR $983040, R3, R4 // 6c64000f
|
||||
|
||||
// TODO: the order of CR operands don't match
|
||||
// TODO: cleanup inconsistency of printing CMPx opcodes with explicit CR arguments.
|
||||
CMP R3, R4 // 7c232000
|
||||
CMP R3, R0 // 7c230000
|
||||
CMP R3, R0, CR1 // CMP R3,CR1,R0 // 7ca30000
|
||||
CMPU R3, R4 // 7c232040
|
||||
CMPU R3, R0 // 7c230040
|
||||
CMPU R3, R0, CR2 // CMPU R3,CR2,R0 // 7d230040
|
||||
CMPW R3, R4 // 7c032000
|
||||
CMPW R3, R0 // 7c030000
|
||||
CMPW R3, R0, CR3 // CMPW R3,CR3,R0 // 7d830000
|
||||
CMPWU R3, R4 // 7c032040
|
||||
CMPB R3,R4,R4 // 7c6423f8
|
||||
CMPWU R3, R0 // 7c030040
|
||||
CMPWU R3, R0, CR4 // CMPWU R3,CR4,R0 // 7e030040
|
||||
CMP R3, $0 // 2c230000
|
||||
CMPU R3, $0 // 28230000
|
||||
CMPW R3, $0 // 2c030000
|
||||
CMPWU R3, $0 // 28030000
|
||||
CMP R3, $0, CR0 // CMP R3,CR0,$0 // 2c230000
|
||||
CMPU R3, $0, CR1 // CMPU R3,CR1,$0 // 28a30000
|
||||
CMPW R3, $0, CR2 // CMPW R3,CR2,$0 // 2d030000
|
||||
CMPW R3, $-32768, CR2 // CMPW R3,CR2,$-32768 // 2d038000
|
||||
CMPWU R3, $0, CR3 // CMPWU R3,CR3,$0 // 29830000
|
||||
CMPWU R3, $0x8008, CR3 // CMPWU R3,CR3,$32776 // 29838008
|
||||
|
||||
CMPEQB R3,R4,CR6 // 7f0321c0
|
||||
CMPB R3,R4,R4 // 7c6423f8
|
||||
|
||||
ADD R3, R4 // 7c841a14
|
||||
ADD R3, R4, R5 // 7ca41a14
|
||||
|
|
|
|||
|
|
@ -20,16 +20,20 @@ import (
|
|||
"cmd/internal/bio"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("asm: ")
|
||||
telemetry.Start()
|
||||
|
||||
buildcfg.Check()
|
||||
GOARCH := buildcfg.GOARCH
|
||||
|
||||
flags.Parse()
|
||||
telemetry.Inc("asm/invocations")
|
||||
telemetry.CountFlags("asm/flag:", *flag.CommandLine)
|
||||
|
||||
architecture := arch.Set(GOARCH, *flags.Shared || *flags.Dynlink)
|
||||
if architecture == nil {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"cmd/internal/buildid"
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
|
|
@ -25,8 +26,11 @@ var wflag = flag.Bool("w", false, "write build ID")
|
|||
func main() {
|
||||
log.SetPrefix("buildid: ")
|
||||
log.SetFlags(0)
|
||||
telemetry.Start()
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
telemetry.Inc("buildid/invocations")
|
||||
telemetry.CountFlags("buildid/flag:", *flag.CommandLine)
|
||||
if flag.NArg() != 1 {
|
||||
usage()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -529,6 +529,9 @@ The following options are available when running cgo directly:
|
|||
Write out input file in Go syntax replacing C package
|
||||
names with real values. Used to generate files in the
|
||||
syscall package when bootstrapping a new target.
|
||||
-ldflags flags
|
||||
Flags to pass to the C linker. The cmd/go tool uses
|
||||
this to pass in the flags in the CGO_LDFLAGS variable.
|
||||
-objdir directory
|
||||
Put all generated files in directory.
|
||||
-srcdir directory
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"cmd/internal/edit"
|
||||
"cmd/internal/notsha256"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
// A Package collects information about the package we're going to write.
|
||||
|
|
@ -242,6 +243,8 @@ var objDir = flag.String("objdir", "", "object directory")
|
|||
var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
|
||||
var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
|
||||
|
||||
var ldflags = flag.String("ldflags", "", "flags to pass to C linker")
|
||||
|
||||
var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
|
||||
var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
|
||||
var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
|
||||
|
|
@ -255,8 +258,11 @@ var goarch, goos, gomips, gomips64 string
|
|||
var gccBaseCmd []string
|
||||
|
||||
func main() {
|
||||
telemetry.Start()
|
||||
objabi.AddVersionFlag() // -V
|
||||
objabi.Flagparse(usage)
|
||||
telemetry.Inc("cgo/invocations")
|
||||
telemetry.CountFlags("cgo/flag:", *flag.CommandLine)
|
||||
|
||||
if *gccgoDefineCgoIncomplete {
|
||||
if !*gccgo {
|
||||
|
|
@ -328,11 +334,11 @@ func main() {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Record CGO_LDFLAGS from the environment for external linking.
|
||||
if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
|
||||
args, err := splitQuoted(ldflags)
|
||||
// Record linker flags for external linking.
|
||||
if *ldflags != "" {
|
||||
args, err := splitQuoted(*ldflags)
|
||||
if err != nil {
|
||||
fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
|
||||
fatalf("bad -ldflags option: %q (%s)", *ldflags, err)
|
||||
}
|
||||
p.addToFlag("LDFLAGS", args)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ var Debug DebugFlags
|
|||
// The -d option takes a comma-separated list of settings.
|
||||
// Each setting is name=value; for ints, name is short for name=1.
|
||||
type DebugFlags struct {
|
||||
AlignHot int `help:"enable hot block alignment (currently requires -pgo)" concurrent:"ok"`
|
||||
Append int `help:"print information about append compilation"`
|
||||
Checkptr int `help:"instrument unsafe pointer conversions\n0: instrumentation disabled\n1: conversions involving unsafe.Pointer are instrumented\n2: conversions to unsafe.Pointer force heap allocation" concurrent:"ok"`
|
||||
Closure int `help:"print information about closure compilation"`
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ func ParseFlags() {
|
|||
|
||||
Debug.ConcurrentOk = true
|
||||
Debug.MaxShapeLen = 500
|
||||
Debug.AlignHot = 1
|
||||
Debug.InlFuncsWithClosures = 1
|
||||
Debug.InlStaticInit = 1
|
||||
Debug.PGOInline = 1
|
||||
|
|
@ -212,6 +213,8 @@ func ParseFlags() {
|
|||
Flag.CompilingRuntime = true
|
||||
}
|
||||
|
||||
Ctxt.Std = Flag.Std
|
||||
|
||||
// Three inputs govern loop iteration variable rewriting, hash, experiment, flag.
|
||||
// The loop variable rewriting is:
|
||||
// IF non-empty hash, then hash determines behavior (function+line match) (*)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/liveness"
|
||||
"cmd/compile/internal/objw"
|
||||
"cmd/compile/internal/pgoir"
|
||||
"cmd/compile/internal/ssagen"
|
||||
"cmd/compile/internal/staticinit"
|
||||
"cmd/compile/internal/types"
|
||||
|
|
@ -112,7 +113,7 @@ func prepareFunc(fn *ir.Func) {
|
|||
// compileFunctions compiles all functions in compilequeue.
|
||||
// It fans out nBackendWorkers to do the work
|
||||
// and waits for them to complete.
|
||||
func compileFunctions() {
|
||||
func compileFunctions(profile *pgoir.Profile) {
|
||||
if race.Enabled {
|
||||
// Randomize compilation order to try to shake out races.
|
||||
tmp := make([]*ir.Func, len(compilequeue))
|
||||
|
|
@ -179,7 +180,7 @@ func compileFunctions() {
|
|||
for _, fn := range fns {
|
||||
fn := fn
|
||||
queue(func(worker int) {
|
||||
ssagen.Compile(fn, worker)
|
||||
ssagen.Compile(fn, worker, profile)
|
||||
compile(fn.Closures)
|
||||
wg.Done()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
|
|||
// as late as possible to maximize how much work we can batch and
|
||||
// process concurrently.
|
||||
if len(compilequeue) != 0 {
|
||||
compileFunctions()
|
||||
compileFunctions(profile)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ var (
|
|||
// TODO(prattmic): Make this non-global.
|
||||
candHotCalleeMap = make(map[*pgoir.IRNode]struct{})
|
||||
|
||||
// Set of functions that contain hot call sites.
|
||||
hasHotCall = make(map[*ir.Func]struct{})
|
||||
|
||||
// List of all hot call sites. CallSiteInfo.Callee is always nil.
|
||||
// TODO(prattmic): Make this non-global.
|
||||
candHotEdgeMap = make(map[pgoir.CallSiteInfo]struct{})
|
||||
|
|
@ -78,6 +81,22 @@ var (
|
|||
inlineHotMaxBudget int32 = 2000
|
||||
)
|
||||
|
||||
func IsPgoHotFunc(fn *ir.Func, profile *pgoir.Profile) bool {
|
||||
if profile == nil {
|
||||
return false
|
||||
}
|
||||
if n, ok := profile.WeightedCG.IRNodes[ir.LinkFuncName(fn)]; ok {
|
||||
_, ok := candHotCalleeMap[n]
|
||||
return ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func HasPgoHotInline(fn *ir.Func) bool {
|
||||
_, has := hasHotCall[fn]
|
||||
return has
|
||||
}
|
||||
|
||||
// PGOInlinePrologue records the hot callsites from ir-graph.
|
||||
func PGOInlinePrologue(p *pgoir.Profile) {
|
||||
if base.Debug.PGOInlineCDFThreshold != "" {
|
||||
|
|
@ -228,14 +247,10 @@ func GarbageCollectUnreferencedHiddenClosures() {
|
|||
func inlineBudget(fn *ir.Func, profile *pgoir.Profile, relaxed bool, verbose bool) int32 {
|
||||
// Update the budget for profile-guided inlining.
|
||||
budget := int32(inlineMaxBudget)
|
||||
if profile != nil {
|
||||
if n, ok := profile.WeightedCG.IRNodes[ir.LinkFuncName(fn)]; ok {
|
||||
if _, ok := candHotCalleeMap[n]; ok {
|
||||
budget = inlineHotMaxBudget
|
||||
if verbose {
|
||||
fmt.Printf("hot-node enabled increased budget=%v for func=%v\n", budget, ir.PkgFuncName(fn))
|
||||
}
|
||||
}
|
||||
if IsPgoHotFunc(fn, profile) {
|
||||
budget = inlineHotMaxBudget
|
||||
if verbose {
|
||||
fmt.Printf("hot-node enabled increased budget=%v for func=%v\n", budget, ir.PkgFuncName(fn))
|
||||
}
|
||||
}
|
||||
if relaxed {
|
||||
|
|
@ -580,7 +595,7 @@ opSwitch:
|
|||
// Check whether we'd actually inline this call. Set
|
||||
// log == false since we aren't actually doing inlining
|
||||
// yet.
|
||||
if ok, _ := canInlineCallExpr(v.curFunc, n, callee, v.isBigFunc, false); ok {
|
||||
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
|
||||
|
|
@ -873,10 +888,11 @@ 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.
|
||||
//
|
||||
// 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) {
|
||||
// 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)
|
||||
// - the score assigned to this specific callsite
|
||||
// - whether the inlined function is "hot" according to PGO.
|
||||
func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool, int32, int32, bool) {
|
||||
maxCost := int32(inlineMaxBudget)
|
||||
if bigCaller {
|
||||
// We use this to restrict inlining into very big functions.
|
||||
|
|
@ -892,19 +908,21 @@ func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool
|
|||
}
|
||||
}
|
||||
|
||||
lineOffset := pgoir.NodeLineOffset(n, caller)
|
||||
csi := pgoir.CallSiteInfo{LineOffset: lineOffset, Caller: caller}
|
||||
_, hot := candHotEdgeMap[csi]
|
||||
|
||||
if metric <= maxCost {
|
||||
// Simple case. Function is already cheap enough.
|
||||
return true, 0, metric
|
||||
return true, 0, metric, hot
|
||||
}
|
||||
|
||||
// We'll also allow inlining of hot functions below inlineHotMaxBudget,
|
||||
// but only in small functions.
|
||||
|
||||
lineOffset := pgoir.NodeLineOffset(n, caller)
|
||||
csi := pgoir.CallSiteInfo{LineOffset: lineOffset, Caller: caller}
|
||||
if _, ok := candHotEdgeMap[csi]; !ok {
|
||||
if !hot {
|
||||
// Cold
|
||||
return false, maxCost, metric
|
||||
return false, maxCost, metric, false
|
||||
}
|
||||
|
||||
// Hot
|
||||
|
|
@ -913,49 +931,50 @@ 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, metric
|
||||
return false, maxCost, metric, false
|
||||
}
|
||||
|
||||
if metric > inlineHotMaxBudget {
|
||||
return false, inlineHotMaxBudget, metric
|
||||
return false, inlineHotMaxBudget, metric, false
|
||||
}
|
||||
|
||||
if !base.PGOHash.MatchPosWithInfo(n.Pos(), "inline", nil) {
|
||||
// De-selected by PGO Hash.
|
||||
return false, maxCost, metric
|
||||
return false, maxCost, metric, false
|
||||
}
|
||||
|
||||
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, metric
|
||||
return true, 0, metric, hot
|
||||
}
|
||||
|
||||
// canInlineCallExpr 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
|
||||
// can be inlined, plus the score computed for the call expr in question,
|
||||
// and whether the callee is hot according to PGO.
|
||||
// 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, int32) {
|
||||
func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCaller bool, log bool) (bool, int32, bool) {
|
||||
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, 0
|
||||
return false, 0, false
|
||||
}
|
||||
|
||||
ok, maxCost, callSiteScore := inlineCostOK(n, callerfn, callee, bigCaller)
|
||||
ok, maxCost, callSiteScore, hot := 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, 0
|
||||
return false, 0, false
|
||||
}
|
||||
|
||||
if callee == callerfn {
|
||||
|
|
@ -963,7 +982,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, 0
|
||||
return false, 0, false
|
||||
}
|
||||
|
||||
if base.Flag.Cfg.Instrumenting && types.IsNoInstrumentPkg(callee.Sym().Pkg) {
|
||||
|
|
@ -977,7 +996,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, 0
|
||||
return false, 0, false
|
||||
}
|
||||
|
||||
if base.Flag.Race && types.IsNoRacePkg(callee.Sym().Pkg) {
|
||||
|
|
@ -985,7 +1004,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, 0
|
||||
return false, 0, false
|
||||
}
|
||||
|
||||
// Check if we've already inlined this function at this particular
|
||||
|
|
@ -1008,11 +1027,11 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa
|
|||
fmt.Sprintf("repeated recursive cycle to %s", ir.PkgFuncName(callee)))
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
return false, 0, false
|
||||
}
|
||||
}
|
||||
|
||||
return true, callSiteScore
|
||||
return true, callSiteScore, hot
|
||||
}
|
||||
|
||||
// mkinlcall returns an OINLCALL node that can replace OCALLFUNC n, or
|
||||
|
|
@ -1023,10 +1042,13 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa
|
|||
//
|
||||
// n.Left = mkinlcall(n.Left, fn, isddd)
|
||||
func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool) *ir.InlinedCallExpr {
|
||||
ok, score := canInlineCallExpr(callerfn, n, fn, bigCaller, true)
|
||||
ok, score, hot := canInlineCallExpr(callerfn, n, fn, bigCaller, true)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if hot {
|
||||
hasHotCall[callerfn] = struct{}{}
|
||||
}
|
||||
typecheck.AssertFixedCall(n)
|
||||
|
||||
parent := base.Ctxt.PosTable.Pos(n.Pos()).Base().InliningIndex()
|
||||
|
|
|
|||
|
|
@ -1551,6 +1551,7 @@ func WriteFuncMap(fn *ir.Func, abiInfo *abi.ABIParamResultInfo) {
|
|||
nbitmap = 2
|
||||
}
|
||||
lsym := base.Ctxt.Lookup(fn.LSym.Name + ".args_stackmap")
|
||||
lsym.Set(obj.AttrLinkname, true) // allow args_stackmap referenced from assembly
|
||||
off := objw.Uint32(lsym, 0, uint32(nbitmap))
|
||||
off = objw.Uint32(lsym, off, uint32(bv.N))
|
||||
off = objw.BitVec(lsym, off, bv)
|
||||
|
|
|
|||
|
|
@ -49,9 +49,7 @@ func checkFiles(m posMap, noders []*noder) (*types2.Package, *types2.Info) {
|
|||
IgnoreBranchErrors: true, // parser already checked via syntax.CheckBranches mode
|
||||
Importer: &importer,
|
||||
Sizes: types2.SizesFor("gc", buildcfg.GOARCH),
|
||||
// Currently, the compiler panics when using Alias types.
|
||||
// TODO(gri) set to true once this is fixed (issue #66873)
|
||||
EnableAlias: false,
|
||||
EnableAlias: true,
|
||||
}
|
||||
if base.Flag.ErrorURL {
|
||||
conf.ErrorURL = " [go.dev/e/%s]"
|
||||
|
|
|
|||
|
|
@ -427,7 +427,9 @@ func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict, wrapped bool) *type
|
|||
r.dict = dict
|
||||
|
||||
typ := r.doTyp()
|
||||
assert(typ != nil)
|
||||
if typ == nil {
|
||||
base.Fatalf("doTyp returned nil for info=%v", info)
|
||||
}
|
||||
|
||||
// For recursive type declarations involving interfaces and aliases,
|
||||
// above r.doTyp() call may have already set pr.typs[idx], so just
|
||||
|
|
@ -741,7 +743,26 @@ func (pr *pkgReader) objIdxMayFail(idx pkgbits.Index, implicits, explicits []*ty
|
|||
|
||||
case pkgbits.ObjAlias:
|
||||
name := do(ir.OTYPE, false)
|
||||
setType(name, r.typ())
|
||||
|
||||
// Clumsy dance: the r.typ() call here might recursively find this
|
||||
// type alias name, before we've set its type (#66873). So we
|
||||
// temporarily clear sym.Def and then restore it later, if still
|
||||
// unset.
|
||||
hack := sym.Def == name
|
||||
if hack {
|
||||
sym.Def = nil
|
||||
}
|
||||
typ := r.typ()
|
||||
if hack {
|
||||
if sym.Def != nil {
|
||||
name = sym.Def.(*ir.Name)
|
||||
assert(name.Type() == typ)
|
||||
return name, nil
|
||||
}
|
||||
sym.Def = name
|
||||
}
|
||||
|
||||
setType(name, typ)
|
||||
name.SetAlias(true)
|
||||
return name, nil
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"internal/buildcfg"
|
||||
"internal/pkgbits"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/ir"
|
||||
|
|
@ -488,6 +489,18 @@ func (w *writer) typInfo(info typeInfo) {
|
|||
// typIdx also reports whether typ is a derived type; that is, whether
|
||||
// its identity depends on type parameters.
|
||||
func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
|
||||
// Strip non-global aliases, because they only appear in inline
|
||||
// bodies anyway. Otherwise, they can cause types.Sym collisions
|
||||
// (e.g., "main.C" for both of the local type aliases in
|
||||
// test/fixedbugs/issue50190.go).
|
||||
for {
|
||||
if alias, ok := typ.(*types2.Alias); ok && !isGlobal(alias.Obj()) {
|
||||
typ = alias.Rhs()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if idx, ok := pw.typsIdx[typ]; ok {
|
||||
return typeInfo{idx: idx, derived: false}
|
||||
}
|
||||
|
|
@ -2598,6 +2611,10 @@ func (pw *pkgWriter) collectDecls(noders []*noder) {
|
|||
pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
|
||||
continue
|
||||
}
|
||||
if strings.Contains(l.remote, "[") && strings.Contains(l.remote, "]") {
|
||||
pw.errorf(l.pos, "//go:linkname reference of an instantiation is not allowed")
|
||||
continue
|
||||
}
|
||||
|
||||
switch obj := pw.curpkg.Scope().Lookup(l.local).(type) {
|
||||
case *types2.Func, *types2.Var:
|
||||
|
|
|
|||
|
|
@ -2004,8 +2004,8 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
|
|||
p := s.Prog(ppc64.ACMP)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = ppc64.REG_R3
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = ppc64.REG_R0
|
||||
p.To.Type = obj.TYPE_CONST
|
||||
p.To.Offset = 0
|
||||
|
||||
p = s.Prog(ppc64.ABNE)
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
|
|
|
|||
|
|
@ -645,6 +645,19 @@
|
|||
(MOVBreg (MOVBZreg x)) => (MOVBreg x)
|
||||
(MOVBZreg (MOVBreg x)) => (MOVBZreg x)
|
||||
|
||||
// Catch any remaining rotate+shift cases
|
||||
(MOVBZreg (SRWconst x [s])) && mergePPC64AndSrwi(0xFF,s) != 0 => (RLWINM [mergePPC64AndSrwi(0xFF,s)] x)
|
||||
(MOVBZreg (RLWINM [r] y)) && mergePPC64AndRlwinm(0xFF,r) != 0 => (RLWINM [mergePPC64AndRlwinm(0xFF,r)] y)
|
||||
(MOVHZreg (RLWINM [r] y)) && mergePPC64AndRlwinm(0xFFFF,r) != 0 => (RLWINM [mergePPC64AndRlwinm(0xFFFF,r)] y)
|
||||
(MOVWZreg (RLWINM [r] y)) && mergePPC64AndRlwinm(0xFFFFFFFF,r) != 0 => (RLWINM [mergePPC64AndRlwinm(0xFFFFFFFF,r)] y)
|
||||
(Select0 (ANDCCconst [m] (RLWINM [r] y))) && mergePPC64AndRlwinm(uint32(m),r) != 0 => (RLWINM [mergePPC64AndRlwinm(uint32(m),r)] y)
|
||||
(SLDconst [s] (RLWINM [r] y)) && mergePPC64SldiRlwinm(s,r) != 0 => (RLWINM [mergePPC64SldiRlwinm(s,r)] y)
|
||||
(RLWINM [r] (MOVHZreg u)) && mergePPC64RlwinmAnd(r,0xFFFF) != 0 => (RLWINM [mergePPC64RlwinmAnd(r,0xFFFF)] u)
|
||||
(RLWINM [r] (Select0 (ANDCCconst [a] u))) && mergePPC64RlwinmAnd(r,uint32(a)) != 0 => (RLWINM [mergePPC64RlwinmAnd(r,uint32(a))] u)
|
||||
// SLWconst is a special case of RLWNM which always zero-extends the result.
|
||||
(SLWconst [s] (MOVWZreg w)) => (SLWconst [s] w)
|
||||
(MOVWZreg w:(SLWconst u)) => w
|
||||
|
||||
// H - there are more combinations than these
|
||||
|
||||
(MOVHZreg y:(MOV(H|B)Zreg _)) => y // repeat
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ type Block struct {
|
|||
// After flagalloc, records whether flags are live at the end of the block.
|
||||
FlagsLiveAtEnd bool
|
||||
|
||||
// A block that would be good to align (according to the optimizer's guesses)
|
||||
Hotness Hotness
|
||||
|
||||
// Subsequent blocks, if any. The number and order depend on the block kind.
|
||||
Succs []Edge
|
||||
|
||||
|
|
@ -112,7 +115,7 @@ func (e Edge) String() string {
|
|||
}
|
||||
|
||||
// BlockKind is the kind of SSA block.
|
||||
type BlockKind int16
|
||||
type BlockKind uint8
|
||||
|
||||
// short form print
|
||||
func (b *Block) String() string {
|
||||
|
|
@ -426,3 +429,17 @@ const (
|
|||
BranchUnknown = BranchPrediction(0)
|
||||
BranchLikely = BranchPrediction(+1)
|
||||
)
|
||||
|
||||
type Hotness int8 // Could use negative numbers for specifically non-hot blocks, but don't, yet.
|
||||
const (
|
||||
// These values are arranged in what seems to be order of increasing alignment importance.
|
||||
// Currently only a few are relevant. Implicitly, they are all in a loop.
|
||||
HotNotFlowIn Hotness = 1 << iota // This block is only reached by branches
|
||||
HotInitial // In the block order, the first one for a given loop. Not necessarily topological header.
|
||||
HotPgo // By PGO-based heuristics, this block occurs in a hot loop
|
||||
|
||||
HotNot = 0
|
||||
HotInitialNotFlowIn = HotInitial | HotNotFlowIn // typically first block of a rotated loop, loop is entered with a branch (not to this block). No PGO
|
||||
HotPgoInitial = HotPgo | HotInitial // special case; single block loop, initial block is header block has a flow-in entry, but PGO says it is hot
|
||||
HotPgoInitialNotFLowIn = HotPgo | HotInitial | HotNotFlowIn // PGO says it is hot, and the loop is rotated so flow enters loop with a branch
|
||||
)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func TestFmaHash(t *testing.T) {
|
|||
t.Logf("%v", cmd.Env)
|
||||
b, e := cmd.CombinedOutput()
|
||||
if e != nil {
|
||||
t.Error(e)
|
||||
t.Errorf("build failed: %v\n%s", e, b)
|
||||
}
|
||||
s := string(b) // Looking for "GOFMAHASH triggered main.main:24"
|
||||
re := "fmahash(0?) triggered .*fma.go:29:..;.*fma.go:18:.."
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ type Func struct {
|
|||
laidout bool // Blocks are ordered
|
||||
NoSplit bool // true if function is marked as nosplit. Used by schedule check pass.
|
||||
dumpFileSeq uint8 // the sequence numbers of dump file. (%s_%02d__%s.dump", funcname, dumpFileSeq, phaseName)
|
||||
IsPgoHot bool
|
||||
|
||||
// when register allocation is done, maps value ids to locations
|
||||
RegAlloc []Location
|
||||
|
|
|
|||
|
|
@ -56,9 +56,20 @@ func loopRotate(f *Func) {
|
|||
}
|
||||
p = e.b
|
||||
}
|
||||
if p == nil || p == b {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
p.Hotness |= HotInitial
|
||||
if f.IsPgoHot {
|
||||
p.Hotness |= HotPgo
|
||||
}
|
||||
// blocks will be arranged so that p is ordered first, if it isn't already.
|
||||
if p == b { // p is header, already first (and also, only block in the loop)
|
||||
continue
|
||||
}
|
||||
p.Hotness |= HotNotFlowIn
|
||||
|
||||
// the loop header b follows p
|
||||
after[p.ID] = []*Block{b}
|
||||
for {
|
||||
nextIdx := idToIdx[b.ID] + 1
|
||||
|
|
|
|||
|
|
@ -1653,6 +1653,56 @@ func mergePPC64ClrlsldiRlwinm(sld int32, rlw int64) int64 {
|
|||
return encodePPC64RotateMask(r_3, int64(mask_3), 32)
|
||||
}
|
||||
|
||||
// Test if RLWINM feeding into an ANDconst can be merged. Return the encoded RLWINM constant,
|
||||
// or 0 if they cannot be merged.
|
||||
func mergePPC64AndRlwinm(mask uint32, rlw int64) int64 {
|
||||
r, _, _, mask_rlw := DecodePPC64RotateMask(rlw)
|
||||
mask_out := (mask_rlw & uint64(mask))
|
||||
|
||||
// Verify the result is still a valid bitmask of <= 32 bits.
|
||||
if !isPPC64WordRotateMask(int64(mask_out)) {
|
||||
return 0
|
||||
}
|
||||
return encodePPC64RotateMask(r, int64(mask_out), 32)
|
||||
}
|
||||
|
||||
// Test if AND feeding into an ANDconst can be merged. Return the encoded RLWINM constant,
|
||||
// or 0 if they cannot be merged.
|
||||
func mergePPC64RlwinmAnd(rlw int64, mask uint32) int64 {
|
||||
r, _, _, mask_rlw := DecodePPC64RotateMask(rlw)
|
||||
|
||||
// Rotate the input mask, combine with the rlwnm mask, and test if it is still a valid rlwinm mask.
|
||||
r_mask := bits.RotateLeft32(mask, int(r))
|
||||
|
||||
mask_out := (mask_rlw & uint64(r_mask))
|
||||
|
||||
// Verify the result is still a valid bitmask of <= 32 bits.
|
||||
if !isPPC64WordRotateMask(int64(mask_out)) {
|
||||
return 0
|
||||
}
|
||||
return encodePPC64RotateMask(r, int64(mask_out), 32)
|
||||
}
|
||||
|
||||
// Test if RLWINM feeding into SRDconst can be merged. Return the encoded RLIWNM constant,
|
||||
// or 0 if they cannot be merged.
|
||||
func mergePPC64SldiRlwinm(sldi, rlw int64) int64 {
|
||||
r_1, mb, me, mask_1 := DecodePPC64RotateMask(rlw)
|
||||
if mb > me || mb < sldi {
|
||||
// Wrapping masks cannot be merged as the upper 32 bits are effectively undefined in this case.
|
||||
// Likewise, if mb is less than the shift amount, it cannot be merged.
|
||||
return 0
|
||||
}
|
||||
// combine the masks, and adjust for the final left shift.
|
||||
mask_3 := mask_1 << sldi
|
||||
r_3 := (r_1 + sldi) & 31 // This can wrap.
|
||||
|
||||
// Verify the result is still a valid bitmask of <= 32 bits.
|
||||
if uint64(uint32(mask_3)) != mask_3 {
|
||||
return 0
|
||||
}
|
||||
return encodePPC64RotateMask(r_3, int64(mask_3), 32)
|
||||
}
|
||||
|
||||
// Compute the encoded RLWINM constant from combining (SLDconst [sld] (SRWconst [srw] x)),
|
||||
// or return 0 if they cannot be combined.
|
||||
func mergePPC64SldiSrw(sld, srw int64) int64 {
|
||||
|
|
|
|||
|
|
@ -611,6 +611,8 @@ func rewriteValuePPC64(v *Value) bool {
|
|||
return rewriteValuePPC64_OpPPC64ORN(v)
|
||||
case OpPPC64ORconst:
|
||||
return rewriteValuePPC64_OpPPC64ORconst(v)
|
||||
case OpPPC64RLWINM:
|
||||
return rewriteValuePPC64_OpPPC64RLWINM(v)
|
||||
case OpPPC64ROTL:
|
||||
return rewriteValuePPC64_OpPPC64ROTL(v)
|
||||
case OpPPC64ROTLW:
|
||||
|
|
@ -6765,6 +6767,40 @@ func rewriteValuePPC64_OpPPC64MOVBZreg(v *Value) bool {
|
|||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBZreg (SRWconst x [s]))
|
||||
// cond: mergePPC64AndSrwi(0xFF,s) != 0
|
||||
// result: (RLWINM [mergePPC64AndSrwi(0xFF,s)] x)
|
||||
for {
|
||||
if v_0.Op != OpPPC64SRWconst {
|
||||
break
|
||||
}
|
||||
s := auxIntToInt64(v_0.AuxInt)
|
||||
x := v_0.Args[0]
|
||||
if !(mergePPC64AndSrwi(0xFF, s) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64AndSrwi(0xFF, s))
|
||||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBZreg (RLWINM [r] y))
|
||||
// cond: mergePPC64AndRlwinm(0xFF,r) != 0
|
||||
// result: (RLWINM [mergePPC64AndRlwinm(0xFF,r)] y)
|
||||
for {
|
||||
if v_0.Op != OpPPC64RLWINM {
|
||||
break
|
||||
}
|
||||
r := auxIntToInt64(v_0.AuxInt)
|
||||
y := v_0.Args[0]
|
||||
if !(mergePPC64AndRlwinm(0xFF, r) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64AndRlwinm(0xFF, r))
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
// match: (MOVBZreg (OR <t> x (MOVWZreg y)))
|
||||
// result: (MOVBZreg (OR <t> x y))
|
||||
for {
|
||||
|
|
@ -8610,6 +8646,23 @@ func rewriteValuePPC64_OpPPC64MOVHZreg(v *Value) bool {
|
|||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVHZreg (RLWINM [r] y))
|
||||
// cond: mergePPC64AndRlwinm(0xFFFF,r) != 0
|
||||
// result: (RLWINM [mergePPC64AndRlwinm(0xFFFF,r)] y)
|
||||
for {
|
||||
if v_0.Op != OpPPC64RLWINM {
|
||||
break
|
||||
}
|
||||
r := auxIntToInt64(v_0.AuxInt)
|
||||
y := v_0.Args[0]
|
||||
if !(mergePPC64AndRlwinm(0xFFFF, r) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64AndRlwinm(0xFFFF, r))
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
// match: (MOVHZreg y:(MOVHZreg _))
|
||||
// result: y
|
||||
for {
|
||||
|
|
@ -9960,6 +10013,33 @@ func rewriteValuePPC64_OpPPC64MOVWZreg(v *Value) bool {
|
|||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (MOVWZreg (RLWINM [r] y))
|
||||
// cond: mergePPC64AndRlwinm(0xFFFFFFFF,r) != 0
|
||||
// result: (RLWINM [mergePPC64AndRlwinm(0xFFFFFFFF,r)] y)
|
||||
for {
|
||||
if v_0.Op != OpPPC64RLWINM {
|
||||
break
|
||||
}
|
||||
r := auxIntToInt64(v_0.AuxInt)
|
||||
y := v_0.Args[0]
|
||||
if !(mergePPC64AndRlwinm(0xFFFFFFFF, r) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64AndRlwinm(0xFFFFFFFF, r))
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
// match: (MOVWZreg w:(SLWconst u))
|
||||
// result: w
|
||||
for {
|
||||
w := v_0
|
||||
if w.Op != OpPPC64SLWconst {
|
||||
break
|
||||
}
|
||||
v.copyOf(w)
|
||||
return true
|
||||
}
|
||||
// match: (MOVWZreg y:(MOVWZreg _))
|
||||
// result: y
|
||||
for {
|
||||
|
|
@ -11346,6 +11426,49 @@ func rewriteValuePPC64_OpPPC64ORconst(v *Value) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValuePPC64_OpPPC64RLWINM(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
// match: (RLWINM [r] (MOVHZreg u))
|
||||
// cond: mergePPC64RlwinmAnd(r,0xFFFF) != 0
|
||||
// result: (RLWINM [mergePPC64RlwinmAnd(r,0xFFFF)] u)
|
||||
for {
|
||||
r := auxIntToInt64(v.AuxInt)
|
||||
if v_0.Op != OpPPC64MOVHZreg {
|
||||
break
|
||||
}
|
||||
u := v_0.Args[0]
|
||||
if !(mergePPC64RlwinmAnd(r, 0xFFFF) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64RlwinmAnd(r, 0xFFFF))
|
||||
v.AddArg(u)
|
||||
return true
|
||||
}
|
||||
// match: (RLWINM [r] (Select0 (ANDCCconst [a] u)))
|
||||
// cond: mergePPC64RlwinmAnd(r,uint32(a)) != 0
|
||||
// result: (RLWINM [mergePPC64RlwinmAnd(r,uint32(a))] u)
|
||||
for {
|
||||
r := auxIntToInt64(v.AuxInt)
|
||||
if v_0.Op != OpSelect0 {
|
||||
break
|
||||
}
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64ANDCCconst {
|
||||
break
|
||||
}
|
||||
a := auxIntToInt64(v_0_0.AuxInt)
|
||||
u := v_0_0.Args[0]
|
||||
if !(mergePPC64RlwinmAnd(r, uint32(a)) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64RlwinmAnd(r, uint32(a)))
|
||||
v.AddArg(u)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
func rewriteValuePPC64_OpPPC64ROTL(v *Value) bool {
|
||||
v_1 := v.Args[1]
|
||||
v_0 := v.Args[0]
|
||||
|
|
@ -12061,6 +12184,24 @@ func rewriteValuePPC64_OpPPC64SLDconst(v *Value) bool {
|
|||
v.AddArg(x)
|
||||
return true
|
||||
}
|
||||
// match: (SLDconst [s] (RLWINM [r] y))
|
||||
// cond: mergePPC64SldiRlwinm(s,r) != 0
|
||||
// result: (RLWINM [mergePPC64SldiRlwinm(s,r)] y)
|
||||
for {
|
||||
s := auxIntToInt64(v.AuxInt)
|
||||
if v_0.Op != OpPPC64RLWINM {
|
||||
break
|
||||
}
|
||||
r := auxIntToInt64(v_0.AuxInt)
|
||||
y := v_0.Args[0]
|
||||
if !(mergePPC64SldiRlwinm(s, r) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64SldiRlwinm(s, r))
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
// match: (SLDconst [c] z:(MOVBZreg x))
|
||||
// cond: c < 8 && z.Uses == 1
|
||||
// result: (CLRLSLDI [newPPC64ShiftAuxInt(c,56,63,64)] x)
|
||||
|
|
@ -12206,6 +12347,19 @@ func rewriteValuePPC64_OpPPC64SLW(v *Value) bool {
|
|||
}
|
||||
func rewriteValuePPC64_OpPPC64SLWconst(v *Value) bool {
|
||||
v_0 := v.Args[0]
|
||||
// match: (SLWconst [s] (MOVWZreg w))
|
||||
// result: (SLWconst [s] w)
|
||||
for {
|
||||
s := auxIntToInt64(v.AuxInt)
|
||||
if v_0.Op != OpPPC64MOVWZreg {
|
||||
break
|
||||
}
|
||||
w := v_0.Args[0]
|
||||
v.reset(OpPPC64SLWconst)
|
||||
v.AuxInt = int64ToAuxInt(s)
|
||||
v.AddArg(w)
|
||||
return true
|
||||
}
|
||||
// match: (SLWconst [c] z:(MOVBZreg x))
|
||||
// cond: z.Uses == 1 && c < 8
|
||||
// result: (CLRLSLWI [newPPC64ShiftAuxInt(c,24,31,32)] x)
|
||||
|
|
@ -14665,6 +14819,28 @@ func rewriteValuePPC64_OpSelect0(v *Value) bool {
|
|||
v.AddArg(v0)
|
||||
return true
|
||||
}
|
||||
// match: (Select0 (ANDCCconst [m] (RLWINM [r] y)))
|
||||
// cond: mergePPC64AndRlwinm(uint32(m),r) != 0
|
||||
// result: (RLWINM [mergePPC64AndRlwinm(uint32(m),r)] y)
|
||||
for {
|
||||
if v_0.Op != OpPPC64ANDCCconst {
|
||||
break
|
||||
}
|
||||
m := auxIntToInt64(v_0.AuxInt)
|
||||
v_0_0 := v_0.Args[0]
|
||||
if v_0_0.Op != OpPPC64RLWINM {
|
||||
break
|
||||
}
|
||||
r := auxIntToInt64(v_0_0.AuxInt)
|
||||
y := v_0_0.Args[0]
|
||||
if !(mergePPC64AndRlwinm(uint32(m), r) != 0) {
|
||||
break
|
||||
}
|
||||
v.reset(OpPPC64RLWINM)
|
||||
v.AuxInt = int64ToAuxInt(mergePPC64AndRlwinm(uint32(m), r))
|
||||
v.AddArg(y)
|
||||
return true
|
||||
}
|
||||
// match: (Select0 (ANDCCconst [1] z:(SRADconst [63] x)))
|
||||
// cond: z.Uses == 1
|
||||
// result: (SRDconst [63] x)
|
||||
|
|
|
|||
|
|
@ -148,6 +148,11 @@ func (s *SymABIs) GenABIWrappers() {
|
|||
// offsets to dispatch arguments, which currently using ABI0
|
||||
// frame layout. Pin it to ABI0.
|
||||
fn.ABI = obj.ABI0
|
||||
// Propagate linkname attribute, which was set on the ABIInternal
|
||||
// symbol.
|
||||
if sym.Linksym().IsLinkname() {
|
||||
sym.LinksymABI(fn.ABI).Set(obj.AttrLinkname, true)
|
||||
}
|
||||
}
|
||||
|
||||
// If cgo-exported, add the definition ABI to the cgo
|
||||
|
|
|
|||
|
|
@ -174,6 +174,14 @@ func (c *nowritebarrierrecChecker) check() {
|
|||
fmt.Fprintf(&err, "\n\t%v: called by %v", base.FmtPos(call.lineno), call.target.Nname)
|
||||
call = funcs[call.target]
|
||||
}
|
||||
// Seeing this error in a failed CI run? It indicates that
|
||||
// a function in the runtime package marked nowritebarrierrec
|
||||
// (the outermost stack element) was found, by a static
|
||||
// reachability analysis over the fully lowered optimized code,
|
||||
// to call a function (fn) that involves a write barrier.
|
||||
//
|
||||
// Even if the call path is infeasable,
|
||||
// you will need to reorganize the code to avoid it.
|
||||
base.ErrorfAt(fn.WBPos, 0, "write barrier prohibited by caller; %v%s", fn.Nname, err.String())
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,11 @@ import (
|
|||
"sync"
|
||||
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/inline"
|
||||
"cmd/compile/internal/ir"
|
||||
"cmd/compile/internal/liveness"
|
||||
"cmd/compile/internal/objw"
|
||||
"cmd/compile/internal/pgoir"
|
||||
"cmd/compile/internal/ssa"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
|
|
@ -296,8 +298,8 @@ const maxStackSize = 1 << 30
|
|||
// uses it to generate a plist,
|
||||
// and flushes that plist to machine code.
|
||||
// worker indicates which of the backend workers is doing the processing.
|
||||
func Compile(fn *ir.Func, worker int) {
|
||||
f := buildssa(fn, worker)
|
||||
func Compile(fn *ir.Func, worker int, profile *pgoir.Profile) {
|
||||
f := buildssa(fn, worker, inline.IsPgoHotFunc(fn, profile) || inline.HasPgoHotInline(fn))
|
||||
// Note: check arg size to fix issue 25507.
|
||||
if f.Frontend().(*ssafn).stksize >= maxStackSize || f.OwnAux.ArgWidth() >= maxStackSize {
|
||||
largeStackFramesMu.Lock()
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ func (s *state) emitOpenDeferInfo() {
|
|||
|
||||
// buildssa builds an SSA function for fn.
|
||||
// worker indicates which of the backend workers is doing the processing.
|
||||
func buildssa(fn *ir.Func, worker int) *ssa.Func {
|
||||
func buildssa(fn *ir.Func, worker int, isPgoHot bool) *ssa.Func {
|
||||
name := ir.FuncName(fn)
|
||||
|
||||
abiSelf := abiForFunc(fn, ssaConfig.ABI0, ssaConfig.ABI1)
|
||||
|
|
@ -373,6 +373,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
|
|||
// Allocate starting block
|
||||
s.f.Entry = s.f.NewBlock(ssa.BlockPlain)
|
||||
s.f.Entry.Pos = fn.Pos()
|
||||
s.f.IsPgoHot = isPgoHot
|
||||
|
||||
if printssa {
|
||||
ssaDF := ssaDumpFile
|
||||
|
|
@ -7302,12 +7303,47 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
|
||||
var argLiveIdx int = -1 // argument liveness info index
|
||||
|
||||
// These control cache line alignment; if the required portion of
|
||||
// a cache line is not available, then pad to obtain cache line
|
||||
// alignment. Not implemented on all architectures, may not be
|
||||
// useful on all architectures.
|
||||
var hotAlign, hotRequire int64
|
||||
|
||||
if base.Debug.AlignHot > 0 {
|
||||
switch base.Ctxt.Arch.Name {
|
||||
// enable this on a case-by-case basis, with benchmarking.
|
||||
// currently shown:
|
||||
// good for amd64
|
||||
// not helpful for Apple Silicon
|
||||
//
|
||||
case "amd64", "386":
|
||||
// Align to 64 if 31 or fewer bytes remain in a cache line
|
||||
// benchmarks a little better than always aligning, and also
|
||||
// adds slightly less to the (PGO-compiled) binary size.
|
||||
hotAlign = 64
|
||||
hotRequire = 31
|
||||
}
|
||||
}
|
||||
|
||||
// Emit basic blocks
|
||||
for i, b := range f.Blocks {
|
||||
s.bstart[b.ID] = s.pp.Next
|
||||
|
||||
s.lineRunStart = nil
|
||||
s.SetPos(s.pp.Pos.WithNotStmt()) // It needs a non-empty Pos, but cannot be a statement boundary (yet).
|
||||
|
||||
if hotAlign > 0 && b.Hotness&ssa.HotPgoInitial == ssa.HotPgoInitial {
|
||||
// So far this has only been shown profitable for PGO-hot loop headers.
|
||||
// The Hotness values allows distinctions betwen initial blocks that are "hot" or not, and "flow-in" or not.
|
||||
// Currently only the initial blocks of loops are tagged in this way;
|
||||
// there are no blocks tagged "pgo-hot" that are not also tagged "initial".
|
||||
// TODO more heuristics, more architectures.
|
||||
p := s.pp.Prog(obj.APCALIGNMAX)
|
||||
p.From.SetConst(hotAlign)
|
||||
p.To.SetConst(hotRequire)
|
||||
}
|
||||
|
||||
s.bstart[b.ID] = s.pp.Next
|
||||
|
||||
if idx, ok := argLiveBlockMap[b.ID]; ok && idx != argLiveIdx {
|
||||
argLiveIdx = idx
|
||||
p := s.pp.Prog(obj.APCDATA)
|
||||
|
|
@ -7466,7 +7502,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
|
|||
// going to emit anyway, and use those instructions instead of the
|
||||
// inline marks.
|
||||
for p := s.pp.Text; p != nil; p = p.Link {
|
||||
if p.As == obj.ANOP || p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT || p.As == obj.APCALIGN || Arch.LinkArch.Family == sys.Wasm {
|
||||
if p.As == obj.ANOP || p.As == obj.AFUNCDATA || p.As == obj.APCDATA || p.As == obj.ATEXT ||
|
||||
p.As == obj.APCALIGN || p.As == obj.APCALIGNMAX || Arch.LinkArch.Family == sys.Wasm {
|
||||
// Don't use 0-sized instructions as inline marks, because we need
|
||||
// to identify inline mark instructions by pc offset.
|
||||
// (Some of these instructions are sometimes zero-sized, sometimes not.
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@ import "go/constant"
|
|||
// All types implement the Type interface.
|
||||
// (This type originally lived in types2. We moved it here
|
||||
// so we could depend on it from other packages without
|
||||
// introducing a circularity.)
|
||||
// introducing an import cycle.)
|
||||
type Type interface {
|
||||
// Underlying returns the underlying type of a type.
|
||||
// Underlying types are never Named, TypeParam, or Alias types.
|
||||
//
|
||||
// See https://go.dev/ref/spec#Underlying_types.
|
||||
Underlying() Type
|
||||
|
||||
// String returns a string representation of a type.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import "fmt"
|
|||
// which points directly to the actual (aliased) type.
|
||||
type Alias struct {
|
||||
obj *TypeName // corresponding declared alias object
|
||||
orig *Alias // original, uninstantiated alias
|
||||
tparams *TypeParamList // type parameters, or nil
|
||||
targs *TypeList // type arguments, or nil
|
||||
fromRHS Type // RHS of type alias declaration; may be an alias
|
||||
actual Type // actual (aliased) type; never an alias
|
||||
}
|
||||
|
|
@ -28,9 +30,34 @@ func NewAlias(obj *TypeName, rhs Type) *Alias {
|
|||
return alias
|
||||
}
|
||||
|
||||
func (a *Alias) Obj() *TypeName { return a.obj }
|
||||
func (a *Alias) Obj() *TypeName { return a.obj }
|
||||
func (a *Alias) String() string { return TypeString(a, nil) }
|
||||
|
||||
// Underlying returns the [underlying type] of the alias type a, which is the
|
||||
// underlying type of the aliased type. Underlying types are never Named,
|
||||
// TypeParam, or Alias types.
|
||||
//
|
||||
// [underlying type]: https://go.dev/ref/spec#Underlying_types.
|
||||
func (a *Alias) Underlying() Type { return unalias(a).Underlying() }
|
||||
func (a *Alias) String() string { return TypeString(a, nil) }
|
||||
|
||||
// Origin returns the generic Alias type of which a is an instance.
|
||||
// If a is not an instance of a generic alias, Origin returns a.
|
||||
func (a *Alias) Origin() *Alias { return a.orig }
|
||||
|
||||
// TypeParams returns the type parameters of the alias type a, or nil.
|
||||
// A generic Alias and its instances have the same type parameters.
|
||||
func (a *Alias) TypeParams() *TypeParamList { return a.tparams }
|
||||
|
||||
// SetTypeParams sets the type parameters of the alias type a.
|
||||
// The alias a must not have type arguments.
|
||||
func (a *Alias) SetTypeParams(tparams []*TypeParam) {
|
||||
assert(a.targs == nil)
|
||||
a.tparams = bindTParams(tparams)
|
||||
}
|
||||
|
||||
// TypeArgs returns the type arguments used to instantiate the Alias type.
|
||||
// If a is not an instance of a generic alias, the result is nil.
|
||||
func (a *Alias) TypeArgs() *TypeList { return a.targs }
|
||||
|
||||
// Rhs returns the type R on the right-hand side of an alias
|
||||
// declaration "type A = R", which may be another alias.
|
||||
|
|
@ -82,7 +109,10 @@ func asNamed(t Type) *Named {
|
|||
// rhs must not be nil.
|
||||
func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
|
||||
assert(rhs != nil)
|
||||
a := &Alias{obj, nil, rhs, nil}
|
||||
a := new(Alias)
|
||||
a.obj = obj
|
||||
a.orig = a
|
||||
a.fromRHS = rhs
|
||||
if obj.typ == nil {
|
||||
obj.typ = a
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ import (
|
|||
. "internal/types/errors"
|
||||
)
|
||||
|
||||
// A genericType implements access to its type parameters.
|
||||
type genericType interface {
|
||||
Type
|
||||
TypeParams() *TypeParamList
|
||||
}
|
||||
|
||||
// Instantiate instantiates the type orig with the given type arguments targs.
|
||||
// orig must be a *Named or a *Signature type. If there is no error, the
|
||||
// resulting Type is an instantiated type of the same kind (either a *Named or
|
||||
|
|
@ -41,17 +47,15 @@ import (
|
|||
// count is incorrect; for *Named types, a panic may occur later inside the
|
||||
// *Named API.
|
||||
func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) {
|
||||
assert(len(targs) > 0)
|
||||
if ctxt == nil {
|
||||
ctxt = NewContext()
|
||||
}
|
||||
orig_ := orig.(genericType) // signature of Instantiate must not change for backward-compatibility
|
||||
|
||||
if validate {
|
||||
var tparams []*TypeParam
|
||||
switch t := orig.(type) {
|
||||
case *Named:
|
||||
tparams = t.TypeParams().list()
|
||||
case *Signature:
|
||||
tparams = t.TypeParams().list()
|
||||
}
|
||||
tparams := orig_.TypeParams().list()
|
||||
assert(len(tparams) > 0)
|
||||
if len(targs) != len(tparams) {
|
||||
return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams))
|
||||
}
|
||||
|
|
@ -60,7 +64,7 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e
|
|||
}
|
||||
}
|
||||
|
||||
inst := (*Checker)(nil).instance(nopos, orig, targs, nil, ctxt)
|
||||
inst := (*Checker)(nil).instance(nopos, orig_, targs, nil, ctxt)
|
||||
return inst, nil
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +79,7 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e
|
|||
// must be non-nil.
|
||||
//
|
||||
// For Named types the resulting instance may be unexpanded.
|
||||
func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, expanding *Named, ctxt *Context) (res Type) {
|
||||
func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, expanding *Named, ctxt *Context) (res Type) {
|
||||
// The order of the contexts below matters: we always prefer instances in the
|
||||
// expanding instance context in order to preserve reference cycles.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -485,9 +485,17 @@ func (t *Named) methodIndex(name string, foldCase bool) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
// TODO(gri) Investigate if Unalias can be moved to where underlying is set.
|
||||
func (t *Named) Underlying() Type { return Unalias(t.resolve().underlying) }
|
||||
func (t *Named) String() string { return TypeString(t, nil) }
|
||||
// Underlying returns the [underlying type] of the named type t, resolving all
|
||||
// forwarding declarations. Underlying types are never Named, TypeParam, or
|
||||
// Alias types.
|
||||
//
|
||||
// [underlying type]: https://go.dev/ref/spec#Underlying_types.
|
||||
func (t *Named) Underlying() Type {
|
||||
// TODO(gri) Investigate if Unalias can be moved to where underlying is set.
|
||||
return Unalias(t.resolve().underlying)
|
||||
}
|
||||
|
||||
func (t *Named) String() string { return TypeString(t, nil) }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Implementation
|
||||
|
|
|
|||
|
|
@ -73,9 +73,6 @@ func (s *Signature) Recv() *Var { return s.recv }
|
|||
// TypeParams returns the type parameters of signature s, or nil.
|
||||
func (s *Signature) TypeParams() *TypeParamList { return s.tparams }
|
||||
|
||||
// SetTypeParams sets the type parameters of signature s.
|
||||
func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) }
|
||||
|
||||
// RecvTypeParams returns the receiver type parameters of signature s, or nil.
|
||||
func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams }
|
||||
|
||||
|
|
|
|||
|
|
@ -898,7 +898,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
lhs := [2]Expr{sKey, sValue} // sKey, sValue may be nil
|
||||
rhs := [2]Type{key, val} // key, val may be nil
|
||||
|
||||
constIntRange := x.mode == constant_ && isInteger(x.typ)
|
||||
rangeOverInt := isInteger(x.typ)
|
||||
|
||||
if isDef {
|
||||
// short variable declaration
|
||||
|
|
@ -933,14 +933,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
continue
|
||||
}
|
||||
|
||||
// initialize lhs variable
|
||||
if constIntRange {
|
||||
if rangeOverInt {
|
||||
assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
||||
check.initVar(obj, &x, "range clause")
|
||||
} else {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
var y operand
|
||||
y.mode = value
|
||||
y.expr = lhs // we don't have a better rhs expression to use here
|
||||
y.typ = typ
|
||||
check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
}
|
||||
assert(obj.typ != nil)
|
||||
}
|
||||
|
|
@ -967,21 +968,30 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
continue
|
||||
}
|
||||
|
||||
if constIntRange {
|
||||
if rangeOverInt {
|
||||
assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
||||
check.assignVar(lhs, nil, &x, "range clause")
|
||||
// If the assignment succeeded, if x was untyped before, it now
|
||||
// has a type inferred via the assignment. It must be an integer.
|
||||
// (go.dev/issues/67027)
|
||||
if x.mode != invalid && !isInteger(x.typ) {
|
||||
check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ)
|
||||
}
|
||||
} else {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
var y operand
|
||||
y.mode = value
|
||||
y.expr = lhs // we don't have a better rhs expression to use here
|
||||
y.typ = typ
|
||||
check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
}
|
||||
}
|
||||
} else if constIntRange {
|
||||
} else if rangeOverInt {
|
||||
// If we don't have any iteration variables, we still need to
|
||||
// check that a (possibly untyped) integer range expression x
|
||||
// is valid.
|
||||
// We do this by checking the assignment _ = x. This ensures
|
||||
// that an untyped x can be converted to a value of type int.
|
||||
// that an untyped x can be converted to a value of its default
|
||||
// type (rune or int).
|
||||
check.assignment(&x, nil, "range clause")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ func (t *TypeParam) SetConstraint(bound Type) {
|
|||
t.iface()
|
||||
}
|
||||
|
||||
// Underlying returns the [underlying type] of the type parameter t, which is
|
||||
// the underlying type of its constraint. This type is always an interface.
|
||||
//
|
||||
// [underlying type]: https://go.dev/ref/spec#Underlying_types.
|
||||
func (t *TypeParam) Underlying() Type {
|
||||
return t.iface()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package main
|
|||
import (
|
||||
"cmd/internal/cov"
|
||||
"cmd/internal/pkgpattern"
|
||||
"cmd/internal/telemetry"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -108,6 +109,8 @@ const (
|
|||
)
|
||||
|
||||
func main() {
|
||||
telemetry.Start()
|
||||
|
||||
// First argument should be mode/subcommand.
|
||||
if len(os.Args) < 2 {
|
||||
usage("missing command selector")
|
||||
|
|
@ -143,6 +146,8 @@ func main() {
|
|||
op.Usage("")
|
||||
}
|
||||
flag.Parse()
|
||||
telemetry.Inc("covdata/invocations")
|
||||
telemetry.CountFlags("covdata/flag:", *flag.CommandLine)
|
||||
|
||||
// Mode-independent flag setup
|
||||
dbgtrace(1, "starting mode-independent setup")
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"cmd/internal/edit"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
const usageMessage = "" +
|
||||
|
|
@ -86,9 +87,13 @@ const (
|
|||
)
|
||||
|
||||
func main() {
|
||||
telemetry.Start()
|
||||
|
||||
objabi.AddVersionFlag()
|
||||
flag.Usage = usage
|
||||
objabi.Flagparse(usage)
|
||||
telemetry.Inc("cover/invocations")
|
||||
telemetry.CountFlags("cover/flag:", *flag.CommandLine)
|
||||
|
||||
// Usage information when no arguments.
|
||||
if flag.NFlag() == 0 && flag.NArg() == 0 {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
|
|
@ -67,8 +69,11 @@ var (
|
|||
func main() {
|
||||
log.SetPrefix("distpack: ")
|
||||
log.SetFlags(0)
|
||||
telemetry.Start()
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
telemetry.Inc("distpack/invocations")
|
||||
telemetry.CountFlags("distpack/flag:", *flag.CommandLine)
|
||||
if flag.NArg() != 0 {
|
||||
usage()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -85,6 +87,7 @@ func usage() {
|
|||
func main() {
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("doc: ")
|
||||
telemetry.Start()
|
||||
dirsInit()
|
||||
err := do(os.Stdout, flag.CommandLine, os.Args[1:])
|
||||
if err != nil {
|
||||
|
|
@ -105,6 +108,8 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
|
|||
flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
|
||||
flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol")
|
||||
flagSet.Parse(args)
|
||||
telemetry.Inc("doc/invocations")
|
||||
telemetry.CountFlags("doc/flag:", *flag.CommandLine)
|
||||
if chdir != "" {
|
||||
if err := os.Chdir(chdir); err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import (
|
|||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -63,8 +65,11 @@ func usage() {
|
|||
}
|
||||
|
||||
func main() {
|
||||
telemetry.Start()
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
telemetry.Inc("fix/invocations")
|
||||
telemetry.CountFlags("fix/flag:", *flag.CommandLine)
|
||||
|
||||
if !version.IsValid(*goVersion) {
|
||||
report(fmt.Errorf("invalid -go=%s", *goVersion))
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ require (
|
|||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5
|
||||
golang.org/x/arch v0.7.0
|
||||
golang.org/x/build v0.0.0-20240222153247-cf4ed81bb19f
|
||||
golang.org/x/mod v0.17.1-0.20240507203540-6686f416970d
|
||||
golang.org/x/mod v0.17.1-0.20240514174713-c0bdc7bd01c9
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.20.0
|
||||
golang.org/x/telemetry v0.0.0-20240510223629-51e8b5d718eb
|
||||
golang.org/x/telemetry v0.0.0-20240515213752-9ff3ad9b3e68
|
||||
golang.org/x/term v0.18.0
|
||||
golang.org/x/tools v0.20.1-0.20240429173604-74c9cfe4d22f
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,14 +26,14 @@ golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
|||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/build v0.0.0-20240222153247-cf4ed81bb19f h1:XQ2eu0I26WsNCKQkRehp+5mwjjChw94trD9LT8LLSq0=
|
||||
golang.org/x/build v0.0.0-20240222153247-cf4ed81bb19f/go.mod h1:HTqTCkubWT8epEK9hDWWGkoOOB7LGSrU1qvWZCSwO50=
|
||||
golang.org/x/mod v0.17.1-0.20240507203540-6686f416970d h1:QS9b5Jvh12iuDV+eYRspS3+7Fn6wOTYI6EAHdeGEsmY=
|
||||
golang.org/x/mod v0.17.1-0.20240507203540-6686f416970d/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.1-0.20240514174713-c0bdc7bd01c9 h1:EfMABMgrJ8+hRjLvhUzJkLKgFv3lYAglGXczg5ggNyk=
|
||||
golang.org/x/mod v0.17.1-0.20240514174713-c0bdc7bd01c9/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240510223629-51e8b5d718eb h1:UTGVF0T+nFaQu6f7USlW8TktAybpMdEjJcF5HyX4dxo=
|
||||
golang.org/x/telemetry v0.0.0-20240510223629-51e8b5d718eb/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
|
||||
golang.org/x/telemetry v0.0.0-20240515213752-9ff3ad9b3e68 h1:UpbHwFpoVYf6i5cMzwsNuPGNsZzfJXFr8R4uUv2HVgk=
|
||||
golang.org/x/telemetry v0.0.0-20240515213752-9ff3ad9b3e68/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
// mod module maintenance
|
||||
// work workspace maintenance
|
||||
// run compile and run Go program
|
||||
// telemetry manage telemetry data and settings
|
||||
// test test packages
|
||||
// tool run specified go tool
|
||||
// version print Go version
|
||||
|
|
@ -456,7 +457,7 @@
|
|||
//
|
||||
// Usage:
|
||||
//
|
||||
// go env [-json] [-u] [-w] [var ...]
|
||||
// go env [-json] [-changed] [-u] [-w] [var ...]
|
||||
//
|
||||
// Env prints Go environment information.
|
||||
//
|
||||
|
|
@ -476,6 +477,10 @@
|
|||
// form NAME=VALUE and changes the default settings
|
||||
// of the named environment variables to the given values.
|
||||
//
|
||||
// The -changed flag prints only those settings whose effective
|
||||
// value differs from the default value that would be obtained in
|
||||
// an empty environment with no prior uses of the -w flag.
|
||||
//
|
||||
// For more about environment variables, see 'go help environment'.
|
||||
//
|
||||
// # Update packages to use new APIs
|
||||
|
|
@ -1201,6 +1206,12 @@
|
|||
//
|
||||
// The -module flag changes the module's path (the go.mod file's module line).
|
||||
//
|
||||
// The -godebug=key=value flag adds a godebug key=value line,
|
||||
// replacing any existing godebug lines with the given key.
|
||||
//
|
||||
// The -dropgodebug=key flag drops any existing godebug lines
|
||||
// with the given key.
|
||||
//
|
||||
// The -require=path@version and -droprequire=path flags
|
||||
// add and drop a requirement on the given module path and version.
|
||||
// Note that -require overrides any existing requirements on path.
|
||||
|
|
@ -1209,6 +1220,14 @@
|
|||
// which make other go.mod adjustments as needed to satisfy
|
||||
// constraints imposed by other modules.
|
||||
//
|
||||
// The -go=version flag sets the expected Go language version.
|
||||
// This flag is mainly for tools that understand Go version dependencies.
|
||||
// Users should prefer 'go get go@version'.
|
||||
//
|
||||
// The -toolchain=version flag sets the Go toolchain to use.
|
||||
// This flag is mainly for tools that understand Go version dependencies.
|
||||
// Users should prefer 'go get toolchain@version'.
|
||||
//
|
||||
// The -exclude=path@version and -dropexclude=path@version flags
|
||||
// add and drop an exclusion for the given module path and version.
|
||||
// Note that -exclude=path@version is a no-op if that exclusion already exists.
|
||||
|
|
@ -1230,13 +1249,9 @@
|
|||
// like "v1.2.3" or a closed interval like "[v1.1.0,v1.1.9]". Note that
|
||||
// -retract=version is a no-op if that retraction already exists.
|
||||
//
|
||||
// The -require, -droprequire, -exclude, -dropexclude, -replace,
|
||||
// -dropreplace, -retract, and -dropretract editing flags may be repeated,
|
||||
// and the changes are applied in the order given.
|
||||
//
|
||||
// The -go=version flag sets the expected Go language version.
|
||||
//
|
||||
// The -toolchain=name flag sets the Go toolchain to use.
|
||||
// The -godebug, -dropgodebug, -require, -droprequire, -exclude, -dropexclude,
|
||||
// -replace, -dropreplace, -retract, and -dropretract editing flags may be
|
||||
// repeated, and the changes are applied in the order given.
|
||||
//
|
||||
// The -print flag prints the final go.mod in its text format instead of
|
||||
// writing it back to go.mod.
|
||||
|
|
@ -1253,6 +1268,7 @@
|
|||
// Module ModPath
|
||||
// Go string
|
||||
// Toolchain string
|
||||
// Godebug []Godebug
|
||||
// Require []Require
|
||||
// Exclude []Module
|
||||
// Replace []Replace
|
||||
|
|
@ -1264,9 +1280,14 @@
|
|||
// Deprecated string
|
||||
// }
|
||||
//
|
||||
// type Godebug struct {
|
||||
// Key string
|
||||
// Value string
|
||||
// }
|
||||
//
|
||||
// type Require struct {
|
||||
// Path string
|
||||
// Version string
|
||||
// Path string
|
||||
// Version string
|
||||
// Indirect bool
|
||||
// }
|
||||
//
|
||||
|
|
@ -1530,6 +1551,12 @@
|
|||
// rewrite the go.mod file. The only time this flag is needed is if no other
|
||||
// flags are specified, as in 'go work edit -fmt'.
|
||||
//
|
||||
// The -godebug=key=value flag adds a godebug key=value line,
|
||||
// replacing any existing godebug lines with the given key.
|
||||
//
|
||||
// The -dropgodebug=key flag drops any existing godebug lines
|
||||
// with the given key.
|
||||
//
|
||||
// The -use=path and -dropuse=path flags
|
||||
// add and drop a use directive from the go.work file's set of module directories.
|
||||
//
|
||||
|
|
@ -1561,10 +1588,16 @@
|
|||
// type GoWork struct {
|
||||
// Go string
|
||||
// Toolchain string
|
||||
// Godebug []Godebug
|
||||
// Use []Use
|
||||
// Replace []Replace
|
||||
// }
|
||||
//
|
||||
// type Godebug struct {
|
||||
// Key string
|
||||
// Value string
|
||||
// }
|
||||
//
|
||||
// type Use struct {
|
||||
// DiskPath string
|
||||
// ModulePath string
|
||||
|
|
@ -1722,6 +1755,38 @@
|
|||
//
|
||||
// See also: go build.
|
||||
//
|
||||
// # Manage telemetry data and settings
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go telemetry [off|local|on]
|
||||
//
|
||||
// Telemetry is used to manage Go telemetry data and settings.
|
||||
//
|
||||
// Telemetry can be in one of three modes: off, local, or on.
|
||||
//
|
||||
// When telemetry is in local mode, counter data is written to the local file
|
||||
// system, but will not be uploaded to remote servers.
|
||||
//
|
||||
// When telemetry is off, local counter data is neither collected nor uploaded.
|
||||
//
|
||||
// When telemetry is on, telemetry data is written to the local file system
|
||||
// and periodically sent to https://telemetry.go.dev/. Uploaded data is used to
|
||||
// help improve the Go toolchain and related tools, and it will be published as
|
||||
// part of a public dataset.
|
||||
//
|
||||
// For more details, see https://telemetry.go.dev/privacy.
|
||||
// This data is collected in accordance with the Google Privacy Policy
|
||||
// (https://policies.google.com/privacy).
|
||||
//
|
||||
// To view the current telemetry mode, run "go telemetry".
|
||||
// To disable telemetry uploading, but keep local data collection, run
|
||||
// "go telemetry local".
|
||||
// To enable both collection and uploading, run “go telemetry on”.
|
||||
// To disable both collection and uploading, run "go telemetry off".
|
||||
//
|
||||
// See https://go.dev/doc/telemetry for more information on telemetry.
|
||||
//
|
||||
// # Test packages
|
||||
//
|
||||
// Usage:
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ func TestMain(m *testing.M) {
|
|||
defer removeAll(testTmpDir)
|
||||
}
|
||||
|
||||
testGOCACHE = cache.DefaultDir()
|
||||
testGOCACHE, _ = cache.DefaultDir()
|
||||
if testenv.HasGoBuild() {
|
||||
testBin = filepath.Join(testTmpDir, "testbin")
|
||||
if err := os.Mkdir(testBin, 0777); err != nil {
|
||||
|
|
@ -1058,7 +1058,7 @@ func TestGoListDeps(t *testing.T) {
|
|||
if runtime.Compiler != "gccgo" {
|
||||
// Check the list is in dependency order.
|
||||
tg.run("list", "-deps", "math")
|
||||
want := "internal/cpu\nunsafe\nmath/bits\nmath\n"
|
||||
want := "unsafe\ninternal/cpu\nmath/bits\nmath\n"
|
||||
out := tg.stdout.String()
|
||||
if !strings.Contains(out, "internal/cpu") {
|
||||
// Some systems don't use internal/cpu.
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func printGoEnv(w io.Writer) {
|
|||
env := envcmd.MkEnv()
|
||||
env = append(env, envcmd.ExtraEnvVars()...)
|
||||
env = append(env, envcmd.ExtraEnvVarsCostly()...)
|
||||
envcmd.PrintEnv(w, env)
|
||||
envcmd.PrintEnv(w, env, false)
|
||||
}
|
||||
|
||||
func printGoDetails(w io.Writer) {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ See golang.org to learn more about Go.
|
|||
// initDefaultCache does the work of finding the default cache
|
||||
// the first time Default is called.
|
||||
func initDefaultCache() {
|
||||
dir := DefaultDir()
|
||||
dir, _ := DefaultDir()
|
||||
if dir == "off" {
|
||||
if defaultDirErr != nil {
|
||||
base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr)
|
||||
|
|
@ -67,14 +67,16 @@ func initDefaultCache() {
|
|||
}
|
||||
|
||||
var (
|
||||
defaultDirOnce sync.Once
|
||||
defaultDir string
|
||||
defaultDirErr error
|
||||
defaultDirOnce sync.Once
|
||||
defaultDir string
|
||||
defaultDirChanged bool // effective value differs from $GOCACHE
|
||||
defaultDirErr error
|
||||
)
|
||||
|
||||
// DefaultDir returns the effective GOCACHE setting.
|
||||
// It returns "off" if the cache is disabled.
|
||||
func DefaultDir() string {
|
||||
// It returns "off" if the cache is disabled,
|
||||
// and reports whether the effective value differs from GOCACHE.
|
||||
func DefaultDir() (string, bool) {
|
||||
// Save the result of the first call to DefaultDir for later use in
|
||||
// initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that
|
||||
// subprocesses will inherit it, but that means initDefaultCache can't
|
||||
|
|
@ -82,10 +84,11 @@ func DefaultDir() string {
|
|||
|
||||
defaultDirOnce.Do(func() {
|
||||
defaultDir = cfg.Getenv("GOCACHE")
|
||||
if filepath.IsAbs(defaultDir) || defaultDir == "off" {
|
||||
return
|
||||
}
|
||||
if defaultDir != "" {
|
||||
defaultDirChanged = true
|
||||
if filepath.IsAbs(defaultDir) || defaultDir == "off" {
|
||||
return
|
||||
}
|
||||
defaultDir = "off"
|
||||
defaultDirErr = fmt.Errorf("GOCACHE is not an absolute path")
|
||||
return
|
||||
|
|
@ -95,11 +98,12 @@ func DefaultDir() string {
|
|||
dir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
defaultDir = "off"
|
||||
defaultDirChanged = true
|
||||
defaultDirErr = fmt.Errorf("GOCACHE is not defined and %v", err)
|
||||
return
|
||||
}
|
||||
defaultDir = filepath.Join(dir, "go-build")
|
||||
})
|
||||
|
||||
return defaultDir
|
||||
return defaultDir, defaultDirChanged
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,9 @@ var (
|
|||
|
||||
// GoPathError is set when GOPATH is not set. it contains an
|
||||
// explanation why GOPATH is unset.
|
||||
GoPathError string
|
||||
GoPathError string
|
||||
GOPATHChanged bool
|
||||
CGOChanged bool
|
||||
)
|
||||
|
||||
func defaultContext() build.Context {
|
||||
|
|
@ -111,7 +113,7 @@ func defaultContext() build.Context {
|
|||
|
||||
// Override defaults computed in go/build with defaults
|
||||
// from go environment configuration file, if known.
|
||||
ctxt.GOPATH = envOr("GOPATH", gopath(ctxt))
|
||||
ctxt.GOPATH, GOPATHChanged = EnvOrAndChanged("GOPATH", gopath(ctxt))
|
||||
ctxt.GOOS = Goos
|
||||
ctxt.GOARCH = Goarch
|
||||
|
||||
|
|
@ -125,14 +127,16 @@ func defaultContext() build.Context {
|
|||
ctxt.ToolTags = save
|
||||
|
||||
// The go/build rule for whether cgo is enabled is:
|
||||
// 1. If $CGO_ENABLED is set, respect it.
|
||||
// 2. Otherwise, if this is a cross-compile, disable cgo.
|
||||
// 3. Otherwise, use built-in default for GOOS/GOARCH.
|
||||
// 1. If $CGO_ENABLED is set, respect it.
|
||||
// 2. Otherwise, if this is a cross-compile, disable cgo.
|
||||
// 3. Otherwise, use built-in default for GOOS/GOARCH.
|
||||
//
|
||||
// Recreate that logic here with the new GOOS/GOARCH setting.
|
||||
if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
|
||||
ctxt.CgoEnabled = v[0] == '1'
|
||||
} else if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
|
||||
ctxt.CgoEnabled = false
|
||||
// We need to run steps 2 and 3 to determine what the default value
|
||||
// of CgoEnabled would be for computing CGOChanged.
|
||||
defaultCgoEnabled := ctxt.CgoEnabled
|
||||
if ctxt.GOOS != runtime.GOOS || ctxt.GOARCH != runtime.GOARCH {
|
||||
defaultCgoEnabled = false
|
||||
} else {
|
||||
// Use built-in default cgo setting for GOOS/GOARCH.
|
||||
// Note that ctxt.GOOS/GOARCH are derived from the preference list
|
||||
|
|
@ -159,11 +163,16 @@ func defaultContext() build.Context {
|
|||
if os.Getenv("CC") == "" {
|
||||
cc := DefaultCC(ctxt.GOOS, ctxt.GOARCH)
|
||||
if _, err := LookPath(cc); err != nil {
|
||||
ctxt.CgoEnabled = false
|
||||
defaultCgoEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctxt.CgoEnabled = defaultCgoEnabled
|
||||
if v := Getenv("CGO_ENABLED"); v == "0" || v == "1" {
|
||||
ctxt.CgoEnabled = v[0] == '1'
|
||||
}
|
||||
CGOChanged = ctxt.CgoEnabled != defaultCgoEnabled
|
||||
|
||||
ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
|
||||
return fsys.Open(path)
|
||||
|
|
@ -262,8 +271,9 @@ func init() {
|
|||
|
||||
// An EnvVar is an environment variable Name=Value.
|
||||
type EnvVar struct {
|
||||
Name string
|
||||
Value string
|
||||
Name string
|
||||
Value string
|
||||
Changed bool // effective Value differs from default
|
||||
}
|
||||
|
||||
// OrigEnv is the original environment of the program at startup.
|
||||
|
|
@ -279,27 +289,28 @@ var envCache struct {
|
|||
m map[string]string
|
||||
}
|
||||
|
||||
// EnvFile returns the name of the Go environment configuration file.
|
||||
func EnvFile() (string, error) {
|
||||
// EnvFile returns the name of the Go environment configuration file,
|
||||
// and reports whether the effective value differs from the default.
|
||||
func EnvFile() (string, bool, error) {
|
||||
if file := os.Getenv("GOENV"); file != "" {
|
||||
if file == "off" {
|
||||
return "", fmt.Errorf("GOENV=off")
|
||||
return "", false, fmt.Errorf("GOENV=off")
|
||||
}
|
||||
return file, nil
|
||||
return file, true, nil
|
||||
}
|
||||
dir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", false, err
|
||||
}
|
||||
if dir == "" {
|
||||
return "", fmt.Errorf("missing user-config dir")
|
||||
return "", false, fmt.Errorf("missing user-config dir")
|
||||
}
|
||||
return filepath.Join(dir, "go/env"), nil
|
||||
return filepath.Join(dir, "go/env"), false, nil
|
||||
}
|
||||
|
||||
func initEnvCache() {
|
||||
envCache.m = make(map[string]string)
|
||||
if file, _ := EnvFile(); file != "" {
|
||||
if file, _, _ := EnvFile(); file != "" {
|
||||
readEnvFile(file, "user")
|
||||
}
|
||||
goroot := findGOROOT(envCache.m["GOROOT"])
|
||||
|
|
@ -397,57 +408,67 @@ var (
|
|||
GOROOTpkg string
|
||||
GOROOTsrc string
|
||||
|
||||
GOBIN = Getenv("GOBIN")
|
||||
GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod"))
|
||||
GOBIN = Getenv("GOBIN")
|
||||
GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod"))
|
||||
|
||||
// Used in envcmd.MkEnv and build ID computations.
|
||||
GOARM = envOr("GOARM", fmt.Sprint(buildcfg.GOARM))
|
||||
GOARM64 = envOr("GOARM64", fmt.Sprint(buildcfg.GOARM64))
|
||||
GO386 = envOr("GO386", buildcfg.GO386)
|
||||
GOAMD64 = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
|
||||
GOMIPS = envOr("GOMIPS", buildcfg.GOMIPS)
|
||||
GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64)
|
||||
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
|
||||
GORISCV64 = envOr("GORISCV64", fmt.Sprintf("rva%du64", buildcfg.GORISCV64))
|
||||
GOWASM = envOr("GOWASM", fmt.Sprint(buildcfg.GOWASM))
|
||||
GOARM64, goARM64Changed = EnvOrAndChanged("GOARM64", fmt.Sprint(buildcfg.GOARM64))
|
||||
GOARM, goARMChanged = EnvOrAndChanged("GOARM", fmt.Sprint(buildcfg.GOARM))
|
||||
GO386, go386Changed = EnvOrAndChanged("GO386", buildcfg.GO386)
|
||||
GOAMD64, goAMD64Changed = EnvOrAndChanged("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
|
||||
GOMIPS, goMIPSChanged = EnvOrAndChanged("GOMIPS", buildcfg.GOMIPS)
|
||||
GOMIPS64, goMIPS64Changed = EnvOrAndChanged("GOMIPS64", buildcfg.GOMIPS64)
|
||||
GOPPC64, goPPC64Changed = EnvOrAndChanged("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
|
||||
GORISCV64, goRISCV64Changed = EnvOrAndChanged("GORISCV64", fmt.Sprintf("rva%du64", buildcfg.GORISCV64))
|
||||
GOWASM, goWASMChanged = EnvOrAndChanged("GOWASM", fmt.Sprint(buildcfg.GOWASM))
|
||||
|
||||
GOPROXY = envOr("GOPROXY", "")
|
||||
GOSUMDB = envOr("GOSUMDB", "")
|
||||
GOPRIVATE = Getenv("GOPRIVATE")
|
||||
GONOPROXY = envOr("GONOPROXY", GOPRIVATE)
|
||||
GONOSUMDB = envOr("GONOSUMDB", GOPRIVATE)
|
||||
GOINSECURE = Getenv("GOINSECURE")
|
||||
GOVCS = Getenv("GOVCS")
|
||||
GOPROXY, GOPROXYChanged = EnvOrAndChanged("GOPROXY", "")
|
||||
GOSUMDB, GOSUMDBChanged = EnvOrAndChanged("GOSUMDB", "")
|
||||
GOPRIVATE = Getenv("GOPRIVATE")
|
||||
GONOPROXY, GONOPROXYChanged = EnvOrAndChanged("GONOPROXY", GOPRIVATE)
|
||||
GONOSUMDB, GONOSUMDBChanged = EnvOrAndChanged("GONOSUMDB", GOPRIVATE)
|
||||
GOINSECURE = Getenv("GOINSECURE")
|
||||
GOVCS = Getenv("GOVCS")
|
||||
)
|
||||
|
||||
// EnvOrAndChanged returns the environment variable value
|
||||
// and reports whether it differs from the default value.
|
||||
func EnvOrAndChanged(name, def string) (string, bool) {
|
||||
val := Getenv(name)
|
||||
if val != "" {
|
||||
return val, val != def
|
||||
}
|
||||
return def, false
|
||||
}
|
||||
|
||||
var SumdbDir = gopathDir("pkg/sumdb")
|
||||
|
||||
// GetArchEnv returns the name and setting of the
|
||||
// GOARCH-specific architecture environment variable.
|
||||
// If the current architecture has no GOARCH-specific variable,
|
||||
// GetArchEnv returns empty key and value.
|
||||
func GetArchEnv() (key, val string) {
|
||||
func GetArchEnv() (key, val string, changed bool) {
|
||||
switch Goarch {
|
||||
case "arm":
|
||||
return "GOARM", GOARM
|
||||
return "GOARM", GOARM, goARMChanged
|
||||
case "arm64":
|
||||
return "GOARM64", GOARM64
|
||||
return "GOARM64", GOARM64, goARM64Changed
|
||||
case "386":
|
||||
return "GO386", GO386
|
||||
return "GO386", GO386, go386Changed
|
||||
case "amd64":
|
||||
return "GOAMD64", GOAMD64
|
||||
return "GOAMD64", GOAMD64, goAMD64Changed
|
||||
case "mips", "mipsle":
|
||||
return "GOMIPS", GOMIPS
|
||||
return "GOMIPS", GOMIPS, goMIPSChanged
|
||||
case "mips64", "mips64le":
|
||||
return "GOMIPS64", GOMIPS64
|
||||
return "GOMIPS64", GOMIPS64, goMIPS64Changed
|
||||
case "ppc64", "ppc64le":
|
||||
return "GOPPC64", GOPPC64
|
||||
return "GOPPC64", GOPPC64, goPPC64Changed
|
||||
case "riscv64":
|
||||
return "GORISCV64", GORISCV64
|
||||
return "GORISCV64", GORISCV64, goRISCV64Changed
|
||||
case "wasm":
|
||||
return "GOWASM", GOWASM
|
||||
return "GOWASM", GOWASM, goWASMChanged
|
||||
}
|
||||
return "", ""
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
// envOr returns Getenv(key) if set, or else def.
|
||||
|
|
@ -565,6 +586,7 @@ func gopathDir(rel string) string {
|
|||
return filepath.Join(list[0], rel)
|
||||
}
|
||||
|
||||
// Keep consistent with go/build.defaultGOPATH.
|
||||
func gopath(ctxt build.Context) string {
|
||||
if len(ctxt.GOPATH) > 0 {
|
||||
return ctxt.GOPATH
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
|
|||
sh := work.NewShell("", fmt.Print)
|
||||
|
||||
if cleanCache {
|
||||
dir := cache.DefaultDir()
|
||||
dir, _ := cache.DefaultDir()
|
||||
if dir != "off" {
|
||||
// Remove the cache subdirectories but not the top cache directory.
|
||||
// The top cache directory may have been created with special permissions
|
||||
|
|
@ -180,7 +180,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
|
|||
// Instead of walking through the entire cache looking for test results,
|
||||
// we write a file to the cache indicating that all test results from before
|
||||
// right now are to be ignored.
|
||||
dir := cache.DefaultDir()
|
||||
dir, _ := cache.DefaultDir()
|
||||
if dir != "off" {
|
||||
f, err := lockedfile.Edit(filepath.Join(dir, "testexpire.txt"))
|
||||
if err == nil {
|
||||
|
|
|
|||
|
|
@ -29,10 +29,11 @@ import (
|
|||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/work"
|
||||
"cmd/internal/quoted"
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
var CmdEnv = &base.Command{
|
||||
UsageLine: "go env [-json] [-u] [-w] [var ...]",
|
||||
UsageLine: "go env [-json] [-changed] [-u] [-w] [var ...]",
|
||||
Short: "print Go environment information",
|
||||
Long: `
|
||||
Env prints Go environment information.
|
||||
|
|
@ -53,6 +54,10 @@ The -w flag requires one or more arguments of the
|
|||
form NAME=VALUE and changes the default settings
|
||||
of the named environment variables to the given values.
|
||||
|
||||
The -changed flag prints only those settings whose effective
|
||||
value differs from the default value that would be obtained in
|
||||
an empty environment with no prior uses of the -w flag.
|
||||
|
||||
For more about environment variables, see 'go help environment'.
|
||||
`,
|
||||
}
|
||||
|
|
@ -64,19 +69,20 @@ func init() {
|
|||
}
|
||||
|
||||
var (
|
||||
envJson = CmdEnv.Flag.Bool("json", false, "")
|
||||
envU = CmdEnv.Flag.Bool("u", false, "")
|
||||
envW = CmdEnv.Flag.Bool("w", false, "")
|
||||
envJson = CmdEnv.Flag.Bool("json", false, "")
|
||||
envU = CmdEnv.Flag.Bool("u", false, "")
|
||||
envW = CmdEnv.Flag.Bool("w", false, "")
|
||||
envChanged = CmdEnv.Flag.Bool("changed", false, "")
|
||||
)
|
||||
|
||||
func MkEnv() []cfg.EnvVar {
|
||||
envFile, _ := cfg.EnvFile()
|
||||
envFile, envFileChanged, _ := cfg.EnvFile()
|
||||
env := []cfg.EnvVar{
|
||||
{Name: "GO111MODULE", Value: cfg.Getenv("GO111MODULE")},
|
||||
{Name: "GOARCH", Value: cfg.Goarch},
|
||||
{Name: "GOARCH", Value: cfg.Goarch, Changed: cfg.Goarch != runtime.GOARCH},
|
||||
{Name: "GOBIN", Value: cfg.GOBIN},
|
||||
{Name: "GOCACHE", Value: cache.DefaultDir()},
|
||||
{Name: "GOENV", Value: envFile},
|
||||
{Name: "GOCACHE"},
|
||||
{Name: "GOENV", Value: envFile, Changed: envFileChanged},
|
||||
{Name: "GOEXE", Value: cfg.ExeSuffix},
|
||||
|
||||
// List the raw value of GOEXPERIMENT, not the cleaned one.
|
||||
|
|
@ -90,63 +96,84 @@ func MkEnv() []cfg.EnvVar {
|
|||
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
|
||||
{Name: "GOHOSTOS", Value: runtime.GOOS},
|
||||
{Name: "GOINSECURE", Value: cfg.GOINSECURE},
|
||||
{Name: "GOMODCACHE", Value: cfg.GOMODCACHE},
|
||||
{Name: "GONOPROXY", Value: cfg.GONOPROXY},
|
||||
{Name: "GONOSUMDB", Value: cfg.GONOSUMDB},
|
||||
{Name: "GOOS", Value: cfg.Goos},
|
||||
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
|
||||
{Name: "GOMODCACHE", Value: cfg.GOMODCACHE, Changed: cfg.GOMODCACHEChanged},
|
||||
{Name: "GONOPROXY", Value: cfg.GONOPROXY, Changed: cfg.GONOPROXYChanged},
|
||||
{Name: "GONOSUMDB", Value: cfg.GONOSUMDB, Changed: cfg.GONOSUMDBChanged},
|
||||
{Name: "GOOS", Value: cfg.Goos, Changed: cfg.Goos != runtime.GOOS},
|
||||
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH, Changed: cfg.GOPATHChanged},
|
||||
{Name: "GOPRIVATE", Value: cfg.GOPRIVATE},
|
||||
{Name: "GOPROXY", Value: cfg.GOPROXY},
|
||||
{Name: "GOPROXY", Value: cfg.GOPROXY, Changed: cfg.GOPROXYChanged},
|
||||
{Name: "GOROOT", Value: cfg.GOROOT},
|
||||
{Name: "GOSUMDB", Value: cfg.GOSUMDB},
|
||||
{Name: "GOSUMDB", Value: cfg.GOSUMDB, Changed: cfg.GOSUMDBChanged},
|
||||
{Name: "GOTMPDIR", Value: cfg.Getenv("GOTMPDIR")},
|
||||
{Name: "GOTOOLCHAIN", Value: cfg.Getenv("GOTOOLCHAIN")},
|
||||
{Name: "GOTOOLDIR", Value: build.ToolDir},
|
||||
{Name: "GOVCS", Value: cfg.GOVCS},
|
||||
{Name: "GOVERSION", Value: runtime.Version()},
|
||||
{Name: "GODEBUG", Value: os.Getenv("GODEBUG")},
|
||||
{Name: "GOTELEMETRY", Value: telemetry.Mode()},
|
||||
{Name: "GOTELEMETRYDIR", Value: telemetry.Dir()},
|
||||
}
|
||||
|
||||
for i := range env {
|
||||
switch env[i].Name {
|
||||
case "GO111MODULE":
|
||||
if env[i].Value != "on" && env[i].Value != "" {
|
||||
env[i].Changed = true
|
||||
}
|
||||
case "GOBIN", "GOEXPERIMENT", "GOFLAGS", "GOINSECURE", "GOPRIVATE", "GOTMPDIR", "GOVCS":
|
||||
if env[i].Value != "" {
|
||||
env[i].Changed = true
|
||||
}
|
||||
case "GOCACHE":
|
||||
env[i].Value, env[i].Changed = cache.DefaultDir()
|
||||
case "GOTOOLCHAIN":
|
||||
if env[i].Value != "auto" {
|
||||
env[i].Changed = true
|
||||
}
|
||||
case "GODEBUG":
|
||||
env[i].Value = os.Getenv("GODEBUG")
|
||||
env[i].Changed = env[i].Value != ""
|
||||
}
|
||||
}
|
||||
|
||||
if work.GccgoBin != "" {
|
||||
env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin})
|
||||
env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoBin, Changed: true})
|
||||
} else {
|
||||
env = append(env, cfg.EnvVar{Name: "GCCGO", Value: work.GccgoName})
|
||||
}
|
||||
|
||||
key, val := cfg.GetArchEnv()
|
||||
if key != "" {
|
||||
env = append(env, cfg.EnvVar{Name: key, Value: val})
|
||||
goarch, val, changed := cfg.GetArchEnv()
|
||||
if goarch != "" {
|
||||
env = append(env, cfg.EnvVar{Name: goarch, Value: val, Changed: changed})
|
||||
}
|
||||
|
||||
cc := cfg.Getenv("CC")
|
||||
ccChanged := true
|
||||
if cc == "" {
|
||||
ccChanged = false
|
||||
cc = cfg.DefaultCC(cfg.Goos, cfg.Goarch)
|
||||
}
|
||||
cxx := cfg.Getenv("CXX")
|
||||
cxxChanged := true
|
||||
if cxx == "" {
|
||||
cxxChanged = false
|
||||
cxx = cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
|
||||
}
|
||||
env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")})
|
||||
env = append(env, cfg.EnvVar{Name: "CC", Value: cc})
|
||||
env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx})
|
||||
ar, arChanged := cfg.EnvOrAndChanged("AR", "ar")
|
||||
env = append(env, cfg.EnvVar{Name: "AR", Value: ar, Changed: arChanged})
|
||||
env = append(env, cfg.EnvVar{Name: "CC", Value: cc, Changed: ccChanged})
|
||||
env = append(env, cfg.EnvVar{Name: "CXX", Value: cxx, Changed: cxxChanged})
|
||||
|
||||
if cfg.BuildContext.CgoEnabled {
|
||||
env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1"})
|
||||
env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "1", Changed: cfg.CGOChanged})
|
||||
} else {
|
||||
env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "0"})
|
||||
env = append(env, cfg.EnvVar{Name: "CGO_ENABLED", Value: "0", Changed: cfg.CGOChanged})
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func envOr(name, def string) string {
|
||||
val := cfg.Getenv(name)
|
||||
if val != "" {
|
||||
return val
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func findEnv(env []cfg.EnvVar, name string) string {
|
||||
for _, e := range env {
|
||||
if e.Name == name {
|
||||
|
|
@ -206,7 +233,7 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
|
|||
return q
|
||||
}
|
||||
|
||||
return []cfg.EnvVar{
|
||||
ret := []cfg.EnvVar{
|
||||
// Note: Update the switch in runEnv below when adding to this list.
|
||||
{Name: "CGO_CFLAGS", Value: join(cflags)},
|
||||
{Name: "CGO_CPPFLAGS", Value: join(cppflags)},
|
||||
|
|
@ -216,6 +243,21 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
|
|||
{Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
|
||||
{Name: "GOGCCFLAGS", Value: join(cmd[3:])},
|
||||
}
|
||||
|
||||
for i := range ret {
|
||||
ev := &ret[i]
|
||||
switch ev.Name {
|
||||
case "GOGCCFLAGS": // GOGCCFLAGS cannot be modified
|
||||
case "CGO_CPPFLAGS":
|
||||
ev.Changed = ev.Value != ""
|
||||
case "PKG_CONFIG":
|
||||
ev.Changed = ev.Value != cfg.DefaultPkgConfig
|
||||
case "CGO_CXXFLAGS", "CGO_CFLAGS", "CGO_FFLAGS", "GGO_LDFLAGS":
|
||||
ev.Changed = ev.Value != work.DefaultCFlags
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// argKey returns the KEY part of the arg KEY=VAL, or else arg itself.
|
||||
|
|
@ -297,27 +339,43 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
|
|||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
if *envJson {
|
||||
// Show only the named vars.
|
||||
if !*envChanged {
|
||||
if *envJson {
|
||||
var es []cfg.EnvVar
|
||||
for _, name := range args {
|
||||
e := cfg.EnvVar{Name: name, Value: findEnv(env, name)}
|
||||
es = append(es, e)
|
||||
}
|
||||
env = es
|
||||
} else {
|
||||
// Print just the values, without names.
|
||||
for _, name := range args {
|
||||
fmt.Printf("%s\n", findEnv(env, name))
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Show only the changed, named vars.
|
||||
var es []cfg.EnvVar
|
||||
for _, name := range args {
|
||||
e := cfg.EnvVar{Name: name, Value: findEnv(env, name)}
|
||||
es = append(es, e)
|
||||
}
|
||||
printEnvAsJSON(es)
|
||||
} else {
|
||||
for _, name := range args {
|
||||
fmt.Printf("%s\n", findEnv(env, name))
|
||||
for _, e := range env {
|
||||
if e.Name == name {
|
||||
es = append(es, e)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
env = es
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// print
|
||||
if *envJson {
|
||||
printEnvAsJSON(env)
|
||||
return
|
||||
printEnvAsJSON(env, *envChanged)
|
||||
} else {
|
||||
PrintEnv(os.Stdout, env, *envChanged)
|
||||
}
|
||||
|
||||
PrintEnv(os.Stdout, env)
|
||||
}
|
||||
|
||||
func runEnvW(args []string) {
|
||||
|
|
@ -423,12 +481,15 @@ func checkBuildConfig(add map[string]string, del map[string]bool) error {
|
|||
}
|
||||
|
||||
// PrintEnv prints the environment variables to w.
|
||||
func PrintEnv(w io.Writer, env []cfg.EnvVar) {
|
||||
func PrintEnv(w io.Writer, env []cfg.EnvVar, onlyChanged bool) {
|
||||
for _, e := range env {
|
||||
if e.Name != "TERM" {
|
||||
if runtime.GOOS != "plan9" && bytes.Contains([]byte(e.Value), []byte{0}) {
|
||||
base.Fatalf("go: internal error: encountered null byte in environment variable %s on non-plan9 platform", e.Name)
|
||||
}
|
||||
if onlyChanged && !e.Changed {
|
||||
continue
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
default:
|
||||
fmt.Fprintf(w, "%s=%s\n", e.Name, shellQuote(e.Value))
|
||||
|
|
@ -503,12 +564,15 @@ func batchEscape(s string) string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func printEnvAsJSON(env []cfg.EnvVar) {
|
||||
func printEnvAsJSON(env []cfg.EnvVar, onlyChanged bool) {
|
||||
m := make(map[string]string)
|
||||
for _, e := range env {
|
||||
if e.Name == "TERM" {
|
||||
continue
|
||||
}
|
||||
if onlyChanged && !e.Changed {
|
||||
continue
|
||||
}
|
||||
m[e.Name] = e.Value
|
||||
}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
|
|
@ -591,7 +655,7 @@ func checkEnvWrite(key, val string) error {
|
|||
}
|
||||
|
||||
func readEnvFileLines(mustExist bool) []string {
|
||||
file, err := cfg.EnvFile()
|
||||
file, _, err := cfg.EnvFile()
|
||||
if file == "" {
|
||||
if mustExist {
|
||||
base.Fatalf("go: cannot find go env config: %v", err)
|
||||
|
|
@ -655,7 +719,7 @@ func updateEnvFile(add map[string]string, del map[string]bool) {
|
|||
}
|
||||
}
|
||||
|
||||
file, err := cfg.EnvFile()
|
||||
file, _, err := cfg.EnvFile()
|
||||
if file == "" {
|
||||
base.Fatalf("go: cannot find go env config: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func FuzzPrintEnvEscape(f *testing.F) {
|
|||
if runtime.GOOS == "windows" {
|
||||
b.WriteString("@echo off\n")
|
||||
}
|
||||
PrintEnv(&b, []cfg.EnvVar{{Name: "var", Value: s}})
|
||||
PrintEnv(&b, []cfg.EnvVar{{Name: "var", Value: s}}, false)
|
||||
var want string
|
||||
if runtime.GOOS == "windows" {
|
||||
fmt.Fprintf(&b, "echo \"%%var%%\"\n")
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
package load
|
||||
|
||||
import (
|
||||
"cmd/go/internal/modload"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
|
|
@ -13,6 +12,9 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/gover"
|
||||
"cmd/go/internal/modload"
|
||||
)
|
||||
|
||||
var ErrNotGoDebug = errors.New("not //go:debug line")
|
||||
|
|
@ -32,25 +34,10 @@ func ParseGoDebug(text string) (key, value string, err error) {
|
|||
if !ok {
|
||||
return "", "", fmt.Errorf("missing key=value")
|
||||
}
|
||||
if strings.ContainsAny(k, " \t") {
|
||||
return "", "", fmt.Errorf("key contains space")
|
||||
if err := modload.CheckGodebug("//go:debug setting", k, v); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if strings.ContainsAny(v, " \t") {
|
||||
return "", "", fmt.Errorf("value contains space")
|
||||
}
|
||||
if strings.ContainsAny(k, ",") {
|
||||
return "", "", fmt.Errorf("key contains comma")
|
||||
}
|
||||
if strings.ContainsAny(v, ",") {
|
||||
return "", "", fmt.Errorf("value contains comma")
|
||||
}
|
||||
|
||||
for _, info := range godebugs.All {
|
||||
if k == info.Name {
|
||||
return k, v, nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("unknown //go:debug setting %q", k)
|
||||
return k, v, nil
|
||||
}
|
||||
|
||||
// defaultGODEBUG returns the default GODEBUG setting for the main package p.
|
||||
|
|
@ -64,14 +51,21 @@ func defaultGODEBUG(p *Package, directives, testDirectives, xtestDirectives []bu
|
|||
if modload.RootMode == modload.NoRoot && p.Module != nil {
|
||||
// This is go install pkg@version or go run pkg@version.
|
||||
// Use the Go version from the package.
|
||||
// If there isn't one, then
|
||||
// If there isn't one, then assume Go 1.20,
|
||||
// the last version before GODEBUGs were introduced.
|
||||
goVersion = p.Module.GoVersion
|
||||
if goVersion == "" {
|
||||
goVersion = "1.20"
|
||||
}
|
||||
}
|
||||
|
||||
m := godebugForGoVersion(goVersion)
|
||||
var m map[string]string
|
||||
for _, g := range modload.MainModules.Godebugs() {
|
||||
if m == nil {
|
||||
m = make(map[string]string)
|
||||
}
|
||||
m[g.Key] = g.Value
|
||||
}
|
||||
for _, list := range [][]build.Directive{p.Internal.Build.Directives, directives, testDirectives, xtestDirectives} {
|
||||
for _, d := range list {
|
||||
k, v, err := ParseGoDebug(d.Text)
|
||||
|
|
@ -84,6 +78,23 @@ func defaultGODEBUG(p *Package, directives, testDirectives, xtestDirectives []bu
|
|||
m[k] = v
|
||||
}
|
||||
}
|
||||
if v, ok := m["default"]; ok {
|
||||
delete(m, "default")
|
||||
v = strings.TrimPrefix(v, "go")
|
||||
if gover.IsValid(v) {
|
||||
goVersion = v
|
||||
}
|
||||
}
|
||||
|
||||
defaults := godebugForGoVersion(goVersion)
|
||||
if defaults != nil {
|
||||
// Apply m on top of defaults.
|
||||
for k, v := range m {
|
||||
defaults[k] = v
|
||||
}
|
||||
m = defaults
|
||||
}
|
||||
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
|
|
|
|||
|
|
@ -604,51 +604,6 @@ func (sp *ImportStack) shorterThan(t []string) bool {
|
|||
// we return the same pointer each time.
|
||||
var packageCache = map[string]*Package{}
|
||||
|
||||
// ClearPackageCache clears the in-memory package cache and the preload caches.
|
||||
// It is only for use by GOPATH-based "go get".
|
||||
// TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function.
|
||||
func ClearPackageCache() {
|
||||
clear(packageCache)
|
||||
resolvedImportCache.Clear()
|
||||
packageDataCache.Clear()
|
||||
}
|
||||
|
||||
// ClearPackageCachePartial clears packages with the given import paths from the
|
||||
// in-memory package cache and the preload caches. It is only for use by
|
||||
// GOPATH-based "go get".
|
||||
// TODO(jayconrod): When GOPATH-based "go get" is removed, delete this function.
|
||||
func ClearPackageCachePartial(args []string) {
|
||||
shouldDelete := make(map[string]bool)
|
||||
for _, arg := range args {
|
||||
shouldDelete[arg] = true
|
||||
if p := packageCache[arg]; p != nil {
|
||||
delete(packageCache, arg)
|
||||
}
|
||||
}
|
||||
resolvedImportCache.DeleteIf(func(key importSpec) bool {
|
||||
return shouldDelete[key.path]
|
||||
})
|
||||
packageDataCache.DeleteIf(func(key string) bool {
|
||||
return shouldDelete[key]
|
||||
})
|
||||
}
|
||||
|
||||
// ReloadPackageNoFlags is like LoadImport but makes sure
|
||||
// not to use the package cache.
|
||||
// It is only for use by GOPATH-based "go get".
|
||||
// TODO(rsc): When GOPATH-based "go get" is removed, delete this function.
|
||||
func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package {
|
||||
p := packageCache[arg]
|
||||
if p != nil {
|
||||
delete(packageCache, arg)
|
||||
resolvedImportCache.DeleteIf(func(key importSpec) bool {
|
||||
return key.path == p.ImportPath
|
||||
})
|
||||
packageDataCache.Delete(p.ImportPath)
|
||||
}
|
||||
return LoadPackage(context.TODO(), PackageOpts{}, arg, base.Cwd(), stk, nil, 0)
|
||||
}
|
||||
|
||||
// dirToImportPath returns the pseudo-import path we use for a package
|
||||
// outside the Go path. It begins with _/ and then contains the full path
|
||||
// to the directory. If the package lives in c:\home\gopher\my\pkg then
|
||||
|
|
@ -2437,7 +2392,7 @@ func (p *Package) setBuildInfo(ctx context.Context, autoVCS bool) {
|
|||
appendSetting("GOEXPERIMENT", cfg.RawGOEXPERIMENT)
|
||||
}
|
||||
appendSetting("GOOS", cfg.BuildContext.GOOS)
|
||||
if key, val := cfg.GetArchEnv(); key != "" && val != "" {
|
||||
if key, val, _ := cfg.GetArchEnv(); key != "" && val != "" {
|
||||
appendSetting(key, val)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@ flags are specified, as in 'go mod edit -fmt'.
|
|||
|
||||
The -module flag changes the module's path (the go.mod file's module line).
|
||||
|
||||
The -godebug=key=value flag adds a godebug key=value line,
|
||||
replacing any existing godebug lines with the given key.
|
||||
|
||||
The -dropgodebug=key flag drops any existing godebug lines
|
||||
with the given key.
|
||||
|
||||
The -require=path@version and -droprequire=path flags
|
||||
add and drop a requirement on the given module path and version.
|
||||
Note that -require overrides any existing requirements on path.
|
||||
|
|
@ -52,6 +58,14 @@ Users should prefer 'go get path@version' or 'go get path@none',
|
|||
which make other go.mod adjustments as needed to satisfy
|
||||
constraints imposed by other modules.
|
||||
|
||||
The -go=version flag sets the expected Go language version.
|
||||
This flag is mainly for tools that understand Go version dependencies.
|
||||
Users should prefer 'go get go@version'.
|
||||
|
||||
The -toolchain=version flag sets the Go toolchain to use.
|
||||
This flag is mainly for tools that understand Go version dependencies.
|
||||
Users should prefer 'go get toolchain@version'.
|
||||
|
||||
The -exclude=path@version and -dropexclude=path@version flags
|
||||
add and drop an exclusion for the given module path and version.
|
||||
Note that -exclude=path@version is a no-op if that exclusion already exists.
|
||||
|
|
@ -73,13 +87,9 @@ retraction on the given version. The version may be a single version
|
|||
like "v1.2.3" or a closed interval like "[v1.1.0,v1.1.9]". Note that
|
||||
-retract=version is a no-op if that retraction already exists.
|
||||
|
||||
The -require, -droprequire, -exclude, -dropexclude, -replace,
|
||||
-dropreplace, -retract, and -dropretract editing flags may be repeated,
|
||||
and the changes are applied in the order given.
|
||||
|
||||
The -go=version flag sets the expected Go language version.
|
||||
|
||||
The -toolchain=name flag sets the Go toolchain to use.
|
||||
The -godebug, -dropgodebug, -require, -droprequire, -exclude, -dropexclude,
|
||||
-replace, -dropreplace, -retract, and -dropretract editing flags may be
|
||||
repeated, and the changes are applied in the order given.
|
||||
|
||||
The -print flag prints the final go.mod in its text format instead of
|
||||
writing it back to go.mod.
|
||||
|
|
@ -96,6 +106,7 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
|||
Module ModPath
|
||||
Go string
|
||||
Toolchain string
|
||||
Godebug []Godebug
|
||||
Require []Require
|
||||
Exclude []Module
|
||||
Replace []Replace
|
||||
|
|
@ -107,9 +118,14 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
|||
Deprecated string
|
||||
}
|
||||
|
||||
type Godebug struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type Require struct {
|
||||
Path string
|
||||
Version string
|
||||
Path string
|
||||
Version string
|
||||
Indirect bool
|
||||
}
|
||||
|
||||
|
|
@ -155,12 +171,14 @@ func (f flagFunc) Set(s string) error { f(s); return nil }
|
|||
func init() {
|
||||
cmdEdit.Run = runEdit // break init cycle
|
||||
|
||||
cmdEdit.Flag.Var(flagFunc(flagGodebug), "godebug", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropGodebug), "dropgodebug", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagRequire), "require", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropRequire), "droprequire", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagExclude), "exclude", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagRetract), "retract", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropRetract), "dropretract", "")
|
||||
|
||||
|
|
@ -369,6 +387,28 @@ func allowedVersionArg(arg string) bool {
|
|||
return !modfile.MustQuote(arg)
|
||||
}
|
||||
|
||||
// flagGodebug implements the -godebug flag.
|
||||
func flagGodebug(arg string) {
|
||||
key, value, ok := strings.Cut(arg, "=")
|
||||
if !ok || strings.ContainsAny(arg, "\"`',") {
|
||||
base.Fatalf("go: -godebug=%s: need key=value", arg)
|
||||
}
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.AddGodebug(key, value); err != nil {
|
||||
base.Fatalf("go: -godebug=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagDropGodebug implements the -dropgodebug flag.
|
||||
func flagDropGodebug(arg string) {
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.DropGodebug(arg); err != nil {
|
||||
base.Fatalf("go: -dropgodebug=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagRequire implements the -require flag.
|
||||
func flagRequire(arg string) {
|
||||
path, version := parsePathVersion("require", arg)
|
||||
|
|
|
|||
|
|
@ -147,7 +147,8 @@ func GetPackage(modroot, pkgdir string) (*IndexPackage, error) {
|
|||
// using the index, for instance because the index is disabled, or the package
|
||||
// is not in a module.
|
||||
func GetModule(modroot string) (*Module, error) {
|
||||
if !enabled || cache.DefaultDir() == "off" {
|
||||
dir, _ := cache.DefaultDir()
|
||||
if !enabled || dir == "off" {
|
||||
return nil, errDisabled
|
||||
}
|
||||
if modroot == "" {
|
||||
|
|
|
|||
|
|
@ -795,13 +795,13 @@ func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements,
|
|||
case pruned:
|
||||
return updatePrunedRoots(ctx, direct, rs, pkgs, add, rootsImported)
|
||||
case workspace:
|
||||
return updateWorkspaceRoots(ctx, rs, add)
|
||||
return updateWorkspaceRoots(ctx, direct, rs, add)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported pruning mode: %v", rs.pruning))
|
||||
}
|
||||
}
|
||||
|
||||
func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Version) (*Requirements, error) {
|
||||
func updateWorkspaceRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) {
|
||||
if len(add) != 0 {
|
||||
// add should be empty in workspace mode because workspace mode implies
|
||||
// -mod=readonly, which in turn implies no new requirements. The code path
|
||||
|
|
@ -812,7 +812,7 @@ func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Ve
|
|||
// return an error.
|
||||
panic("add is not empty")
|
||||
}
|
||||
return rs, nil
|
||||
return newRequirements(workspace, rs.rootModules, direct), nil
|
||||
}
|
||||
|
||||
// tidyPrunedRoots returns a minimal set of root requirements that maintains the
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/godebugs"
|
||||
"internal/lazyregexp"
|
||||
"io"
|
||||
"os"
|
||||
|
|
@ -241,6 +242,27 @@ func (mms *MainModuleSet) GoVersion() string {
|
|||
return gover.DefaultGoModVersion
|
||||
}
|
||||
|
||||
// Godebugs returns the godebug lines set on the single module, in module mode,
|
||||
// or on the go.work file in workspace mode.
|
||||
// The caller must not modify the result.
|
||||
func (mms *MainModuleSet) Godebugs() []*modfile.Godebug {
|
||||
if inWorkspaceMode() {
|
||||
if mms.workFile != nil {
|
||||
return mms.workFile.Godebug
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if mms != nil && len(mms.versions) == 1 {
|
||||
f := mms.ModFile(mms.mustGetSingleMainModule())
|
||||
if f == nil {
|
||||
// Special case: we are outside a module, like 'go run x.go'.
|
||||
return nil
|
||||
}
|
||||
return f.Godebug
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Toolchain returns the toolchain set on the single module, in module mode,
|
||||
// or the go.work file in workspace mode.
|
||||
func (mms *MainModuleSet) Toolchain() string {
|
||||
|
|
@ -675,6 +697,12 @@ func loadWorkFile(path string) (workFile *modfile.WorkFile, modRoots []string, e
|
|||
modRoots = append(modRoots, modRoot)
|
||||
}
|
||||
|
||||
for _, g := range wf.Godebug {
|
||||
if err := CheckGodebug("godebug", g.Key, g.Value); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return wf, modRoots, nil
|
||||
}
|
||||
|
||||
|
|
@ -914,6 +942,19 @@ func loadModFile(ctx context.Context, opts *PackageOpts) (*Requirements, error)
|
|||
}
|
||||
}
|
||||
|
||||
if !inWorkspaceMode() {
|
||||
ok := true
|
||||
for _, g := range f.Godebug {
|
||||
if err := CheckGodebug("godebug", g.Key, g.Value); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%s: %v", base.ShortPath(filepath.Dir(gomod)), err))
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
modFiles = append(modFiles, f)
|
||||
mainModule := f.Module.Mod
|
||||
mainModules = append(mainModules, mainModule)
|
||||
|
|
@ -1257,6 +1298,7 @@ func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainModules
|
||||
}
|
||||
|
||||
|
|
@ -1276,6 +1318,7 @@ func requirementsFromModFiles(ctx context.Context, workFile *modfile.WorkFile, m
|
|||
toolchain = workFile.Toolchain.Name
|
||||
}
|
||||
roots = appendGoAndToolchainRoots(roots, goVersion, toolchain, direct)
|
||||
direct = directRequirements(modFiles)
|
||||
} else {
|
||||
pruning = pruningForGoVersion(MainModules.GoVersion())
|
||||
if len(modFiles) != 1 {
|
||||
|
|
@ -1297,6 +1340,18 @@ const (
|
|||
withToolchainRoot = true
|
||||
)
|
||||
|
||||
func directRequirements(modFiles []*modfile.File) map[string]bool {
|
||||
direct := make(map[string]bool)
|
||||
for _, modFile := range modFiles {
|
||||
for _, r := range modFile.Require {
|
||||
if !r.Indirect {
|
||||
direct[r.Mod.Path] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return direct
|
||||
}
|
||||
|
||||
func rootsFromModFile(m module.Version, modFile *modfile.File, addToolchainRoot addToolchainRoot) (roots []module.Version, direct map[string]bool) {
|
||||
direct = make(map[string]bool)
|
||||
padding := 2 // Add padding for the toolchain and go version, added upon return.
|
||||
|
|
@ -2054,3 +2109,33 @@ func suggestGopkgIn(path string) string {
|
|||
}
|
||||
return url + ".v" + m
|
||||
}
|
||||
|
||||
func CheckGodebug(verb, k, v string) error {
|
||||
if strings.ContainsAny(k, " \t") {
|
||||
return fmt.Errorf("key contains space")
|
||||
}
|
||||
if strings.ContainsAny(v, " \t") {
|
||||
return fmt.Errorf("value contains space")
|
||||
}
|
||||
if strings.ContainsAny(k, ",") {
|
||||
return fmt.Errorf("key contains comma")
|
||||
}
|
||||
if strings.ContainsAny(v, ",") {
|
||||
return fmt.Errorf("value contains comma")
|
||||
}
|
||||
if k == "default" {
|
||||
if !strings.HasPrefix(v, "go") || !gover.IsValid(v[len("go"):]) {
|
||||
return fmt.Errorf("value for default= must be goVERSION")
|
||||
}
|
||||
if gover.Compare(v[len("go"):], gover.Local()) > 0 {
|
||||
return fmt.Errorf("default=%s too new (toolchain is go%s)", v, gover.Local())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for _, info := range godebugs.All {
|
||||
if k == info.Name {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unknown %s %q", verb, k)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1375,10 +1375,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
|
|||
Module: dep.mod,
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if pkg.err == nil && cfg.BuildMod != "mod" {
|
||||
} else if pkg.err == nil && cfg.BuildMod != "mod" {
|
||||
if v, ok := rs.rootSelected(dep.mod.Path); !ok || v != dep.mod.Version {
|
||||
// dep.mod is not an explicit dependency, but needs to be.
|
||||
// Because we are not in "mod" mode, we will not be able to update it.
|
||||
|
|
|
|||
|
|
@ -180,41 +180,3 @@ func (c *Cache[K, V]) Get(key K) (V, bool) {
|
|||
}
|
||||
return e.result, true
|
||||
}
|
||||
|
||||
// Clear removes all entries in the cache.
|
||||
//
|
||||
// Concurrent calls to Get may return old values. Concurrent calls to Do
|
||||
// may return old values or store results in entries that have been deleted.
|
||||
//
|
||||
// TODO(jayconrod): Delete this after the package cache clearing functions
|
||||
// in internal/load have been removed.
|
||||
func (c *Cache[K, V]) Clear() {
|
||||
c.m.Clear()
|
||||
}
|
||||
|
||||
// Delete removes an entry from the map. It is safe to call Delete for an
|
||||
// entry that does not exist. Delete will return quickly, even if the result
|
||||
// for a key is still being computed; the computation will finish, but the
|
||||
// result won't be accessible through the cache.
|
||||
//
|
||||
// TODO(jayconrod): Delete this after the package cache clearing functions
|
||||
// in internal/load have been removed.
|
||||
func (c *Cache[K, V]) Delete(key K) {
|
||||
c.m.Delete(key)
|
||||
}
|
||||
|
||||
// DeleteIf calls pred for each key in the map. If pred returns true for a key,
|
||||
// DeleteIf removes the corresponding entry. If the result for a key is
|
||||
// still being computed, DeleteIf will remove the entry without waiting for
|
||||
// the computation to finish. The result won't be accessible through the cache.
|
||||
//
|
||||
// TODO(jayconrod): Delete this after the package cache clearing functions
|
||||
// in internal/load have been removed.
|
||||
func (c *Cache[K, V]) DeleteIf(pred func(key K) bool) {
|
||||
c.m.Range(func(key, _ any) bool {
|
||||
if key := key.(K); pred(key) {
|
||||
c.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(unix || windows)
|
||||
//go:build !unix
|
||||
|
||||
package script
|
||||
|
||||
func isETXTBSY(err error) bool {
|
||||
// syscall.ETXTBSY is only meaningful on Unix platforms.
|
||||
return false
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build unix || windows
|
||||
//go:build unix
|
||||
|
||||
package script
|
||||
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2024 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 telemetrycmd implements the "go telemetry" command.
|
||||
package telemetrycmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/internal/telemetry"
|
||||
)
|
||||
|
||||
var CmdTelemetry = &base.Command{
|
||||
UsageLine: "go telemetry [off|local|on]",
|
||||
Short: "manage telemetry data and settings",
|
||||
Long: `Telemetry is used to manage Go telemetry data and settings.
|
||||
|
||||
Telemetry can be in one of three modes: off, local, or on.
|
||||
|
||||
When telemetry is in local mode, counter data is written to the local file
|
||||
system, but will not be uploaded to remote servers.
|
||||
|
||||
When telemetry is off, local counter data is neither collected nor uploaded.
|
||||
|
||||
When telemetry is on, telemetry data is written to the local file system
|
||||
and periodically sent to https://telemetry.go.dev/. Uploaded data is used to
|
||||
help improve the Go toolchain and related tools, and it will be published as
|
||||
part of a public dataset.
|
||||
|
||||
For more details, see https://telemetry.go.dev/privacy.
|
||||
This data is collected in accordance with the Google Privacy Policy
|
||||
(https://policies.google.com/privacy).
|
||||
|
||||
To view the current telemetry mode, run "go telemetry".
|
||||
To disable telemetry uploading, but keep local data collection, run
|
||||
"go telemetry local".
|
||||
To enable both collection and uploading, run “go telemetry on”.
|
||||
To disable both collection and uploading, run "go telemetry off".
|
||||
|
||||
See https://go.dev/doc/telemetry for more information on telemetry.
|
||||
`,
|
||||
Run: runTelemetry,
|
||||
}
|
||||
|
||||
func init() {
|
||||
base.AddChdirFlag(&CmdTelemetry.Flag)
|
||||
}
|
||||
|
||||
func runTelemetry(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
fmt.Println(telemetry.Mode())
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) != 1 {
|
||||
cmd.Usage()
|
||||
}
|
||||
|
||||
mode := args[0]
|
||||
if mode != "local" && mode != "off" && mode != "on" {
|
||||
cmd.Usage()
|
||||
}
|
||||
if old := telemetry.Mode(); old == mode {
|
||||
return
|
||||
}
|
||||
|
||||
if err := telemetry.SetMode(mode); err != nil {
|
||||
base.Fatalf("go: failed to set the telemetry mode to %s: %v", mode, err)
|
||||
}
|
||||
if mode == "on" {
|
||||
fmt.Fprintln(os.Stderr, telemetryOnMessage())
|
||||
}
|
||||
}
|
||||
|
||||
func telemetryOnMessage() string {
|
||||
return `Telemetry uploading is now enabled and data will be periodically sent to
|
||||
https://telemetry.go.dev/. Uploaded data is used to help improve the Go
|
||||
toolchain and related tools, and it will be published as part of a public
|
||||
dataset.
|
||||
|
||||
For more details, see https://telemetry.go.dev/privacy.
|
||||
This data is collected in accordance with the Google Privacy Policy
|
||||
(https://policies.google.com/privacy).
|
||||
|
||||
To disable telemetry uploading, but keep local data collection, run
|
||||
“go telemetry local”.
|
||||
To disable both collection and uploading, run “go telemetry off“.`
|
||||
}
|
||||
|
|
@ -837,7 +837,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
|
|||
// Read testcache expiration time, if present.
|
||||
// (We implement go clean -testcache by writing an expiration date
|
||||
// instead of searching out and deleting test result cache entries.)
|
||||
if dir := cache.DefaultDir(); dir != "off" {
|
||||
if dir, _ := cache.DefaultDir(); dir != "off" {
|
||||
if data, _ := lockedfile.Read(filepath.Join(dir, "testexpire.txt")); len(data) > 0 && data[len(data)-1] == '\n' {
|
||||
if t, err := strconv.ParseInt(string(data[:len(data)-1]), 10, 64); err == nil {
|
||||
testCacheExpire = time.Unix(0, t)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ import (
|
|||
"cmd/internal/sys"
|
||||
)
|
||||
|
||||
const defaultCFlags = "-O2 -g"
|
||||
const DefaultCFlags = "-O2 -g"
|
||||
|
||||
// actionList returns the list of actions in the dag rooted at root
|
||||
// as visited in a depth-first post-order traversal.
|
||||
|
|
@ -337,7 +337,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
|
|||
}
|
||||
|
||||
// GOARM, GOMIPS, etc.
|
||||
key, val := cfg.GetArchEnv()
|
||||
key, val, _ := cfg.GetArchEnv()
|
||||
fmt.Fprintf(h, "%s=%s\n", key, val)
|
||||
|
||||
if cfg.CleanGOEXPERIMENT != "" {
|
||||
|
|
@ -1140,13 +1140,15 @@ type vetConfig struct {
|
|||
NonGoFiles []string // absolute paths to package non-Go files
|
||||
IgnoredFiles []string // absolute paths to ignored source files
|
||||
|
||||
ImportMap map[string]string // map import path in source code to package path
|
||||
PackageFile map[string]string // map package path to .a file with export data
|
||||
Standard map[string]bool // map package path to whether it's in the standard library
|
||||
PackageVetx map[string]string // map package path to vetx data from earlier vet run
|
||||
VetxOnly bool // only compute vetx data; don't report detected problems
|
||||
VetxOutput string // write vetx data to this output file
|
||||
GoVersion string // Go version for package
|
||||
ModulePath string // module path (may be "" on module error)
|
||||
ModuleVersion string // module version (may be "" on main module or module error)
|
||||
ImportMap map[string]string // map import path in source code to package path
|
||||
PackageFile map[string]string // map package path to .a file with export data
|
||||
Standard map[string]bool // map package path to whether it's in the standard library
|
||||
PackageVetx map[string]string // map package path to vetx data from earlier vet run
|
||||
VetxOnly bool // only compute vetx data; don't report detected problems
|
||||
VetxOutput string // write vetx data to this output file
|
||||
GoVersion string // Go version for package
|
||||
|
||||
SucceedOnTypecheckFailure bool // awful hack; see #18395 and below
|
||||
}
|
||||
|
|
@ -1187,6 +1189,11 @@ func buildVetConfig(a *Action, srcfiles []string) {
|
|||
v = gover.DefaultGoModVersion
|
||||
}
|
||||
vcfg.GoVersion = "go" + v
|
||||
|
||||
if a.Package.Module.Error == nil {
|
||||
vcfg.ModulePath = a.Package.Module.Path
|
||||
vcfg.ModuleVersion = a.Package.Module.Version
|
||||
}
|
||||
}
|
||||
a.vetCfg = vcfg
|
||||
for i, raw := range a.Package.Internal.RawImports {
|
||||
|
|
@ -1412,7 +1419,7 @@ func (b *Builder) printLinkerConfig(h io.Writer, p *load.Package) {
|
|||
}
|
||||
|
||||
// GOARM, GOMIPS, etc.
|
||||
key, val := cfg.GetArchEnv()
|
||||
key, val, _ := cfg.GetArchEnv()
|
||||
fmt.Fprintf(h, "%s=%s\n", key, val)
|
||||
|
||||
if cfg.CleanGOEXPERIMENT != "" {
|
||||
|
|
@ -2453,13 +2460,13 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
|
|||
|
||||
cmdArgs := str.StringList(compiler, flag)
|
||||
if strings.HasPrefix(flag, "-Wl,") /* linker flag */ {
|
||||
ldflags, err := buildFlags("LDFLAGS", defaultCFlags, nil, checkLinkerFlags)
|
||||
ldflags, err := buildFlags("LDFLAGS", DefaultCFlags, nil, checkLinkerFlags)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
cmdArgs = append(cmdArgs, ldflags...)
|
||||
} else { /* compiler flag, add "-c" */
|
||||
cflags, err := buildFlags("CFLAGS", defaultCFlags, nil, checkCompilerFlags)
|
||||
cflags, err := buildFlags("CFLAGS", DefaultCFlags, nil, checkCompilerFlags)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
|
@ -2700,16 +2707,16 @@ func (b *Builder) CFlags(p *load.Package) (cppflags, cflags, cxxflags, fflags, l
|
|||
if cppflags, err = buildFlags("CPPFLAGS", "", p.CgoCPPFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if cflags, err = buildFlags("CFLAGS", defaultCFlags, p.CgoCFLAGS, checkCompilerFlags); err != nil {
|
||||
if cflags, err = buildFlags("CFLAGS", DefaultCFlags, p.CgoCFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if cxxflags, err = buildFlags("CXXFLAGS", defaultCFlags, p.CgoCXXFLAGS, checkCompilerFlags); err != nil {
|
||||
if cxxflags, err = buildFlags("CXXFLAGS", DefaultCFlags, p.CgoCXXFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if fflags, err = buildFlags("FFLAGS", defaultCFlags, p.CgoFFLAGS, checkCompilerFlags); err != nil {
|
||||
if fflags, err = buildFlags("FFLAGS", DefaultCFlags, p.CgoFFLAGS, checkCompilerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
if ldflags, err = buildFlags("LDFLAGS", defaultCFlags, p.CgoLDFLAGS, checkLinkerFlags); err != nil {
|
||||
if ldflags, err = buildFlags("LDFLAGS", DefaultCFlags, p.CgoLDFLAGS, checkLinkerFlags); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -2812,7 +2819,10 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
|
|||
cgoflags = append(cgoflags, "-import_syscall=false")
|
||||
}
|
||||
|
||||
// Update $CGO_LDFLAGS with p.CgoLDFLAGS.
|
||||
// cgoLDFLAGS, which includes p.CgoLDFLAGS, can be very long.
|
||||
// Pass it to cgo on the command line, so that we use a
|
||||
// response file if necessary.
|
||||
//
|
||||
// These flags are recorded in the generated _cgo_gotypes.go file
|
||||
// using //go:cgo_ldflag directives, the compiler records them in the
|
||||
// object file for the package, and then the Go linker passes them
|
||||
|
|
@ -2820,12 +2830,16 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
|
|||
// consists of the original $CGO_LDFLAGS (unchecked) and all the
|
||||
// flags put together from source code (checked).
|
||||
cgoenv := b.cCompilerEnv()
|
||||
var ldflagsOption []string
|
||||
if len(cgoLDFLAGS) > 0 {
|
||||
flags := make([]string, len(cgoLDFLAGS))
|
||||
for i, f := range cgoLDFLAGS {
|
||||
flags[i] = strconv.Quote(f)
|
||||
}
|
||||
cgoenv = append(cgoenv, "CGO_LDFLAGS="+strings.Join(flags, " "))
|
||||
ldflagsOption = []string{"-ldflags=" + strings.Join(flags, " ")}
|
||||
|
||||
// Remove CGO_LDFLAGS from the environment.
|
||||
cgoenv = append(cgoenv, "CGO_LDFLAGS=")
|
||||
}
|
||||
|
||||
if cfg.BuildToolchainName == "gccgo" {
|
||||
|
|
@ -2863,7 +2877,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
|
|||
cgoflags = append(cgoflags, "-trimpath", strings.Join(trimpath, ";"))
|
||||
}
|
||||
|
||||
if err := sh.run(p.Dir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
|
||||
if err := sh.run(p.Dir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, ldflagsOption, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
outGo = append(outGo, gofiles...)
|
||||
|
|
|
|||
|
|
@ -114,7 +114,8 @@ func (sh *Shell) moveOrCopyFile(dst, src string, perm fs.FileMode, force bool) e
|
|||
// Otherwise fall back to standard copy.
|
||||
|
||||
// If the source is in the build cache, we need to copy it.
|
||||
if strings.HasPrefix(src, cache.DefaultDir()) {
|
||||
dir, _ := cache.DefaultDir()
|
||||
if strings.HasPrefix(src, dir) {
|
||||
return sh.CopyFile(dst, src, perm, force)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,12 @@ This reformatting is also implied by any other modifications that use or
|
|||
rewrite the go.mod file. The only time this flag is needed is if no other
|
||||
flags are specified, as in 'go work edit -fmt'.
|
||||
|
||||
The -godebug=key=value flag adds a godebug key=value line,
|
||||
replacing any existing godebug lines with the given key.
|
||||
|
||||
The -dropgodebug=key flag drops any existing godebug lines
|
||||
with the given key.
|
||||
|
||||
The -use=path and -dropuse=path flags
|
||||
add and drop a use directive from the go.work file's set of module directories.
|
||||
|
||||
|
|
@ -69,10 +75,16 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
|||
type GoWork struct {
|
||||
Go string
|
||||
Toolchain string
|
||||
Godebug []Godebug
|
||||
Use []Use
|
||||
Replace []Replace
|
||||
}
|
||||
|
||||
type Godebug struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type Use struct {
|
||||
DiskPath string
|
||||
ModulePath string
|
||||
|
|
@ -110,6 +122,8 @@ func (f flagFunc) Set(s string) error { f(s); return nil }
|
|||
func init() {
|
||||
cmdEdit.Run = runEditwork // break init cycle
|
||||
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkGodebug), "godebug", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkDropGodebug), "dropgodebug", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkUse), "use", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "")
|
||||
|
|
@ -206,6 +220,28 @@ func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
|
|||
modload.WriteWorkFile(gowork, workFile)
|
||||
}
|
||||
|
||||
// flagEditworkGodebug implements the -godebug flag.
|
||||
func flagEditworkGodebug(arg string) {
|
||||
key, value, ok := strings.Cut(arg, "=")
|
||||
if !ok || strings.ContainsAny(arg, "\"`',") {
|
||||
base.Fatalf("go: -godebug=%s: need key=value", arg)
|
||||
}
|
||||
workedits = append(workedits, func(f *modfile.WorkFile) {
|
||||
if err := f.AddGodebug(key, value); err != nil {
|
||||
base.Fatalf("go: -godebug=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagEditworkDropGodebug implements the -dropgodebug flag.
|
||||
func flagEditworkDropGodebug(arg string) {
|
||||
workedits = append(workedits, func(f *modfile.WorkFile) {
|
||||
if err := f.DropGodebug(arg); err != nil {
|
||||
base.Fatalf("go: -dropgodebug=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagEditworkUse implements the -use flag.
|
||||
func flagEditworkUse(arg string) {
|
||||
workedits = append(workedits, func(f *modfile.WorkFile) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import (
|
|||
"cmd/go/internal/modget"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/run"
|
||||
"cmd/go/internal/telemetrycmd"
|
||||
"cmd/go/internal/test"
|
||||
"cmd/go/internal/tool"
|
||||
"cmd/go/internal/toolchain"
|
||||
|
|
@ -61,6 +62,7 @@ func init() {
|
|||
modcmd.CmdMod,
|
||||
workcmd.CmdWork,
|
||||
run.CmdRun,
|
||||
telemetrycmd.CmdTelemetry,
|
||||
test.CmdTest,
|
||||
tool.CmdTool,
|
||||
version.CmdVersion,
|
||||
|
|
@ -92,10 +94,11 @@ var counterErrorsGOPATHEntryRelative = telemetry.NewCounter("go/errors:gopath-en
|
|||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
telemetry.StartWithUpload() // Open the telemetry counter file so counters can be written to it.
|
||||
telemetry.Start() // Open the telemetry counter file so counters can be written to it.
|
||||
handleChdirFlag()
|
||||
toolchain.Select()
|
||||
|
||||
telemetry.StartWithUpload() // Run the upload process. Opening the counter file is idempotent.
|
||||
flag.Usage = base.Usage
|
||||
flag.Parse()
|
||||
telemetry.Inc("go/invocations")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# Issue #66456
|
||||
|
||||
[!cgo] skip
|
||||
[GOOS:windows] skip
|
||||
[GOOS:plan9] skip
|
||||
|
||||
# Generate a file with a very long #cgo LDFLAGS line.
|
||||
# This used to cause "go build" to fail with "argument list too long".
|
||||
go generate
|
||||
|
||||
# Build with the generated file.
|
||||
go build
|
||||
|
||||
-- go.mod --
|
||||
module cgolongcmd
|
||||
|
||||
go 1.22
|
||||
-- generate.go --
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("package p\n")
|
||||
buf.WriteString("// #cgo LDFLAGS:")
|
||||
for i := range 10000 {
|
||||
fmt.Fprintf(&buf, " -Wl,-rpath,/nonexistentpath/%d", i)
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(`import "C"`+"\n")
|
||||
if err := os.WriteFile("generated.go", buf.Bytes(), 0o644); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
-- gen.go --
|
||||
package p
|
||||
|
||||
//go:generate go run generate.go
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Test query for non-defaults in the env
|
||||
|
||||
env GOTOOLCHAIN=local
|
||||
env GOSUMDB=nodefault
|
||||
env GOPROXY=nodefault
|
||||
env GO111MODULE=auto
|
||||
env CGO_CFLAGS=nodefault
|
||||
env CGO_CPPFLAGS=nodefault
|
||||
|
||||
go env -changed
|
||||
# linux output like GOTOOLCHAIN='local'
|
||||
# windows output like GOTOOLCHAIN=local
|
||||
stdout 'GOTOOLCHAIN=''?local''?'
|
||||
stdout 'GOSUMDB=''?nodefault''?'
|
||||
stdout 'GOPROXY=''?nodefault''?'
|
||||
stdout 'GO111MODULE=''?auto''?'
|
||||
stdout 'CGO_CFLAGS=''?nodefault''?'
|
||||
stdout 'CGO_CPPFLAGS=''?nodefault''?'
|
||||
|
||||
go env -changed -json
|
||||
stdout '"GOTOOLCHAIN": "local"'
|
||||
stdout '"GOSUMDB": "nodefault"'
|
||||
stdout '"GOPROXY": "nodefault"'
|
||||
stdout '"GO111MODULE": "auto"'
|
||||
stdout '"CGO_CFLAGS": "nodefault"'
|
||||
stdout '"CGO_CPPFLAGS": "nodefault"'
|
||||
|
||||
[GOOS:windows] env GOOS=linux
|
||||
[!GOOS:windows] env GOOS=windows
|
||||
[GOARCH:amd64] env GOARCH=arm64
|
||||
[!GOARCH:amd64] env GOARCH=amd64
|
||||
|
||||
go env -changed GOOS
|
||||
[GOOS:windows] stdout 'set GOOS=linux'
|
||||
[!GOOS:windows] stdout 'GOOS=''windows'''
|
||||
go env -changed GOARCH
|
||||
[GOARCH:amd64] stdout 'set GOARCH=arm64|GOARCH=''arm64'''
|
||||
[!GOARCH:amd64] stdout 'set GOARCH=amd64|GOARCH=''amd64'''
|
||||
|
||||
go env -changed -json GOOS
|
||||
[GOOS:windows] stdout '"GOOS": "linux"'
|
||||
[!GOOS:windows] stdout '"GOOS": "windows"'
|
||||
go env -changed -json GOARCH
|
||||
[GOARCH:amd64] stdout '"GOARCH": "arm64"'
|
||||
[!GOARCH:amd64] stdout '"GOARCH": "amd64"'
|
||||
|
|
@ -45,6 +45,35 @@ cp go.mod.21 go.mod
|
|||
stderr 'go: module . listed in go.work file requires go >= 1.21'
|
||||
rm go.work
|
||||
|
||||
# Go 1.21 go.mod with godebug default=go1.20
|
||||
rm go.work
|
||||
cp go.mod.21 go.mod
|
||||
go mod edit -godebug default=go1.20 -godebug asynctimerchan=0
|
||||
go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
|
||||
stdout panicnil=1
|
||||
stdout asynctimerchan=0
|
||||
|
||||
# Go 1.21 go.work with godebug default=go1.20
|
||||
cp go.work.21 go.work
|
||||
go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
|
||||
! stdout panicnil # go.work wins
|
||||
stdout asynctimerchan=1 # go.work wins
|
||||
go work edit -godebug default=go1.20 -godebug asynctimerchan=0
|
||||
go list -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
|
||||
stdout panicnil=1
|
||||
stdout asynctimerchan=0
|
||||
rm go.work
|
||||
|
||||
# Go 1.21 go.mod with //go:debug default=go1.20 in program
|
||||
cp go.mod.21 go.mod
|
||||
go list -tags godebug -f '{{.Module.GoVersion}} {{.DefaultGODEBUG}}'
|
||||
stdout panicnil=1
|
||||
stdout asynctimerchan=0
|
||||
|
||||
# Invalid //go:debug line should be diagnosed at build.
|
||||
! go build -tags godebugbad
|
||||
stderr 'invalid //go:debug: value contains space'
|
||||
|
||||
[short] skip
|
||||
|
||||
# Programs in Go 1.21 work module should trigger run-time error.
|
||||
|
|
@ -105,6 +134,19 @@ func main() {
|
|||
panic(nil)
|
||||
}
|
||||
|
||||
-- godebug.go --
|
||||
//go:build godebug
|
||||
//go:debug default=go1.20
|
||||
//go:debug asynctimerchan=0
|
||||
|
||||
package main
|
||||
|
||||
-- godebugbad.go --
|
||||
//go:build godebugbad
|
||||
//go:debug default=go1.20 asynctimerchan=0
|
||||
|
||||
package main
|
||||
|
||||
-- q/go.mod --
|
||||
go 1.20
|
||||
module q
|
||||
|
|
|
|||
|
|
@ -87,6 +87,16 @@ go mod init foo
|
|||
go mod edit -module local-only -require=other-local@v1.0.0 -replace other-local@v1.0.0=./other
|
||||
cmpenv go.mod go.mod.edit
|
||||
|
||||
# go mod edit -godebug
|
||||
cd $WORK/g
|
||||
cp go.mod.start go.mod
|
||||
go mod edit -godebug key=value
|
||||
cmpenv go.mod go.mod.edit
|
||||
go mod edit -dropgodebug key2
|
||||
cmpenv go.mod go.mod.edit
|
||||
go mod edit -dropgodebug key
|
||||
cmpenv go.mod go.mod.start
|
||||
|
||||
-- x.go --
|
||||
package x
|
||||
|
||||
|
|
@ -338,3 +348,13 @@ module m
|
|||
"Replace": null,
|
||||
"Retract": null
|
||||
}
|
||||
-- $WORK/g/go.mod.start --
|
||||
module g
|
||||
|
||||
go 1.10
|
||||
-- $WORK/g/go.mod.edit --
|
||||
module g
|
||||
|
||||
go 1.10
|
||||
|
||||
godebug key=value
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
# Test that ModuleDirect.Public is correctly set on go list output.
|
||||
# This is a regression test for issue #66789.
|
||||
|
||||
# In this test, the workspace contains modules example.com/a and
|
||||
# example.com/b. Module example.com/a has a direct requirement
|
||||
# on rsc.io/sampler, and an indirect requirement on golang.org/x/text
|
||||
# through rsc.io/isampler. Module example.com/b has a direct
|
||||
# requirement on example.com/c which is incorrectly marked as indirect
|
||||
# in module example.com/b's go.mod file.
|
||||
|
||||
# Check that go list -m processes the indirect annotations in the
|
||||
# go.mod file.
|
||||
go list -f '{{.Path}} {{.Indirect}}' -m all
|
||||
stdout 'example.com/a false'
|
||||
stdout 'example.com/b false'
|
||||
stdout 'rsc.io/sampler false'
|
||||
stdout 'golang.org/x/text true'
|
||||
stdout 'example.com/c true' # Uses the information in go.mod without checking imports.
|
||||
|
||||
# Check that 'go list all' correctly populates "indirect" module annotation.
|
||||
go list -f '{{.ImportPath}} {{with .Module}}{{.Indirect}}{{end}}' all
|
||||
stdout 'example.com/a false'
|
||||
stdout 'example.com/b false'
|
||||
stdout 'rsc.io/sampler false'
|
||||
stdout 'golang.org/x/text/language true'
|
||||
stdout 'example.com/c false'
|
||||
|
||||
-- go.work --
|
||||
go 1.23
|
||||
|
||||
use ./a
|
||||
use ./b
|
||||
-- a/go.mod --
|
||||
module example.com/a
|
||||
|
||||
go 1.23
|
||||
|
||||
require rsc.io/sampler v1.2.1
|
||||
|
||||
require golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
|
||||
-- a/a.go --
|
||||
package a
|
||||
|
||||
import "rsc.io/sampler"
|
||||
|
||||
func A() string {
|
||||
return sampler.Hello()
|
||||
}
|
||||
-- b/go.mod --
|
||||
module example.com/b
|
||||
|
||||
go 1.23
|
||||
|
||||
// The indrect comment below is inaccurate. Its purpose
|
||||
// is to test that it is corrected when enough packages
|
||||
// are loaded to correct it.
|
||||
|
||||
require example.com/c v1.0.0 // indirect
|
||||
|
||||
replace example.com/c => ../c
|
||||
-- b/b.go --
|
||||
package b
|
||||
|
||||
import "example.com/c"
|
||||
|
||||
func B() {
|
||||
c.C()
|
||||
}
|
||||
-- c/go.mod --
|
||||
module example.com/c
|
||||
|
||||
go 1.23
|
||||
-- c/c.go --
|
||||
package c
|
||||
|
||||
func C() {}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Tests for the telemetry subcommand,
|
||||
|
||||
# The script test framework sets TEST_TELEMETRY_DIR (overriding the
|
||||
# default telemetry dir location) and then checks that at least one
|
||||
# counter has been written per script tests.
|
||||
# Run go before unsetting TEST_TELEMETRY_DIR to make the tests happy.
|
||||
# We want to unset it so the environment we're testing is as close
|
||||
# to a user's environment.
|
||||
go help telemetry
|
||||
env TEST_TELEMETRY_DIR=
|
||||
|
||||
# Set userconfig dir, which is determined by os.UserConfigDir.
|
||||
# The telemetry dir is determined using that.
|
||||
mkdir $WORK/userconfig
|
||||
env AppData=$WORK\userconfig # windows
|
||||
[GOOS:windows] env userconfig=$AppData
|
||||
env HOME=$WORK/userconfig # darwin,unix,ios
|
||||
[GOOS:darwin] env userconfig=$HOME'/Library/Application Support'
|
||||
[GOOS:ios] env userconfig=$HOME'/Library/Application Support'
|
||||
[!GOOS:windows] [!GOOS:darwin] [!GOOS:ios] [!GOOS:plan9] env userconfig=$HOME/.config
|
||||
env home=$WORK/userconfig # plan9
|
||||
[GOOS:plan9] env userconfig=$home/lib
|
||||
|
||||
go telemetry
|
||||
stdout 'local'
|
||||
|
||||
go telemetry off
|
||||
go telemetry
|
||||
stdout 'off'
|
||||
go env GOTELEMETRY
|
||||
stdout 'off'
|
||||
|
||||
go telemetry local
|
||||
go telemetry
|
||||
stdout 'local'
|
||||
go env GOTELEMETRY
|
||||
stdout 'local'
|
||||
|
||||
go telemetry on
|
||||
go telemetry
|
||||
stdout 'on'
|
||||
go env GOTELEMETRY
|
||||
stdout 'on'
|
||||
|
||||
go env
|
||||
stdout 'GOTELEMETRY=''?on''?'
|
||||
stdout 'GOTELEMETRYDIR=''?'$userconfig'[\\/]go[\\/]telemetry''?'
|
||||
! go env -w GOTELEMETRY=off
|
||||
stderr '^go: unknown go command variable GOTELEMETRY$'
|
||||
! go env -w GOTELEMETRYDIR=foo
|
||||
stderr '^go: unknown go command variable GOTELEMETRYDIR$'
|
||||
|
|
@ -34,9 +34,20 @@ cmp stdout go.work.want_print
|
|||
go work edit -json -go 1.19 -use b -dropuse c -replace 'x.1@v1.4.0 = ../z' -dropreplace x.1 -dropreplace x.1@v1.3.0
|
||||
cmp stdout go.work.want_json
|
||||
|
||||
# go work edit -godebug
|
||||
cd $WORK/g
|
||||
cp go.work.start go.work
|
||||
go work edit -godebug key=value
|
||||
cmpenv go.work go.work.edit
|
||||
go work edit -dropgodebug key2
|
||||
cmpenv go.work go.work.edit
|
||||
go work edit -dropgodebug key
|
||||
cmpenv go.work go.work.start
|
||||
|
||||
# go work edit -print -fmt
|
||||
env GOWORK=$GOPATH/src/unformatted
|
||||
go work edit -print -fmt
|
||||
cmp stdout formatted
|
||||
cmp stdout $GOPATH/src/formatted
|
||||
|
||||
-- m/go.mod --
|
||||
module m
|
||||
|
|
@ -164,3 +175,13 @@ replace (
|
|||
x.1 v1.3.0 => y.1 v1.4.0
|
||||
x.1 v1.4.0 => ../z
|
||||
)
|
||||
-- $WORK/g/go.work.start --
|
||||
use g
|
||||
|
||||
go 1.10
|
||||
-- $WORK/g/go.work.edit --
|
||||
use g
|
||||
|
||||
go 1.10
|
||||
|
||||
godebug key=value
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cmd/internal/telemetry"
|
||||
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
|
|
@ -372,8 +374,11 @@ func main() {
|
|||
}
|
||||
|
||||
func gofmtMain(s *sequencer) {
|
||||
telemetry.Start()
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
telemetry.Inc("gofmt/invocations")
|
||||
telemetry.CountFlags("gofmt/flag:", *flag.CommandLine)
|
||||
|
||||
if *cpuprofile != "" {
|
||||
fdSem <- true
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ const (
|
|||
_ // was ObjFlagNeedNameExpansion
|
||||
ObjFlagFromAssembly // object is from asm src, not go
|
||||
ObjFlagUnlinkable // unlinkable package (linker will emit an error)
|
||||
ObjFlagStd // standard library package
|
||||
)
|
||||
|
||||
// Sym.Flag
|
||||
|
|
@ -304,6 +305,7 @@ const (
|
|||
SymFlagDict
|
||||
SymFlagPkgInit
|
||||
SymFlagLinkname
|
||||
SymFlagABIWrapper
|
||||
)
|
||||
|
||||
// Returns the length of the name of the symbol.
|
||||
|
|
@ -336,6 +338,7 @@ func (s *Sym) IsItab() bool { return s.Flag2()&SymFlagItab != 0 }
|
|||
func (s *Sym) IsDict() bool { return s.Flag2()&SymFlagDict != 0 }
|
||||
func (s *Sym) IsPkgInit() bool { return s.Flag2()&SymFlagPkgInit != 0 }
|
||||
func (s *Sym) IsLinkname() bool { return s.Flag2()&SymFlagLinkname != 0 }
|
||||
func (s *Sym) ABIWrapper() bool { return s.Flag2()&SymFlagABIWrapper != 0 }
|
||||
|
||||
func (s *Sym) SetName(x string, w *Writer) {
|
||||
binary.LittleEndian.PutUint32(s[:], uint32(len(x)))
|
||||
|
|
@ -882,3 +885,4 @@ func (r *Reader) Flags() uint32 {
|
|||
func (r *Reader) Shared() bool { return r.Flags()&ObjFlagShared != 0 }
|
||||
func (r *Reader) FromAssembly() bool { return r.Flags()&ObjFlagFromAssembly != 0 }
|
||||
func (r *Reader) Unlinkable() bool { return r.Flags()&ObjFlagUnlinkable != 0 }
|
||||
func (r *Reader) Std() bool { return r.Flags()&ObjFlagStd != 0 }
|
||||
|
|
|
|||
|
|
@ -889,9 +889,10 @@ var optab = []Optab{
|
|||
{obj.ANOP, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // nop variants, see #40689
|
||||
{obj.ANOP, C_ZREG, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
|
||||
{obj.ANOP, C_VREG, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0},
|
||||
{obj.ADUFFZERO, C_NONE, C_NONE, C_NONE, C_SBRA, C_NONE, 5, 4, 0, 0, 0}, // same as AB/ABL
|
||||
{obj.ADUFFCOPY, C_NONE, C_NONE, C_NONE, C_SBRA, C_NONE, 5, 4, 0, 0, 0}, // same as AB/ABL
|
||||
{obj.APCALIGN, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // align code
|
||||
{obj.ADUFFZERO, C_NONE, C_NONE, C_NONE, C_SBRA, C_NONE, 5, 4, 0, 0, 0}, // same as AB/ABL
|
||||
{obj.ADUFFCOPY, C_NONE, C_NONE, C_NONE, C_SBRA, C_NONE, 5, 4, 0, 0, 0}, // same as AB/ABL
|
||||
{obj.APCALIGN, C_LCON, C_NONE, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0, 0}, // align code
|
||||
{obj.APCALIGNMAX, C_LCON, C_NONE, C_NONE, C_LCON, C_NONE, 0, 0, 0, 0, 0}, // align code, conditional
|
||||
}
|
||||
|
||||
// Valid pstate field values, and value to use in instruction.
|
||||
|
|
@ -1109,13 +1110,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
m = o.size(c.ctxt, p)
|
||||
if m == 0 {
|
||||
switch p.As {
|
||||
case obj.APCALIGN:
|
||||
alignedValue := p.From.Offset
|
||||
m = pcAlignPadLength(ctxt, pc, alignedValue)
|
||||
// Update the current text symbol alignment value.
|
||||
if int32(alignedValue) > cursym.Func().Align {
|
||||
cursym.Func().Align = int32(alignedValue)
|
||||
}
|
||||
case obj.APCALIGN, obj.APCALIGNMAX:
|
||||
m = obj.AlignmentPadding(int32(pc), p, ctxt, cursym)
|
||||
break
|
||||
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
|
||||
continue
|
||||
|
|
@ -1181,9 +1177,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
|
||||
if m == 0 {
|
||||
switch p.As {
|
||||
case obj.APCALIGN:
|
||||
alignedValue := p.From.Offset
|
||||
m = pcAlignPadLength(ctxt, pc, alignedValue)
|
||||
case obj.APCALIGN, obj.APCALIGNMAX:
|
||||
m = obj.AlignmentPaddingLength(int32(pc), p, ctxt)
|
||||
break
|
||||
case obj.ANOP, obj.AFUNCDATA, obj.APCDATA:
|
||||
continue
|
||||
|
|
@ -1214,9 +1209,8 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|||
if sz > 4*len(out) {
|
||||
log.Fatalf("out array in span7 is too small, need at least %d for %v", sz/4, p)
|
||||
}
|
||||
if p.As == obj.APCALIGN {
|
||||
alignedValue := p.From.Offset
|
||||
v := pcAlignPadLength(c.ctxt, p.Pc, alignedValue)
|
||||
if p.As == obj.APCALIGN || p.As == obj.APCALIGNMAX {
|
||||
v := obj.AlignmentPaddingLength(int32(p.Pc), p, c.ctxt)
|
||||
for i = 0; i < int(v/4); i++ {
|
||||
// emit ANOOP instruction by the padding size
|
||||
c.ctxt.Arch.ByteOrder.PutUint32(bp, OP_NOOP)
|
||||
|
|
@ -3316,6 +3310,7 @@ func buildop(ctxt *obj.Link) {
|
|||
obj.AUNDEF,
|
||||
obj.AFUNCDATA,
|
||||
obj.APCALIGN,
|
||||
obj.APCALIGNMAX,
|
||||
obj.APCDATA,
|
||||
obj.ADUFFZERO,
|
||||
obj.ADUFFCOPY:
|
||||
|
|
|
|||
|
|
@ -416,6 +416,7 @@ const (
|
|||
AJMP
|
||||
ANOP
|
||||
APCALIGN
|
||||
APCALIGNMAX // currently x86, amd64 and arm64
|
||||
APCDATA
|
||||
ARET
|
||||
AGETCALLERPC
|
||||
|
|
@ -1047,6 +1048,7 @@ type Link struct {
|
|||
InParallel bool // parallel backend phase in effect
|
||||
UseBASEntries bool // use Base Address Selection Entries in location lists and PC ranges
|
||||
IsAsm bool // is the source assembly language, which may contain surprising idioms (e.g., call tables)
|
||||
Std bool // is standard library package
|
||||
|
||||
// state for writing objects
|
||||
Text []*LSym
|
||||
|
|
@ -1058,6 +1060,10 @@ type Link struct {
|
|||
// to Data.
|
||||
constSyms []*LSym
|
||||
|
||||
// Windows SEH symbols are also data symbols that can be created
|
||||
// concurrently.
|
||||
SEHSyms []*LSym
|
||||
|
||||
// pkgIdx maps package path to index. The index is used for
|
||||
// symbol reference in the object file.
|
||||
pkgIdx map[string]int32
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) {
|
|||
if ctxt.IsAsm {
|
||||
flags |= goobj.ObjFlagFromAssembly
|
||||
}
|
||||
if ctxt.Std {
|
||||
flags |= goobj.ObjFlagStd
|
||||
}
|
||||
h := goobj.Header{
|
||||
Magic: goobj.Magic,
|
||||
Fingerprint: ctxt.Fingerprint,
|
||||
|
|
@ -309,6 +312,7 @@ func (w *writer) StringTable() {
|
|||
const cutoff = int64(2e9) // 2 GB (or so; looks better in errors than 2^31)
|
||||
|
||||
func (w *writer) Sym(s *LSym) {
|
||||
name := s.Name
|
||||
abi := uint16(s.ABI())
|
||||
if s.Static() {
|
||||
abi = goobj.SymABIstatic
|
||||
|
|
@ -348,10 +352,15 @@ func (w *writer) Sym(s *LSym) {
|
|||
if s.IsPkgInit() {
|
||||
flag2 |= goobj.SymFlagPkgInit
|
||||
}
|
||||
if s.IsLinkname() || w.ctxt.IsAsm { // assembly reference is treated the same as linkname
|
||||
if s.IsLinkname() || (w.ctxt.IsAsm && name != "") || name == "main.main" {
|
||||
// Assembly reference is treated the same as linkname,
|
||||
// but not for unnamed (aux) symbols.
|
||||
// The runtime linknames main.main.
|
||||
flag2 |= goobj.SymFlagLinkname
|
||||
}
|
||||
name := s.Name
|
||||
if s.ABIWrapper() {
|
||||
flag2 |= goobj.SymFlagABIWrapper
|
||||
}
|
||||
if strings.HasPrefix(name, "gofile..") {
|
||||
name = filepath.ToSlash(name)
|
||||
}
|
||||
|
|
@ -788,14 +797,18 @@ func genFuncInfoSyms(ctxt *Link) {
|
|||
fn.FuncInfoSym = isym
|
||||
b.Reset()
|
||||
|
||||
auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym, fn.sehUnwindInfoSym}
|
||||
auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym}
|
||||
for _, s := range auxsyms {
|
||||
if s == nil || s.Size == 0 {
|
||||
continue
|
||||
}
|
||||
if s.OnList() {
|
||||
panic("a symbol is added to defs multiple times")
|
||||
}
|
||||
s.PkgIdx = goobj.PkgIdxSelf
|
||||
s.SymIdx = symidx
|
||||
s.Set(AttrIndexed, true)
|
||||
s.Set(AttrOnList, true)
|
||||
symidx++
|
||||
infosyms = append(infosyms, s)
|
||||
}
|
||||
|
|
@ -839,6 +852,9 @@ func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
|
|||
ver := ""
|
||||
if ctxt.Debugasm > 1 {
|
||||
ver = fmt.Sprintf("<%d>", s.ABI())
|
||||
if ctxt.Debugasm > 2 {
|
||||
ver += fmt.Sprintf("<idx %d %d>", s.PkgIdx, s.SymIdx)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver)
|
||||
if s.Type != 0 {
|
||||
|
|
|
|||
|
|
@ -472,12 +472,12 @@ var optabBase = []Optab{
|
|||
|
||||
{as: ACMP, a1: C_REG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMP, a1: C_REG, a6: C_S16CON, type_: 71, size: 4},
|
||||
{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_S16CON, type_: 71, size: 4},
|
||||
{as: ACMP, a1: C_REG, a6: C_S16CON, type_: 70, size: 4},
|
||||
{as: ACMP, a1: C_REG, a2: C_CREG, a6: C_S16CON, type_: 70, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a6: C_U16CON, type_: 71, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_U16CON, type_: 71, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a6: C_U16CON, type_: 70, size: 4},
|
||||
{as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_U16CON, type_: 70, size: 4},
|
||||
{as: AFCMPO, a1: C_FREG, a6: C_FREG, type_: 70, size: 4},
|
||||
{as: AFCMPO, a1: C_FREG, a2: C_CREG, a6: C_FREG, type_: 70, size: 4},
|
||||
{as: ATW, a1: C_32CON, a2: C_REG, a6: C_REG, type_: 60, size: 4},
|
||||
|
|
@ -3449,23 +3449,13 @@ func asmout(c *ctxt9, p *obj.Prog, o *Optab, out *[5]uint32) {
|
|||
|
||||
o1 = AOP_RRR(OP_MTCRF, uint32(p.From.Reg), 0, 0) | uint32(v)<<12
|
||||
|
||||
case 70: /* [f]cmp r,r,cr*/
|
||||
var r int
|
||||
if p.Reg == 0 {
|
||||
r = 0
|
||||
case 70: /* cmp* r,r,cr or cmp*i r,i,cr or fcmp f,f,cr or cmpeqb r,r */
|
||||
r := uint32(p.Reg&7) << 2
|
||||
if p.To.Type == obj.TYPE_CONST {
|
||||
o1 = AOP_IRR(c.opirr(p.As), r, uint32(p.From.Reg), uint32(uint16(p.To.Offset)))
|
||||
} else {
|
||||
r = (int(p.Reg) & 7) << 2
|
||||
o1 = AOP_RRR(c.oprrr(p.As), r, uint32(p.From.Reg), uint32(p.To.Reg))
|
||||
}
|
||||
o1 = AOP_RRR(c.oprrr(p.As), uint32(r), uint32(p.From.Reg), uint32(p.To.Reg))
|
||||
|
||||
case 71: /* cmp[l] r,i,cr*/
|
||||
var r int
|
||||
if p.Reg == 0 {
|
||||
r = 0
|
||||
} else {
|
||||
r = (int(p.Reg) & 7) << 2
|
||||
}
|
||||
o1 = AOP_RRR(c.opirr(p.As), uint32(r), uint32(p.From.Reg), 0) | uint32(c.regoff(&p.To))&0xffff
|
||||
|
||||
case 72: /* slbmte (Rb+Rs -> slb[Rb]) -> Rs, Rb */
|
||||
o1 = AOP_RRR(c.oprrr(p.As), uint32(p.From.Reg), 0, uint32(p.To.Reg))
|
||||
|
|
|
|||
|
|
@ -245,6 +245,13 @@ func (ctxt *Link) NumberSyms() {
|
|||
ctxt.Data = append(ctxt.Data, ctxt.constSyms...)
|
||||
ctxt.constSyms = nil
|
||||
|
||||
// So are SEH symbols.
|
||||
sort.Slice(ctxt.SEHSyms, func(i, j int) bool {
|
||||
return ctxt.SEHSyms[i].Name < ctxt.SEHSyms[j].Name
|
||||
})
|
||||
ctxt.Data = append(ctxt.Data, ctxt.SEHSyms...)
|
||||
ctxt.SEHSyms = nil
|
||||
|
||||
ctxt.pkgIdx = make(map[string]int32)
|
||||
ctxt.defs = []*LSym{}
|
||||
ctxt.hashed64defs = []*LSym{}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package obj
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/objabi"
|
||||
"fmt"
|
||||
"internal/abi"
|
||||
"internal/buildcfg"
|
||||
|
|
@ -642,6 +643,7 @@ var Anames = []string{
|
|||
"JMP",
|
||||
"NOP",
|
||||
"PCALIGN",
|
||||
"PCALIGNMAX",
|
||||
"PCDATA",
|
||||
"RET",
|
||||
"GETCALLERPC",
|
||||
|
|
@ -667,3 +669,62 @@ func abiDecorate(a *Addr, abiDetail bool) string {
|
|||
}
|
||||
return fmt.Sprintf("<%s>", a.Sym.ABI())
|
||||
}
|
||||
|
||||
// AlignmentPadding bytes to add to align code as requested.
|
||||
// Alignment is restricted to powers of 2 between 8 and 2048 inclusive.
|
||||
//
|
||||
// pc_: current offset in function, in bytes
|
||||
// p: a PCALIGN or PCALIGNMAX prog
|
||||
// ctxt: the context, for current function
|
||||
// cursym: current function being assembled
|
||||
// returns number of bytes of padding needed,
|
||||
// updates minimum alignment for the function.
|
||||
func AlignmentPadding(pc int32, p *Prog, ctxt *Link, cursym *LSym) int {
|
||||
v := AlignmentPaddingLength(pc, p, ctxt)
|
||||
requireAlignment(p.From.Offset, ctxt, cursym)
|
||||
return v
|
||||
}
|
||||
|
||||
// AlignmentPaddingLength is the number of bytes to add to align code as requested.
|
||||
// Alignment is restricted to powers of 2 between 8 and 2048 inclusive.
|
||||
// This only computes the length and does not update the (missing parameter)
|
||||
// current function's own required alignment.
|
||||
//
|
||||
// pc: current offset in function, in bytes
|
||||
// p: a PCALIGN or PCALIGNMAX prog
|
||||
// ctxt: the context, for current function
|
||||
// returns number of bytes of padding needed,
|
||||
func AlignmentPaddingLength(pc int32, p *Prog, ctxt *Link) int {
|
||||
a := p.From.Offset
|
||||
if !((a&(a-1) == 0) && 8 <= a && a <= 2048) {
|
||||
ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", a)
|
||||
return 0
|
||||
}
|
||||
pc64 := int64(pc)
|
||||
lob := pc64 & (a - 1) // Low Order Bits -- if not zero, then not aligned
|
||||
if p.As == APCALIGN {
|
||||
if lob != 0 {
|
||||
return int(a - lob)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
// emit as many as s bytes of padding to obtain alignment
|
||||
s := p.To.Offset
|
||||
if s < 0 || s >= a {
|
||||
ctxt.Diag("PCALIGNMAX 'amount' %d must be non-negative and smaller than the aligment %d\n", s, a)
|
||||
return 0
|
||||
}
|
||||
if s >= a-lob {
|
||||
return int(a - lob)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// requireAlignment ensures that the function is aligned enough to support
|
||||
// the required code alignment
|
||||
func requireAlignment(a int64, ctxt *Link, cursym *LSym) {
|
||||
// TODO remove explicit knowledge about AIX.
|
||||
if ctxt.Headtype != objabi.Haix && cursym.Func().Align < int32(a) {
|
||||
cursym.Func().Align = int32(a)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2036,29 +2036,21 @@ type nopPad struct {
|
|||
n int32 // Size of the pad
|
||||
}
|
||||
|
||||
// Padding bytes to add to align code as requested.
|
||||
// Alignment is restricted to powers of 2 between 8 and 2048 inclusive.
|
||||
// requireAlignment ensures that the function alignment is at
|
||||
// least as high as a, which should be a power of two
|
||||
// and between 8 and 2048, inclusive.
|
||||
//
|
||||
// pc: current offset in function, in bytes
|
||||
// a: requested alignment, in bytes
|
||||
// cursym: current function being assembled
|
||||
// returns number of bytes of padding needed
|
||||
func addpad(pc, a int64, ctxt *obj.Link, cursym *obj.LSym) int {
|
||||
// the boolean result indicates whether the alignment meets those constraints
|
||||
func requireAlignment(a int64, ctxt *obj.Link, cursym *obj.LSym) bool {
|
||||
if !((a&(a-1) == 0) && 8 <= a && a <= 2048) {
|
||||
ctxt.Diag("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d\n", a)
|
||||
return 0
|
||||
return false
|
||||
}
|
||||
|
||||
// By default function alignment is 32 bytes for amd64
|
||||
if cursym.Func().Align < int32(a) {
|
||||
cursym.Func().Align = int32(a)
|
||||
}
|
||||
|
||||
if pc&(a-1) != 0 {
|
||||
return int(a - (pc & (a - 1)))
|
||||
}
|
||||
|
||||
return 0
|
||||
return true
|
||||
}
|
||||
|
||||
func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
||||
|
|
@ -2144,17 +2136,17 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
|||
c0 := c
|
||||
c = pjc.padJump(ctxt, s, p, c)
|
||||
|
||||
if p.As == obj.APCALIGN {
|
||||
aln := p.From.Offset
|
||||
v := addpad(int64(c), aln, ctxt, s)
|
||||
if p.As == obj.APCALIGN || p.As == obj.APCALIGNMAX {
|
||||
v := obj.AlignmentPadding(c, p, ctxt, s)
|
||||
if v > 0 {
|
||||
s.Grow(int64(c) + int64(v))
|
||||
fillnop(s.P[c:], int(v))
|
||||
}
|
||||
|
||||
p.Pc = int64(c)
|
||||
c += int32(v)
|
||||
pPrev = p
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
if maxLoopPad > 0 && p.Back&branchLoopHead != 0 && c&(loopAlign-1) != 0 {
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ func populateSeh(ctxt *obj.Link, s *obj.LSym) (sehsym *obj.LSym) {
|
|||
s.Type = objabi.SSEHUNWINDINFO
|
||||
s.Set(obj.AttrDuplicateOK, true)
|
||||
s.Set(obj.AttrLocal, true)
|
||||
s.Set(obj.AttrContentAddressable, true)
|
||||
if exceptionHandler != nil {
|
||||
r := obj.Addrel(s)
|
||||
r.Off = int32(len(buf.data) - 4)
|
||||
|
|
@ -158,8 +159,6 @@ func populateSeh(ctxt *obj.Link, s *obj.LSym) (sehsym *obj.LSym) {
|
|||
r.Sym = exceptionHandler
|
||||
r.Type = objabi.R_PEIMAGEOFF
|
||||
}
|
||||
// Note: AttrContentAddressable cannot be set here,
|
||||
// because the content-addressable-handling code
|
||||
// does not know about aux symbols.
|
||||
ctxt.SEHSyms = append(ctxt.SEHSyms, s)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue