diff --git a/doc/go1.16.html b/doc/go1.16.html
index 2fb7222482..509956fbf2 100644
--- a/doc/go1.16.html
+++ b/doc/go1.16.html
@@ -38,6 +38,17 @@ Do not send CLs removing the interior tags from such phrases.
netbsd/arm64 port).
+
386
+
+
+ As announced in the Go 1.15 release notes,
+ Go 1.16 drops support for x87 mode compilation (GO386=387).
+ Support for non-SSE2 processors is now available using soft float
+ mode (GO386=softfloat).
+ Users running on non-SSE2 processors should replace GO386=387
+ with GO386=softfloat.
+
+
Tools
@@ -162,6 +173,8 @@ Do not send CLs removing the interior tags from such phrases.
TODO: update with final numbers later in the release.
+
+
Core library
@@ -200,6 +213,14 @@ Do not send CLs removing the interior tags from such phrases.
with "use of closed network connection".
+ For interface types and values, Method,
+ MethodByName, and
+ NumMethod now
+ operate on the interface's exported method set, rather than its full method set.
+
@@ -273,5 +294,18 @@ Do not send CLs removing the interior tags from such phrases.
of the form "Range": "bytes=--N" where "-N" is a negative suffix length, for
example "Range": "bytes=--2". It now replies with a 416 "Range Not Satisfiable" response.
+
+
+ Cookies set with SameSiteDefaultMode now behave according to the current
+ spec (no attribute is set) instead of generating a SameSite key without a value.
+
diff --git a/doc/install-source.html b/doc/install-source.html
index 86a4644c0c..c6dc3aed43 100644
--- a/doc/install-source.html
+++ b/doc/install-source.html
@@ -666,16 +666,13 @@ For example, you should not set $GOHOSTARCH to
arm on an x86 system.
-
$GO386 (for 386 only, default is auto-detected
-if built on either 386 or amd64, 387 otherwise)
+
$GO386 (for 386 only, defaults to sse2)
-This controls the code generated by gc to use either the 387 floating-point unit
-(set to 387) or SSE2 instructions (set to sse2) for
-floating point computations.
+This variable controls how gc implements floating point computations.
-
GO386=387: use x87 for floating point operations; should support all x86 chips (Pentium MMX or later).
-
GO386=sse2: use SSE2 for floating point operations; has better performance than 387, but only available on Pentium 4/Opteron/Athlon 64 or later.
+
GO386=softfloat: use software floating point operations; should support all x86 chips (Pentium MMX or later).
+
GO386=sse2: use SSE2 for floating point operations; has better performance but only available on Pentium 4/Opteron/Athlon 64 or later.
diff --git a/misc/cgo/test/sigaltstack.go b/misc/cgo/test/sigaltstack.go
index 27b753a147..034cc4b371 100644
--- a/misc/cgo/test/sigaltstack.go
+++ b/misc/cgo/test/sigaltstack.go
@@ -62,7 +62,7 @@ import (
func testSigaltstack(t *testing.T) {
switch {
- case runtime.GOOS == "solaris", runtime.GOOS == "illumos", (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64":
+ case runtime.GOOS == "solaris", runtime.GOOS == "illumos", runtime.GOOS == "ios" && runtime.GOARCH == "arm64":
t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
}
diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go
index 2e223ea369..6ed25d8948 100644
--- a/misc/cgo/testcarchive/carchive_test.go
+++ b/misc/cgo/testcarchive/carchive_test.go
@@ -603,7 +603,7 @@ func TestExtar(t *testing.T) {
if runtime.Compiler == "gccgo" {
t.Skip("skipping -extar test when using gccgo")
}
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skip("shell scripts are not executable on iOS hosts")
}
diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s
index e277c04b7c..7f495b90bb 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64.s
@@ -81,6 +81,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
SHA512H2 V4.D2, V3, V2 // 628464ce
SHA512SU0 V9.D2, V8.D2 // 2881c0ce
SHA512SU1 V7.D2, V6.D2, V5.D2 // c58867ce
+ VRAX1 V26.D2, V29.D2, V30.D2 // be8f7ace
+ VXAR $63, V27.D2, V21.D2, V26.D2 // bafe9bce
VADDV V0.S4, V0 // 00b8b14e
VMOVI $82, V0.B16 // 40e6024f
VUADDLV V6.B16, V6 // c638306e
@@ -139,6 +141,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VTBL V14.B16, [V3.B16, V4.B16, V5.B16], V17.B16 // 71400e4e
VTBL V13.B16, [V29.B16, V30.B16, V31.B16, V0.B16], V28.B16 // bc630d4e
VTBL V3.B8, [V27.B16], V8.B8 // 6803030e
+ VEOR3 V2.B16, V7.B16, V12.B16, V25.B16 // 990907ce
+ VBCAX V1.B16, V2.B16, V26.B16, V31.B16 // 5f0722ce
VZIP1 V16.H8, V3.H8, V19.H8 // 7338504e
VZIP2 V22.D2, V25.D2, V21.D2 // 357bd64e
VZIP1 V6.D2, V9.D2, V11.D2 // 2b39c64e
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 5c44fb72f4..7d02ac3c54 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -170,35 +170,51 @@ func usage() {
var ptrSizeMap = map[string]int64{
"386": 4,
+ "alpha": 8,
"amd64": 8,
"arm": 4,
"arm64": 8,
+ "m68k": 4,
"mips": 4,
"mipsle": 4,
"mips64": 8,
"mips64le": 8,
+ "nios2": 4,
+ "ppc": 4,
"ppc64": 8,
"ppc64le": 8,
+ "riscv": 4,
"riscv64": 8,
"s390": 4,
"s390x": 8,
+ "sh": 4,
+ "shbe": 4,
+ "sparc": 4,
"sparc64": 8,
}
var intSizeMap = map[string]int64{
"386": 4,
+ "alpha": 8,
"amd64": 8,
"arm": 4,
"arm64": 8,
+ "m68k": 4,
"mips": 4,
"mipsle": 4,
"mips64": 8,
"mips64le": 8,
+ "nios2": 4,
+ "ppc": 4,
"ppc64": 8,
"ppc64le": 8,
+ "riscv": 4,
"riscv64": 8,
"s390": 4,
"s390x": 8,
+ "sh": 4,
+ "shbe": 4,
+ "sparc": 4,
"sparc64": 8,
}
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index 5ced66c0da..0cb9fe9e62 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -126,30 +126,6 @@ const (
aliasTag
)
-// untype returns the "pseudo" untyped type for a Ctype (import/export use only).
-// (we can't use a pre-initialized array because we must be sure all types are
-// set up)
-func untype(ctype Ctype) *types.Type {
- switch ctype {
- case CTINT:
- return types.Idealint
- case CTRUNE:
- return types.Idealrune
- case CTFLT:
- return types.Idealfloat
- case CTCPLX:
- return types.Idealcomplex
- case CTSTR:
- return types.Idealstring
- case CTBOOL:
- return types.Idealbool
- case CTNIL:
- return types.Types[TNIL]
- }
- Fatalf("exporter: unknown Ctype")
- return nil
-}
-
var predecl []*types.Type // initialized lazily
func predeclared() []*types.Type {
@@ -184,13 +160,13 @@ func predeclared() []*types.Type {
types.Errortype,
// untyped types
- untype(CTBOOL),
- untype(CTINT),
- untype(CTRUNE),
- untype(CTFLT),
- untype(CTCPLX),
- untype(CTSTR),
- untype(CTNIL),
+ types.UntypedBool,
+ types.UntypedInt,
+ types.UntypedRune,
+ types.UntypedFloat,
+ types.UntypedComplex,
+ types.UntypedString,
+ types.Types[TNIL],
// package unsafe
types.Types[TUNSAFEPTR],
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
index d881be485e..b28c0fc8d0 100644
--- a/src/cmd/compile/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -1019,17 +1019,17 @@ func nodlit(v Val) *Node {
func idealType(ct Ctype) *types.Type {
switch ct {
case CTSTR:
- return types.Idealstring
+ return types.UntypedString
case CTBOOL:
- return types.Idealbool
+ return types.UntypedBool
case CTINT:
- return types.Idealint
+ return types.UntypedInt
case CTRUNE:
- return types.Idealrune
+ return types.UntypedRune
case CTFLT:
- return types.Idealfloat
+ return types.UntypedFloat
case CTCPLX:
- return types.Idealcomplex
+ return types.UntypedComplex
case CTNIL:
return types.Types[TNIL]
}
@@ -1080,17 +1080,17 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) {
func ctype(t *types.Type) Ctype {
switch t {
- case types.Idealbool:
+ case types.UntypedBool:
return CTBOOL
- case types.Idealstring:
+ case types.UntypedString:
return CTSTR
- case types.Idealint:
+ case types.UntypedInt:
return CTINT
- case types.Idealrune:
+ case types.UntypedRune:
return CTRUNE
- case types.Idealfloat:
+ case types.UntypedFloat:
return CTFLT
- case types.Idealcomplex:
+ case types.UntypedComplex:
return CTCPLX
}
Fatalf("bad type %v", t)
@@ -1111,17 +1111,17 @@ func defaultType(t *types.Type) *types.Type {
}
switch t {
- case types.Idealbool:
+ case types.UntypedBool:
return types.Types[TBOOL]
- case types.Idealstring:
+ case types.UntypedString:
return types.Types[TSTRING]
- case types.Idealint:
+ case types.UntypedInt:
return types.Types[TINT]
- case types.Idealrune:
+ case types.UntypedRune:
return types.Runetype
- case types.Idealfloat:
+ case types.UntypedFloat:
return types.Types[TFLOAT64]
- case types.Idealcomplex:
+ case types.UntypedComplex:
return types.Types[TCOMPLEX128]
}
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index 44bea2b1fd..839c2c2c75 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -96,7 +96,7 @@ func importsym(ipkg *types.Pkg, s *types.Sym, op Op) *Node {
return n
}
-// pkgtype returns the named type declared by symbol s.
+// importtype returns the named type declared by symbol s.
// If no such type has been declared yet, a forward declaration is returned.
// ipkg is the package being imported
func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
index d4af451506..36b596338f 100644
--- a/src/cmd/compile/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -773,17 +773,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
var name string
switch t {
- case types.Idealbool:
+ case types.UntypedBool:
name = "untyped bool"
- case types.Idealstring:
+ case types.UntypedString:
name = "untyped string"
- case types.Idealint:
+ case types.UntypedInt:
name = "untyped int"
- case types.Idealrune:
+ case types.UntypedRune:
name = "untyped rune"
- case types.Idealfloat:
+ case types.UntypedFloat:
name = "untyped float"
- case types.Idealcomplex:
+ case types.UntypedComplex:
name = "untyped complex"
default:
name = basicnames[t.Etype]
@@ -1333,7 +1333,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
n.Orig.exprfmt(s, prec, mode)
return
}
- if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.Idealbool && n.Type != types.Idealstring {
+ if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.UntypedBool && n.Type != types.UntypedString {
// Need parens when type begins with what might
// be misinterpreted as a unary operator: * or <-.
if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == types.Crecv) {
diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go
index 3be3b0a213..df08a4a6c2 100644
--- a/src/cmd/compile/internal/gc/iexport.go
+++ b/src/cmd/compile/internal/gc/iexport.go
@@ -751,11 +751,11 @@ func (w *exportWriter) param(f *types.Field) {
func constTypeOf(typ *types.Type) Ctype {
switch typ {
- case types.Idealint, types.Idealrune:
+ case types.UntypedInt, types.UntypedRune:
return CTINT
- case types.Idealfloat:
+ case types.UntypedFloat:
return CTFLT
- case types.Idealcomplex:
+ case types.UntypedComplex:
return CTCPLX
}
@@ -780,8 +780,8 @@ func constTypeOf(typ *types.Type) Ctype {
}
func (w *exportWriter) value(typ *types.Type, v Val) {
- if typ.IsUntyped() {
- typ = untype(v.Ctype())
+ if vt := idealType(v.Ctype()); typ.IsUntyped() && typ != vt {
+ Fatalf("exporter: untyped type mismatch, have: %v, want: %v", typ, vt)
}
w.typ(typ)
diff --git a/src/cmd/compile/internal/gc/iimport.go b/src/cmd/compile/internal/gc/iimport.go
index 0c5e469c57..5f107eeec7 100644
--- a/src/cmd/compile/internal/gc/iimport.go
+++ b/src/cmd/compile/internal/gc/iimport.go
@@ -375,7 +375,7 @@ func (p *importReader) value() (typ *types.Type, v Val) {
v.U = p.string()
case CTINT:
x := new(Mpint)
- x.Rune = typ == types.Idealrune
+ x.Rune = typ == types.UntypedRune
p.mpint(&x.Val, typ)
v.U = x
case CTFLT:
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index 21429af782..229fcfeaee 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -1275,8 +1275,9 @@ func dtypesym(t *types.Type) *obj.LSym {
}
ot = dgopkgpath(lsym, ot, tpkg)
+ xcount := sort.Search(n, func(i int) bool { return !types.IsExported(m[i].name.Name) })
ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t))
- ot = duintptr(lsym, ot, uint64(n))
+ ot = duintptr(lsym, ot, uint64(xcount))
ot = duintptr(lsym, ot, uint64(n))
dataAdd := imethodSize() * n
ot = dextratype(lsym, ot, t, dataAdd)
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 32394c4b1a..e363f4f723 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -50,12 +50,10 @@ func initssaconfig() {
// Caching is disabled in the backend, so generating these here avoids allocations.
_ = types.NewPtr(types.Types[TINTER]) // *interface{}
_ = types.NewPtr(types.NewPtr(types.Types[TSTRING])) // **string
- _ = types.NewPtr(types.NewPtr(types.Idealstring)) // **string
_ = types.NewPtr(types.NewSlice(types.Types[TINTER])) // *[]interface{}
_ = types.NewPtr(types.NewPtr(types.Bytetype)) // **byte
_ = types.NewPtr(types.NewSlice(types.Bytetype)) // *[]byte
_ = types.NewPtr(types.NewSlice(types.Types[TSTRING])) // *[]string
- _ = types.NewPtr(types.NewSlice(types.Idealstring)) // *[]string
_ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[TUINT8]))) // ***uint8
_ = types.NewPtr(types.Types[TINT16]) // *int16
_ = types.NewPtr(types.Types[TINT64]) // *int64
@@ -4251,6 +4249,7 @@ func (s *state) openDeferExit() {
s.lastDeferExit = deferExit
s.lastDeferCount = len(s.openDefers)
zeroval := s.constInt8(types.Types[TUINT8], 0)
+ testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
// Test for and run defers in reverse order
for i := len(s.openDefers) - 1; i >= 0; i-- {
r := s.openDefers[i]
@@ -4288,23 +4287,38 @@ func (s *state) openDeferExit() {
stksize := fn.Type.ArgWidth()
var ACArgs []ssa.Param
var ACResults []ssa.Param
+ var callArgs []*ssa.Value
if r.rcvr != nil {
// rcvr in case of OCALLINTER
v := s.load(r.rcvr.Type.Elem(), r.rcvr)
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
- s.store(types.Types[TUINTPTR], addr, v)
+ if testLateExpansion {
+ callArgs = append(callArgs, v)
+ } else {
+ s.store(types.Types[TUINTPTR], addr, v)
+ }
}
for j, argAddrVal := range r.argVals {
f := getParam(r.n, j)
pt := types.NewPtr(f.Type)
- addr := s.constOffPtrSP(pt, argStart+f.Offset)
- ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart + f.Offset)})
- if !canSSAType(f.Type) {
- s.move(f.Type, addr, argAddrVal)
+ ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + f.Offset)})
+ if testLateExpansion {
+ var a *ssa.Value
+ if !canSSAType(f.Type) {
+ a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem())
+ } else {
+ a = s.load(f.Type, argAddrVal)
+ }
+ callArgs = append(callArgs, a)
} else {
- argVal := s.load(f.Type, argAddrVal)
- s.storeType(f.Type, addr, argVal, 0, false)
+ addr := s.constOffPtrSP(pt, argStart+f.Offset)
+ if !canSSAType(f.Type) {
+ s.move(f.Type, addr, argAddrVal)
+ } else {
+ argVal := s.load(f.Type, argAddrVal)
+ s.storeType(f.Type, addr, argVal, 0, false)
+ }
}
}
var call *ssa.Value
@@ -4312,13 +4326,31 @@ func (s *state) openDeferExit() {
v := s.load(r.closure.Type.Elem(), r.closure)
s.maybeNilCheckClosure(v, callDefer)
codeptr := s.rawLoad(types.Types[TUINTPTR], v)
- call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, v, s.mem())
+ aux := ssa.ClosureAuxCall(ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, s.mem())
+ call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
+ call.AddArgs(callArgs...)
+ } else {
+ call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, aux, codeptr, v, s.mem())
+ }
} else {
- // Do a static call if the original call was a static function or method
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem())
+ aux := ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, s.mem())
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ } else {
+ // Do a static call if the original call was a static function or method
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ }
}
call.AuxInt = stksize
- s.vars[&memVar] = call
+ if testLateExpansion {
+ s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
+ } else {
+ s.vars[&memVar] = call
+ }
// Make sure that the stack slots with pointers are kept live
// through the call (which is a pre-emption point). Also, we will
// use the first call of the last defer exit to compute liveness
@@ -4375,9 +4407,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
switch n.Op {
case OCALLFUNC:
- if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) {
- testLateExpansion = true
- }
+ testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC {
sym = fn.Sym
break
@@ -4392,9 +4422,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
if fn.Op != ODOTMETH {
s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
}
- if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) {
- testLateExpansion = true
- }
+ testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
if k == callNormal {
sym = fn.Sym
break
@@ -4406,9 +4434,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
if fn.Op != ODOTINTER {
s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
}
- if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) {
- testLateExpansion = true
- }
+ testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
var iclosure *ssa.Value
iclosure, rcvr = s.getClosureAndRcvr(fn)
if k == callNormal {
@@ -4427,6 +4453,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
var call *ssa.Value
if k == callDeferStack {
+ testLateExpansion = ssa.LateCallExpansionEnabledWithin(s.f)
// Make a defer struct d on the stack.
t := deferstruct(stksize)
d := tempAt(n.Pos, s.curfn, t)
@@ -4477,10 +4504,17 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
}
// Call runtime.deferprocStack with pointer to _defer record.
- arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
- s.store(types.Types[TUINTPTR], arg0, addr)
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())})
- call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults), s.mem())
+ aux := ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, addr, s.mem())
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ } else {
+ arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
+ s.store(types.Types[TUINTPTR], arg0, addr)
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ }
if stksize < int64(Widthptr) {
// We need room for both the call to deferprocStack and the call to
// the deferred function.
@@ -5039,15 +5073,22 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
s.prevCall = nil
// Write args to the stack
off := Ctxt.FixedFrameSize()
+ testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
var ACArgs []ssa.Param
var ACResults []ssa.Param
+ var callArgs []*ssa.Value
+
for _, arg := range args {
t := arg.Type
off = Rnd(off, t.Alignment())
- ptr := s.constOffPtrSP(t.PtrTo(), off)
size := t.Size()
ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
- s.store(t, ptr, arg)
+ if testLateExpansion {
+ callArgs = append(callArgs, arg)
+ } else {
+ ptr := s.constOffPtrSP(t.PtrTo(), off)
+ s.store(t, ptr, arg)
+ }
off += size
}
off = Rnd(off, int64(Widthreg))
@@ -5061,8 +5102,17 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
}
// Issue call
- call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn, ACArgs, ACResults), s.mem())
- s.vars[&memVar] = call
+ var call *ssa.Value
+ aux := ssa.StaticAuxCall(fn, ACArgs, ACResults)
+ if testLateExpansion {
+ callArgs = append(callArgs, s.mem())
+ call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
+ call.AddArgs(callArgs...)
+ s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
+ } else {
+ call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
+ s.vars[&memVar] = call
+ }
if !returns {
// Finish block
@@ -5078,11 +5128,24 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
// Load results
res := make([]*ssa.Value, len(results))
- for i, t := range results {
- off = Rnd(off, t.Alignment())
- ptr := s.constOffPtrSP(types.NewPtr(t), off)
- res[i] = s.load(t, ptr)
- off += t.Size()
+ if testLateExpansion {
+ for i, t := range results {
+ off = Rnd(off, t.Alignment())
+ if canSSAType(t) {
+ res[i] = s.newValue1I(ssa.OpSelectN, t, int64(i), call)
+ } else {
+ addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(t), int64(i), call)
+ res[i] = s.rawLoad(t, addr)
+ }
+ off += t.Size()
+ }
+ } else {
+ for i, t := range results {
+ off = Rnd(off, t.Alignment())
+ ptr := s.constOffPtrSP(types.NewPtr(t), off)
+ res[i] = s.load(t, ptr)
+ off += t.Size()
+ }
}
off = Rnd(off, int64(Widthptr))
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 07547df36e..0242832322 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -825,7 +825,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
// Convert ideal bool from comparison to plain bool
// if the next step is non-bool (like interface{}).
- if n.Type == types.Idealbool && !t.IsBoolean() {
+ if n.Type == types.UntypedBool && !t.IsBoolean() {
if n.Op == ONAME || n.Op == OLITERAL {
r := nod(OCONVNOP, n, nil)
r.Type = types.Types[TBOOL]
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 138b0acc53..bf0410900f 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -440,7 +440,7 @@ func (c *exprClause) test(exprname *Node) *Node {
// Optimize "switch true { ...}" and "switch false { ... }".
if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
- if exprname.Val().U.(bool) {
+ if exprname.Bool() {
return c.lo
} else {
return nodl(c.pos, ONOT, c.lo, nil)
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 0eb0dae373..769341ee04 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -361,7 +361,7 @@ func typecheck1(n *Node, top int) (res *Node) {
ok |= ctxExpr
if n.Type == nil && n.Val().Ctype() == CTSTR {
- n.Type = types.Idealstring
+ n.Type = types.UntypedString
}
case ONONAME:
@@ -623,8 +623,8 @@ func typecheck1(n *Node, top int) (res *Node) {
// no defaultlit for left
// the outer context gives the type
n.Type = l.Type
- if (l.Type == types.Idealfloat || l.Type == types.Idealcomplex) && r.Op == OLITERAL {
- n.Type = types.Idealint
+ if (l.Type == types.UntypedFloat || l.Type == types.UntypedComplex) && r.Op == OLITERAL {
+ n.Type = types.UntypedInt
}
break
@@ -777,7 +777,7 @@ func typecheck1(n *Node, top int) (res *Node) {
if iscmp[n.Op] {
evconst(n)
- t = types.Idealbool
+ t = types.UntypedBool
if n.Op != OLITERAL {
l, r = defaultlit2(l, r, true)
n.Left = l
@@ -1458,7 +1458,7 @@ func typecheck1(n *Node, top int) (res *Node) {
// Determine result type.
switch t.Etype {
case TIDEAL:
- n.Type = types.Idealfloat
+ n.Type = types.UntypedFloat
case TCOMPLEX64:
n.Type = types.Types[TFLOAT32]
case TCOMPLEX128:
@@ -1504,7 +1504,7 @@ func typecheck1(n *Node, top int) (res *Node) {
return n
case TIDEAL:
- t = types.Idealcomplex
+ t = types.UntypedComplex
case TFLOAT32:
t = types.Types[TCOMPLEX64]
@@ -2724,9 +2724,9 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string {
// e.g in error messages about wrong arguments to return.
func sigrepr(t *types.Type, isddd bool) string {
switch t {
- case types.Idealstring:
+ case types.UntypedString:
return "string"
- case types.Idealbool:
+ case types.UntypedBool:
return "bool"
}
diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go
index 04861c8dd4..ff8cabd8e3 100644
--- a/src/cmd/compile/internal/gc/universe.go
+++ b/src/cmd/compile/internal/gc/universe.go
@@ -123,21 +123,21 @@ func lexinit() {
asNode(s2.Def).SetSubOp(s.op)
}
- types.Idealstring = types.New(TSTRING)
- types.Idealbool = types.New(TBOOL)
+ types.UntypedString = types.New(TSTRING)
+ types.UntypedBool = types.New(TBOOL)
types.Types[TANY] = types.New(TANY)
s := builtinpkg.Lookup("true")
s.Def = asTypesNode(nodbool(true))
asNode(s.Def).Sym = lookup("true")
asNode(s.Def).Name = new(Name)
- asNode(s.Def).Type = types.Idealbool
+ asNode(s.Def).Type = types.UntypedBool
s = builtinpkg.Lookup("false")
s.Def = asTypesNode(nodbool(false))
asNode(s.Def).Sym = lookup("false")
asNode(s.Def).Name = new(Name)
- asNode(s.Def).Type = types.Idealbool
+ asNode(s.Def).Type = types.UntypedBool
s = lookup("_")
s.Block = -100
@@ -351,7 +351,7 @@ func typeinit() {
sizeofString = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
dowidth(types.Types[TSTRING])
- dowidth(types.Idealstring)
+ dowidth(types.UntypedString)
}
func makeErrorInterface() *types.Type {
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index 992936b2d3..bbd9aeee51 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -39,7 +39,9 @@ func expandCalls(f *Func) {
hiOffset = 4
}
- pairTypes := func(et types.EType) (tHi, tLo *types.Type) {
+ // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target
+ // that has no 64-bit integer registers.
+ intPairTypes := func(et types.EType) (tHi, tLo *types.Type) {
tHi = tUint32
if et == types.TINT64 {
tHi = tInt32
@@ -147,8 +149,8 @@ func expandCalls(f *Func) {
}
}
- // storeArg converts stores of SSA-able aggregates into a series of stores of smaller types into
- // individual parameter slots.
+ // storeArg converts stores of SSA-able aggregate arguments (passed to a call) into a series of stores of
+ // smaller types into individual parameter slots.
// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
var storeArg func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value
storeArg = func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value {
@@ -165,7 +167,7 @@ func expandCalls(f *Func) {
return storeArg(pos, b, a.Args[0], t.Elem(), offset, mem)
case OpInt64Make:
- tHi, tLo := pairTypes(t.Etype)
+ tHi, tLo := intPairTypes(t.Etype)
mem = storeArg(pos, b, a.Args[0], tHi, offset+hiOffset, mem)
return storeArg(pos, b, a.Args[1], tLo, offset+lowOffset, mem)
}
@@ -207,7 +209,7 @@ func expandCalls(f *Func) {
if t.Width == regSize {
break
}
- tHi, tLo := pairTypes(t.Etype)
+ tHi, tLo := intPairTypes(t.Etype)
sel := src.Block.NewValue1(pos, OpInt64Hi, tHi, src)
mem = splitStore(dst, sel, mem, v, tHi, offset+hiOffset, firstStorePos)
firstStorePos = firstStorePos.WithNotStmt()
@@ -261,6 +263,9 @@ func expandCalls(f *Func) {
return x
}
+ // rewriteArgs removes all the Args from a call and converts the call args into appropriate
+ // stores (or later, register movement). Extra args for interface and closure calls are ignored,
+ // but removed.
rewriteArgs := func(v *Value, firstArg int) *Value {
// Thread the stores on the memory arg
aux := v.Aux.(*AuxCall)
@@ -283,7 +288,7 @@ func expandCalls(f *Func) {
// TODO this will be more complicated with registers in the picture.
src := a.Args[0]
dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(auxI), sp)
- if a.Uses == 1 {
+ if a.Uses == 1 && a.Block == v.Block {
a.reset(OpMove)
a.Pos = pos
a.Type = types.TypeMem
@@ -292,7 +297,7 @@ func expandCalls(f *Func) {
a.SetArgs3(dst, src, mem)
mem = a
} else {
- mem = a.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem)
+ mem = v.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem)
mem.AuxInt = aux.SizeOfArg(auxI)
}
} else {
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 0df7b4a5d7..ec2c67c1fa 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -672,7 +672,7 @@ func (f *Func) Idom() []*Block {
return f.cachedIdom
}
-// sdom returns a sparse tree representing the dominator relationships
+// Sdom returns a sparse tree representing the dominator relationships
// among the blocks of f.
func (f *Func) Sdom() SparseTree {
if f.cachedSdom == nil {
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index 408678f054..8a253035e0 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -1274,8 +1274,8 @@
(CMPQconst (ANDQconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
(CMPQconst (ANDLconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
(CMPLconst (ANDLconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
-(CMPWconst (ANDLconst _ [m]) [n]) && 0 <= m && int16(m) < n => (FlagLT_ULT)
-(CMPBconst (ANDLconst _ [m]) [n]) && 0 <= m && int8(m) < n => (FlagLT_ULT)
+(CMPWconst (ANDLconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < n => (FlagLT_ULT)
+(CMPBconst (ANDLconst _ [m]) [n]) && 0 <= int8(m) && int8(m) < n => (FlagLT_ULT)
// TESTQ c c sets flags like CMPQ c 0.
(TESTQconst [c] (MOVQconst [d])) && int64(c) == d && c == 0 => (FlagEQ)
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index 3518dd1e3c..85839303c5 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -541,10 +541,10 @@ var genericOps = []opData{
{name: "SelectN", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the auxint'th member.
{name: "SelectNAddr", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the address of auxint'th member. Used for un-SSA-able result types.
- // Atomic operations used for semantically inlining runtime/internal/atomic.
- // Atomic loads return a new memory so that the loads are properly ordered
- // with respect to other loads and stores.
- // TODO: use for sync/atomic at some point.
+ // Atomic operations used for semantically inlining sync/atomic and
+ // runtime/internal/atomic. Atomic loads return a new memory so that
+ // the loads are properly ordered with respect to other loads and
+ // stores.
{name: "AtomicLoad8", argLength: 2, typ: "(UInt8,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
{name: "AtomicLoad32", argLength: 2, typ: "(UInt32,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
{name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index 3d7eb8c9a4..32ef26f98d 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -6886,7 +6886,7 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value) bool {
return true
}
// match: (CMPBconst (ANDLconst _ [m]) [n])
- // cond: 0 <= m && int8(m) < n
+ // cond: 0 <= int8(m) && int8(m) < n
// result: (FlagLT_ULT)
for {
n := auxIntToInt8(v.AuxInt)
@@ -6894,7 +6894,7 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value) bool {
break
}
m := auxIntToInt32(v_0.AuxInt)
- if !(0 <= m && int8(m) < n) {
+ if !(0 <= int8(m) && int8(m) < n) {
break
}
v.reset(OpAMD64FlagLT_ULT)
@@ -8243,7 +8243,7 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value) bool {
return true
}
// match: (CMPWconst (ANDLconst _ [m]) [n])
- // cond: 0 <= m && int16(m) < n
+ // cond: 0 <= int16(m) && int16(m) < n
// result: (FlagLT_ULT)
for {
n := auxIntToInt16(v.AuxInt)
@@ -8251,7 +8251,7 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value) bool {
break
}
m := auxIntToInt32(v_0.AuxInt)
- if !(0 <= m && int16(m) < n) {
+ if !(0 <= int16(m) && int16(m) < n) {
break
}
v.reset(OpAMD64FlagLT_ULT)
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index 9601fab9e0..1485b70059 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -287,6 +287,7 @@ func tokstring(tok token) string {
// Convenience methods using the current token position.
func (p *parser) pos() Pos { return p.posAt(p.line, p.col) }
+func (p *parser) error(msg string) { p.errorAt(p.pos(), msg) }
func (p *parser) syntaxError(msg string) { p.syntaxErrorAt(p.pos(), msg) }
// The stopset contains keywords that start a statement.
@@ -997,17 +998,20 @@ loop:
// x[i:j...
t.Index[1] = p.expr()
}
- if p.got(_Colon) {
+ if p.tok == _Colon {
t.Full = true
// x[i:j:...]
if t.Index[1] == nil {
p.error("middle index required in 3-index slice")
+ t.Index[1] = p.badExpr()
}
+ p.next()
if p.tok != _Rbrack {
// x[i:j:k...
t.Index[2] = p.expr()
} else {
p.error("final index required in 3-index slice")
+ t.Index[2] = p.badExpr()
}
}
p.want(_Rbrack)
@@ -1836,6 +1840,7 @@ func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleS
if p.tok == _Lbrace {
if keyword == _If {
p.syntaxError("missing condition in if statement")
+ cond = p.badExpr()
}
return
}
@@ -1907,6 +1912,9 @@ done:
} else {
p.syntaxErrorAt(semi.pos, "missing condition in if statement")
}
+ b := new(BadExpr)
+ b.pos = semi.pos
+ cond = b
}
case *ExprStmt:
cond = s.X
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index 5d1d5d4008..023ab9af88 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -105,14 +105,14 @@ var (
Errortype *Type
// Types to represent untyped string and boolean constants.
- Idealstring *Type
- Idealbool *Type
+ UntypedString *Type
+ UntypedBool *Type
// Types to represent untyped numeric constants.
- Idealint = New(TIDEAL)
- Idealrune = New(TIDEAL)
- Idealfloat = New(TIDEAL)
- Idealcomplex = New(TIDEAL)
+ UntypedInt = New(TIDEAL)
+ UntypedRune = New(TIDEAL)
+ UntypedFloat = New(TIDEAL)
+ UntypedComplex = New(TIDEAL)
)
// A Type represents a Go type.
@@ -1436,7 +1436,7 @@ func (t *Type) IsUntyped() bool {
if t == nil {
return false
}
- if t == Idealstring || t == Idealbool {
+ if t == UntypedString || t == UntypedBool {
return true
}
switch t.Etype {
diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go
index 2d20b6a6d0..e137daa3fc 100644
--- a/src/cmd/compile/internal/x86/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -7,6 +7,9 @@ package x86
import (
"cmd/compile/internal/gc"
"cmd/internal/obj/x86"
+ "cmd/internal/objabi"
+ "fmt"
+ "os"
)
func Init(arch *gc.Arch) {
@@ -15,6 +18,18 @@ func Init(arch *gc.Arch) {
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
arch.MAXWIDTH = (1 << 32) - 1
+ switch v := objabi.GO386; v {
+ case "sse2":
+ case "softfloat":
+ arch.SoftFloat = true
+ case "387":
+ fmt.Fprintf(os.Stderr, "unsupported setting GO386=387. Consider using GO386=softfloat instead.\n")
+ gc.Exit(1)
+ default:
+ fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v)
+ gc.Exit(1)
+
+ }
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 3b3eb113b1..69a66abd2d 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -30,6 +30,7 @@ var (
gohostos string
goos string
goarm string
+ go386 string
gomips string
gomips64 string
goppc64 string
@@ -141,6 +142,12 @@ func xinit() {
}
goarm = b
+ b = os.Getenv("GO386")
+ if b == "" {
+ b = "sse2"
+ }
+ go386 = b
+
b = os.Getenv("GOMIPS")
if b == "" {
b = "hardfloat"
@@ -212,6 +219,7 @@ func xinit() {
defaultldso = os.Getenv("GO_LDSO")
// For tools being invoked but also for os.ExpandEnv.
+ os.Setenv("GO386", go386)
os.Setenv("GOARCH", goarch)
os.Setenv("GOARM", goarm)
os.Setenv("GOHOSTARCH", gohostarch)
@@ -1153,6 +1161,9 @@ func cmdenv() {
if goarch == "arm" {
xprintf(format, "GOARM", goarm)
}
+ if goarch == "386" {
+ xprintf(format, "GO386", go386)
+ }
if goarch == "mips" || goarch == "mipsle" {
xprintf(format, "GOMIPS", gomips)
}
diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go
index 67d1d72db4..2744951597 100644
--- a/src/cmd/dist/buildruntime.go
+++ b/src/cmd/dist/buildruntime.go
@@ -41,6 +41,7 @@ func mkzversion(dir, file string) {
// package objabi
//
// const defaultGOROOT =
+// const defaultGO386 =
// const defaultGOARM =
// const defaultGOMIPS =
// const defaultGOMIPS64 =
@@ -69,6 +70,7 @@ func mkzbootstrap(file string) {
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "import \"runtime\"\n")
fmt.Fprintln(&buf)
+ fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index da894e3eef..03e6866d62 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -903,7 +903,7 @@ func (t *tester) addCmd(dt *distTest, dir string, cmdline ...interface{}) *exec.
}
func (t *tester) iOS() bool {
- return (goos == "darwin" || goos == "ios") && goarch == "arm64"
+ return goos == "ios"
}
func (t *tester) out(v string) {
@@ -992,7 +992,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
case "c-shared":
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
- "darwin-amd64",
+ "darwin-amd64", "darwin-arm64",
"freebsd-amd64",
"android-arm", "android-arm64", "android-386",
"windows-amd64", "windows-386":
@@ -1011,7 +1011,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-s390x", "linux-ppc64le":
return true
- case "darwin-amd64":
+ case "darwin-amd64", "darwin-arm64":
return true
case "freebsd-amd64":
return true
@@ -1023,7 +1023,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
"android-amd64", "android-arm", "android-arm64", "android-386":
return true
- case "darwin-amd64":
+ case "darwin-amd64", "darwin-arm64":
return true
case "windows-amd64", "windows-386", "windows-arm":
return true
diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go
index 47602833d3..39530e3c2d 100644
--- a/src/cmd/doc/doc_test.go
+++ b/src/cmd/doc/doc_test.go
@@ -36,7 +36,7 @@ func TestMain(m *testing.M) {
}
func maybeSkip(t *testing.T) {
- if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ if runtime.GOOS == "ios" {
t.Skip("iOS does not have a full file tree")
}
}
diff --git a/src/cmd/fix/gotypes.go b/src/cmd/fix/gotypes.go
index 8a4019cc8c..031f85c9cc 100644
--- a/src/cmd/fix/gotypes.go
+++ b/src/cmd/fix/gotypes.go
@@ -21,11 +21,11 @@ var gotypesFix = fix{
}
func gotypes(f *ast.File) bool {
- truth := fixGoTypes(f)
+ fixed := fixGoTypes(f)
if fixGoExact(f) {
- truth = true
+ fixed = true
}
- return truth
+ return fixed
}
func fixGoTypes(f *ast.File) bool {
diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go
index e72c66398f..d19dde6b4a 100644
--- a/src/cmd/fix/main.go
+++ b/src/cmd/fix/main.go
@@ -137,6 +137,21 @@ func processFile(filename string, useStdin bool) error {
return err
}
+ // Make sure file is in canonical format.
+ // This "fmt" pseudo-fix cannot be disabled.
+ newSrc, err := gofmtFile(file)
+ if err != nil {
+ return err
+ }
+ if !bytes.Equal(newSrc, src) {
+ newFile, err := parser.ParseFile(fset, filename, newSrc, parserMode)
+ if err != nil {
+ return err
+ }
+ file = newFile
+ fmt.Fprintf(&fixlog, " fmt")
+ }
+
// Apply all fixes to file.
newFile := file
fixed := false
@@ -180,7 +195,7 @@ func processFile(filename string, useStdin bool) error {
// output of the printer run on a standard AST generated by the parser,
// but the source we generated inside the loop above is the
// output of the printer run on a mangled AST generated by a fixer.
- newSrc, err := gofmtFile(newFile)
+ newSrc, err = gofmtFile(newFile)
if err != nil {
return err
}
diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go
index 66e0cdcec0..f45155b06d 100644
--- a/src/cmd/fix/typecheck.go
+++ b/src/cmd/fix/typecheck.go
@@ -207,7 +207,7 @@ func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, ass
return nil
}()
if err != nil {
- fmt.Printf("warning: no cgo types: %s\n", err)
+ fmt.Fprintf(os.Stderr, "go fix: warning: no cgo types: %s\n", err)
}
}
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index 59d6152e2a..56941b0541 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -3,7 +3,7 @@ module cmd
go 1.16
require (
- github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3
+ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect
golang.org/x/arch v0.0.0-20200826200359-b19915210f00
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index 1b6d680d62..2b505c4354 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -1,8 +1,8 @@
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 h1:S1+yTUaFPXuDZnPDbO+TrDFIjPzQraYH8/CwSlu9Fac=
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 14840efb22..5cb32c80e9 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -1852,6 +1852,9 @@
// GOARM
// For GOARCH=arm, the ARM architecture for which to compile.
// Valid values are 5, 6, 7.
+// GO386
+// For GOARCH=386, how to implement floating point instructions.
+// Valid values are sse2 (default), softfloat.
// GOMIPS
// For GOARCH=mips{,le}, whether to use floating point instructions.
// Valid values are hardfloat (default), softfloat.
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 66a52c86ad..093ea2ffa1 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -58,11 +58,10 @@ func init() {
switch runtime.GOOS {
case "android", "js":
canRun = false
- case "darwin", "ios":
- switch runtime.GOARCH {
- case "arm64":
- canRun = false
- }
+ case "darwin":
+ // nothing to do
+ case "ios":
+ canRun = false
case "linux":
switch runtime.GOARCH {
case "arm":
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index 9169c12d8f..67d581f6e6 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -256,6 +256,7 @@ var (
// Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
+ GO386 = envOr("GO386", objabi.GO386)
GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
@@ -279,6 +280,8 @@ func GetArchEnv() (key, val string) {
switch Goarch {
case "arm":
return "GOARM", GOARM
+ case "386":
+ return "GO386", GO386
case "mips", "mipsle":
return "GOMIPS", GOMIPS
case "mips64", "mips64le":
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index e1f2400f60..59d0ded658 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -203,10 +203,19 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
}
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
- // Only if we're listing all environment variables ("go env")
- // or the variables being requested are in the extra list.
- needCostly := true
- if len(args) > 0 {
+ needCostly := false
+ if *envU || *envW {
+ // We're overwriting or removing default settings,
+ // so it doesn't really matter what the existing settings are.
+ //
+ // Moreover, we haven't validated the new settings yet, so it is
+ // important that we NOT perform any actions based on them,
+ // such as initializing the builder to compute other variables.
+ } else if len(args) == 0 {
+ // We're listing all environment variables ("go env"),
+ // including the expensive ones.
+ needCostly = true
+ } else {
needCostly = false
for _, arg := range args {
switch argKey(arg) {
@@ -269,6 +278,13 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
}
}
+ gotmp, okGOTMP := add["GOTMPDIR"]
+ if okGOTMP {
+ if !filepath.IsAbs(gotmp) && gotmp != "" {
+ base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
+ }
+ }
+
updateEnvFile(add, nil)
return
}
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index befa10a0e4..8dfabbaa4a 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -581,6 +581,9 @@ Architecture-specific environment variables:
GOARM
For GOARCH=arm, the ARM architecture for which to compile.
Valid values are 5, 6, 7.
+ GO386
+ For GOARCH=386, how to implement floating point instructions.
+ Valid values are sse2 (default), softfloat.
GOMIPS
For GOARCH=mips{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat.
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index 5cc77915e7..5b3c2f0ff2 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -75,19 +75,20 @@ type PackagePublic struct {
// Source files
// If you add to this list you MUST add to p.AllFiles (below) too.
// Otherwise file name security lists will not apply to any new additions.
- GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
- CgoFiles []string `json:",omitempty"` // .go source files that import "C"
- CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles
- IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints
- CFiles []string `json:",omitempty"` // .c source files
- CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
- MFiles []string `json:",omitempty"` // .m source files
- HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
- FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files
- SFiles []string `json:",omitempty"` // .s source files
- SwigFiles []string `json:",omitempty"` // .swig files
- SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
- SysoFiles []string `json:",omitempty"` // .syso system object files added to package
+ GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string `json:",omitempty"` // .go source files that import "C"
+ CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles
+ IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints
+ IgnoredOtherFiles []string `json:",omitempty"` // non-.go source files ignored due to build constraints
+ CFiles []string `json:",omitempty"` // .c source files
+ CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
+ MFiles []string `json:",omitempty"` // .m source files
+ HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
+ FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files
+ SFiles []string `json:",omitempty"` // .s source files
+ SwigFiles []string `json:",omitempty"` // .swig files
+ SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
+ SysoFiles []string `json:",omitempty"` // .syso system object files added to package
// Cgo directives
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
@@ -127,6 +128,7 @@ func (p *Package) AllFiles() []string {
p.CgoFiles,
// no p.CompiledGoFiles, because they are from GoFiles or generated by us
p.IgnoredGoFiles,
+ p.IgnoredOtherFiles,
p.CFiles,
p.CXXFiles,
p.MFiles,
@@ -185,7 +187,7 @@ type NoGoError struct {
}
func (e *NoGoError) Error() string {
- if len(e.Package.constraintIgnoredGoFiles()) > 0 {
+ if len(e.Package.IgnoredGoFiles) > 0 {
// Go files exist, but they were ignored due to build constraints.
return "build constraints exclude all Go files in " + e.Package.Dir
}
@@ -330,6 +332,7 @@ func (p *Package) copyBuild(pp *build.Package) {
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
+ p.IgnoredOtherFiles = pp.IgnoredOtherFiles
p.CFiles = pp.CFiles
p.CXXFiles = pp.CXXFiles
p.MFiles = pp.MFiles
@@ -2009,22 +2012,7 @@ func (p *Package) InternalXGoFiles() []string {
// using absolute paths. "Possibly relevant" means that files are not excluded
// due to build tags, but files with names beginning with . or _ are still excluded.
func (p *Package) InternalAllGoFiles() []string {
- return p.mkAbs(str.StringList(p.constraintIgnoredGoFiles(), p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles))
-}
-
-// constraintIgnoredGoFiles returns the list of Go files ignored for reasons
-// other than having a name beginning with '.' or '_'.
-func (p *Package) constraintIgnoredGoFiles() []string {
- if len(p.IgnoredGoFiles) == 0 {
- return nil
- }
- files := make([]string, 0, len(p.IgnoredGoFiles))
- for _, f := range p.IgnoredGoFiles {
- if f != "" && f[0] != '.' && f[0] != '_' {
- files = append(files, f)
- }
- }
- return files
+ return p.mkAbs(str.StringList(p.IgnoredGoFiles, p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles))
}
// usesSwig reports whether the package needs to run SWIG.
diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go
index d85eddf767..df4cfdab1a 100644
--- a/src/cmd/go/internal/modfetch/codehost/codehost.go
+++ b/src/cmd/go/internal/modfetch/codehost/codehost.go
@@ -79,9 +79,8 @@ type Repo interface {
ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error)
// RecentTag returns the most recent tag on rev or one of its predecessors
- // with the given prefix and major version.
- // An empty major string matches any major version.
- RecentTag(rev, prefix, major string) (tag string, err error)
+ // with the given prefix. allowed may be used to filter out unwanted versions.
+ RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error)
// DescendsFrom reports whether rev or any of its ancestors has the given tag.
//
diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go
index 31921324a7..5a35829c98 100644
--- a/src/cmd/go/internal/modfetch/codehost/git.go
+++ b/src/cmd/go/internal/modfetch/codehost/git.go
@@ -644,7 +644,7 @@ func (r *gitRepo) readFileRevs(tags []string, file string, fileMap map[string]*F
return missing, nil
}
-func (r *gitRepo) RecentTag(rev, prefix, major string) (tag string, err error) {
+func (r *gitRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) {
info, err := r.Stat(rev)
if err != nil {
return "", err
@@ -680,7 +680,10 @@ func (r *gitRepo) RecentTag(rev, prefix, major string) (tag string, err error) {
// NOTE: Do not replace the call to semver.Compare with semver.Max.
// We want to return the actual tag, not a canonicalized version of it,
// and semver.Max currently canonicalizes (see golang.org/issue/32700).
- if c := semver.Canonical(semtag); c != "" && strings.HasPrefix(semtag, c) && (major == "" || semver.Major(c) == major) && semver.Compare(semtag, highest) > 0 {
+ if c := semver.Canonical(semtag); c == "" || !strings.HasPrefix(semtag, c) || !allowed(semtag) {
+ continue
+ }
+ if semver.Compare(semtag, highest) > 0 {
highest = semtag
}
}
diff --git a/src/cmd/go/internal/modfetch/codehost/vcs.go b/src/cmd/go/internal/modfetch/codehost/vcs.go
index 7284557f4b..6278cb21e1 100644
--- a/src/cmd/go/internal/modfetch/codehost/vcs.go
+++ b/src/cmd/go/internal/modfetch/codehost/vcs.go
@@ -395,7 +395,7 @@ func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[s
return nil, vcsErrorf("ReadFileRevs not implemented")
}
-func (r *vcsRepo) RecentTag(rev, prefix, major string) (tag string, err error) {
+func (r *vcsRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) {
// We don't technically need to lock here since we're returning an error
// uncondititonally, but doing so anyway will help to avoid baking in
// lock-inversion bugs.
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
index d043903336..d99a31d360 100644
--- a/src/cmd/go/internal/modfetch/coderepo.go
+++ b/src/cmd/go/internal/modfetch/coderepo.go
@@ -419,9 +419,14 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
tagPrefix = r.codeDir + "/"
}
+ isRetracted, err := r.retractedVersions()
+ if err != nil {
+ isRetracted = func(string) bool { return false }
+ }
+
// tagToVersion returns the version obtained by trimming tagPrefix from tag.
- // If the tag is invalid or a pseudo-version, tagToVersion returns an empty
- // version.
+ // If the tag is invalid, retracted, or a pseudo-version, tagToVersion returns
+ // an empty version.
tagToVersion := func(tag string) (v string, tagIsCanonical bool) {
if !strings.HasPrefix(tag, tagPrefix) {
return "", false
@@ -436,6 +441,9 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
if v == "" || !strings.HasPrefix(trimmed, v) {
return "", false // Invalid or incomplete version (just vX or vX.Y).
}
+ if isRetracted(v) {
+ return "", false
+ }
if v == trimmed {
tagIsCanonical = true
}
@@ -500,15 +508,24 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
return checkGoMod()
}
+ // Find the highest tagged version in the revision's history, subject to
+ // major version and +incompatible constraints. Use that version as the
+ // pseudo-version base so that the pseudo-version sorts higher. Ignore
+ // retracted versions.
+ allowedMajor := func(major string) func(v string) bool {
+ return func(v string) bool {
+ return (major == "" || semver.Major(v) == major) && !isRetracted(v)
+ }
+ }
if pseudoBase == "" {
var tag string
if r.pseudoMajor != "" || canUseIncompatible() {
- tag, _ = r.code.RecentTag(info.Name, tagPrefix, r.pseudoMajor)
+ tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor(r.pseudoMajor))
} else {
// Allow either v1 or v0, but not incompatible higher versions.
- tag, _ = r.code.RecentTag(info.Name, tagPrefix, "v1")
+ tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v1"))
if tag == "" {
- tag, _ = r.code.RecentTag(info.Name, tagPrefix, "v0")
+ tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v0"))
}
}
pseudoBase, _ = tagToVersion(tag) // empty if the tag is invalid
@@ -869,6 +886,57 @@ func (r *codeRepo) modPrefix(rev string) string {
return r.modPath + "@" + rev
}
+func (r *codeRepo) retractedVersions() (func(string) bool, error) {
+ versions, err := r.Versions("")
+ if err != nil {
+ return nil, err
+ }
+
+ for i, v := range versions {
+ if strings.HasSuffix(v, "+incompatible") {
+ versions = versions[:i]
+ break
+ }
+ }
+ if len(versions) == 0 {
+ return func(string) bool { return false }, nil
+ }
+
+ var highest string
+ for i := len(versions) - 1; i >= 0; i-- {
+ v := versions[i]
+ if semver.Prerelease(v) == "" {
+ highest = v
+ break
+ }
+ }
+ if highest == "" {
+ highest = versions[len(versions)-1]
+ }
+
+ data, err := r.GoMod(highest)
+ if err != nil {
+ return nil, err
+ }
+ f, err := modfile.ParseLax("go.mod", data, nil)
+ if err != nil {
+ return nil, err
+ }
+ retractions := make([]modfile.VersionInterval, len(f.Retract))
+ for _, r := range f.Retract {
+ retractions = append(retractions, r.VersionInterval)
+ }
+
+ return func(v string) bool {
+ for _, r := range retractions {
+ if semver.Compare(r.Low, v) <= 0 && semver.Compare(v, r.High) <= 0 {
+ return true
+ }
+ }
+ return false
+ }, nil
+}
+
func (r *codeRepo) Zip(dst io.Writer, version string) error {
if version != module.CanonicalVersion(version) {
return fmt.Errorf("version %s is not canonical", version)
diff --git a/src/cmd/go/internal/work/build_test.go b/src/cmd/go/internal/work/build_test.go
index afed0fba72..904aee0684 100644
--- a/src/cmd/go/internal/work/build_test.go
+++ b/src/cmd/go/internal/work/build_test.go
@@ -221,10 +221,8 @@ func pkgImportPath(pkgpath string) *load.Package {
// See https://golang.org/issue/18878.
func TestRespectSetgidDir(t *testing.T) {
switch runtime.GOOS {
- case "darwin", "ios":
- if runtime.GOARCH == "arm64" {
- t.Skip("can't set SetGID bit with chmod on iOS")
- }
+ case "ios":
+ t.Skip("can't set SetGID bit with chmod on iOS")
case "windows", "plan9":
t.Skip("chown/chmod setgid are not supported on Windows or Plan 9")
}
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index e68b322c7d..074bcc16c0 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -922,12 +922,13 @@ func (b *Builder) loadCachedSrcFiles(a *Action) error {
// vetConfig is the configuration passed to vet describing a single package.
type vetConfig struct {
- ID string // package ID (example: "fmt [fmt.test]")
- Compiler string // compiler name (gc, gccgo)
- Dir string // directory containing package
- ImportPath string // canonical import path ("package path")
- GoFiles []string // absolute paths to package source files
- NonGoFiles []string // absolute paths to package non-Go files
+ ID string // package ID (example: "fmt [fmt.test]")
+ Compiler string // compiler name (gc, gccgo)
+ Dir string // directory containing package
+ ImportPath string // canonical import path ("package path")
+ GoFiles []string // absolute paths to package source files
+ 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
@@ -951,20 +952,23 @@ func buildVetConfig(a *Action, srcfiles []string) {
}
}
+ ignored := str.StringList(a.Package.IgnoredGoFiles, a.Package.IgnoredOtherFiles)
+
// Pass list of absolute paths to vet,
// so that vet's error messages will use absolute paths,
// so that we can reformat them relative to the directory
// in which the go command is invoked.
vcfg := &vetConfig{
- ID: a.Package.ImportPath,
- Compiler: cfg.BuildToolchainName,
- Dir: a.Package.Dir,
- GoFiles: mkAbsFiles(a.Package.Dir, gofiles),
- NonGoFiles: mkAbsFiles(a.Package.Dir, nongofiles),
- ImportPath: a.Package.ImportPath,
- ImportMap: make(map[string]string),
- PackageFile: make(map[string]string),
- Standard: make(map[string]bool),
+ ID: a.Package.ImportPath,
+ Compiler: cfg.BuildToolchainName,
+ Dir: a.Package.Dir,
+ GoFiles: mkAbsFiles(a.Package.Dir, gofiles),
+ NonGoFiles: mkAbsFiles(a.Package.Dir, nongofiles),
+ IgnoredFiles: mkAbsFiles(a.Package.Dir, ignored),
+ ImportPath: a.Package.ImportPath,
+ ImportMap: make(map[string]string),
+ PackageFile: make(map[string]string),
+ Standard: make(map[string]bool),
}
a.vetCfg = vcfg
for i, raw := range a.Package.Internal.RawImports {
@@ -1052,17 +1056,28 @@ func (b *Builder) vet(ctx context.Context, a *Action) error {
// This is OK as long as the packages that are farther down the
// dependency tree turn on *more* analysis, as here.
// (The unsafeptr check does not write any facts for use by
- // later vet runs.)
+ // later vet runs, nor does unreachable.)
if a.Package.Goroot && !VetExplicit && VetTool == "" {
+ // Turn off -unsafeptr checks.
+ // There's too much unsafe.Pointer code
+ // that vet doesn't like in low-level packages
+ // like runtime, sync, and reflect.
// Note that $GOROOT/src/buildall.bash
// does the same for the misc-compile trybots
// and should be updated if these flags are
// changed here.
- //
- // There's too much unsafe.Pointer code
- // that vet doesn't like in low-level packages
- // like runtime, sync, and reflect.
vetFlags = []string{"-unsafeptr=false"}
+
+ // Also turn off -unreachable checks during go test.
+ // During testing it is very common to make changes
+ // like hard-coded forced returns or panics that make
+ // code unreachable. It's unreasonable to insist on files
+ // not having any unreachable code during "go test".
+ // (buildall.bash still runs with -unreachable enabled
+ // for the overall whole-tree scan.)
+ if cfg.CmdName == "test" {
+ vetFlags = append(vetFlags, "-unreachable=false")
+ }
}
// Note: We could decide that vet should compute export data for
diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt
index 2366c3f580..bdb9bc4077 100644
--- a/src/cmd/go/testdata/script/env_write.txt
+++ b/src/cmd/go/testdata/script/env_write.txt
@@ -24,6 +24,12 @@ stdout GOARCH=
stdout GOOS=
stdout GOROOT=
+# checking errors
+! go env -w
+stderr 'go env -w: no KEY=VALUE arguments given'
+! go env -u
+stderr 'go env -u: no arguments given'
+
# go env -w changes default setting
env root=
[windows] env root=c:
@@ -97,6 +103,26 @@ stderr 'GOPATH entry cannot start with shell metacharacter'
! go env -w GOPATH=./go
stderr 'GOPATH entry is relative; must be absolute path'
+# go env -w rejects invalid GOTMPDIR values
+! go env -w GOTMPDIR=x
+stderr 'go env -w: GOTMPDIR must be an absolute path'
+
+# go env -w should accept absolute GOTMPDIR value
+# and should not create it
+[windows] go env -w GOTMPDIR=$WORK\x\y\z
+[!windows] go env -w GOTMPDIR=$WORK/x/y/z
+! exists $WORK/x/y/z
+# we should be able to clear an env
+go env -u GOTMPDIR
+go env GOTMPDIR
+stdout ^$
+
+[windows] go env -w GOTMPDIR=$WORK\x\y\z
+[!windows] go env -w GOTMPDIR=$WORK/x/y/z
+go env -w GOTMPDIR=
+go env GOTMPDIR
+stdout ^$
+
# go env -w/-u checks validity of GOOS/ARCH combinations
env GOOS=
env GOARCH=
diff --git a/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt
new file mode 100644
index 0000000000..93609f36c9
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt
@@ -0,0 +1,62 @@
+# When converting a commit to a pseudo-version, don't use a retracted version
+# as the base.
+# Verifies golang.org/issue/41700.
+
+[!net] skip
+[!exec:git] skip
+env GOPROXY=direct
+env GOSUMDB=off
+go mod init m
+
+# Control: check that v1.0.0 is the only version and is retracted.
+go list -m -versions vcs-test.golang.org/git/retract-pseudo.git
+stdout '^vcs-test.golang.org/git/retract-pseudo.git$'
+go list -m -versions -retracted vcs-test.golang.org/git/retract-pseudo.git
+stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.0$'
+
+# 713affd19d7b is a commit after v1.0.0. Don't use v1.0.0 as the base.
+go list -m vcs-test.golang.org/git/retract-pseudo.git@713affd19d7b
+stdout '^vcs-test.golang.org/git/retract-pseudo.git v0.0.0-20201009173747-713affd19d7b$'
+
+# 64c061ed4371 is the commit v1.0.0 refers to. Don't convert to v1.0.0.
+go list -m vcs-test.golang.org/git/retract-pseudo.git@64c061ed4371
+stdout '^vcs-test.golang.org/git/retract-pseudo.git v0.0.0-20201009173747-64c061ed4371'
+
+# A retracted version is a valid base. Retraction should not validate existing
+# pseudo-versions, nor should it turn invalid pseudo-versions valid.
+go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-713affd19d7b
+go list -m vcs-test.golang.org/git/retract-pseudo.git
+stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.1-0.20201009173747-713affd19d7b$'
+
+! go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371
+stderr '^go get vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$'
+
+-- retract-pseudo.sh --
+#!/bin/bash
+
+# This is not part of the test.
+# Run this to generate and update the repository on vcs-test.golang.org.
+
+set -euo pipefail
+
+rm -rf retract-pseudo
+mkdir retract-pseudo
+cd retract-pseudo
+git init
+
+# Create the module.
+# Retract v1.0.0 and tag v1.0.0 at the same commit.
+# The module has no unretracted release versions.
+go mod init vcs-test.golang.org/git/retract-pseudo.git
+go mod edit -retract v1.0.0
+echo 'package p' >p.go
+git add -A
+git commit -m 'create module retract-pseudo'
+git tag v1.0.0
+
+# Commit a trivial change so the default branch does not point to v1.0.0.
+git mv p.go q.go
+git commit -m 'trivial change'
+
+zip -r ../retract-pseudo.zip .
+gsutil cp ../retract-pseudo.zip gs://vcs-test/git/retract-pseudo.zip
diff --git a/src/cmd/go/testdata/script/vet_flags.txt b/src/cmd/go/testdata/script/vet_flags.txt
index b85b133c19..e2e3f5bc55 100644
--- a/src/cmd/go/testdata/script/vet_flags.txt
+++ b/src/cmd/go/testdata/script/vet_flags.txt
@@ -2,21 +2,25 @@ env GO111MODULE=on
# Issue 35837: "go vet -" should use the requested
# analyzers, not the default analyzers for 'go test'.
-go vet -n -unreachable=false encoding/binary
-stderr '-unreachable=false'
+go vet -n -buildtags=false runtime
+stderr '-buildtags=false'
! stderr '-unsafeptr=false'
# Issue 37030: "go vet " without other flags should disable the
# unsafeptr check by default.
-go vet -n encoding/binary
+go vet -n runtime
stderr '-unsafeptr=false'
! stderr '-unreachable=false'
# However, it should be enabled if requested explicitly.
-go vet -n -unsafeptr encoding/binary
+go vet -n -unsafeptr runtime
stderr '-unsafeptr'
! stderr '-unsafeptr=false'
+# -unreachable is disabled during test but on during plain vet.
+go test -n runtime
+stderr '-unreachable=false'
+
# A flag terminator should be allowed before the package list.
go vet -n -- .
@@ -60,10 +64,10 @@ stderr '[/\\]vet'$GOEXE'["]? .* -errorsas .* ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
# "go test" on a standard package should by default disable an explicit list.
go test -x -run=none encoding/binary
-stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
+stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false -unreachable=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
go test -x -vet= -run=none encoding/binary
-stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
+stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false -unreachable=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
# Both should allow users to override via the -vet flag.
go test -x -vet=unreachable -run=none .
diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go
index 1ca41c15ba..33319e48df 100644
--- a/src/cmd/internal/obj/arm64/a.out.go
+++ b/src/cmd/internal/obj/arm64/a.out.go
@@ -958,9 +958,11 @@ const (
AVADDP
AVAND
AVBIF
+ AVBCAX
AVCMEQ
AVCNT
AVEOR
+ AVEOR3
AVMOV
AVLD1
AVLD2
@@ -989,6 +991,7 @@ const (
AVPMULL2
AVEXT
AVRBIT
+ AVRAX1
AVUSHR
AVUSHLL
AVUSHLL2
@@ -1001,6 +1004,7 @@ const (
AVBSL
AVBIT
AVTBL
+ AVXAR
AVZIP1
AVZIP2
AVCMTST
diff --git a/src/cmd/internal/obj/arm64/anames.go b/src/cmd/internal/obj/arm64/anames.go
index 900cdba817..e5534e26b9 100644
--- a/src/cmd/internal/obj/arm64/anames.go
+++ b/src/cmd/internal/obj/arm64/anames.go
@@ -464,9 +464,11 @@ var Anames = []string{
"VADDP",
"VAND",
"VBIF",
+ "VBCAX",
"VCMEQ",
"VCNT",
"VEOR",
+ "VEOR3",
"VMOV",
"VLD1",
"VLD2",
@@ -495,6 +497,7 @@ var Anames = []string{
"VPMULL2",
"VEXT",
"VRBIT",
+ "VRAX1",
"VUSHR",
"VUSHLL",
"VUSHLL2",
@@ -507,6 +510,7 @@ var Anames = []string{
"VBSL",
"VBIT",
"VTBL",
+ "VXAR",
"VZIP1",
"VZIP2",
"VCMTST",
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index ee4a33eef4..c46066313e 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -843,6 +843,8 @@ var optab = []Optab{
{ASHA256H, C_ARNG, C_VREG, C_NONE, C_VREG, 1, 4, 0, 0, 0},
{AVREV32, C_ARNG, C_NONE, C_NONE, C_ARNG, 83, 4, 0, 0, 0},
{AVPMULL, C_ARNG, C_ARNG, C_NONE, C_ARNG, 93, 4, 0, 0, 0},
+ {AVEOR3, C_ARNG, C_ARNG, C_ARNG, C_ARNG, 103, 4, 0, 0, 0},
+ {AVXAR, C_VCON, C_ARNG, C_ARNG, C_ARNG, 104, 4, 0, 0, 0},
{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 90, 4, 0, 0, 0},
{obj.APCDATA, C_VCON, C_NONE, C_NONE, C_VCON, 0, 0, 0, 0, 0},
@@ -2769,6 +2771,7 @@ func buildop(ctxt *obj.Link) {
case AVADD:
oprangeset(AVSUB, t)
+ oprangeset(AVRAX1, t)
case AAESD:
oprangeset(AAESE, t)
@@ -2827,6 +2830,9 @@ func buildop(ctxt *obj.Link) {
oprangeset(AVLD4, t)
oprangeset(AVLD4R, t)
+ case AVEOR3:
+ oprangeset(AVBCAX, t)
+
case ASHA1H,
AVCNT,
AVMOV,
@@ -2839,7 +2845,8 @@ func buildop(ctxt *obj.Link) {
AVDUP,
AVMOVI,
APRFM,
- AVEXT:
+ AVEXT,
+ AVXAR:
break
case obj.ANOP,
@@ -3120,12 +3127,13 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
case 6: /* b ,O(R); bl ,O(R) */
o1 = c.opbrr(p, p.As)
-
o1 |= uint32(p.To.Reg&31) << 5
- rel := obj.Addrel(c.cursym)
- rel.Off = int32(c.pc)
- rel.Siz = 0
- rel.Type = objabi.R_CALLIND
+ if p.As == obj.ACALL {
+ rel := obj.Addrel(c.cursym)
+ rel.Off = int32(c.pc)
+ rel.Siz = 0
+ rel.Type = objabi.R_CALLIND
+ }
case 7: /* beq s */
o1 = c.opbra(p, p.As)
@@ -4204,7 +4212,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
rel.Add = 0
rel.Type = objabi.R_ARM64_GOTPCREL
- case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub/vbif/vuzip1/vuzip2 Vm., Vn., Vd. */
+ case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub/vbif/vuzip1/vuzip2/vrax1 Vm., Vn., Vd. */
af := int((p.From.Reg >> 5) & 15)
af3 := int((p.Reg >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
@@ -4268,6 +4276,12 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
} else {
size = 0
}
+ case AVRAX1:
+ if af != ARNG_2D {
+ c.ctxt.Diag("invalid arrangement: %v", p)
+ }
+ size = 0
+ Q = 0
}
o1 |= (uint32(Q&1) << 30) | (uint32(size&3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
@@ -5185,6 +5199,51 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
c.ctxt.Diag("shift amount out of range: %v\n", p)
}
o1 |= uint32(immh)<<19 | uint32(shift)<<16 | uint32(rf&31)<<5 | uint32(p.To.Reg&31)
+ case 103: /* VEOR3/VBCAX Va.B16, Vm.B16, Vn.B16, Vd.B16 */
+ ta := (p.From.Reg >> 5) & 15
+ tm := (p.Reg >> 5) & 15
+ td := (p.To.Reg >> 5) & 15
+ tn := ((p.GetFrom3().Reg) >> 5) & 15
+
+ if ta != tm || ta != tn || ta != td || ta != ARNG_16B {
+ c.ctxt.Diag("invalid arrangement: %v", p)
+ break
+ }
+
+ o1 = c.oprrr(p, p.As)
+ ra := int(p.From.Reg)
+ rm := int(p.Reg)
+ rn := int(p.GetFrom3().Reg)
+ rd := int(p.To.Reg)
+ o1 |= uint32(rm&31)<<16 | uint32(ra&31)<<10 | uint32(rn&31)<<5 | uint32(rd)&31
+
+ case 104: /* vxar $imm4, Vm., Vn., Vd. */
+ af := ((p.GetFrom3().Reg) >> 5) & 15
+ at := (p.To.Reg >> 5) & 15
+ a := (p.Reg >> 5) & 15
+ index := int(p.From.Offset)
+
+ if af != a || af != at {
+ c.ctxt.Diag("invalid arrangement: %v", p)
+ break
+ }
+
+ if af != ARNG_2D {
+ c.ctxt.Diag("invalid arrangement, should be D2: %v", p)
+ break
+ }
+
+ if index < 0 || index > 63 {
+ c.ctxt.Diag("illegal offset: %v", p)
+ }
+
+ o1 = c.opirr(p, p.As)
+ rf := (p.GetFrom3().Reg) & 31
+ rt := (p.To.Reg) & 31
+ r := (p.Reg) & 31
+
+ o1 |= (uint32(r&31) << 16) | (uint32(index&63) << 10) | (uint32(rf&31) << 5) | uint32(rt&31)
+
}
out[0] = o1
out[1] = o2
@@ -5760,6 +5819,9 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
case AVAND:
return 7<<25 | 1<<21 | 7<<10
+ case AVBCAX:
+ return 0xCE<<24 | 1<<21
+
case AVCMEQ:
return 1<<29 | 0x71<<21 | 0x23<<10
@@ -5775,12 +5837,18 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
case AVEOR:
return 1<<29 | 0x71<<21 | 7<<10
+ case AVEOR3:
+ return 0xCE << 24
+
case AVORR:
return 7<<25 | 5<<21 | 7<<10
case AVREV16:
return 3<<26 | 2<<24 | 1<<21 | 3<<11
+ case AVRAX1:
+ return 0xCE<<24 | 3<<21 | 1<<15 | 3<<10
+
case AVREV32:
return 11<<26 | 2<<24 | 1<<21 | 1<<11
@@ -6038,6 +6106,8 @@ func (c *ctxt7) opirr(p *obj.Prog, a obj.As) uint32 {
case AVUSHLL2, AVUXTL2:
return 3<<29 | 15<<24 | 0x29<<10
+ case AVXAR:
+ return 0xCE<<24 | 1<<23
}
c.ctxt.Diag("%v: bad irr %v", p, a)
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index 56da854f16..f1bc2583cb 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -589,7 +589,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q1.To.Reg = REGSP
q1.Spadj = c.autosize
- if c.ctxt.Headtype == objabi.Hdarwin {
+ if objabi.GOOS == "ios" {
// iOS does not support SA_ONSTACK. We will run the signal handler
// on the G stack. If we write below SP, it may be clobbered by
// the signal handler. So we save LR after decrementing SP.
diff --git a/src/cmd/internal/objabi/util.go b/src/cmd/internal/objabi/util.go
index cedb2d0a26..b81b73a022 100644
--- a/src/cmd/internal/objabi/util.go
+++ b/src/cmd/internal/objabi/util.go
@@ -24,6 +24,7 @@ var (
GOROOT = envOr("GOROOT", defaultGOROOT)
GOARCH = envOr("GOARCH", defaultGOARCH)
GOOS = envOr("GOOS", defaultGOOS)
+ GO386 = envOr("GO386", defaultGO386)
GOAMD64 = goamd64()
GOARM = goarm()
GOMIPS = gomips()
@@ -135,14 +136,6 @@ func init() {
if GOARCH != "amd64" {
Regabi_enabled = 0
}
-
- if v := os.Getenv("GO386"); v != "" && v != "sse2" {
- msg := fmt.Sprintf("unsupported setting GO386=%s", v)
- if v == "387" {
- msg += ". 387 support was dropped in Go 1.16. Consider using gccgo instead."
- }
- log.Fatal(msg)
- }
}
// Note: must agree with runtime.framepointer_enabled.
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index b2b3b02bf6..41e5ec1432 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -32,6 +32,7 @@ func MSanSupported(goos, goarch string) bool {
}
// MustLinkExternal reports whether goos/goarch requires external linking.
+// (This is the opposite of internal/testenv.CanInternalLink. Keep them in sync.)
func MustLinkExternal(goos, goarch string) bool {
switch goos {
case "android":
@@ -69,7 +70,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
- "darwin/amd64",
+ "darwin/amd64", "darwin/arm64",
"windows/amd64", "windows/386":
return true
}
@@ -86,7 +87,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
- "darwin/amd64",
+ "darwin/amd64", "darwin/arm64",
"aix/ppc64",
"windows/386", "windows/amd64", "windows/arm":
return true
@@ -104,7 +105,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
switch platform {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",
"android/amd64", "android/arm", "android/arm64", "android/386",
- "darwin/amd64",
+ "darwin/amd64", "darwin/arm64",
"freebsd/amd64":
return true
}
diff --git a/src/cmd/internal/sys/supported_test.go b/src/cmd/internal/sys/supported_test.go
new file mode 100644
index 0000000000..1217814af5
--- /dev/null
+++ b/src/cmd/internal/sys/supported_test.go
@@ -0,0 +1,18 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sys
+
+import (
+ "internal/testenv"
+ "runtime"
+ "testing"
+)
+
+func TestMustLinkExternalMatchesTestenv(t *testing.T) {
+ // MustLinkExternal and testenv.CanInternalLink are the exact opposite.
+ if b := MustLinkExternal(runtime.GOOS, runtime.GOARCH); b != !testenv.CanInternalLink() {
+ t.Fatalf("MustLinkExternal() == %v, testenv.CanInternalLink() == %v, don't match", b, testenv.CanInternalLink())
+ }
+}
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 945b83822c..1d2aa591d7 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -371,7 +371,7 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
rt := r.Type
siz := r.Size
- if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 {
+ if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_GOTPCREL {
if ldr.SymDynid(rs) < 0 {
ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
return false
@@ -415,6 +415,22 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
}
v |= 1 << 24 // pc-relative bit
v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
+ case objabi.R_ARM64_GOTPCREL:
+ siz = 4
+ // Two relocation entries: MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 MACHO_ARM64_RELOC_GOT_LOAD_PAGE21
+ // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff + 4))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ }
+ out.Write32(uint32(sectoff + 4))
+ out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
+ if r.Xadd != 0 {
+ out.Write32(uint32(sectoff))
+ out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
+ }
+ v |= 1 << 24 // pc-relative bit
+ v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
}
switch siz {
@@ -457,7 +473,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
}
nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
- if target.IsDarwin() && rt == objabi.R_ADDRARM64 && xadd != 0 {
+ if target.IsDarwin() && xadd != 0 {
nExtReloc = 4 // need another two relocations for non-zero addend
}
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index 9aa59fa3e3..aaf74b58de 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -39,7 +39,13 @@ func (mode *BuildMode) Set(s string) error {
case "pie":
switch objabi.GOOS {
case "aix", "android", "linux", "windows":
- case "darwin", "freebsd":
+ case "darwin":
+ switch objabi.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
switch objabi.GOARCH {
case "amd64":
default:
@@ -95,7 +101,13 @@ func (mode *BuildMode) Set(s string) error {
default:
return badmode()
}
- case "darwin", "freebsd":
+ case "darwin":
+ switch objabi.GOARCH {
+ case "amd64", "arm64":
+ default:
+ return badmode()
+ }
+ case "freebsd":
switch objabi.GOARCH {
case "amd64":
default:
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index 22948521f5..a66506d392 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -238,6 +238,10 @@ func TestSizes(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables")
}
+
+ // External linking may bring in C symbols with unknown size. Skip.
+ testenv.MustInternalLink(t)
+
t.Parallel()
// DWARF sizes should never be -1.
@@ -919,6 +923,7 @@ func TestAbstractOriginSanityIssue26237(t *testing.T) {
func TestRuntimeTypeAttrInternal(t *testing.T) {
testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t)
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables")
@@ -1018,6 +1023,9 @@ func main() {
t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
}
+ if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
+ return // everything is PIE on ARM64, addresses are relocated
+ }
if rtAttr.(uint64)+types.Addr != addr {
t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
}
@@ -1203,6 +1211,15 @@ func main() {
}
}
+ // When external linking, we put all symbols in the symbol table (so the
+ // external linker can find them). Skip the symbol table check.
+ // TODO: maybe there is some way to tell the external linker not to put
+ // those symbols in the executable's symbol table? Prefix the symbol name
+ // with "." or "L" to pretend it is a label?
+ if !testenv.CanInternalLink() {
+ return
+ }
+
syms, err := f.Symbols()
if err != nil {
t.Fatalf("error reading symbols: %v", err)
diff --git a/src/cmd/link/internal/ld/ld_test.go b/src/cmd/link/internal/ld/ld_test.go
index 4367c1028e..cdfaadb17d 100644
--- a/src/cmd/link/internal/ld/ld_test.go
+++ b/src/cmd/link/internal/ld/ld_test.go
@@ -18,8 +18,13 @@ import (
)
func TestUndefinedRelocErrors(t *testing.T) {
- t.Parallel()
testenv.MustHaveGoBuild(t)
+
+ // When external linking, symbols may be defined externally, so we allow
+ // undefined symbols and let external linker resolve. Skip the test.
+ testenv.MustInternalLink(t)
+
+ t.Parallel()
dir, err := ioutil.TempDir("", "go-build")
if err != nil {
t.Fatal(err)
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index cd630e9eae..5fe028d321 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1254,7 +1254,9 @@ func (ctxt *Link) hostlink() {
// -headerpad is incompatible with -fembed-bitcode.
argv = append(argv, "-Wl,-headerpad,1144")
}
- if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) {
+ if ctxt.DynlinkingGo() && objabi.GOOS != "ios" {
+ // -flat_namespace is deprecated on iOS.
+ // It is useful for supporting plugins. We don't support plugins on iOS.
argv = append(argv, "-Wl,-flat_namespace")
}
if !combineDwarf {
@@ -1327,9 +1329,6 @@ func (ctxt *Link) hostlink() {
case BuildModeCShared:
if ctxt.HeadType == objabi.Hdarwin {
argv = append(argv, "-dynamiclib")
- if ctxt.Arch.Family != sys.AMD64 {
- argv = append(argv, "-Wl,-read_only_relocs,suppress")
- }
} else {
// ELF.
argv = append(argv, "-Wl,-Bsymbolic")
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 9765ce18d3..2c7f6111de 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -76,36 +76,38 @@ const (
)
const (
- MACHO_CPU_AMD64 = 1<<24 | 7
- MACHO_CPU_386 = 7
- MACHO_SUBCPU_X86 = 3
- MACHO_CPU_ARM = 12
- MACHO_SUBCPU_ARM = 0
- MACHO_SUBCPU_ARMV7 = 9
- MACHO_CPU_ARM64 = 1<<24 | 12
- MACHO_SUBCPU_ARM64_ALL = 0
- MACHO32SYMSIZE = 12
- MACHO64SYMSIZE = 16
- MACHO_X86_64_RELOC_UNSIGNED = 0
- MACHO_X86_64_RELOC_SIGNED = 1
- MACHO_X86_64_RELOC_BRANCH = 2
- MACHO_X86_64_RELOC_GOT_LOAD = 3
- MACHO_X86_64_RELOC_GOT = 4
- MACHO_X86_64_RELOC_SUBTRACTOR = 5
- MACHO_X86_64_RELOC_SIGNED_1 = 6
- MACHO_X86_64_RELOC_SIGNED_2 = 7
- MACHO_X86_64_RELOC_SIGNED_4 = 8
- MACHO_ARM_RELOC_VANILLA = 0
- MACHO_ARM_RELOC_PAIR = 1
- MACHO_ARM_RELOC_SECTDIFF = 2
- MACHO_ARM_RELOC_BR24 = 5
- MACHO_ARM64_RELOC_UNSIGNED = 0
- MACHO_ARM64_RELOC_BRANCH26 = 2
- MACHO_ARM64_RELOC_PAGE21 = 3
- MACHO_ARM64_RELOC_PAGEOFF12 = 4
- MACHO_ARM64_RELOC_ADDEND = 10
- MACHO_GENERIC_RELOC_VANILLA = 0
- MACHO_FAKE_GOTPCREL = 100
+ MACHO_CPU_AMD64 = 1<<24 | 7
+ MACHO_CPU_386 = 7
+ MACHO_SUBCPU_X86 = 3
+ MACHO_CPU_ARM = 12
+ MACHO_SUBCPU_ARM = 0
+ MACHO_SUBCPU_ARMV7 = 9
+ MACHO_CPU_ARM64 = 1<<24 | 12
+ MACHO_SUBCPU_ARM64_ALL = 0
+ MACHO32SYMSIZE = 12
+ MACHO64SYMSIZE = 16
+ MACHO_X86_64_RELOC_UNSIGNED = 0
+ MACHO_X86_64_RELOC_SIGNED = 1
+ MACHO_X86_64_RELOC_BRANCH = 2
+ MACHO_X86_64_RELOC_GOT_LOAD = 3
+ MACHO_X86_64_RELOC_GOT = 4
+ MACHO_X86_64_RELOC_SUBTRACTOR = 5
+ MACHO_X86_64_RELOC_SIGNED_1 = 6
+ MACHO_X86_64_RELOC_SIGNED_2 = 7
+ MACHO_X86_64_RELOC_SIGNED_4 = 8
+ MACHO_ARM_RELOC_VANILLA = 0
+ MACHO_ARM_RELOC_PAIR = 1
+ MACHO_ARM_RELOC_SECTDIFF = 2
+ MACHO_ARM_RELOC_BR24 = 5
+ MACHO_ARM64_RELOC_UNSIGNED = 0
+ MACHO_ARM64_RELOC_BRANCH26 = 2
+ MACHO_ARM64_RELOC_PAGE21 = 3
+ MACHO_ARM64_RELOC_PAGEOFF12 = 4
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
+ MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
+ MACHO_ARM64_RELOC_ADDEND = 10
+ MACHO_GENERIC_RELOC_VANILLA = 0
+ MACHO_FAKE_GOTPCREL = 100
)
const (
@@ -473,6 +475,18 @@ func (ctxt *Link) domacho() {
sb.SetReachable(true)
sb.AddUint8(0)
}
+
+ // Do not export C symbols dynamically in plugins, as runtime C symbols like crosscall2
+ // are in pclntab and end up pointing at the host binary, breaking unwinding.
+ // See Issue #18190.
+ if ctxt.BuildMode == BuildModePlugin {
+ for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} {
+ s := ctxt.loader.Lookup(name, 0)
+ if s != 0 {
+ ctxt.loader.SetAttrCgoExportDynamic(s, false)
+ }
+ }
+ }
}
func machoadddynlib(lib string, linkmode LinkMode) {
@@ -897,19 +911,12 @@ func machosymtab(ctxt *Link) {
symtab.AddUint32(ctxt.Arch, uint32(symstr.Size()))
export := machoShouldExport(ctxt, ldr, s)
- isGoSymbol := strings.Contains(ldr.SymExtname(s), ".")
- // In normal buildmodes, only add _ to C symbols, as
- // Go symbols have dot in the name.
- //
- // Do not export C symbols in plugins, as runtime C
- // symbols like crosscall2 are in pclntab and end up
- // pointing at the host binary, breaking unwinding.
- // See Issue #18190.
- cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(ldr.SymName(s)))
- if cexport || export || isGoSymbol {
- symstr.AddUint8('_')
- }
+ // Prefix symbol names with "_" to match the system toolchain.
+ // (We used to only prefix C symbols, which is all required for the build.
+ // But some tools don't recognize Go symbols as symbols, so we prefix them
+ // as well.)
+ symstr.AddUint8('_')
// replace "·" as ".", because DTrace cannot handle it.
symstr.Addstring(strings.Replace(ldr.SymExtname(s), "·", ".", -1))
@@ -920,10 +927,13 @@ func machosymtab(ctxt *Link) {
symtab.AddUint16(ctxt.Arch, 0) // desc
symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value
} else {
- if ldr.AttrCgoExport(s) || export {
- symtab.AddUint8(0x0f)
+ if export || ldr.AttrCgoExportDynamic(s) {
+ symtab.AddUint8(0x0f) // N_SECT | N_EXT
+ } else if ldr.AttrCgoExportStatic(s) {
+ // Only export statically, not dynamically. (N_PEXT is like hidden visibility)
+ symtab.AddUint8(0x1f) // N_SECT | N_EXT | N_PEXT
} else {
- symtab.AddUint8(0x0e)
+ symtab.AddUint8(0x0e) // N_SECT
}
o := s
if outer := ldr.OuterSym(o); outer != 0 {
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 75e63248df..facb30fe15 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -13,7 +13,6 @@ import (
"fmt"
"os"
"path/filepath"
- "strings"
)
// pclntab holds the state needed for pclntab generation.
@@ -113,23 +112,7 @@ func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.Compilat
return state, compUnits, funcs
}
-// onlycsymbol looks at a symbol's name to report whether this is a
-// symbol that is referenced by C code
-func onlycsymbol(sname string) bool {
- switch sname {
- case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
- return true
- }
- if strings.HasPrefix(sname, "_cgoexp_") {
- return true
- }
- return false
-}
-
func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
- if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(ctxt.loader.SymName(s)) {
- return false
- }
// We want to generate func table entries only for the "lowest
// level" symbols, not containers of subsymbols.
return !container.Has(s)
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index b7611f207c..6729568766 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -181,6 +181,7 @@ main.x: relocation target main.zero not defined
func TestIssue33979(t *testing.T) {
testenv.MustHaveGoBuild(t)
testenv.MustHaveCGO(t)
+ testenv.MustInternalLink(t)
// Skip test on platforms that do not support cgo internal linking.
switch runtime.GOARCH {
diff --git a/src/cmd/nm/nm_cgo_test.go b/src/cmd/nm/nm_cgo_test.go
index 9a257e0ed2..58f2c24908 100644
--- a/src/cmd/nm/nm_cgo_test.go
+++ b/src/cmd/nm/nm_cgo_test.go
@@ -15,6 +15,11 @@ func canInternalLink() bool {
switch runtime.GOOS {
case "aix":
return false
+ case "darwin":
+ switch runtime.GOARCH {
+ case "arm64":
+ return false
+ }
case "dragonfly":
return false
case "freebsd":
diff --git a/src/cmd/nm/nm_test.go b/src/cmd/nm/nm_test.go
index 413a4eb06f..382446e9fe 100644
--- a/src/cmd/nm/nm_test.go
+++ b/src/cmd/nm/nm_test.go
@@ -173,6 +173,9 @@ func testGoExec(t *testing.T, iscgo, isexternallinker bool) {
if runtime.GOOS == "windows" {
return true
}
+ if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
+ return true // On darwin/arm64 everything is PIE
+ }
return false
}
diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go
index 903f9cc1db..c1ddbe372f 100644
--- a/src/cmd/pprof/pprof.go
+++ b/src/cmd/pprof/pprof.go
@@ -171,7 +171,10 @@ func (*objTool) Demangle(names []string) (map[string]string, error) {
return make(map[string]string), nil
}
-func (t *objTool) Disasm(file string, start, end uint64) ([]driver.Inst, error) {
+func (t *objTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]driver.Inst, error) {
+ if intelSyntax {
+ return nil, fmt.Errorf("printing assembly in Intel syntax is not supported")
+ }
d, err := t.cachedDisasm(file)
if err != nil {
return nil, err
diff --git a/src/cmd/vendor/github.com/google/pprof/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/driver/driver.go
index 9bcbc8295a..e65bc2f417 100644
--- a/src/cmd/vendor/github.com/google/pprof/driver/driver.go
+++ b/src/cmd/vendor/github.com/google/pprof/driver/driver.go
@@ -142,7 +142,7 @@ type ObjTool interface {
// Disasm disassembles the named object file, starting at
// the start address and stopping at (before) the end address.
- Disasm(file string, start, end uint64) ([]Inst, error)
+ Disasm(file string, start, end uint64, intelSyntax bool) ([]Inst, error)
}
// An Inst is a single instruction in an assembly listing.
@@ -269,8 +269,8 @@ func (f *internalObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym,
return pluginSyms, nil
}
-func (o *internalObjTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
- insts, err := o.ObjTool.Disasm(file, start, end)
+func (o *internalObjTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
+ insts, err := o.ObjTool.Disasm(file, start, end, intelSyntax)
if err != nil {
return nil, err
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
index 967726d1fa..4b67cc4ab0 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
@@ -19,6 +19,7 @@ import (
"debug/elf"
"debug/macho"
"encoding/binary"
+ "errors"
"fmt"
"io"
"os"
@@ -26,6 +27,7 @@ import (
"path/filepath"
"regexp"
"runtime"
+ "strconv"
"strings"
"sync"
@@ -39,6 +41,8 @@ type Binutils struct {
rep *binrep
}
+var objdumpLLVMVerRE = regexp.MustCompile(`LLVM version (?:(\d*)\.(\d*)\.(\d*)|.*(trunk).*)`)
+
// binrep is an immutable representation for Binutils. It is atomically
// replaced on every mutation to provide thread-safe access.
type binrep struct {
@@ -51,6 +55,7 @@ type binrep struct {
nmFound bool
objdump string
objdumpFound bool
+ isLLVMObjdump bool
// if fast, perform symbolization using nm (symbol names only),
// instead of file-line detail from the slower addr2line.
@@ -132,15 +137,103 @@ func initTools(b *binrep, config string) {
}
defaultPath := paths[""]
- b.llvmSymbolizer, b.llvmSymbolizerFound = findExe("llvm-symbolizer", append(paths["llvm-symbolizer"], defaultPath...))
- b.addr2line, b.addr2lineFound = findExe("addr2line", append(paths["addr2line"], defaultPath...))
- if !b.addr2lineFound {
- // On MacOS, brew installs addr2line under gaddr2line name, so search for
- // that if the tool is not found by its default name.
- b.addr2line, b.addr2lineFound = findExe("gaddr2line", append(paths["addr2line"], defaultPath...))
+ b.llvmSymbolizer, b.llvmSymbolizerFound = chooseExe([]string{"llvm-symbolizer"}, []string{}, append(paths["llvm-symbolizer"], defaultPath...))
+ b.addr2line, b.addr2lineFound = chooseExe([]string{"addr2line"}, []string{"gaddr2line"}, append(paths["addr2line"], defaultPath...))
+ // The "-n" option is supported by LLVM since 2011. The output of llvm-nm
+ // and GNU nm with "-n" option is interchangeable for our purposes, so we do
+ // not need to differrentiate them.
+ b.nm, b.nmFound = chooseExe([]string{"llvm-nm", "nm"}, []string{"gnm"}, append(paths["nm"], defaultPath...))
+ b.objdump, b.objdumpFound, b.isLLVMObjdump = findObjdump(append(paths["objdump"], defaultPath...))
+}
+
+// findObjdump finds and returns path to preferred objdump binary.
+// Order of preference is: llvm-objdump, objdump.
+// On MacOS only, also looks for gobjdump with least preference.
+// Accepts a list of paths and returns:
+// a string with path to the preferred objdump binary if found,
+// or an empty string if not found;
+// a boolean if any acceptable objdump was found;
+// a boolean indicating if it is an LLVM objdump.
+func findObjdump(paths []string) (string, bool, bool) {
+ objdumpNames := []string{"llvm-objdump", "objdump"}
+ if runtime.GOOS == "darwin" {
+ objdumpNames = append(objdumpNames, "gobjdump")
}
- b.nm, b.nmFound = findExe("nm", append(paths["nm"], defaultPath...))
- b.objdump, b.objdumpFound = findExe("objdump", append(paths["objdump"], defaultPath...))
+
+ for _, objdumpName := range objdumpNames {
+ if objdump, objdumpFound := findExe(objdumpName, paths); objdumpFound {
+ cmdOut, err := exec.Command(objdump, "--version").Output()
+ if err != nil {
+ continue
+ }
+ if isLLVMObjdump(string(cmdOut)) {
+ return objdump, true, true
+ }
+ if isBuObjdump(string(cmdOut)) {
+ return objdump, true, false
+ }
+ }
+ }
+ return "", false, false
+}
+
+// chooseExe finds and returns path to preferred binary. names is a list of
+// names to search on both Linux and OSX. osxNames is a list of names specific
+// to OSX. names always has a higher priority than osxNames. The order of
+// the name within each list decides its priority (e.g. the first name has a
+// higher priority than the second name in the list).
+//
+// It returns a string with path to the binary and a boolean indicating if any
+// acceptable binary was found.
+func chooseExe(names, osxNames []string, paths []string) (string, bool) {
+ if runtime.GOOS == "darwin" {
+ names = append(names, osxNames...)
+ }
+ for _, name := range names {
+ if binary, found := findExe(name, paths); found {
+ return binary, true
+ }
+ }
+ return "", false
+}
+
+// isLLVMObjdump accepts a string with path to an objdump binary,
+// and returns a boolean indicating if the given binary is an LLVM
+// objdump binary of an acceptable version.
+func isLLVMObjdump(output string) bool {
+ fields := objdumpLLVMVerRE.FindStringSubmatch(output)
+ if len(fields) != 5 {
+ return false
+ }
+ if fields[4] == "trunk" {
+ return true
+ }
+ verMajor, err := strconv.Atoi(fields[1])
+ if err != nil {
+ return false
+ }
+ verPatch, err := strconv.Atoi(fields[3])
+ if err != nil {
+ return false
+ }
+ if runtime.GOOS == "linux" && verMajor >= 8 {
+ // Ensure LLVM objdump is at least version 8.0 on Linux.
+ // Some flags, like --demangle, and double dashes for options are
+ // not supported by previous versions.
+ return true
+ }
+ if runtime.GOOS == "darwin" {
+ // Ensure LLVM objdump is at least version 10.0.1 on MacOS.
+ return verMajor > 10 || (verMajor == 10 && verPatch >= 1)
+ }
+ return false
+}
+
+// isBuObjdump accepts a string with path to an objdump binary,
+// and returns a boolean indicating if the given binary is a GNU
+// binutils objdump binary. No version check is performed.
+func isBuObjdump(output string) bool {
+ return strings.Contains(output, "GNU objdump")
}
// findExe looks for an executable command on a set of paths.
@@ -157,12 +250,25 @@ func findExe(cmd string, paths []string) (string, bool) {
// Disasm returns the assembly instructions for the specified address range
// of a binary.
-func (bu *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
+func (bu *Binutils) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
b := bu.get()
- cmd := exec.Command(b.objdump, "-d", "-C", "--no-show-raw-insn", "-l",
- fmt.Sprintf("--start-address=%#x", start),
- fmt.Sprintf("--stop-address=%#x", end),
- file)
+ if !b.objdumpFound {
+ return nil, errors.New("cannot disasm: no objdump tool available")
+ }
+ args := []string{"--disassemble-all", "--demangle", "--no-show-raw-insn",
+ "--line-numbers", fmt.Sprintf("--start-address=%#x", start),
+ fmt.Sprintf("--stop-address=%#x", end)}
+
+ if intelSyntax {
+ if b.isLLVMObjdump {
+ args = append(args, "--x86-asm-syntax=intel")
+ } else {
+ args = append(args, "-M", "intel")
+ }
+ }
+
+ args = append(args, file)
+ cmd := exec.Command(b.objdump, args...)
out, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("%v: %v", cmd.Args, err)
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
index 28c89aa163..d0be614bdc 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
@@ -25,10 +25,11 @@ import (
)
var (
- nmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
- objdumpAsmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
- objdumpOutputFileLine = regexp.MustCompile(`^(.*):([0-9]+)`)
- objdumpOutputFunction = regexp.MustCompile(`^(\S.*)\(\):`)
+ nmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
+ objdumpAsmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
+ objdumpOutputFileLine = regexp.MustCompile(`^;?\s?(.*):([0-9]+)`)
+ objdumpOutputFunction = regexp.MustCompile(`^;?\s?(\S.*)\(\):`)
+ objdumpOutputFunctionLLVM = regexp.MustCompile(`^([[:xdigit:]]+)?\s?(.*):`)
)
func findSymbols(syms []byte, file string, r *regexp.Regexp, address uint64) ([]*plugin.Sym, error) {
@@ -143,6 +144,11 @@ func disassemble(asm []byte) ([]plugin.Inst, error) {
if fields := objdumpOutputFunction.FindStringSubmatch(input); len(fields) == 2 {
function = fields[1]
continue
+ } else {
+ if fields := objdumpOutputFunctionLLVM.FindStringSubmatch(input); len(fields) == 3 {
+ function = fields[2]
+ continue
+ }
}
// Reset on unrecognized lines.
function, file, line = "", "", 0
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
index 9fc1eea1f0..492400c5f3 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
@@ -69,8 +69,9 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
flagHTTP := flag.String("http", "", "Present interactive web UI at the specified http host:port")
flagNoBrowser := flag.Bool("no_browser", false, "Skip opening a browswer for the interactive web UI")
- // Flags used during command processing
- installedFlags := installFlags(flag)
+ // Flags that set configuration properties.
+ cfg := currentConfig()
+ configFlagSetter := installConfigFlags(flag, &cfg)
flagCommands := make(map[string]*bool)
flagParamCommands := make(map[string]*string)
@@ -107,8 +108,8 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
}
}
- // Report conflicting options
- if err := updateFlags(installedFlags); err != nil {
+ // Apply any specified flags to cfg.
+ if err := configFlagSetter(); err != nil {
return nil, nil, err
}
@@ -124,7 +125,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
return nil, nil, errors.New("-no_browser only makes sense with -http")
}
- si := pprofVariables["sample_index"].value
+ si := cfg.SampleIndex
si = sampleIndex(flagTotalDelay, si, "delay", "-total_delay", o.UI)
si = sampleIndex(flagMeanDelay, si, "delay", "-mean_delay", o.UI)
si = sampleIndex(flagContentions, si, "contentions", "-contentions", o.UI)
@@ -132,10 +133,10 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
si = sampleIndex(flagInUseObjects, si, "inuse_objects", "-inuse_objects", o.UI)
si = sampleIndex(flagAllocSpace, si, "alloc_space", "-alloc_space", o.UI)
si = sampleIndex(flagAllocObjects, si, "alloc_objects", "-alloc_objects", o.UI)
- pprofVariables.set("sample_index", si)
+ cfg.SampleIndex = si
if *flagMeanDelay {
- pprofVariables.set("mean", "true")
+ cfg.Mean = true
}
source := &source{
@@ -154,7 +155,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
return nil, nil, err
}
- normalize := pprofVariables["normalize"].boolValue()
+ normalize := cfg.Normalize
if normalize && len(source.Base) == 0 {
return nil, nil, errors.New("must have base profile to normalize by")
}
@@ -163,6 +164,8 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
if bu, ok := o.Obj.(*binutils.Binutils); ok {
bu.SetTools(*flagTools)
}
+
+ setCurrentConfig(cfg)
return source, cmd, nil
}
@@ -194,66 +197,72 @@ func dropEmpty(list []*string) []string {
return l
}
-// installFlags creates command line flags for pprof variables.
-func installFlags(flag plugin.FlagSet) flagsInstalled {
- f := flagsInstalled{
- ints: make(map[string]*int),
- bools: make(map[string]*bool),
- floats: make(map[string]*float64),
- strings: make(map[string]*string),
- }
- for n, v := range pprofVariables {
- switch v.kind {
- case boolKind:
- if v.group != "" {
- // Set all radio variables to false to identify conflicts.
- f.bools[n] = flag.Bool(n, false, v.help)
+// installConfigFlags creates command line flags for configuration
+// fields and returns a function which can be called after flags have
+// been parsed to copy any flags specified on the command line to
+// *cfg.
+func installConfigFlags(flag plugin.FlagSet, cfg *config) func() error {
+ // List of functions for setting the different parts of a config.
+ var setters []func()
+ var err error // Holds any errors encountered while running setters.
+
+ for _, field := range configFields {
+ n := field.name
+ help := configHelp[n]
+ var setter func()
+ switch ptr := cfg.fieldPtr(field).(type) {
+ case *bool:
+ f := flag.Bool(n, *ptr, help)
+ setter = func() { *ptr = *f }
+ case *int:
+ f := flag.Int(n, *ptr, help)
+ setter = func() { *ptr = *f }
+ case *float64:
+ f := flag.Float64(n, *ptr, help)
+ setter = func() { *ptr = *f }
+ case *string:
+ if len(field.choices) == 0 {
+ f := flag.String(n, *ptr, help)
+ setter = func() { *ptr = *f }
} else {
- f.bools[n] = flag.Bool(n, v.boolValue(), v.help)
+ // Make a separate flag per possible choice.
+ // Set all flags to initially false so we can
+ // identify conflicts.
+ bools := make(map[string]*bool)
+ for _, choice := range field.choices {
+ bools[choice] = flag.Bool(choice, false, configHelp[choice])
+ }
+ setter = func() {
+ var set []string
+ for k, v := range bools {
+ if *v {
+ set = append(set, k)
+ }
+ }
+ switch len(set) {
+ case 0:
+ // Leave as default value.
+ case 1:
+ *ptr = set[0]
+ default:
+ err = fmt.Errorf("conflicting options set: %v", set)
+ }
+ }
}
- case intKind:
- f.ints[n] = flag.Int(n, v.intValue(), v.help)
- case floatKind:
- f.floats[n] = flag.Float64(n, v.floatValue(), v.help)
- case stringKind:
- f.strings[n] = flag.String(n, v.value, v.help)
}
+ setters = append(setters, setter)
}
- return f
-}
-// updateFlags updates the pprof variables according to the flags
-// parsed in the command line.
-func updateFlags(f flagsInstalled) error {
- vars := pprofVariables
- groups := map[string]string{}
- for n, v := range f.bools {
- vars.set(n, fmt.Sprint(*v))
- if *v {
- g := vars[n].group
- if g != "" && groups[g] != "" {
- return fmt.Errorf("conflicting options %q and %q set", n, groups[g])
+ return func() error {
+ // Apply the setter for every flag.
+ for _, setter := range setters {
+ setter()
+ if err != nil {
+ return err
}
- groups[g] = n
}
+ return nil
}
- for n, v := range f.ints {
- vars.set(n, fmt.Sprint(*v))
- }
- for n, v := range f.floats {
- vars.set(n, fmt.Sprint(*v))
- }
- for n, v := range f.strings {
- vars.set(n, *v)
- }
- return nil
-}
-
-type flagsInstalled struct {
- ints map[string]*int
- bools map[string]*bool
- floats map[string]*float64
- strings map[string]*string
}
// isBuildID determines if the profile may contain a build ID, by
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
index f52471490a..4397e253e0 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
@@ -22,7 +22,6 @@ import (
"os/exec"
"runtime"
"sort"
- "strconv"
"strings"
"time"
@@ -70,9 +69,7 @@ func AddCommand(cmd string, format int, post PostProcessor, desc, usage string)
// SetVariableDefault sets the default value for a pprof
// variable. This enables extensions to set their own defaults.
func SetVariableDefault(variable, value string) {
- if v := pprofVariables[variable]; v != nil {
- v.value = value
- }
+ configure(variable, value)
}
// PostProcessor is a function that applies post-processing to the report output
@@ -124,130 +121,132 @@ var pprofCommands = commands{
"weblist": {report.WebList, nil, invokeVisualizer("html", browsers()), true, "Display annotated source in a web browser", listHelp("weblist", false)},
}
-// pprofVariables are the configuration parameters that affect the
-// reported generated by pprof.
-var pprofVariables = variables{
+// configHelp contains help text per configuration parameter.
+var configHelp = map[string]string{
// Filename for file-based output formats, stdout by default.
- "output": &variable{stringKind, "", "", helpText("Output filename for file-based outputs")},
+ "output": helpText("Output filename for file-based outputs"),
// Comparisons.
- "drop_negative": &variable{boolKind, "f", "", helpText(
+ "drop_negative": helpText(
"Ignore negative differences",
- "Do not show any locations with values <0.")},
+ "Do not show any locations with values <0."),
// Graph handling options.
- "call_tree": &variable{boolKind, "f", "", helpText(
+ "call_tree": helpText(
"Create a context-sensitive call tree",
- "Treat locations reached through different paths as separate.")},
+ "Treat locations reached through different paths as separate."),
// Display options.
- "relative_percentages": &variable{boolKind, "f", "", helpText(
+ "relative_percentages": helpText(
"Show percentages relative to focused subgraph",
"If unset, percentages are relative to full graph before focusing",
- "to facilitate comparison with original graph.")},
- "unit": &variable{stringKind, "minimum", "", helpText(
+ "to facilitate comparison with original graph."),
+ "unit": helpText(
"Measurement units to display",
"Scale the sample values to this unit.",
"For time-based profiles, use seconds, milliseconds, nanoseconds, etc.",
"For memory profiles, use megabytes, kilobytes, bytes, etc.",
- "Using auto will scale each value independently to the most natural unit.")},
- "compact_labels": &variable{boolKind, "f", "", "Show minimal headers"},
- "source_path": &variable{stringKind, "", "", "Search path for source files"},
- "trim_path": &variable{stringKind, "", "", "Path to trim from source paths before search"},
+ "Using auto will scale each value independently to the most natural unit."),
+ "compact_labels": "Show minimal headers",
+ "source_path": "Search path for source files",
+ "trim_path": "Path to trim from source paths before search",
+ "intel_syntax": helpText(
+ "Show assembly in Intel syntax",
+ "Only applicable to commands `disasm` and `weblist`"),
// Filtering options
- "nodecount": &variable{intKind, "-1", "", helpText(
+ "nodecount": helpText(
"Max number of nodes to show",
"Uses heuristics to limit the number of locations to be displayed.",
- "On graphs, dotted edges represent paths through nodes that have been removed.")},
- "nodefraction": &variable{floatKind, "0.005", "", "Hide nodes below *total"},
- "edgefraction": &variable{floatKind, "0.001", "", "Hide edges below *total"},
- "trim": &variable{boolKind, "t", "", helpText(
+ "On graphs, dotted edges represent paths through nodes that have been removed."),
+ "nodefraction": "Hide nodes below *total",
+ "edgefraction": "Hide edges below *total",
+ "trim": helpText(
"Honor nodefraction/edgefraction/nodecount defaults",
- "Set to false to get the full profile, without any trimming.")},
- "focus": &variable{stringKind, "", "", helpText(
+ "Set to false to get the full profile, without any trimming."),
+ "focus": helpText(
"Restricts to samples going through a node matching regexp",
"Discard samples that do not include a node matching this regexp.",
- "Matching includes the function name, filename or object name.")},
- "ignore": &variable{stringKind, "", "", helpText(
+ "Matching includes the function name, filename or object name."),
+ "ignore": helpText(
"Skips paths going through any nodes matching regexp",
"If set, discard samples that include a node matching this regexp.",
- "Matching includes the function name, filename or object name.")},
- "prune_from": &variable{stringKind, "", "", helpText(
+ "Matching includes the function name, filename or object name."),
+ "prune_from": helpText(
"Drops any functions below the matched frame.",
"If set, any frames matching the specified regexp and any frames",
- "below it will be dropped from each sample.")},
- "hide": &variable{stringKind, "", "", helpText(
+ "below it will be dropped from each sample."),
+ "hide": helpText(
"Skips nodes matching regexp",
"Discard nodes that match this location.",
"Other nodes from samples that include this location will be shown.",
- "Matching includes the function name, filename or object name.")},
- "show": &variable{stringKind, "", "", helpText(
+ "Matching includes the function name, filename or object name."),
+ "show": helpText(
"Only show nodes matching regexp",
"If set, only show nodes that match this location.",
- "Matching includes the function name, filename or object name.")},
- "show_from": &variable{stringKind, "", "", helpText(
+ "Matching includes the function name, filename or object name."),
+ "show_from": helpText(
"Drops functions above the highest matched frame.",
"If set, all frames above the highest match are dropped from every sample.",
- "Matching includes the function name, filename or object name.")},
- "tagfocus": &variable{stringKind, "", "", helpText(
+ "Matching includes the function name, filename or object name."),
+ "tagfocus": helpText(
"Restricts to samples with tags in range or matched by regexp",
"Use name=value syntax to limit the matching to a specific tag.",
"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
- "String tag filter examples: foo, foo.*bar, mytag=foo.*bar")},
- "tagignore": &variable{stringKind, "", "", helpText(
+ "String tag filter examples: foo, foo.*bar, mytag=foo.*bar"),
+ "tagignore": helpText(
"Discard samples with tags in range or matched by regexp",
"Use name=value syntax to limit the matching to a specific tag.",
"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
- "String tag filter examples: foo, foo.*bar, mytag=foo.*bar")},
- "tagshow": &variable{stringKind, "", "", helpText(
+ "String tag filter examples: foo, foo.*bar, mytag=foo.*bar"),
+ "tagshow": helpText(
"Only consider tags matching this regexp",
- "Discard tags that do not match this regexp")},
- "taghide": &variable{stringKind, "", "", helpText(
+ "Discard tags that do not match this regexp"),
+ "taghide": helpText(
"Skip tags matching this regexp",
- "Discard tags that match this regexp")},
+ "Discard tags that match this regexp"),
// Heap profile options
- "divide_by": &variable{floatKind, "1", "", helpText(
+ "divide_by": helpText(
"Ratio to divide all samples before visualization",
- "Divide all samples values by a constant, eg the number of processors or jobs.")},
- "mean": &variable{boolKind, "f", "", helpText(
+ "Divide all samples values by a constant, eg the number of processors or jobs."),
+ "mean": helpText(
"Average sample value over first value (count)",
"For memory profiles, report average memory per allocation.",
- "For time-based profiles, report average time per event.")},
- "sample_index": &variable{stringKind, "", "", helpText(
+ "For time-based profiles, report average time per event."),
+ "sample_index": helpText(
"Sample value to report (0-based index or name)",
"Profiles contain multiple values per sample.",
- "Use sample_index=i to select the ith value (starting at 0).")},
- "normalize": &variable{boolKind, "f", "", helpText(
- "Scales profile based on the base profile.")},
+ "Use sample_index=i to select the ith value (starting at 0)."),
+ "normalize": helpText(
+ "Scales profile based on the base profile."),
// Data sorting criteria
- "flat": &variable{boolKind, "t", "cumulative", helpText("Sort entries based on own weight")},
- "cum": &variable{boolKind, "f", "cumulative", helpText("Sort entries based on cumulative weight")},
+ "flat": helpText("Sort entries based on own weight"),
+ "cum": helpText("Sort entries based on cumulative weight"),
// Output granularity
- "functions": &variable{boolKind, "t", "granularity", helpText(
+ "functions": helpText(
"Aggregate at the function level.",
- "Ignores the filename where the function was defined.")},
- "filefunctions": &variable{boolKind, "t", "granularity", helpText(
+ "Ignores the filename where the function was defined."),
+ "filefunctions": helpText(
"Aggregate at the function level.",
- "Takes into account the filename where the function was defined.")},
- "files": &variable{boolKind, "f", "granularity", "Aggregate at the file level."},
- "lines": &variable{boolKind, "f", "granularity", "Aggregate at the source code line level."},
- "addresses": &variable{boolKind, "f", "granularity", helpText(
+ "Takes into account the filename where the function was defined."),
+ "files": "Aggregate at the file level.",
+ "lines": "Aggregate at the source code line level.",
+ "addresses": helpText(
"Aggregate at the address level.",
- "Includes functions' addresses in the output.")},
- "noinlines": &variable{boolKind, "f", "", helpText(
+ "Includes functions' addresses in the output."),
+ "noinlines": helpText(
"Ignore inlines.",
- "Attributes inlined functions to their first out-of-line caller.")},
+ "Attributes inlined functions to their first out-of-line caller."),
}
func helpText(s ...string) string {
return strings.Join(s, "\n") + "\n"
}
-// usage returns a string describing the pprof commands and variables.
-// if commandLine is set, the output reflect cli usage.
+// usage returns a string describing the pprof commands and configuration
+// options. if commandLine is set, the output reflect cli usage.
func usage(commandLine bool) string {
var prefix string
if commandLine {
@@ -269,40 +268,33 @@ func usage(commandLine bool) string {
} else {
help = " Commands:\n"
commands = append(commands, fmtHelp("o/options", "List options and their current values"))
- commands = append(commands, fmtHelp("quit/exit/^D", "Exit pprof"))
+ commands = append(commands, fmtHelp("q/quit/exit/^D", "Exit pprof"))
}
help = help + strings.Join(commands, "\n") + "\n\n" +
" Options:\n"
- // Print help for variables after sorting them.
- // Collect radio variables by their group name to print them together.
- radioOptions := make(map[string][]string)
+ // Print help for configuration options after sorting them.
+ // Collect choices for multi-choice options print them together.
var variables []string
- for name, vr := range pprofVariables {
- if vr.group != "" {
- radioOptions[vr.group] = append(radioOptions[vr.group], name)
+ var radioStrings []string
+ for _, f := range configFields {
+ if len(f.choices) == 0 {
+ variables = append(variables, fmtHelp(prefix+f.name, configHelp[f.name]))
continue
}
- variables = append(variables, fmtHelp(prefix+name, vr.help))
- }
- sort.Strings(variables)
-
- help = help + strings.Join(variables, "\n") + "\n\n" +
- " Option groups (only set one per group):\n"
-
- var radioStrings []string
- for radio, ops := range radioOptions {
- sort.Strings(ops)
- s := []string{fmtHelp(radio, "")}
- for _, op := range ops {
- s = append(s, " "+fmtHelp(prefix+op, pprofVariables[op].help))
+ // Format help for for this group.
+ s := []string{fmtHelp(f.name, "")}
+ for _, choice := range f.choices {
+ s = append(s, " "+fmtHelp(prefix+choice, configHelp[choice]))
}
-
radioStrings = append(radioStrings, strings.Join(s, "\n"))
}
+ sort.Strings(variables)
sort.Strings(radioStrings)
- return help + strings.Join(radioStrings, "\n")
+ return help + strings.Join(variables, "\n") + "\n\n" +
+ " Option groups (only set one per group):\n" +
+ strings.Join(radioStrings, "\n")
}
func reportHelp(c string, cum, redirect bool) string {
@@ -445,105 +437,8 @@ func invokeVisualizer(suffix string, visualizers []string) PostProcessor {
}
}
-// variables describe the configuration parameters recognized by pprof.
-type variables map[string]*variable
-
-// variable is a single configuration parameter.
-type variable struct {
- kind int // How to interpret the value, must be one of the enums below.
- value string // Effective value. Only values appropriate for the Kind should be set.
- group string // boolKind variables with the same Group != "" cannot be set simultaneously.
- help string // Text describing the variable, in multiple lines separated by newline.
-}
-
-const (
- // variable.kind must be one of these variables.
- boolKind = iota
- intKind
- floatKind
- stringKind
-)
-
-// set updates the value of a variable, checking that the value is
-// suitable for the variable Kind.
-func (vars variables) set(name, value string) error {
- v := vars[name]
- if v == nil {
- return fmt.Errorf("no variable %s", name)
- }
- var err error
- switch v.kind {
- case boolKind:
- var b bool
- if b, err = stringToBool(value); err == nil {
- if v.group != "" && !b {
- err = fmt.Errorf("%q can only be set to true", name)
- }
- }
- case intKind:
- _, err = strconv.Atoi(value)
- case floatKind:
- _, err = strconv.ParseFloat(value, 64)
- case stringKind:
- // Remove quotes, particularly useful for empty values.
- if len(value) > 1 && strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`) {
- value = value[1 : len(value)-1]
- }
- }
- if err != nil {
- return err
- }
- vars[name].value = value
- if group := vars[name].group; group != "" {
- for vname, vvar := range vars {
- if vvar.group == group && vname != name {
- vvar.value = "f"
- }
- }
- }
- return err
-}
-
-// boolValue returns the value of a boolean variable.
-func (v *variable) boolValue() bool {
- b, err := stringToBool(v.value)
- if err != nil {
- panic("unexpected value " + v.value + " for bool ")
- }
- return b
-}
-
-// intValue returns the value of an intKind variable.
-func (v *variable) intValue() int {
- i, err := strconv.Atoi(v.value)
- if err != nil {
- panic("unexpected value " + v.value + " for int ")
- }
- return i
-}
-
-// floatValue returns the value of a Float variable.
-func (v *variable) floatValue() float64 {
- f, err := strconv.ParseFloat(v.value, 64)
- if err != nil {
- panic("unexpected value " + v.value + " for float ")
- }
- return f
-}
-
-// stringValue returns a canonical representation for a variable.
-func (v *variable) stringValue() string {
- switch v.kind {
- case boolKind:
- return fmt.Sprint(v.boolValue())
- case intKind:
- return fmt.Sprint(v.intValue())
- case floatKind:
- return fmt.Sprint(v.floatValue())
- }
- return v.value
-}
-
+// stringToBool is a custom parser for bools. We avoid using strconv.ParseBool
+// to remain compatible with old pprof behavior (e.g., treating "" as true).
func stringToBool(s string) (bool, error) {
switch strings.ToLower(s) {
case "true", "t", "yes", "y", "1", "":
@@ -554,13 +449,3 @@ func stringToBool(s string) (bool, error) {
return false, fmt.Errorf(`illegal value "%s" for bool variable`, s)
}
}
-
-// makeCopy returns a duplicate of a set of shell variables.
-func (vars variables) makeCopy() variables {
- varscopy := make(variables, len(vars))
- for n, v := range vars {
- vcopy := *v
- varscopy[n] = &vcopy
- }
- return varscopy
-}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
new file mode 100644
index 0000000000..b3f82f22c9
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
@@ -0,0 +1,367 @@
+package driver
+
+import (
+ "fmt"
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// config holds settings for a single named config.
+// The JSON tag name for a field is used both for JSON encoding and as
+// a named variable.
+type config struct {
+ // Filename for file-based output formats, stdout by default.
+ Output string `json:"-"`
+
+ // Display options.
+ CallTree bool `json:"call_tree,omitempty"`
+ RelativePercentages bool `json:"relative_percentages,omitempty"`
+ Unit string `json:"unit,omitempty"`
+ CompactLabels bool `json:"compact_labels,omitempty"`
+ SourcePath string `json:"-"`
+ TrimPath string `json:"-"`
+ IntelSyntax bool `json:"intel_syntax,omitempty"`
+ Mean bool `json:"mean,omitempty"`
+ SampleIndex string `json:"-"`
+ DivideBy float64 `json:"-"`
+ Normalize bool `json:"normalize,omitempty"`
+ Sort string `json:"sort,omitempty"`
+
+ // Filtering options
+ DropNegative bool `json:"drop_negative,omitempty"`
+ NodeCount int `json:"nodecount,omitempty"`
+ NodeFraction float64 `json:"nodefraction,omitempty"`
+ EdgeFraction float64 `json:"edgefraction,omitempty"`
+ Trim bool `json:"trim,omitempty"`
+ Focus string `json:"focus,omitempty"`
+ Ignore string `json:"ignore,omitempty"`
+ PruneFrom string `json:"prune_from,omitempty"`
+ Hide string `json:"hide,omitempty"`
+ Show string `json:"show,omitempty"`
+ ShowFrom string `json:"show_from,omitempty"`
+ TagFocus string `json:"tagfocus,omitempty"`
+ TagIgnore string `json:"tagignore,omitempty"`
+ TagShow string `json:"tagshow,omitempty"`
+ TagHide string `json:"taghide,omitempty"`
+ NoInlines bool `json:"noinlines,omitempty"`
+
+ // Output granularity
+ Granularity string `json:"granularity,omitempty"`
+}
+
+// defaultConfig returns the default configuration values; it is unaffected by
+// flags and interactive assignments.
+func defaultConfig() config {
+ return config{
+ Unit: "minimum",
+ NodeCount: -1,
+ NodeFraction: 0.005,
+ EdgeFraction: 0.001,
+ Trim: true,
+ DivideBy: 1.0,
+ Sort: "flat",
+ Granularity: "functions",
+ }
+}
+
+// currentConfig holds the current configuration values; it is affected by
+// flags and interactive assignments.
+var currentCfg = defaultConfig()
+var currentMu sync.Mutex
+
+func currentConfig() config {
+ currentMu.Lock()
+ defer currentMu.Unlock()
+ return currentCfg
+}
+
+func setCurrentConfig(cfg config) {
+ currentMu.Lock()
+ defer currentMu.Unlock()
+ currentCfg = cfg
+}
+
+// configField contains metadata for a single configuration field.
+type configField struct {
+ name string // JSON field name/key in variables
+ urlparam string // URL parameter name
+ saved bool // Is field saved in settings?
+ field reflect.StructField // Field in config
+ choices []string // Name Of variables in group
+ defaultValue string // Default value for this field.
+}
+
+var (
+ configFields []configField // Precomputed metadata per config field
+
+ // configFieldMap holds an entry for every config field as well as an
+ // entry for every valid choice for a multi-choice field.
+ configFieldMap map[string]configField
+)
+
+func init() {
+ // Config names for fields that are not saved in settings and therefore
+ // do not have a JSON name.
+ notSaved := map[string]string{
+ // Not saved in settings, but present in URLs.
+ "SampleIndex": "sample_index",
+
+ // Following fields are also not placed in URLs.
+ "Output": "output",
+ "SourcePath": "source_path",
+ "TrimPath": "trim_path",
+ "DivideBy": "divide_by",
+ }
+
+ // choices holds the list of allowed values for config fields that can
+ // take on one of a bounded set of values.
+ choices := map[string][]string{
+ "sort": {"cum", "flat"},
+ "granularity": {"functions", "filefunctions", "files", "lines", "addresses"},
+ }
+
+ // urlparam holds the mapping from a config field name to the URL
+ // parameter used to hold that config field. If no entry is present for
+ // a name, the corresponding field is not saved in URLs.
+ urlparam := map[string]string{
+ "drop_negative": "dropneg",
+ "call_tree": "calltree",
+ "relative_percentages": "rel",
+ "unit": "unit",
+ "compact_labels": "compact",
+ "intel_syntax": "intel",
+ "nodecount": "n",
+ "nodefraction": "nf",
+ "edgefraction": "ef",
+ "trim": "trim",
+ "focus": "f",
+ "ignore": "i",
+ "prune_from": "prunefrom",
+ "hide": "h",
+ "show": "s",
+ "show_from": "sf",
+ "tagfocus": "tf",
+ "tagignore": "ti",
+ "tagshow": "ts",
+ "taghide": "th",
+ "mean": "mean",
+ "sample_index": "si",
+ "normalize": "norm",
+ "sort": "sort",
+ "granularity": "g",
+ "noinlines": "noinlines",
+ }
+
+ def := defaultConfig()
+ configFieldMap = map[string]configField{}
+ t := reflect.TypeOf(config{})
+ for i, n := 0, t.NumField(); i < n; i++ {
+ field := t.Field(i)
+ js := strings.Split(field.Tag.Get("json"), ",")
+ if len(js) == 0 {
+ continue
+ }
+ // Get the configuration name for this field.
+ name := js[0]
+ if name == "-" {
+ name = notSaved[field.Name]
+ if name == "" {
+ // Not a configurable field.
+ continue
+ }
+ }
+ f := configField{
+ name: name,
+ urlparam: urlparam[name],
+ saved: (name == js[0]),
+ field: field,
+ choices: choices[name],
+ }
+ f.defaultValue = def.get(f)
+ configFields = append(configFields, f)
+ configFieldMap[f.name] = f
+ for _, choice := range f.choices {
+ configFieldMap[choice] = f
+ }
+ }
+}
+
+// fieldPtr returns a pointer to the field identified by f in *cfg.
+func (cfg *config) fieldPtr(f configField) interface{} {
+ // reflect.ValueOf: converts to reflect.Value
+ // Elem: dereferences cfg to make *cfg
+ // FieldByIndex: fetches the field
+ // Addr: takes address of field
+ // Interface: converts back from reflect.Value to a regular value
+ return reflect.ValueOf(cfg).Elem().FieldByIndex(f.field.Index).Addr().Interface()
+}
+
+// get returns the value of field f in cfg.
+func (cfg *config) get(f configField) string {
+ switch ptr := cfg.fieldPtr(f).(type) {
+ case *string:
+ return *ptr
+ case *int:
+ return fmt.Sprint(*ptr)
+ case *float64:
+ return fmt.Sprint(*ptr)
+ case *bool:
+ return fmt.Sprint(*ptr)
+ }
+ panic(fmt.Sprintf("unsupported config field type %v", f.field.Type))
+}
+
+// set sets the value of field f in cfg to value.
+func (cfg *config) set(f configField, value string) error {
+ switch ptr := cfg.fieldPtr(f).(type) {
+ case *string:
+ if len(f.choices) > 0 {
+ // Verify that value is one of the allowed choices.
+ for _, choice := range f.choices {
+ if choice == value {
+ *ptr = value
+ return nil
+ }
+ }
+ return fmt.Errorf("invalid %q value %q", f.name, value)
+ }
+ *ptr = value
+ case *int:
+ v, err := strconv.Atoi(value)
+ if err != nil {
+ return err
+ }
+ *ptr = v
+ case *float64:
+ v, err := strconv.ParseFloat(value, 64)
+ if err != nil {
+ return err
+ }
+ *ptr = v
+ case *bool:
+ v, err := stringToBool(value)
+ if err != nil {
+ return err
+ }
+ *ptr = v
+ default:
+ panic(fmt.Sprintf("unsupported config field type %v", f.field.Type))
+ }
+ return nil
+}
+
+// isConfigurable returns true if name is either the name of a config field, or
+// a valid value for a multi-choice config field.
+func isConfigurable(name string) bool {
+ _, ok := configFieldMap[name]
+ return ok
+}
+
+// isBoolConfig returns true if name is either name of a boolean config field,
+// or a valid value for a multi-choice config field.
+func isBoolConfig(name string) bool {
+ f, ok := configFieldMap[name]
+ if !ok {
+ return false
+ }
+ if name != f.name {
+ return true // name must be one possible value for the field
+ }
+ var cfg config
+ _, ok = cfg.fieldPtr(f).(*bool)
+ return ok
+}
+
+// completeConfig returns the list of configurable names starting with prefix.
+func completeConfig(prefix string) []string {
+ var result []string
+ for v := range configFieldMap {
+ if strings.HasPrefix(v, prefix) {
+ result = append(result, v)
+ }
+ }
+ return result
+}
+
+// configure stores the name=value mapping into the current config, correctly
+// handling the case when name identifies a particular choice in a field.
+func configure(name, value string) error {
+ currentMu.Lock()
+ defer currentMu.Unlock()
+ f, ok := configFieldMap[name]
+ if !ok {
+ return fmt.Errorf("unknown config field %q", name)
+ }
+ if f.name == name {
+ return currentCfg.set(f, value)
+ }
+ // name must be one of the choices. If value is true, set field-value
+ // to name.
+ if v, err := strconv.ParseBool(value); v && err == nil {
+ return currentCfg.set(f, name)
+ }
+ return fmt.Errorf("unknown config field %q", name)
+}
+
+// resetTransient sets all transient fields in *cfg to their currently
+// configured values.
+func (cfg *config) resetTransient() {
+ current := currentConfig()
+ cfg.Output = current.Output
+ cfg.SourcePath = current.SourcePath
+ cfg.TrimPath = current.TrimPath
+ cfg.DivideBy = current.DivideBy
+ cfg.SampleIndex = current.SampleIndex
+}
+
+// applyURL updates *cfg based on params.
+func (cfg *config) applyURL(params url.Values) error {
+ for _, f := range configFields {
+ var value string
+ if f.urlparam != "" {
+ value = params.Get(f.urlparam)
+ }
+ if value == "" {
+ continue
+ }
+ if err := cfg.set(f, value); err != nil {
+ return fmt.Errorf("error setting config field %s: %v", f.name, err)
+ }
+ }
+ return nil
+}
+
+// makeURL returns a URL based on initialURL that contains the config contents
+// as parameters. The second result is true iff a parameter value was changed.
+func (cfg *config) makeURL(initialURL url.URL) (url.URL, bool) {
+ q := initialURL.Query()
+ changed := false
+ for _, f := range configFields {
+ if f.urlparam == "" || !f.saved {
+ continue
+ }
+ v := cfg.get(f)
+ if v == f.defaultValue {
+ v = "" // URL for of default value is the empty string.
+ } else if f.field.Type.Kind() == reflect.Bool {
+ // Shorten bool values to "f" or "t"
+ v = v[:1]
+ }
+ if q.Get(f.urlparam) == v {
+ continue
+ }
+ changed = true
+ if v == "" {
+ q.Del(f.urlparam)
+ } else {
+ q.Set(f.urlparam, v)
+ }
+ }
+ if changed {
+ initialURL.RawQuery = q.Encode()
+ }
+ return initialURL, changed
+}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
index 1be749aa32..878f2e1ead 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
@@ -50,7 +50,7 @@ func PProf(eo *plugin.Options) error {
}
if cmd != nil {
- return generateReport(p, cmd, pprofVariables, o)
+ return generateReport(p, cmd, currentConfig(), o)
}
if src.HTTPHostport != "" {
@@ -59,7 +59,7 @@ func PProf(eo *plugin.Options) error {
return interactive(p, o)
}
-func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) (*command, *report.Report, error) {
+func generateRawReport(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) (*command, *report.Report, error) {
p = p.Copy() // Prevent modification to the incoming profile.
// Identify units of numeric tags in profile.
@@ -71,16 +71,16 @@ func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plug
panic("unexpected nil command")
}
- vars = applyCommandOverrides(cmd[0], c.format, vars)
+ cfg = applyCommandOverrides(cmd[0], c.format, cfg)
// Delay focus after configuring report to get percentages on all samples.
- relative := vars["relative_percentages"].boolValue()
+ relative := cfg.RelativePercentages
if relative {
- if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
+ if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
return nil, nil, err
}
}
- ropt, err := reportOptions(p, numLabelUnits, vars)
+ ropt, err := reportOptions(p, numLabelUnits, cfg)
if err != nil {
return nil, nil, err
}
@@ -95,19 +95,19 @@ func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plug
rpt := report.New(p, ropt)
if !relative {
- if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
+ if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
return nil, nil, err
}
}
- if err := aggregate(p, vars); err != nil {
+ if err := aggregate(p, cfg); err != nil {
return nil, nil, err
}
return c, rpt, nil
}
-func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
- c, rpt, err := generateRawReport(p, cmd, vars, o)
+func generateReport(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) error {
+ c, rpt, err := generateRawReport(p, cmd, cfg, o)
if err != nil {
return err
}
@@ -129,7 +129,7 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.
}
// If no output is specified, use default visualizer.
- output := vars["output"].value
+ output := cfg.Output
if output == "" {
if c.visualizer != nil {
return c.visualizer(src, os.Stdout, o.UI)
@@ -151,7 +151,7 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.
return out.Close()
}
-func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
+func applyCommandOverrides(cmd string, outputFormat int, cfg config) config {
// Some report types override the trim flag to false below. This is to make
// sure the default heuristics of excluding insignificant nodes and edges
// from the call graph do not apply. One example where it is important is
@@ -160,55 +160,55 @@ func applyCommandOverrides(cmd string, outputFormat int, v variables) variables
// data is selected. So, with trimming enabled, the report could end up
// showing no data if the specified function is "uninteresting" as far as the
// trimming is concerned.
- trim := v["trim"].boolValue()
+ trim := cfg.Trim
switch cmd {
case "disasm", "weblist":
trim = false
- v.set("addresses", "t")
+ cfg.Granularity = "addresses"
// Force the 'noinlines' mode so that source locations for a given address
// collapse and there is only one for the given address. Without this
// cumulative metrics would be double-counted when annotating the assembly.
// This is because the merge is done by address and in case of an inlined
// stack each of the inlined entries is a separate callgraph node.
- v.set("noinlines", "t")
+ cfg.NoInlines = true
case "peek":
trim = false
case "list":
trim = false
- v.set("lines", "t")
+ cfg.Granularity = "lines"
// Do not force 'noinlines' to be false so that specifying
// "-list foo -noinlines" is supported and works as expected.
case "text", "top", "topproto":
- if v["nodecount"].intValue() == -1 {
- v.set("nodecount", "0")
+ if cfg.NodeCount == -1 {
+ cfg.NodeCount = 0
}
default:
- if v["nodecount"].intValue() == -1 {
- v.set("nodecount", "80")
+ if cfg.NodeCount == -1 {
+ cfg.NodeCount = 80
}
}
switch outputFormat {
case report.Proto, report.Raw, report.Callgrind:
trim = false
- v.set("addresses", "t")
- v.set("noinlines", "f")
+ cfg.Granularity = "addresses"
+ cfg.NoInlines = false
}
if !trim {
- v.set("nodecount", "0")
- v.set("nodefraction", "0")
- v.set("edgefraction", "0")
+ cfg.NodeCount = 0
+ cfg.NodeFraction = 0
+ cfg.EdgeFraction = 0
}
- return v
+ return cfg
}
-func aggregate(prof *profile.Profile, v variables) error {
+func aggregate(prof *profile.Profile, cfg config) error {
var function, filename, linenumber, address bool
- inlines := !v["noinlines"].boolValue()
- switch {
- case v["addresses"].boolValue():
+ inlines := !cfg.NoInlines
+ switch cfg.Granularity {
+ case "addresses":
if inlines {
return nil
}
@@ -216,15 +216,15 @@ func aggregate(prof *profile.Profile, v variables) error {
filename = true
linenumber = true
address = true
- case v["lines"].boolValue():
+ case "lines":
function = true
filename = true
linenumber = true
- case v["files"].boolValue():
+ case "files":
filename = true
- case v["functions"].boolValue():
+ case "functions":
function = true
- case v["filefunctions"].boolValue():
+ case "filefunctions":
function = true
filename = true
default:
@@ -233,8 +233,8 @@ func aggregate(prof *profile.Profile, v variables) error {
return prof.Aggregate(inlines, function, filename, linenumber, address)
}
-func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars variables) (*report.Options, error) {
- si, mean := vars["sample_index"].value, vars["mean"].boolValue()
+func reportOptions(p *profile.Profile, numLabelUnits map[string]string, cfg config) (*report.Options, error) {
+ si, mean := cfg.SampleIndex, cfg.Mean
value, meanDiv, sample, err := sampleFormat(p, si, mean)
if err != nil {
return nil, err
@@ -245,29 +245,37 @@ func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars var
stype = "mean_" + stype
}
- if vars["divide_by"].floatValue() == 0 {
+ if cfg.DivideBy == 0 {
return nil, fmt.Errorf("zero divisor specified")
}
var filters []string
- for _, k := range []string{"focus", "ignore", "hide", "show", "show_from", "tagfocus", "tagignore", "tagshow", "taghide"} {
- v := vars[k].value
+ addFilter := func(k string, v string) {
if v != "" {
filters = append(filters, k+"="+v)
}
}
+ addFilter("focus", cfg.Focus)
+ addFilter("ignore", cfg.Ignore)
+ addFilter("hide", cfg.Hide)
+ addFilter("show", cfg.Show)
+ addFilter("show_from", cfg.ShowFrom)
+ addFilter("tagfocus", cfg.TagFocus)
+ addFilter("tagignore", cfg.TagIgnore)
+ addFilter("tagshow", cfg.TagShow)
+ addFilter("taghide", cfg.TagHide)
ropt := &report.Options{
- CumSort: vars["cum"].boolValue(),
- CallTree: vars["call_tree"].boolValue(),
- DropNegative: vars["drop_negative"].boolValue(),
+ CumSort: cfg.Sort == "cum",
+ CallTree: cfg.CallTree,
+ DropNegative: cfg.DropNegative,
- CompactLabels: vars["compact_labels"].boolValue(),
- Ratio: 1 / vars["divide_by"].floatValue(),
+ CompactLabels: cfg.CompactLabels,
+ Ratio: 1 / cfg.DivideBy,
- NodeCount: vars["nodecount"].intValue(),
- NodeFraction: vars["nodefraction"].floatValue(),
- EdgeFraction: vars["edgefraction"].floatValue(),
+ NodeCount: cfg.NodeCount,
+ NodeFraction: cfg.NodeFraction,
+ EdgeFraction: cfg.EdgeFraction,
ActiveFilters: filters,
NumLabelUnits: numLabelUnits,
@@ -277,10 +285,12 @@ func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars var
SampleType: stype,
SampleUnit: sample.Unit,
- OutputUnit: vars["unit"].value,
+ OutputUnit: cfg.Unit,
- SourcePath: vars["source_path"].stringValue(),
- TrimPath: vars["trim_path"].stringValue(),
+ SourcePath: cfg.SourcePath,
+ TrimPath: cfg.TrimPath,
+
+ IntelSyntax: cfg.IntelSyntax,
}
if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_focus.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_focus.go
index af7b8d478a..048ba17cb0 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_focus.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_focus.go
@@ -28,15 +28,15 @@ import (
var tagFilterRangeRx = regexp.MustCompile("([+-]?[[:digit:]]+)([[:alpha:]]+)?")
// applyFocus filters samples based on the focus/ignore options
-func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, v variables, ui plugin.UI) error {
- focus, err := compileRegexOption("focus", v["focus"].value, nil)
- ignore, err := compileRegexOption("ignore", v["ignore"].value, err)
- hide, err := compileRegexOption("hide", v["hide"].value, err)
- show, err := compileRegexOption("show", v["show"].value, err)
- showfrom, err := compileRegexOption("show_from", v["show_from"].value, err)
- tagfocus, err := compileTagFilter("tagfocus", v["tagfocus"].value, numLabelUnits, ui, err)
- tagignore, err := compileTagFilter("tagignore", v["tagignore"].value, numLabelUnits, ui, err)
- prunefrom, err := compileRegexOption("prune_from", v["prune_from"].value, err)
+func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, cfg config, ui plugin.UI) error {
+ focus, err := compileRegexOption("focus", cfg.Focus, nil)
+ ignore, err := compileRegexOption("ignore", cfg.Ignore, err)
+ hide, err := compileRegexOption("hide", cfg.Hide, err)
+ show, err := compileRegexOption("show", cfg.Show, err)
+ showfrom, err := compileRegexOption("show_from", cfg.ShowFrom, err)
+ tagfocus, err := compileTagFilter("tagfocus", cfg.TagFocus, numLabelUnits, ui, err)
+ tagignore, err := compileTagFilter("tagignore", cfg.TagIgnore, numLabelUnits, ui, err)
+ prunefrom, err := compileRegexOption("prune_from", cfg.PruneFrom, err)
if err != nil {
return err
}
@@ -54,8 +54,8 @@ func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, v variab
warnNoMatches(tagfocus == nil || tfm, "TagFocus", ui)
warnNoMatches(tagignore == nil || tim, "TagIgnore", ui)
- tagshow, err := compileRegexOption("tagshow", v["tagshow"].value, err)
- taghide, err := compileRegexOption("taghide", v["taghide"].value, err)
+ tagshow, err := compileRegexOption("tagshow", cfg.TagShow, err)
+ taghide, err := compileRegexOption("taghide", cfg.TagHide, err)
tns, tnh := prof.FilterTagsByName(tagshow, taghide)
warnNoMatches(tagshow == nil || tns, "TagShow", ui)
warnNoMatches(tagignore == nil || tnh, "TagHide", ui)
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
index 13613cff86..fbeb765dbc 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
@@ -38,7 +38,10 @@ type treeNode struct {
func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
// Force the call tree so that the graph is a tree.
// Also do not trim the tree so that the flame graph contains all functions.
- rpt, errList := ui.makeReport(w, req, []string{"svg"}, "call_tree", "true", "trim", "false")
+ rpt, errList := ui.makeReport(w, req, []string{"svg"}, func(cfg *config) {
+ cfg.CallTree = true
+ cfg.Trim = false
+ })
if rpt == nil {
return // error already reported
}
@@ -96,7 +99,7 @@ func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
return
}
- ui.render(w, "flamegraph", rpt, errList, config.Labels, webArgs{
+ ui.render(w, req, "flamegraph", rpt, errList, config.Labels, webArgs{
FlameGraph: template.JS(b),
Nodes: nodeArr,
})
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
index 3a458b0b77..777fb90bfb 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
@@ -34,17 +34,14 @@ var tailDigitsRE = regexp.MustCompile("[0-9]+$")
func interactive(p *profile.Profile, o *plugin.Options) error {
// Enter command processing loop.
o.UI.SetAutoComplete(newCompleter(functionNames(p)))
- pprofVariables.set("compact_labels", "true")
- pprofVariables["sample_index"].help += fmt.Sprintf("Or use sample_index=name, with name in %v.\n", sampleTypes(p))
+ configure("compact_labels", "true")
+ configHelp["sample_index"] += fmt.Sprintf("Or use sample_index=name, with name in %v.\n", sampleTypes(p))
// Do not wait for the visualizer to complete, to allow multiple
// graphs to be visualized simultaneously.
interactiveMode = true
shortcuts := profileShortcuts(p)
- // Get all groups in pprofVariables to allow for clearer error messages.
- groups := groupOptions(pprofVariables)
-
greetings(p, o.UI)
for {
input, err := o.UI.ReadLine("(pprof) ")
@@ -69,7 +66,12 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
}
value = strings.TrimSpace(value)
}
- if v := pprofVariables[name]; v != nil {
+ if isConfigurable(name) {
+ // All non-bool options require inputs
+ if len(s) == 1 && !isBoolConfig(name) {
+ o.UI.PrintErr(fmt.Errorf("please specify a value, e.g. %s=", name))
+ continue
+ }
if name == "sample_index" {
// Error check sample_index=xxx to ensure xxx is a valid sample type.
index, err := p.SampleIndexByName(value)
@@ -77,23 +79,17 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
o.UI.PrintErr(err)
continue
}
+ if index < 0 || index >= len(p.SampleType) {
+ o.UI.PrintErr(fmt.Errorf("invalid sample_index %q", value))
+ continue
+ }
value = p.SampleType[index].Type
}
- if err := pprofVariables.set(name, value); err != nil {
+ if err := configure(name, value); err != nil {
o.UI.PrintErr(err)
}
continue
}
- // Allow group=variable syntax by converting into variable="".
- if v := pprofVariables[value]; v != nil && v.group == name {
- if err := pprofVariables.set(value, ""); err != nil {
- o.UI.PrintErr(err)
- }
- continue
- } else if okValues := groups[name]; okValues != nil {
- o.UI.PrintErr(fmt.Errorf("unrecognized value for %s: %q. Use one of %s", name, value, strings.Join(okValues, ", ")))
- continue
- }
}
tokens := strings.Fields(input)
@@ -105,16 +101,16 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
case "o", "options":
printCurrentOptions(p, o.UI)
continue
- case "exit", "quit":
+ case "exit", "quit", "q":
return nil
case "help":
commandHelp(strings.Join(tokens[1:], " "), o.UI)
continue
}
- args, vars, err := parseCommandLine(tokens)
+ args, cfg, err := parseCommandLine(tokens)
if err == nil {
- err = generateReportWrapper(p, args, vars, o)
+ err = generateReportWrapper(p, args, cfg, o)
}
if err != nil {
@@ -124,30 +120,13 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
}
}
-// groupOptions returns a map containing all non-empty groups
-// mapped to an array of the option names in that group in
-// sorted order.
-func groupOptions(vars variables) map[string][]string {
- groups := make(map[string][]string)
- for name, option := range vars {
- group := option.group
- if group != "" {
- groups[group] = append(groups[group], name)
- }
- }
- for _, names := range groups {
- sort.Strings(names)
- }
- return groups
-}
-
var generateReportWrapper = generateReport // For testing purposes.
// greetings prints a brief welcome and some overall profile
// information before accepting interactive commands.
func greetings(p *profile.Profile, ui plugin.UI) {
numLabelUnits := identifyNumLabelUnits(p, ui)
- ropt, err := reportOptions(p, numLabelUnits, pprofVariables)
+ ropt, err := reportOptions(p, numLabelUnits, currentConfig())
if err == nil {
rpt := report.New(p, ropt)
ui.Print(strings.Join(report.ProfileLabels(rpt), "\n"))
@@ -200,27 +179,16 @@ func sampleTypes(p *profile.Profile) []string {
func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
var args []string
- type groupInfo struct {
- set string
- values []string
- }
- groups := make(map[string]*groupInfo)
- for n, o := range pprofVariables {
- v := o.stringValue()
+ current := currentConfig()
+ for _, f := range configFields {
+ n := f.name
+ v := current.get(f)
comment := ""
- if g := o.group; g != "" {
- gi, ok := groups[g]
- if !ok {
- gi = &groupInfo{}
- groups[g] = gi
- }
- if o.boolValue() {
- gi.set = n
- }
- gi.values = append(gi.values, n)
- continue
- }
switch {
+ case len(f.choices) > 0:
+ values := append([]string{}, f.choices...)
+ sort.Strings(values)
+ comment = "[" + strings.Join(values, " | ") + "]"
case n == "sample_index":
st := sampleTypes(p)
if v == "" {
@@ -242,18 +210,13 @@ func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
}
args = append(args, fmt.Sprintf(" %-25s = %-20s %s", n, v, comment))
}
- for g, vars := range groups {
- sort.Strings(vars.values)
- comment := commentStart + " [" + strings.Join(vars.values, " | ") + "]"
- args = append(args, fmt.Sprintf(" %-25s = %-20s %s", g, vars.set, comment))
- }
sort.Strings(args)
ui.Print(strings.Join(args, "\n"))
}
// parseCommandLine parses a command and returns the pprof command to
-// execute and a set of variables for the report.
-func parseCommandLine(input []string) ([]string, variables, error) {
+// execute and the configuration to use for the report.
+func parseCommandLine(input []string) ([]string, config, error) {
cmd, args := input[:1], input[1:]
name := cmd[0]
@@ -267,25 +230,32 @@ func parseCommandLine(input []string) ([]string, variables, error) {
}
}
if c == nil {
- return nil, nil, fmt.Errorf("unrecognized command: %q", name)
+ if _, ok := configHelp[name]; ok {
+ value := ""
+ if len(args) > 0 {
+ value = args[0]
+ }
+ return nil, config{}, fmt.Errorf("did you mean: %s=%s", name, value)
+ }
+ return nil, config{}, fmt.Errorf("unrecognized command: %q", name)
}
if c.hasParam {
if len(args) == 0 {
- return nil, nil, fmt.Errorf("command %s requires an argument", name)
+ return nil, config{}, fmt.Errorf("command %s requires an argument", name)
}
cmd = append(cmd, args[0])
args = args[1:]
}
- // Copy the variables as options set in the command line are not persistent.
- vcopy := pprofVariables.makeCopy()
+ // Copy config since options set in the command line should not persist.
+ vcopy := currentConfig()
var focus, ignore string
for i := 0; i < len(args); i++ {
t := args[i]
- if _, err := strconv.ParseInt(t, 10, 32); err == nil {
- vcopy.set("nodecount", t)
+ if n, err := strconv.ParseInt(t, 10, 32); err == nil {
+ vcopy.NodeCount = int(n)
continue
}
switch t[0] {
@@ -294,14 +264,14 @@ func parseCommandLine(input []string) ([]string, variables, error) {
if outputFile == "" {
i++
if i >= len(args) {
- return nil, nil, fmt.Errorf("unexpected end of line after >")
+ return nil, config{}, fmt.Errorf("unexpected end of line after >")
}
outputFile = args[i]
}
- vcopy.set("output", outputFile)
+ vcopy.Output = outputFile
case '-':
if t == "--cum" || t == "-cum" {
- vcopy.set("cum", "t")
+ vcopy.Sort = "cum"
continue
}
ignore = catRegex(ignore, t[1:])
@@ -311,30 +281,27 @@ func parseCommandLine(input []string) ([]string, variables, error) {
}
if name == "tags" {
- updateFocusIgnore(vcopy, "tag", focus, ignore)
+ if focus != "" {
+ vcopy.TagFocus = focus
+ }
+ if ignore != "" {
+ vcopy.TagIgnore = ignore
+ }
} else {
- updateFocusIgnore(vcopy, "", focus, ignore)
+ if focus != "" {
+ vcopy.Focus = focus
+ }
+ if ignore != "" {
+ vcopy.Ignore = ignore
+ }
}
-
- if vcopy["nodecount"].intValue() == -1 && (name == "text" || name == "top") {
- vcopy.set("nodecount", "10")
+ if vcopy.NodeCount == -1 && (name == "text" || name == "top") {
+ vcopy.NodeCount = 10
}
return cmd, vcopy, nil
}
-func updateFocusIgnore(v variables, prefix, f, i string) {
- if f != "" {
- focus := prefix + "focus"
- v.set(focus, catRegex(v[focus].value, f))
- }
-
- if i != "" {
- ignore := prefix + "ignore"
- v.set(ignore, catRegex(v[ignore].value, i))
- }
-}
-
func catRegex(a, b string) string {
if a != "" && b != "" {
return a + "|" + b
@@ -362,8 +329,8 @@ func commandHelp(args string, ui plugin.UI) {
return
}
- if v := pprofVariables[args]; v != nil {
- ui.Print(v.help + "\n")
+ if help, ok := configHelp[args]; ok {
+ ui.Print(help + "\n")
return
}
@@ -373,18 +340,17 @@ func commandHelp(args string, ui plugin.UI) {
// newCompleter creates an autocompletion function for a set of commands.
func newCompleter(fns []string) func(string) string {
return func(line string) string {
- v := pprofVariables
switch tokens := strings.Fields(line); len(tokens) {
case 0:
// Nothing to complete
case 1:
// Single token -- complete command name
- if match := matchVariableOrCommand(v, tokens[0]); match != "" {
+ if match := matchVariableOrCommand(tokens[0]); match != "" {
return match
}
case 2:
if tokens[0] == "help" {
- if match := matchVariableOrCommand(v, tokens[1]); match != "" {
+ if match := matchVariableOrCommand(tokens[1]); match != "" {
return tokens[0] + " " + match
}
return line
@@ -408,26 +374,19 @@ func newCompleter(fns []string) func(string) string {
}
// matchVariableOrCommand attempts to match a string token to the prefix of a Command.
-func matchVariableOrCommand(v variables, token string) string {
+func matchVariableOrCommand(token string) string {
token = strings.ToLower(token)
- found := ""
+ var matches []string
for cmd := range pprofCommands {
if strings.HasPrefix(cmd, token) {
- if found != "" {
- return ""
- }
- found = cmd
+ matches = append(matches, cmd)
}
}
- for variable := range v {
- if strings.HasPrefix(variable, token) {
- if found != "" {
- return ""
- }
- found = variable
- }
+ matches = append(matches, completeConfig(token)...)
+ if len(matches) == 1 {
+ return matches[0]
}
- return found
+ return ""
}
// functionCompleter replaces provided substring with a function
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
new file mode 100644
index 0000000000..f72314b185
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
@@ -0,0 +1,157 @@
+package driver
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "path/filepath"
+)
+
+// settings holds pprof settings.
+type settings struct {
+ // Configs holds a list of named UI configurations.
+ Configs []namedConfig `json:"configs"`
+}
+
+// namedConfig associates a name with a config.
+type namedConfig struct {
+ Name string `json:"name"`
+ config
+}
+
+// settingsFileName returns the name of the file where settings should be saved.
+func settingsFileName() (string, error) {
+ // Return "pprof/settings.json" under os.UserConfigDir().
+ dir, err := os.UserConfigDir()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(dir, "pprof", "settings.json"), nil
+}
+
+// readSettings reads settings from fname.
+func readSettings(fname string) (*settings, error) {
+ data, err := ioutil.ReadFile(fname)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return &settings{}, nil
+ }
+ return nil, fmt.Errorf("could not read settings: %w", err)
+ }
+ settings := &settings{}
+ if err := json.Unmarshal(data, settings); err != nil {
+ return nil, fmt.Errorf("could not parse settings: %w", err)
+ }
+ for i := range settings.Configs {
+ settings.Configs[i].resetTransient()
+ }
+ return settings, nil
+}
+
+// writeSettings saves settings to fname.
+func writeSettings(fname string, settings *settings) error {
+ data, err := json.MarshalIndent(settings, "", " ")
+ if err != nil {
+ return fmt.Errorf("could not encode settings: %w", err)
+ }
+
+ // create the settings directory if it does not exist
+ // XDG specifies permissions 0700 when creating settings dirs:
+ // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
+ return fmt.Errorf("failed to create settings directory: %w", err)
+ }
+
+ if err := ioutil.WriteFile(fname, data, 0644); err != nil {
+ return fmt.Errorf("failed to write settings: %w", err)
+ }
+ return nil
+}
+
+// configMenuEntry holds information for a single config menu entry.
+type configMenuEntry struct {
+ Name string
+ URL string
+ Current bool // Is this the currently selected config?
+ UserConfig bool // Is this a user-provided config?
+}
+
+// configMenu returns a list of items to add to a menu in the web UI.
+func configMenu(fname string, url url.URL) []configMenuEntry {
+ // Start with system configs.
+ configs := []namedConfig{{Name: "Default", config: defaultConfig()}}
+ if settings, err := readSettings(fname); err == nil {
+ // Add user configs.
+ configs = append(configs, settings.Configs...)
+ }
+
+ // Convert to menu entries.
+ result := make([]configMenuEntry, len(configs))
+ lastMatch := -1
+ for i, cfg := range configs {
+ dst, changed := cfg.config.makeURL(url)
+ if !changed {
+ lastMatch = i
+ }
+ result[i] = configMenuEntry{
+ Name: cfg.Name,
+ URL: dst.String(),
+ UserConfig: (i != 0),
+ }
+ }
+ // Mark the last matching config as currennt
+ if lastMatch >= 0 {
+ result[lastMatch].Current = true
+ }
+ return result
+}
+
+// editSettings edits settings by applying fn to them.
+func editSettings(fname string, fn func(s *settings) error) error {
+ settings, err := readSettings(fname)
+ if err != nil {
+ return err
+ }
+ if err := fn(settings); err != nil {
+ return err
+ }
+ return writeSettings(fname, settings)
+}
+
+// setConfig saves the config specified in request to fname.
+func setConfig(fname string, request url.URL) error {
+ q := request.Query()
+ name := q.Get("config")
+ if name == "" {
+ return fmt.Errorf("invalid config name")
+ }
+ cfg := currentConfig()
+ if err := cfg.applyURL(q); err != nil {
+ return err
+ }
+ return editSettings(fname, func(s *settings) error {
+ for i, c := range s.Configs {
+ if c.Name == name {
+ s.Configs[i].config = cfg
+ return nil
+ }
+ }
+ s.Configs = append(s.Configs, namedConfig{Name: name, config: cfg})
+ return nil
+ })
+}
+
+// removeConfig removes config from fname.
+func removeConfig(fname, config string) error {
+ return editSettings(fname, func(s *settings) error {
+ for i, c := range s.Configs {
+ if c.Name == config {
+ s.Configs = append(s.Configs[:i], s.Configs[i+1:]...)
+ return nil
+ }
+ }
+ return fmt.Errorf("config %s not found", config)
+ })
+}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
index 89b8882a6b..4f7610c7e5 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
@@ -166,6 +166,73 @@ a {
color: gray;
pointer-events: none;
}
+.menu-check-mark {
+ position: absolute;
+ left: 2px;
+}
+.menu-delete-btn {
+ position: absolute;
+ right: 2px;
+}
+
+{{/* Used to disable events when a modal dialog is displayed */}}
+#dialog-overlay {
+ display: none;
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(1,1,1,0.1);
+}
+
+.dialog {
+ {{/* Displayed centered horizontally near the top */}}
+ display: none;
+ position: fixed;
+ margin: 0px;
+ top: 60px;
+ left: 50%;
+ transform: translateX(-50%);
+
+ z-index: 3;
+ font-size: 125%;
+ background-color: #ffffff;
+ box-shadow: 0 1px 5px rgba(0,0,0,.3);
+}
+.dialog-header {
+ font-size: 120%;
+ border-bottom: 1px solid #CCCCCC;
+ width: 100%;
+ text-align: center;
+ background: #EEEEEE;
+ user-select: none;
+}
+.dialog-footer {
+ border-top: 1px solid #CCCCCC;
+ width: 100%;
+ text-align: right;
+ padding: 10px;
+}
+.dialog-error {
+ margin: 10px;
+ color: red;
+}
+.dialog input {
+ margin: 10px;
+ font-size: inherit;
+}
+.dialog button {
+ margin-left: 10px;
+ font-size: inherit;
+}
+#save-dialog, #delete-dialog {
+ width: 50%;
+ max-width: 20em;
+}
+#delete-prompt {
+ padding: 10px;
+}
#content {
overflow-y: scroll;
@@ -200,6 +267,8 @@ table thead {
font-family: 'Roboto Medium', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
}
table tr th {
+ position: sticky;
+ top: 0;
background-color: #ddd;
text-align: right;
padding: .3em .5em;
@@ -282,6 +351,24 @@ table tr td {
+