Merge remote-tracking branch 'origin/master' into feature/atomic-and-or

Change-Id: I4fa61dfec1b161a8b6820bd739928f688576d2f5
This commit is contained in:
Mauri de Souza Meneguzzo 2024-05-16 19:05:51 -03:00
commit f174297007
260 changed files with 3993 additions and 1499 deletions

View File

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

5
api/next/66249.txt Normal file
View File

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

1
api/next/66339.txt Normal file
View File

@ -0,0 +1 @@
pkg go/ast, func Preorder(Node) iter.Seq[Node] #66339

1
api/next/66405.txt Normal file
View File

@ -0,0 +1 @@
pkg net/http, type Request struct, Pattern string #66405

4
api/next/67143.txt Normal file
View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,2 @@
The new [Preorder] function returns a convenient iterator over all the
nodes of a syntax tree.

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
<!-- pacify TestCheckAPIFragments -->

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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