diff --git a/doc/modules.md b/doc/modules.md
new file mode 100644
index 00000000000..d9bc73efb30
--- /dev/null
+++ b/doc/modules.md
@@ -0,0 +1,61 @@
+
+
+
+
+## Introduction
+
+## Glossary
+
+## Packages, modules, and versions
+
+## go.mod files
+
+### go.mod file format
+
+### Minimal version selection (MVS)
+
+### Compatibility with non-module repositories
+
+## Module-aware build commands
+
+### Enabling modules
+
+### Initializing modules
+
+### Build commands
+
+### Vendoring
+
+### `go mod download`
+
+### `go mod verify`
+
+### `go mod edit`
+
+### `go clean -modcache`
+
+### Module commands outside a module
+
+## Retrieving modules
+
+### GOPROXY protocol
+
+### Module zip requirements
+
+### Privacy
+
+### Private modules
+
+## Authenticating modules
+
+### go.sum file format
+
+### Checksum database
+
+### Privacy
diff --git a/doc/play/hello.go b/doc/play/hello.go
index 078ddff8f41..3badf12538b 100644
--- a/doc/play/hello.go
+++ b/doc/play/hello.go
@@ -1,3 +1,5 @@
+// You can edit this code!
+// Click here and start typing.
package main
import "fmt"
diff --git a/misc/benchcmp b/misc/benchcmp
deleted file mode 100755
index 84d92eefd40..00000000000
--- a/misc/benchcmp
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-echo 'misc/benchcmp has moved:' >&2
-echo ' go get -u golang.org/x/tools/cmd/benchcmp' >&2
-exit 2
diff --git a/src/bytes/hash/hash.go b/src/bytes/hash/hash.go
index 0e44e37ae73..cc78b229019 100644
--- a/src/bytes/hash/hash.go
+++ b/src/bytes/hash/hash.go
@@ -130,7 +130,9 @@ func MakeSeed(s uint64) Seed {
// New returns a new Hash object. Different hash objects allocated by
// this function will very likely have different seeds.
func New() *Hash {
- seed := Seed{s: uint64(runtime_fastrand())}
+ s1 := uint64(runtime_fastrand())
+ s2 := uint64(runtime_fastrand())
+ seed := Seed{s: s1<<32 + s2}
return &Hash{
seed: seed,
state: seed,
diff --git a/src/bytes/hash/hash_test.go b/src/bytes/hash/hash_test.go
index 311f451be2e..f36d5068310 100644
--- a/src/bytes/hash/hash_test.go
+++ b/src/bytes/hash/hash_test.go
@@ -61,6 +61,20 @@ func TestHashBytesVsString(t *testing.T) {
}
}
+func TestHashHighBytes(t *testing.T) {
+ // See issue 34925.
+ const N = 10
+ m := map[uint64]struct{}{}
+ for i := 0; i < N; i++ {
+ h := hash.New()
+ h.AddString("foo")
+ m[h.Hash()>>32] = struct{}{}
+ }
+ if len(m) < N/2 {
+ t.Errorf("from %d seeds, wanted at least %d different hashes; got %d", N, N/2, len(m))
+ }
+}
+
// Make sure a Hash implements the hash.Hash and hash.Hash64 interfaces.
var _ basehash.Hash = &hash.Hash{}
var _ basehash.Hash64 = &hash.Hash{}
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index f227d7f8502..8c3bf81bf78 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -55,7 +55,7 @@ For example:
The default pkg-config tool may be changed by setting the PKG_CONFIG environment variable.
-For security reasons, only a limited set of flags are allowed, notably -D, -I, and -l.
+For security reasons, only a limited set of flags are allowed, notably -D, -U, -I, and -l.
To allow additional flags, set CGO_CFLAGS_ALLOW to a regular expression
matching the new flags. To disallow flags that would otherwise be allowed,
set CGO_CFLAGS_DISALLOW to a regular expression matching arguments
@@ -99,7 +99,7 @@ Will be expanded to:
When the Go tool sees that one or more Go files use the special import
"C", it will look for other non-Go files in the directory and compile
-them as part of the Go package. Any .c, .s, or .S files will be
+them as part of the Go package. Any .c, .s, .S or .sx files will be
compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index a82ed0995cd..480ff6523ac 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -164,6 +164,14 @@ func duff(size int64) (int64, int64) {
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
switch v.Op {
+ case ssa.OpAMD64VFMADD231SD:
+ p := s.Prog(v.Op.Asm())
+ p.From = obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[2].Reg()}
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: v.Reg()}
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[1].Reg()})
+ if v.Reg() != v.Args[0].Reg() {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
+ }
case ssa.OpAMD64ADDQ, ssa.OpAMD64ADDL:
r := v.Reg()
r1 := v.Args[0].Reg()
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 2c77912f215..e20a72cfc8e 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -226,7 +226,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = r
case ssa.OpARMSRR:
genregshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR)
- case ssa.OpARMMULAF, ssa.OpARMMULAD, ssa.OpARMMULSF, ssa.OpARMMULSD:
+ case ssa.OpARMMULAF, ssa.OpARMMULAD, ssa.OpARMMULSF, ssa.OpARMMULSD, ssa.OpARMFMULAD:
r := v.Reg()
r0 := v.Args[0].Reg()
r1 := v.Args[1].Reg()
diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go
index a2b5b537408..44a06fd7278 100644
--- a/src/cmd/compile/internal/gc/align.go
+++ b/src/cmd/compile/internal/gc/align.go
@@ -178,6 +178,11 @@ func widstruct(errtype *types.Type, t *types.Type, o int64, flag int) int64 {
// have not already been calculated, it calls Fatal.
// This is used to prevent data races in the back end.
func dowidth(t *types.Type) {
+ // Calling dowidth when typecheck tracing enabled is not safe.
+ // See issue #33658.
+ if enableTrace && skipDowidthForTracing {
+ return
+ }
if Widthptr == 0 {
Fatalf("dowidth without betypeinit")
}
diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
index a770356ea04..17c45cab15b 100644
--- a/src/cmd/compile/internal/gc/builtin.go
+++ b/src/cmd/compile/internal/gc/builtin.go
@@ -185,6 +185,8 @@ var runtimeDecls = [...]struct {
{"checkptrArithmetic", funcTag, 122},
{"x86HasPOPCNT", varTag, 15},
{"x86HasSSE41", varTag, 15},
+ {"x86HasFMA", varTag, 15},
+ {"armHasVFPv4", varTag, 15},
{"arm64HasATOMICS", varTag, 15},
}
@@ -310,7 +312,7 @@ func runtimeTypes() []*types.Type {
typs[117] = functype(nil, []*Node{anonfield(typs[23]), anonfield(typs[23])}, []*Node{anonfield(typs[23])})
typs[118] = functype(nil, []*Node{anonfield(typs[50])}, nil)
typs[119] = functype(nil, []*Node{anonfield(typs[50]), anonfield(typs[50])}, nil)
- typs[120] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[1])}, nil)
+ typs[120] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[1]), anonfield(typs[50])}, nil)
typs[121] = types.NewSlice(typs[56])
typs[122] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[121])}, nil)
return typs[:]
diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go
index 3e9055b2acc..3fc82c26812 100644
--- a/src/cmd/compile/internal/gc/builtin/runtime.go
+++ b/src/cmd/compile/internal/gc/builtin/runtime.go
@@ -235,10 +235,12 @@ func racewriterange(addr, size uintptr)
func msanread(addr, size uintptr)
func msanwrite(addr, size uintptr)
-func checkptrAlignment(unsafe.Pointer, *byte)
+func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
// architecture variants
var x86HasPOPCNT bool
var x86HasSSE41 bool
+var x86HasFMA bool
+var armHasVFPv4 bool
var arm64HasATOMICS bool
diff --git a/src/cmd/compile/internal/gc/escape.go b/src/cmd/compile/internal/gc/escape.go
index e25c79998cd..fdf327d7151 100644
--- a/src/cmd/compile/internal/gc/escape.go
+++ b/src/cmd/compile/internal/gc/escape.go
@@ -471,8 +471,8 @@ func (e *Escape) exprSkipInit(k EscHole, n *Node) {
e.discard(max)
case OCONV, OCONVNOP:
- if checkPtr(e.curfn) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() {
- // When -d=checkptr is enabled, treat
+ if checkPtr(e.curfn, 2) && n.Type.Etype == TUNSAFEPTR && n.Left.Type.IsPtr() {
+ // When -d=checkptr=2 is enabled, treat
// conversions to unsafe.Pointer as an
// escaping operation. This allows better
// runtime instrumentation, since we can more
@@ -880,7 +880,9 @@ func (e *Escape) augmentParamHole(k EscHole, where *Node) EscHole {
// non-transient location to avoid arguments from being
// transiently allocated.
if where.Op == ODEFER && e.loopDepth == 1 {
- where.Esc = EscNever // force stack allocation of defer record (see ssa.go)
+ // force stack allocation of defer record, unless open-coded
+ // defers are used (see ssa.go)
+ where.Esc = EscNever
return e.later(k)
}
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index f6ad3752a0a..d05f754f30b 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -311,6 +311,8 @@ var (
racewriterange,
x86HasPOPCNT,
x86HasSSE41,
+ x86HasFMA,
+ armHasVFPv4,
arm64HasATOMICS,
typedmemclr,
typedmemmove,
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index e05b454805f..2b502c46014 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -61,7 +61,7 @@ func pragmaValue(verb string) syntax.Pragma {
case "go:norace":
return Norace
case "go:nosplit":
- return Nosplit
+ return Nosplit | NoCheckPtr // implies NoCheckPtr (see #34972)
case "go:noinline":
return Noinline
case "go:nocheckptr":
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 121342e80d9..4684dfb88f2 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -53,6 +53,7 @@ var (
Debug_typecheckinl int
Debug_gendwarfinl int
Debug_softfloat int
+ Debug_defer int
)
// Debug arguments.
@@ -83,6 +84,7 @@ var debugtab = []struct {
{"typecheckinl", "eager typechecking of inline function bodies", &Debug_typecheckinl},
{"dwarfinl", "print information about DWARF inlined function creation", &Debug_gendwarfinl},
{"softfloat", "force compiler to emit soft-float code", &Debug_softfloat},
+ {"defer", "print information about defer compilation", &Debug_defer},
}
const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=]
@@ -94,6 +96,11 @@ const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=]
const debugHelpFooter = `
is key-specific.
+Key "checkptr" supports values:
+ "0": instrumentation disabled
+ "1": conversions involving unsafe.Pointer are instrumented
+ "2": conversions to unsafe.Pointer force heap allocation
+
Key "pctab" supports values:
"pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata"
`
@@ -338,6 +345,11 @@ func Main(archInit func(*Arch)) {
if flag_race && flag_msan {
log.Fatal("cannot use both -race and -msan")
}
+ if (flag_race || flag_msan) && objabi.GOOS != "windows" {
+ // -race and -msan imply -d=checkptr for now (except on windows).
+ // TODO(mdempsky): Re-evaluate before Go 1.14. See #34964.
+ Debug_checkptr = 1
+ }
if ispkgin(omit_pkgs) {
flag_race = false
flag_msan = false
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index ae0fc1dbc12..a3e82cf6993 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -296,6 +296,9 @@ func addGCLocals() {
}
ggloblsym(x, int32(len(x.P)), attr)
}
+ if x := s.Func.OpenCodedDeferInfo; x != nil {
+ ggloblsym(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
+ }
}
}
diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
index 1745b92e6b3..5f0ece0ad7a 100644
--- a/src/cmd/compile/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -863,7 +863,16 @@ func (lv *Liveness) solve() {
newliveout.vars.Set(pos)
}
case ssa.BlockExit:
- // panic exit - nothing to do
+ if lv.fn.Func.HasDefer() && !lv.fn.Func.OpenCodedDeferDisallowed() {
+ // All stack slots storing args for open-coded
+ // defers are live at panic exit (since they
+ // will be used in running defers)
+ for i, n := range lv.vars {
+ if n.Name.OpenDeferSlot() {
+ newliveout.vars.Set(int32(i))
+ }
+ }
+ }
default:
// A variable is live on output from this block
// if it is live on input to some successor.
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index 9e3dca25c81..f614b60685e 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -112,27 +112,6 @@ func bmap(t *types.Type) *types.Type {
elems := makefield("elems", arr)
field = append(field, elems)
- // Make sure the overflow pointer is the last memory in the struct,
- // because the runtime assumes it can use size-ptrSize as the
- // offset of the overflow pointer. We double-check that property
- // below once the offsets and size are computed.
- //
- // BUCKETSIZE is 8, so the struct is aligned to 64 bits to this point.
- // On 32-bit systems, the max alignment is 32-bit, and the
- // overflow pointer will add another 32-bit field, and the struct
- // will end with no padding.
- // On 64-bit systems, the max alignment is 64-bit, and the
- // overflow pointer will add another 64-bit field, and the struct
- // will end with no padding.
- // On nacl/amd64p32, however, the max alignment is 64-bit,
- // but the overflow pointer will add only a 32-bit field,
- // so if the struct needs 64-bit padding (because a key or elem does)
- // then it would end with an extra 32-bit padding field.
- // Preempt that by emitting the padding here.
- if int(elemtype.Align) > Widthptr || int(keytype.Align) > Widthptr {
- field = append(field, makefield("pad", types.Types[TUINTPTR]))
- }
-
// If keys and elems have no pointers, the map implementation
// can keep a list of overflow pointers on the side so that
// buckets can be marked as having no pointers.
@@ -196,7 +175,7 @@ func bmap(t *types.Type) *types.Type {
}
// Double-check that overflow field is final memory in struct,
- // with no padding at end. See comment above.
+ // with no padding at end.
if overflow.Offset != bucket.Width-int64(Widthptr) {
Fatalf("bad offset of overflow in bmap for %v", t)
}
@@ -338,6 +317,7 @@ func deferstruct(stksize int64) *types.Type {
makefield("siz", types.Types[TUINT32]),
makefield("started", types.Types[TBOOL]),
makefield("heap", types.Types[TBOOL]),
+ makefield("openDefer", types.Types[TBOOL]),
makefield("sp", types.Types[TUINTPTR]),
makefield("pc", types.Types[TUINTPTR]),
// Note: the types here don't really matter. Defer structures
@@ -346,6 +326,9 @@ func deferstruct(stksize int64) *types.Type {
makefield("fn", types.Types[TUINTPTR]),
makefield("_panic", types.Types[TUINTPTR]),
makefield("link", types.Types[TUINTPTR]),
+ makefield("framepc", types.Types[TUINTPTR]),
+ makefield("varp", types.Types[TUINTPTR]),
+ makefield("fd", types.Types[TUINTPTR]),
makefield("args", argtype),
}
diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go
index f4725c0eb29..ce4a216c2e2 100644
--- a/src/cmd/compile/internal/gc/sizeof_test.go
+++ b/src/cmd/compile/internal/gc/sizeof_test.go
@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {Func{}, 116, 208},
+ {Func{}, 124, 224},
{Name{}, 32, 56},
{Param{}, 24, 48},
{Node{}, 76, 128},
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index dd8dacd1497..dff559a7baf 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -29,6 +29,10 @@ var ssaDumpStdout bool // whether to dump to stdout
var ssaDumpCFG string // generate CFGs for these phases
const ssaDumpFile = "ssa.html"
+// The max number of defers in a function using open-coded defers. We enforce this
+// limit because the deferBits bitmask is currently a single byte (to minimize code size)
+const maxOpenDefers = 8
+
// ssaDumpInlined holds all inlined functions when ssaDump contains a function name.
var ssaDumpInlined []*Node
@@ -91,6 +95,8 @@ func initssaconfig() {
racewriterange = sysfunc("racewriterange")
x86HasPOPCNT = sysvar("x86HasPOPCNT") // bool
x86HasSSE41 = sysvar("x86HasSSE41") // bool
+ x86HasFMA = sysvar("x86HasFMA") // bool
+ armHasVFPv4 = sysvar("armHasVFPv4") // bool
arm64HasATOMICS = sysvar("arm64HasATOMICS") // bool
typedmemclr = sysfunc("typedmemclr")
typedmemmove = sysfunc("typedmemmove")
@@ -165,6 +171,111 @@ func initssaconfig() {
SigPanic = sysfunc("sigpanic")
}
+// getParam returns the Field of ith param of node n (which is a
+// function/method/interface call), where the receiver of a method call is
+// considered as the 0th parameter. This does not include the receiver of an
+// interface call.
+func getParam(n *Node, i int) *types.Field {
+ t := n.Left.Type
+ if n.Op == OCALLMETH {
+ if i == 0 {
+ return t.Recv()
+ }
+ return t.Params().Field(i - 1)
+ }
+ return t.Params().Field(i)
+}
+
+// dvarint writes a varint v to the funcdata in symbol x and returns the new offset
+func dvarint(x *obj.LSym, off int, v int64) int {
+ if v < 0 || v > 1e9 {
+ panic(fmt.Sprintf("dvarint: bad offset for funcdata - %v", v))
+ }
+ if v < 1<<7 {
+ return duint8(x, off, uint8(v))
+ }
+ off = duint8(x, off, uint8((v&127)|128))
+ if v < 1<<14 {
+ return duint8(x, off, uint8(v>>7))
+ }
+ off = duint8(x, off, uint8(((v>>7)&127)|128))
+ if v < 1<<21 {
+ return duint8(x, off, uint8(v>>14))
+ }
+ off = duint8(x, off, uint8(((v>>14)&127)|128))
+ if v < 1<<28 {
+ return duint8(x, off, uint8(v>>21))
+ }
+ off = duint8(x, off, uint8(((v>>21)&127)|128))
+ return duint8(x, off, uint8(v>>28))
+}
+
+// emitOpenDeferInfo emits FUNCDATA information about the defers in a function
+// that is using open-coded defers. This funcdata is used to determine the active
+// defers in a function and execute those defers during panic processing.
+//
+// The funcdata is all encoded in varints (since values will almost always be less than
+// 128, but stack offsets could potentially be up to 2Gbyte). All "locations" (offsets)
+// for stack variables are specified as the number of bytes below varp (pointer to the
+// top of the local variables) for their starting address. The format is:
+//
+// - Max total argument size among all the defers
+// - Offset of the deferBits variable
+// - Number of defers in the function
+// - Information about each defer call, in reverse order of appearance in the function:
+// - Total argument size of the call
+// - Offset of the closure value to call
+// - Number of arguments (including interface receiver or method receiver as first arg)
+// - Information about each argument
+// - Offset of the stored defer argument in this function's frame
+// - Size of the argument
+// - Offset of where argument should be placed in the args frame when making call
+func (s *state) emitOpenDeferInfo() {
+ x := Ctxt.Lookup(s.curfn.Func.lsym.Name + ".opendefer")
+ s.curfn.Func.lsym.Func.OpenCodedDeferInfo = x
+ off := 0
+
+ // Compute maxargsize (max size of arguments for all defers)
+ // first, so we can output it first to the funcdata
+ var maxargsize int64
+ for i := len(s.openDefers) - 1; i >= 0; i-- {
+ r := s.openDefers[i]
+ argsize := r.n.Left.Type.ArgWidth()
+ if argsize > maxargsize {
+ maxargsize = argsize
+ }
+ }
+ off = dvarint(x, off, maxargsize)
+ off = dvarint(x, off, -s.deferBitsTemp.Xoffset)
+ off = dvarint(x, off, int64(len(s.openDefers)))
+
+ // Write in reverse-order, for ease of running in that order at runtime
+ for i := len(s.openDefers) - 1; i >= 0; i-- {
+ r := s.openDefers[i]
+ off = dvarint(x, off, r.n.Left.Type.ArgWidth())
+ off = dvarint(x, off, -r.closureNode.Xoffset)
+ numArgs := len(r.argNodes)
+ if r.rcvrNode != nil {
+ // If there's an interface receiver, treat/place it as the first
+ // arg. (If there is a method receiver, it's already included as
+ // first arg in r.argNodes.)
+ numArgs++
+ }
+ off = dvarint(x, off, int64(numArgs))
+ if r.rcvrNode != nil {
+ off = dvarint(x, off, -r.rcvrNode.Xoffset)
+ off = dvarint(x, off, s.config.PtrSize)
+ off = dvarint(x, off, 0)
+ }
+ for j, arg := range r.argNodes {
+ f := getParam(r.n, j)
+ off = dvarint(x, off, -arg.Xoffset)
+ off = dvarint(x, off, f.Type.Size())
+ off = dvarint(x, off, f.Offset)
+ }
+ }
+}
+
// buildssa builds an SSA function for fn.
// worker indicates which of the backend workers is doing the processing.
func buildssa(fn *Node, worker int) *ssa.Func {
@@ -227,11 +338,55 @@ func buildssa(fn *Node, worker int) *ssa.Func {
s.labeledNodes = map[*Node]*ssaLabel{}
s.fwdVars = map[*Node]*ssa.Value{}
s.startmem = s.entryNewValue0(ssa.OpInitMem, types.TypeMem)
+
+ s.hasOpenDefers = Debug['N'] == 0 && s.hasdefer && !s.curfn.Func.OpenCodedDeferDisallowed()
+ if s.hasOpenDefers && (Ctxt.Flag_shared || Ctxt.Flag_dynlink) && thearch.LinkArch.Name == "386" {
+ // Don't support open-coded defers for 386 ONLY when using shared
+ // libraries, because there is extra code (added by rewriteToUseGot())
+ // preceding the deferreturn/ret code that is generated by gencallret()
+ // that we don't track correctly.
+ s.hasOpenDefers = false
+ }
+ if s.hasOpenDefers && s.curfn.Func.Exit.Len() > 0 {
+ // Skip doing open defers if there is any extra exit code (likely
+ // copying heap-allocated return values or race detection), since
+ // we will not generate that code in the case of the extra
+ // deferreturn/ret segment.
+ s.hasOpenDefers = false
+ }
+ if s.hasOpenDefers &&
+ s.curfn.Func.numReturns*s.curfn.Func.numDefers > 15 {
+ // Since we are generating defer calls at every exit for
+ // open-coded defers, skip doing open-coded defers if there are
+ // too many returns (especially if there are multiple defers).
+ // Open-coded defers are most important for improving performance
+ // for smaller functions (which don't have many returns).
+ s.hasOpenDefers = false
+ }
+
s.sp = s.entryNewValue0(ssa.OpSP, types.Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead
s.sb = s.entryNewValue0(ssa.OpSB, types.Types[TUINTPTR])
s.startBlock(s.f.Entry)
s.vars[&memVar] = s.startmem
+ if s.hasOpenDefers {
+ // Create the deferBits variable and stack slot. deferBits is a
+ // bitmask showing which of the open-coded defers in this function
+ // have been activated.
+ deferBitsTemp := tempAt(src.NoXPos, s.curfn, types.Types[TUINT8])
+ s.deferBitsTemp = deferBitsTemp
+ // For this value, AuxInt is initialized to zero by default
+ startDeferBits := s.entryNewValue0(ssa.OpConst8, types.Types[TUINT8])
+ s.vars[&deferBitsVar] = startDeferBits
+ s.deferBitsAddr = s.addr(deferBitsTemp, false)
+ s.store(types.Types[TUINT8], s.deferBitsAddr, startDeferBits)
+ // Make sure that the deferBits stack slot is kept alive (for use
+ // by panics) and stores to deferBits are not eliminated, even if
+ // all checking code on deferBits in the function exit can be
+ // eliminated, because the defer statements were all
+ // unconditional.
+ s.vars[&memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, deferBitsTemp, s.mem(), false)
+ }
// Generate addresses of local declarations
s.decladdrs = map[*Node]*ssa.Value{}
@@ -287,6 +442,11 @@ func buildssa(fn *Node, worker int) *ssa.Func {
// Main call to ssa package to compile function
ssa.Compile(s.f)
+
+ if s.hasOpenDefers {
+ s.emitOpenDeferInfo()
+ }
+
return s.f
}
@@ -375,6 +535,29 @@ func (s *state) updateUnsetPredPos(b *ssa.Block) {
}
}
+// Information about each open-coded defer.
+type openDeferInfo struct {
+ // The ODEFER node representing the function call of the defer
+ n *Node
+ // If defer call is closure call, the address of the argtmp where the
+ // closure is stored.
+ closure *ssa.Value
+ // The node representing the argtmp where the closure is stored - used for
+ // function, method, or interface call, to store a closure that panic
+ // processing can use for this defer.
+ closureNode *Node
+ // If defer call is interface call, the address of the argtmp where the
+ // receiver is stored
+ rcvr *ssa.Value
+ // The node representing the argtmp where the receiver is stored
+ rcvrNode *Node
+ // The addresses of the argtmps where the evaluated arguments of the defer
+ // function call are stored.
+ argVals []*ssa.Value
+ // The nodes representing the argtmps where the args of the defer are stored
+ argNodes []*Node
+}
+
type state struct {
// configuration (arch) information
config *ssa.Config
@@ -416,6 +599,9 @@ type state struct {
startmem *ssa.Value
sp *ssa.Value
sb *ssa.Value
+ // value representing address of where deferBits autotmp is stored
+ deferBitsAddr *ssa.Value
+ deferBitsTemp *Node
// line number stack. The current line number is top of stack
line []src.XPos
@@ -432,6 +618,19 @@ type state struct {
cgoUnsafeArgs bool
hasdefer bool // whether the function contains a defer statement
softFloat bool
+ hasOpenDefers bool // whether we are doing open-coded defers
+
+ // If doing open-coded defers, list of info about the defer calls in
+ // scanning order. Hence, at exit we should run these defers in reverse
+ // order of this list
+ openDefers []*openDeferInfo
+ // For open-coded defers, this is the beginning and end blocks of the last
+ // defer exit code that we have generated so far. We use these to share
+ // code between exits if the shareDeferExits option (disabled by default)
+ // is on.
+ lastDeferExit *ssa.Block // Entry block of last defer exit code we generated
+ lastDeferFinalBlock *ssa.Block // Final block of last defer exit code we generated
+ lastDeferCount int // Number of defers encountered at that point
}
type funcLine struct {
@@ -469,12 +668,13 @@ var (
memVar = Node{Op: ONAME, Sym: &types.Sym{Name: "mem"}}
// dummy nodes for temporary variables
- ptrVar = Node{Op: ONAME, Sym: &types.Sym{Name: "ptr"}}
- lenVar = Node{Op: ONAME, Sym: &types.Sym{Name: "len"}}
- newlenVar = Node{Op: ONAME, Sym: &types.Sym{Name: "newlen"}}
- capVar = Node{Op: ONAME, Sym: &types.Sym{Name: "cap"}}
- typVar = Node{Op: ONAME, Sym: &types.Sym{Name: "typ"}}
- okVar = Node{Op: ONAME, Sym: &types.Sym{Name: "ok"}}
+ ptrVar = Node{Op: ONAME, Sym: &types.Sym{Name: "ptr"}}
+ lenVar = Node{Op: ONAME, Sym: &types.Sym{Name: "len"}}
+ newlenVar = Node{Op: ONAME, Sym: &types.Sym{Name: "newlen"}}
+ capVar = Node{Op: ONAME, Sym: &types.Sym{Name: "cap"}}
+ typVar = Node{Op: ONAME, Sym: &types.Sym{Name: "typ"}}
+ okVar = Node{Op: ONAME, Sym: &types.Sym{Name: "ok"}}
+ deferBitsVar = Node{Op: ONAME, Sym: &types.Sym{Name: "deferBits"}}
)
// startBlock sets the current block we're generating code in to b.
@@ -865,11 +1065,26 @@ func (s *state) stmt(n *Node) {
}
}
case ODEFER:
- d := callDefer
- if n.Esc == EscNever {
- d = callDeferStack
+ if Debug_defer > 0 {
+ var defertype string
+ if s.hasOpenDefers {
+ defertype = "open-coded"
+ } else if n.Esc == EscNever {
+ defertype = "stack-allocated"
+ } else {
+ defertype = "heap-allocated"
+ }
+ Warnl(n.Pos, "%s defer", defertype)
+ }
+ if s.hasOpenDefers {
+ s.openDeferRecord(n.Left)
+ } else {
+ d := callDefer
+ if n.Esc == EscNever {
+ d = callDeferStack
+ }
+ s.call(n.Left, d)
}
- s.call(n.Left, d)
case OGO:
s.call(n.Left, callGo)
@@ -1286,12 +1501,28 @@ func (s *state) stmt(n *Node) {
}
}
+// If true, share as many open-coded defer exits as possible (with the downside of
+// worse line-number information)
+const shareDeferExits = false
+
// exit processes any code that needs to be generated just before returning.
// It returns a BlockRet block that ends the control flow. Its control value
// will be set to the final memory state.
func (s *state) exit() *ssa.Block {
if s.hasdefer {
- s.rtcall(Deferreturn, true, nil)
+ if s.hasOpenDefers {
+ if shareDeferExits && s.lastDeferExit != nil && len(s.openDefers) == s.lastDeferCount {
+ if s.curBlock.Kind != ssa.BlockPlain {
+ panic("Block for an exit should be BlockPlain")
+ }
+ s.curBlock.AddEdgeTo(s.lastDeferExit)
+ s.endBlock()
+ return s.lastDeferFinalBlock
+ }
+ s.openDeferExit()
+ } else {
+ s.rtcall(Deferreturn, true, nil)
+ }
}
// Run exit code. Typically, this code copies heap-allocated PPARAMOUT
@@ -1314,6 +1545,9 @@ func (s *state) exit() *ssa.Block {
b := s.endBlock()
b.Kind = ssa.BlockRet
b.SetControl(m)
+ if s.hasdefer && s.hasOpenDefers {
+ s.lastDeferFinalBlock = b
+ }
return b
}
@@ -3321,6 +3555,81 @@ func init() {
return s.newValue2(ssa.OpCopysign, types.Types[TFLOAT64], args[0], args[1])
},
sys.PPC64, sys.Wasm)
+ addF("math", "Fma",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ return s.newValue3(ssa.OpFma, types.Types[TFLOAT64], args[0], args[1], args[2])
+ },
+ sys.ARM64, sys.PPC64, sys.S390X)
+ addF("math", "Fma",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ if !s.config.UseFMA {
+ a := s.call(n, callNormal)
+ s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ return s.variable(n, types.Types[TFLOAT64])
+ }
+ addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), x86HasFMA, s.sb)
+ v := s.load(types.Types[TBOOL], addr)
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.SetControl(v)
+ bTrue := s.f.NewBlock(ssa.BlockPlain)
+ bFalse := s.f.NewBlock(ssa.BlockPlain)
+ bEnd := s.f.NewBlock(ssa.BlockPlain)
+ b.AddEdgeTo(bTrue)
+ b.AddEdgeTo(bFalse)
+ b.Likely = ssa.BranchLikely // >= haswell cpus are common
+
+ // We have the intrinsic - use it directly.
+ s.startBlock(bTrue)
+ s.vars[n] = s.newValue3(ssa.OpFma, types.Types[TFLOAT64], args[0], args[1], args[2])
+ s.endBlock().AddEdgeTo(bEnd)
+
+ // Call the pure Go version.
+ s.startBlock(bFalse)
+ a := s.call(n, callNormal)
+ s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ s.endBlock().AddEdgeTo(bEnd)
+
+ // Merge results.
+ s.startBlock(bEnd)
+ return s.variable(n, types.Types[TFLOAT64])
+ },
+ sys.AMD64)
+ addF("math", "Fma",
+ func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+ if !s.config.UseFMA {
+ a := s.call(n, callNormal)
+ s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ return s.variable(n, types.Types[TFLOAT64])
+ }
+ addr := s.entryNewValue1A(ssa.OpAddr, types.Types[TBOOL].PtrTo(), armHasVFPv4, s.sb)
+ v := s.load(types.Types[TBOOL], addr)
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.SetControl(v)
+ bTrue := s.f.NewBlock(ssa.BlockPlain)
+ bFalse := s.f.NewBlock(ssa.BlockPlain)
+ bEnd := s.f.NewBlock(ssa.BlockPlain)
+ b.AddEdgeTo(bTrue)
+ b.AddEdgeTo(bFalse)
+ b.Likely = ssa.BranchLikely
+
+ // We have the intrinsic - use it directly.
+ s.startBlock(bTrue)
+ s.vars[n] = s.newValue3(ssa.OpFma, types.Types[TFLOAT64], args[0], args[1], args[2])
+ s.endBlock().AddEdgeTo(bEnd)
+
+ // Call the pure Go version.
+ s.startBlock(bFalse)
+ a := s.call(n, callNormal)
+ s.vars[n] = s.load(types.Types[TFLOAT64], a)
+ s.endBlock().AddEdgeTo(bEnd)
+
+ // Merge results.
+ s.startBlock(bEnd)
+ return s.variable(n, types.Types[TFLOAT64])
+ },
+ sys.ARM)
makeRoundAMD64 := func(op ssa.Op) func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
return func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
@@ -3764,6 +4073,230 @@ func (s *state) intrinsicArgs(n *Node) []*ssa.Value {
return args
}
+// openDeferRecord adds code to evaluate and store the args for an open-code defer
+// call, and records info about the defer, so we can generate proper code on the
+// exit paths. n is the sub-node of the defer node that is the actual function
+// call. We will also record funcdata information on where the args are stored
+// (as well as the deferBits variable), and this will enable us to run the proper
+// defer calls during panics.
+func (s *state) openDeferRecord(n *Node) {
+ // Do any needed expression evaluation for the args (including the
+ // receiver, if any). This may be evaluating something like 'autotmp_3 =
+ // once.mutex'. Such a statement will create a mapping in s.vars[] from
+ // the autotmp name to the evaluated SSA arg value, but won't do any
+ // stores to the stack.
+ s.stmtList(n.List)
+
+ var args []*ssa.Value
+ var argNodes []*Node
+
+ opendefer := &openDeferInfo{
+ n: n,
+ }
+ fn := n.Left
+ if n.Op == OCALLFUNC {
+ // We must always store the function value in a stack slot for the
+ // runtime panic code to use. But in the defer exit code, we will
+ // call the function directly if it is a static function.
+ closureVal := s.expr(fn)
+ closure := s.openDeferSave(fn, fn.Type, closureVal)
+ opendefer.closureNode = closure.Aux.(*Node)
+ if !(fn.Op == ONAME && fn.Class() == PFUNC) {
+ opendefer.closure = closure
+ }
+ } else if n.Op == OCALLMETH {
+ if fn.Op != ODOTMETH {
+ Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
+ }
+ closureVal := s.getMethodClosure(fn)
+ // We must always store the function value in a stack slot for the
+ // runtime panic code to use. But in the defer exit code, we will
+ // call the method directly.
+ closure := s.openDeferSave(fn, fn.Type, closureVal)
+ opendefer.closureNode = closure.Aux.(*Node)
+ } else {
+ if fn.Op != ODOTINTER {
+ Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
+ }
+ closure, rcvr := s.getClosureAndRcvr(fn)
+ opendefer.closure = s.openDeferSave(fn, closure.Type, closure)
+ // Important to get the receiver type correct, so it is recognized
+ // as a pointer for GC purposes.
+ opendefer.rcvr = s.openDeferSave(nil, fn.Type.Recv().Type, rcvr)
+ opendefer.closureNode = opendefer.closure.Aux.(*Node)
+ opendefer.rcvrNode = opendefer.rcvr.Aux.(*Node)
+ }
+ for _, argn := range n.Rlist.Slice() {
+ v := s.openDeferSave(argn, argn.Type, s.expr(argn))
+ args = append(args, v)
+ argNodes = append(argNodes, v.Aux.(*Node))
+ }
+ opendefer.argVals = args
+ opendefer.argNodes = argNodes
+ index := len(s.openDefers)
+ s.openDefers = append(s.openDefers, opendefer)
+
+ // Update deferBits only after evaluation and storage to stack of
+ // args/receiver/interface is successful.
+ bitvalue := s.constInt8(types.Types[TUINT8], 1<= 0; i-- {
+ r := s.openDefers[i]
+ bCond := s.f.NewBlock(ssa.BlockPlain)
+ bEnd := s.f.NewBlock(ssa.BlockPlain)
+
+ deferBits := s.variable(&deferBitsVar, types.Types[TUINT8])
+ // Generate code to check if the bit associated with the current
+ // defer is set.
+ bitval := s.constInt8(types.Types[TUINT8], 1< %p %s %v tc=%d type=%#L\n", pos, indent, n, op, n, tc, typ)
}
}
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index ebae392808b..7d9f0cbd584 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -214,6 +214,18 @@ func walkstmt(n *Node) *Node {
case ODEFER:
Curfn.Func.SetHasDefer(true)
+ Curfn.Func.numDefers++
+ if Curfn.Func.numDefers > maxOpenDefers {
+ // Don't allow open-coded defers if there are more than
+ // 8 defers in the function, since we use a single
+ // byte to record active defers.
+ Curfn.Func.SetOpenCodedDeferDisallowed(true)
+ }
+ if n.Esc != EscNever {
+ // If n.Esc is not EscNever, then this defer occurs in a loop,
+ // so open-coded defers cannot be used in this function.
+ Curfn.Func.SetOpenCodedDeferDisallowed(true)
+ }
fallthrough
case OGO:
switch n.Left.Op {
@@ -255,6 +267,7 @@ func walkstmt(n *Node) *Node {
walkstmtlist(n.Rlist.Slice())
case ORETURN:
+ Curfn.Func.numReturns++
if n.List.Len() == 0 {
break
}
@@ -951,9 +964,9 @@ opswitch:
case OCONV, OCONVNOP:
n.Left = walkexpr(n.Left, init)
- if n.Op == OCONVNOP && checkPtr(Curfn) {
+ if n.Op == OCONVNOP && checkPtr(Curfn, 1) {
if n.Type.IsPtr() && n.Left.Type.Etype == TUNSAFEPTR { // unsafe.Pointer to *T
- n = walkCheckPtrAlignment(n, init)
+ n = walkCheckPtrAlignment(n, init, nil)
break
}
if n.Type.Etype == TUNSAFEPTR && n.Left.Type.Etype == TUINTPTR { // uintptr to unsafe.Pointer
@@ -1120,7 +1133,12 @@ opswitch:
n.List.SetSecond(walkexpr(n.List.Second(), init))
case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
- n.Left = walkexpr(n.Left, init)
+ checkSlice := checkPtr(Curfn, 1) && n.Op == OSLICE3ARR && n.Left.Op == OCONVNOP && n.Left.Left.Type.Etype == TUNSAFEPTR
+ if checkSlice {
+ n.Left.Left = walkexpr(n.Left.Left, init)
+ } else {
+ n.Left = walkexpr(n.Left, init)
+ }
low, high, max := n.SliceBounds()
low = walkexpr(low, init)
if low != nil && isZero(low) {
@@ -1130,6 +1148,9 @@ opswitch:
high = walkexpr(high, init)
max = walkexpr(max, init)
n.SetSliceBounds(low, high, max)
+ if checkSlice {
+ n.Left = walkCheckPtrAlignment(n.Left, init, max)
+ }
if n.Op.IsSlice3() {
if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
// Reduce x[i:j:cap(x)] to x[i:j].
@@ -3912,13 +3933,29 @@ func isRuneCount(n *Node) bool {
return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTR2RUNES
}
-func walkCheckPtrAlignment(n *Node, init *Nodes) *Node {
- if n.Type.Elem().Alignment() == 1 && n.Type.Elem().Size() == 1 {
+func walkCheckPtrAlignment(n *Node, init *Nodes, count *Node) *Node {
+ if !n.Type.IsPtr() {
+ Fatalf("expected pointer type: %v", n.Type)
+ }
+ elem := n.Type.Elem()
+ if count != nil {
+ if !elem.IsArray() {
+ Fatalf("expected array type: %v", elem)
+ }
+ elem = elem.Elem()
+ }
+
+ size := elem.Size()
+ if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) {
return n
}
+ if count == nil {
+ count = nodintconst(1)
+ }
+
n.Left = cheapexpr(n.Left, init)
- init.Append(mkcall("checkptrAlignment", nil, init, convnop(n.Left, types.Types[TUNSAFEPTR]), typename(n.Type.Elem())))
+ init.Append(mkcall("checkptrAlignment", nil, init, convnop(n.Left, types.Types[TUNSAFEPTR]), typename(elem), conv(count, types.Types[TUINTPTR])))
return n
}
@@ -3941,6 +3978,10 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
return n
}
+ if n.Left.Op == ODOTPTR && isReflectHeaderDataField(n.Left) {
+ return n
+ }
+
// Find original unsafe.Pointer operands involved in this
// arithmetic expression.
//
@@ -3976,7 +4017,8 @@ func walkCheckPtrArithmetic(n *Node, init *Nodes) *Node {
}
// checkPtr reports whether pointer checking should be enabled for
-// function fn.
-func checkPtr(fn *Node) bool {
- return Debug_checkptr != 0 && fn.Func.Pragma&NoCheckPtr == 0
+// function fn at a given level. See debugHelpFooter for defined
+// levels.
+func checkPtr(fn *Node, level int) bool {
+ return Debug_checkptr >= level && fn.Func.Pragma&NoCheckPtr == 0
}
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 4041a480b31..3cbbfcfa4e4 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -43,6 +43,7 @@ type Config struct {
Race bool // race detector enabled
NeedsFpScratch bool // No direct move between GP and FP register sets
BigEndian bool //
+ UseFMA bool // Use hardware FMA operation
}
type (
@@ -326,12 +327,18 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.ctxt = ctxt
c.optimize = optimize
c.useSSE = true
+ c.UseFMA = true
- // Don't use Duff's device nor SSE on Plan 9 AMD64, because
- // floating point operations are not allowed in note handler.
- if objabi.GOOS == "plan9" && arch == "amd64" {
- c.noDuffDevice = true
- c.useSSE = false
+ // On Plan 9, floating point operations are not allowed in note handler.
+ if objabi.GOOS == "plan9" {
+ // Don't use FMA on Plan 9
+ c.UseFMA = false
+
+ // Don't use Duff's device and SSE on Plan 9 AMD64.
+ if arch == "amd64" {
+ c.noDuffDevice = true
+ c.useSSE = false
+ }
}
if ctxt.Flag_shared {
diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go
index 6b9bcedadbf..88af7a6f4af 100644
--- a/src/cmd/compile/internal/ssa/deadstore.go
+++ b/src/cmd/compile/internal/ssa/deadstore.go
@@ -170,6 +170,11 @@ func elimDeadAutosGeneric(f *Func) {
return
case OpVarLive:
// Don't delete the auto if it needs to be kept alive.
+
+ // We depend on this check to keep the autotmp stack slots
+ // for open-coded defers from being removed (since they
+ // may not be used by the inline code, but will be used by
+ // panic processing).
n, ok := v.Aux.(GCNode)
if !ok || n.StorageClass() != ClassAuto {
return
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index cdd5161913c..332e2018997 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -32,8 +32,16 @@ type Func struct {
Type *types.Type // type signature of the function.
Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID)
Entry *Block // the entry basic block
- bid idAlloc // block ID allocator
- vid idAlloc // value ID allocator
+
+ // If we are using open-coded defers, this is the first call to a deferred
+ // function in the final defer exit sequence that we generated. This call
+ // should be after all defer statements, and will have all args, etc. of
+ // all defer calls as live. The liveness info of this call will be used
+ // for the deferreturn/ret segment generated for functions with open-coded
+ // defers.
+ LastDeferExit *Value
+ bid idAlloc // block ID allocator
+ vid idAlloc // value ID allocator
// Given an environment variable used for debug hash match,
// what file (if any) receives the yes/no logging?
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index 8ef51f95659..d4484084a1a 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -113,6 +113,7 @@
(Floor x) -> (ROUNDSD [1] x)
(Ceil x) -> (ROUNDSD [2] x)
(Trunc x) -> (ROUNDSD [3] x)
+(Fma x y z) -> (VFMADD231SD z x y)
// Lowering extension
// Note: we always extend to 64 bits even though some ops don't need that many result bits.
@@ -225,9 +226,6 @@
(NeqPtr x y) && config.PtrSize == 4 -> (SETNE (CMPL x y))
(Neq(32|64)F x y) -> (SETNEF (UCOMIS(S|D) x y))
-(Int64Hi x) -> (SHRQconst [32] x) // needed for amd64p32
-(Int64Lo x) -> x
-
// Lowering loads
(Load ptr mem) && (is64BitInt(t) || isPtr(t) && config.PtrSize == 8) -> (MOVQload ptr mem)
(Load ptr mem) && (is32BitInt(t) || isPtr(t) && config.PtrSize == 4) -> (MOVLload ptr mem)
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index 3fa5cfbb963..5924fa497ac 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -147,6 +147,7 @@ func init() {
fp01 = regInfo{inputs: nil, outputs: fponly}
fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
+ fp31 = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
fp21load = regInfo{inputs: []regMask{fp, gpspsb, 0}, outputs: fponly}
fpgp = regInfo{inputs: fponly, outputs: gponly}
gpfp = regInfo{inputs: gponly, outputs: fponly}
@@ -478,6 +479,10 @@ func init() {
// Any use must be preceded by a successful check of runtime.x86HasSSE41.
{name: "ROUNDSD", argLength: 1, reg: fp11, aux: "Int8", asm: "ROUNDSD"}, // rounds arg0 depending on auxint, 1 means math.Floor, 2 Ceil, 3 Trunc
+ // VFMADD231SD only exists on platforms with the FMA3 instruction set.
+ // Any use must be preceded by a successful check of runtime.support_fma.
+ {name: "VFMADD231SD", argLength: 3, reg: fp31, resultInArg0: true, asm: "VFMADD231SD"},
+
{name: "SBBQcarrymask", argLength: 1, reg: flagsgp, asm: "SBBQ"}, // (int64)(-1) if carry is set, 0 if carry is clear.
{name: "SBBLcarrymask", argLength: 1, reg: flagsgp, asm: "SBBL"}, // (int32)(-1) if carry is set, 0 if carry is clear.
// Note: SBBW and SBBB are subsumed by SBBL
diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules
index 4ab388cae9c..c1c73e23ec2 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM.rules
@@ -210,6 +210,9 @@
(Round(32|64)F x) -> x
+// fused-multiply-add
+(Fma x y z) -> (FMULAD z x y)
+
// comparisons
(Eq8 x y) -> (Equal (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
(Eq16 x y) -> (Equal (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules
index 7edd19e9cc7..26ae004572d 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules
@@ -90,6 +90,7 @@
(Round x) -> (FRINTAD x)
(RoundToEven x) -> (FRINTND x)
(Trunc x) -> (FRINTZD x)
+(Fma x y z) -> (FMADDD z x y)
// lowering rotates
(RotateLeft8 x (MOVDconst [c])) -> (Or8 (Lsh8x64 x (MOVDconst [c&7])) (Rsh8Ux64 x (MOVDconst [-c&7])))
diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go
index 9795215c8ac..bde170864de 100644
--- a/src/cmd/compile/internal/ssa/gen/ARMOps.go
+++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go
@@ -192,6 +192,10 @@ func init() {
{name: "MULSF", argLength: 3, reg: fp31, asm: "MULSF", resultInArg0: true}, // arg0 - (arg1 * arg2)
{name: "MULSD", argLength: 3, reg: fp31, asm: "MULSD", resultInArg0: true}, // arg0 - (arg1 * arg2)
+ // FMULAD only exists on platforms with the VFPv4 instruction set.
+ // Any use must be preceded by a successful check of runtime.arm_support_vfpv4.
+ {name: "FMULAD", argLength: 3, reg: fp31, asm: "FMULAD", resultInArg0: true}, // arg0 + (arg1 * arg2)
+
{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"}, // arg0 & auxInt
{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true}, // arg0 | arg1
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index 59cce4ed579..239414f01bc 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -68,6 +68,7 @@
(Round x) -> (FROUND x)
(Copysign x y) -> (FCPSGN y x)
(Abs x) -> (FABS x)
+(Fma x y z) -> (FMADD x y z)
// Lowering constants
(Const(64|32|16|8) [val]) -> (MOVDconst [val])
diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules
index 4e459043b11..d7cb972b815 100644
--- a/src/cmd/compile/internal/ssa/gen/S390X.rules
+++ b/src/cmd/compile/internal/ssa/gen/S390X.rules
@@ -139,6 +139,7 @@
(Trunc x) -> (FIDBR [5] x)
(RoundToEven x) -> (FIDBR [4] x)
(Round x) -> (FIDBR [1] x)
+(Fma x y z) -> (FMADD z x y)
// Atomic loads and stores.
// The SYNC instruction (fast-BCR-serialization) prevents store-load
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index df0dd8cabce..7bd79312e30 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -302,6 +302,20 @@ var genericOps = []opData{
{name: "Abs", argLength: 1}, // absolute value arg0
{name: "Copysign", argLength: 2}, // copy sign from arg0 to arg1
+ // 3-input opcode.
+ // Fused-multiply-add, float64 only.
+ // When a*b+c is exactly zero (before rounding), then the result is +0 or -0.
+ // The 0's sign is determined according to the standard rules for the
+ // addition (-0 if both a*b and c are -0, +0 otherwise).
+ //
+ // Otherwise, when a*b+c rounds to zero, then the resulting 0's sign is
+ // determined by the sign of the exact result a*b+c.
+ // See section 6.3 in ieee754.
+ //
+ // When the multiply is an infinity times a zero, the result is NaN.
+ // See section 7.2 in ieee754.
+ {name: "Fma", argLength: 3}, // compute (a*b)+c without intermediate rounding
+
// Data movement, max argument length for Phi is indefinite so just pick
// a really large number
{name: "Phi", argLength: -1, zeroWidth: true}, // select an argument based on which predecessor block we came from
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index b7e65174f9a..1bac3919144 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -743,6 +743,7 @@ const (
OpAMD64POPCNTL
OpAMD64SQRTSD
OpAMD64ROUNDSD
+ OpAMD64VFMADD231SD
OpAMD64SBBQcarrymask
OpAMD64SBBLcarrymask
OpAMD64SETEQ
@@ -924,6 +925,7 @@ const (
OpARMMULAD
OpARMMULSF
OpARMMULSD
+ OpARMFMULAD
OpARMAND
OpARMANDconst
OpARMOR
@@ -2419,6 +2421,7 @@ const (
OpRoundToEven
OpAbs
OpCopysign
+ OpFma
OpPhi
OpCopy
OpConvert
@@ -9624,6 +9627,22 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "VFMADD231SD",
+ argLen: 3,
+ resultInArg0: true,
+ asm: x86.AVFMADD231SD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ {2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+ },
+ },
+ },
{
name: "SBBQcarrymask",
argLen: 1,
@@ -12101,6 +12120,22 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "FMULAD",
+ argLen: 3,
+ resultInArg0: true,
+ asm: arm.AFMULAD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+ {1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+ {2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+ },
+ outputs: []outputInfo{
+ {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+ },
+ },
+ },
{
name: "AND",
argLen: 2,
@@ -30593,6 +30628,11 @@ var opcodeTable = [...]opInfo{
argLen: 2,
generic: true,
},
+ {
+ name: "Fma",
+ argLen: 3,
+ generic: true,
+ },
{
name: "Phi",
argLen: -1,
diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go
index 4f9a4d53fba..ce5f6f2cfab 100644
--- a/src/cmd/compile/internal/ssa/prove.go
+++ b/src/cmd/compile/internal/ssa/prove.go
@@ -164,7 +164,8 @@ type factsTable struct {
// order is a couple of partial order sets that record information
// about relations between SSA values in the signed and unsigned
// domain.
- order [2]*poset
+ orderS *poset
+ orderU *poset
// known lower and upper bounds on individual values.
limits map[ID]limit
@@ -187,10 +188,10 @@ var checkpointBound = limitFact{}
func newFactsTable(f *Func) *factsTable {
ft := &factsTable{}
- ft.order[0] = f.newPoset() // signed
- ft.order[1] = f.newPoset() // unsigned
- ft.order[0].SetUnsigned(false)
- ft.order[1].SetUnsigned(true)
+ ft.orderS = f.newPoset()
+ ft.orderU = f.newPoset()
+ ft.orderS.SetUnsigned(false)
+ ft.orderU.SetUnsigned(true)
ft.facts = make(map[pair]relation)
ft.stack = make([]fact, 4)
ft.limits = make(map[ID]limit)
@@ -221,23 +222,23 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
if d == signed || d == unsigned {
var ok bool
- idx := 0
+ order := ft.orderS
if d == unsigned {
- idx = 1
+ order = ft.orderU
}
switch r {
case lt:
- ok = ft.order[idx].SetOrder(v, w)
+ ok = order.SetOrder(v, w)
case gt:
- ok = ft.order[idx].SetOrder(w, v)
+ ok = order.SetOrder(w, v)
case lt | eq:
- ok = ft.order[idx].SetOrderOrEqual(v, w)
+ ok = order.SetOrderOrEqual(v, w)
case gt | eq:
- ok = ft.order[idx].SetOrderOrEqual(w, v)
+ ok = order.SetOrderOrEqual(w, v)
case eq:
- ok = ft.order[idx].SetEqual(v, w)
+ ok = order.SetEqual(v, w)
case lt | gt:
- ok = ft.order[idx].SetNonEqual(v, w)
+ ok = order.SetNonEqual(v, w)
default:
panic("unknown relation")
}
@@ -588,6 +589,7 @@ func (ft *factsTable) isNonNegative(v *Value) bool {
}
// Check if the recorded limits can prove that the value is positive
+
if l, has := ft.limits[v.ID]; has && (l.min >= 0 || l.umax <= uint64(max)) {
return true
}
@@ -610,7 +612,7 @@ func (ft *factsTable) isNonNegative(v *Value) bool {
}
// Check if the signed poset can prove that the value is >= 0
- return ft.order[0].OrderedOrEqual(ft.zero, v)
+ return ft.orderS.OrderedOrEqual(ft.zero, v)
}
// checkpoint saves the current state of known relations.
@@ -621,8 +623,8 @@ func (ft *factsTable) checkpoint() {
}
ft.stack = append(ft.stack, checkpointFact)
ft.limitStack = append(ft.limitStack, checkpointBound)
- ft.order[0].Checkpoint()
- ft.order[1].Checkpoint()
+ ft.orderS.Checkpoint()
+ ft.orderU.Checkpoint()
}
// restore restores known relation to the state just
@@ -658,8 +660,8 @@ func (ft *factsTable) restore() {
ft.limits[old.vid] = old.limit
}
}
- ft.order[0].Undo()
- ft.order[1].Undo()
+ ft.orderS.Undo()
+ ft.orderU.Undo()
}
func lessByID(v, w *Value) bool {
@@ -922,7 +924,7 @@ func prove(f *Func) {
ft.restore()
// Return the posets to the free list
- for _, po := range ft.order {
+ for _, po := range []*poset{ft.orderS, ft.orderU} {
// Make sure it's empty as it should be. A non-empty poset
// might cause errors and miscompilations if reused.
if checkEnabled {
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index 386086f4b02..45634a25eb0 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -768,6 +768,8 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpEqPtr_0(v)
case OpFloor:
return rewriteValueAMD64_OpFloor_0(v)
+ case OpFma:
+ return rewriteValueAMD64_OpFma_0(v)
case OpGeq16:
return rewriteValueAMD64_OpGeq16_0(v)
case OpGeq16U:
@@ -824,10 +826,6 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpHmul64_0(v)
case OpHmul64u:
return rewriteValueAMD64_OpHmul64u_0(v)
- case OpInt64Hi:
- return rewriteValueAMD64_OpInt64Hi_0(v)
- case OpInt64Lo:
- return rewriteValueAMD64_OpInt64Lo_0(v)
case OpInterCall:
return rewriteValueAMD64_OpInterCall_0(v)
case OpIsInBounds:
@@ -52331,6 +52329,20 @@ func rewriteValueAMD64_OpFloor_0(v *Value) bool {
return true
}
}
+func rewriteValueAMD64_OpFma_0(v *Value) bool {
+ // match: (Fma x y z)
+ // result: (VFMADD231SD z x y)
+ for {
+ z := v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpAMD64VFMADD231SD)
+ v.AddArg(z)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+}
func rewriteValueAMD64_OpGeq16_0(v *Value) bool {
b := v.Block
// match: (Geq16 x y)
@@ -52713,28 +52725,6 @@ func rewriteValueAMD64_OpHmul64u_0(v *Value) bool {
return true
}
}
-func rewriteValueAMD64_OpInt64Hi_0(v *Value) bool {
- // match: (Int64Hi x)
- // result: (SHRQconst [32] x)
- for {
- x := v.Args[0]
- v.reset(OpAMD64SHRQconst)
- v.AuxInt = 32
- v.AddArg(x)
- return true
- }
-}
-func rewriteValueAMD64_OpInt64Lo_0(v *Value) bool {
- // match: (Int64Lo x)
- // result: x
- for {
- x := v.Args[0]
- v.reset(OpCopy)
- v.Type = x.Type
- v.AddArg(x)
- return true
- }
-}
func rewriteValueAMD64_OpInterCall_0(v *Value) bool {
// match: (InterCall [argwid] entry mem)
// result: (CALLinter [argwid] entry mem)
diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go
index ece2fe4fe9c..8cb534d8f62 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM.go
@@ -538,6 +538,8 @@ func rewriteValueARM(v *Value) bool {
return rewriteValueARM_OpEqB_0(v)
case OpEqPtr:
return rewriteValueARM_OpEqPtr_0(v)
+ case OpFma:
+ return rewriteValueARM_OpFma_0(v)
case OpGeq16:
return rewriteValueARM_OpGeq16_0(v)
case OpGeq16U:
@@ -17159,6 +17161,20 @@ func rewriteValueARM_OpEqPtr_0(v *Value) bool {
return true
}
}
+func rewriteValueARM_OpFma_0(v *Value) bool {
+ // match: (Fma x y z)
+ // result: (FMULAD z x y)
+ for {
+ z := v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpARMFMULAD)
+ v.AddArg(z)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+}
func rewriteValueARM_OpGeq16_0(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go
index dfb5554f622..a5f74fab51b 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM64.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -571,6 +571,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpEqPtr_0(v)
case OpFloor:
return rewriteValueARM64_OpFloor_0(v)
+ case OpFma:
+ return rewriteValueARM64_OpFma_0(v)
case OpGeq16:
return rewriteValueARM64_OpGeq16_0(v)
case OpGeq16U:
@@ -28565,6 +28567,20 @@ func rewriteValueARM64_OpFloor_0(v *Value) bool {
return true
}
}
+func rewriteValueARM64_OpFma_0(v *Value) bool {
+ // match: (Fma x y z)
+ // result: (FMADDD z x y)
+ for {
+ z := v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpARM64FMADDD)
+ v.AddArg(z)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+}
func rewriteValueARM64_OpGeq16_0(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 7f49d98bd11..1b462b28bb3 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -181,6 +181,8 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpEqPtr_0(v)
case OpFloor:
return rewriteValuePPC64_OpFloor_0(v)
+ case OpFma:
+ return rewriteValuePPC64_OpFma_0(v)
case OpGeq16:
return rewriteValuePPC64_OpGeq16_0(v)
case OpGeq16U:
@@ -1988,6 +1990,20 @@ func rewriteValuePPC64_OpFloor_0(v *Value) bool {
return true
}
}
+func rewriteValuePPC64_OpFma_0(v *Value) bool {
+ // match: (Fma x y z)
+ // result: (FMADD x y z)
+ for {
+ z := v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpPPC64FMADD)
+ v.AddArg(x)
+ v.AddArg(y)
+ v.AddArg(z)
+ return true
+ }
+}
func rewriteValuePPC64_OpGeq16_0(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go
index 72bbdc0e57c..343a7381eaf 100644
--- a/src/cmd/compile/internal/ssa/rewriteS390X.go
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -166,6 +166,8 @@ func rewriteValueS390X(v *Value) bool {
return rewriteValueS390X_OpEqPtr_0(v)
case OpFloor:
return rewriteValueS390X_OpFloor_0(v)
+ case OpFma:
+ return rewriteValueS390X_OpFma_0(v)
case OpGeq16:
return rewriteValueS390X_OpGeq16_0(v)
case OpGeq16U:
@@ -1918,6 +1920,20 @@ func rewriteValueS390X_OpFloor_0(v *Value) bool {
return true
}
}
+func rewriteValueS390X_OpFma_0(v *Value) bool {
+ // match: (Fma x y z)
+ // result: (FMADD z x y)
+ for {
+ z := v.Args[2]
+ x := v.Args[0]
+ y := v.Args[1]
+ v.reset(OpS390XFMADD)
+ v.AddArg(z)
+ v.AddArg(x)
+ v.AddArg(y)
+ return true
+ }
+}
func rewriteValueS390X_OpGeq16_0(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go
index ea970a61da8..8a56e390113 100644
--- a/src/cmd/cover/cover_test.go
+++ b/src/cmd/cover/cover_test.go
@@ -457,7 +457,7 @@ func TestHtmlUnformatted(t *testing.T) {
t.Fatal(err)
}
- if err := ioutil.WriteFile(filepath.Join(htmlUDir, "go.mod"), []byte("module htmlunformatted\n"), 0444); err != nil {
+ if err := ioutil.WriteFile(filepath.Join(htmlUDir, "go.mod"), []byte("module htmlunformatted\n"), 0666); err != nil {
t.Fatal(err)
}
@@ -540,7 +540,7 @@ func TestFuncWithDuplicateLines(t *testing.T) {
t.Fatal(err)
}
- if err := ioutil.WriteFile(filepath.Join(lineDupDir, "go.mod"), []byte("module linedup\n"), 0444); err != nil {
+ if err := ioutil.WriteFile(filepath.Join(lineDupDir, "go.mod"), []byte("module linedup\n"), 0666); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(lineDupGo, []byte(lineDupContents), 0444); err != nil {
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index ea290b10690..6c8e558f296 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -814,7 +814,7 @@ func runInstall(dir string, ch chan struct{}) {
// Define GOMIPS_value from gomips.
asmArgs = append(asmArgs, "-D", "GOMIPS_"+gomips)
}
- if goarch == "mips64" || goarch == "mipsle64" {
+ if goarch == "mips64" || goarch == "mips64le" {
// Define GOMIPS64_value from gomips64.
asmArgs = append(asmArgs, "-D", "GOMIPS64_"+gomips64)
}
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index e4a2eaa9082..77ec02a8a9a 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -8,5 +8,5 @@ require (
golang.org/x/arch v0.0.0-20190815191158-8a70ba74b3a1
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
- golang.org/x/tools v0.0.0-20190925211824-e4ea94538f5b
+ golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5
)
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index 7c3ee7304b4..6a3d6094162 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -14,7 +14,7 @@ golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dz
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/tools v0.0.0-20190925211824-e4ea94538f5b h1:gyG4T6EqWG9fqSgT0VbHhzp8bHbFux5mvlgz1gUkEaQ=
-golang.org/x/tools v0.0.0-20190925211824-e4ea94538f5b/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5 h1:TFUhCYbgGMOGnRxJv+j0iAcxCjk8oGjXXWNejQBhUUs=
+golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 4774ee8201e..fad2d9f0feb 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -150,6 +150,16 @@
// -mod mode
// module download mode to use: readonly or vendor.
// See 'go help modules' for more.
+// -modcacherw
+// leave newly-created directories in the module cache read-write
+// instead of making them read-only.
+// -modfile file
+// in module aware mode, read (and possibly write) an alternate go.mod
+// file instead of the one in the module root directory. A file named
+// "go.mod" must still be present in order to determine the module root
+// directory, but it is not accessed. When -modfile is specified, an
+// alternate go.sum file is also used: its path is derived from the
+// -modfile flag by trimming the ".mod" extension and appending ".sum".
// -pkgdir dir
// install and load all packages from dir instead of the usual locations.
// For example, when building with a non-standard configuration,
@@ -1510,8 +1520,8 @@
// extension will be passed to SWIG. Any file with a .swigcxx extension
// will be passed to SWIG with the -c++ option.
//
-// When either cgo or SWIG is used, go build will pass any .c, .m, .s,
-// or .S files to the C compiler, and any .cc, .cpp, .cxx files to the C++
+// When either cgo or SWIG is used, go build will pass any .c, .m, .s, .S
+// or .sx files to the C compiler, and any .cc, .cpp, .cxx files to the C++
// compiler. The CC or CXX environment variables may be set to determine
// the C or C++ compiler, respectively, to use.
//
@@ -1730,7 +1740,7 @@
// .m
// Objective-C source files. Only useful with cgo, and always
// compiled with the OS-native compiler.
-// .s, .S
+// .s, .S, .sx
// Assembler source files.
// If the package uses cgo or SWIG, these will be assembled with the
// OS-native assembler (typically gcc (sic)); otherwise they
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index e4cf953b9e5..a6bb8d69b04 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -4938,35 +4938,31 @@ func TestTestRegexps(t *testing.T) {
// BenchmarkXX is run but only with N=1, once
// BenchmarkX/Y is run in full, twice
want := `=== RUN TestX
+ TestX: x_test.go:6: LOG: X running
=== RUN TestX/Y
- x_test.go:6: LOG: X running
- x_test.go:8: LOG: Y running
+ TestX/Y: x_test.go:8: LOG: Y running
=== RUN TestXX
- z_test.go:10: LOG: XX running
+ TestXX: z_test.go:10: LOG: XX running
=== RUN TestX
+ TestX: x_test.go:6: LOG: X running
=== RUN TestX/Y
- x_test.go:6: LOG: X running
- x_test.go:8: LOG: Y running
+ TestX/Y: x_test.go:8: LOG: Y running
=== RUN TestXX
- z_test.go:10: LOG: XX running
---- BENCH: BenchmarkX/Y
- x_test.go:15: LOG: Y running N=1
- x_test.go:15: LOG: Y running N=100
- x_test.go:15: LOG: Y running N=10000
- x_test.go:15: LOG: Y running N=1000000
- x_test.go:15: LOG: Y running N=100000000
- x_test.go:15: LOG: Y running N=1000000000
---- BENCH: BenchmarkX/Y
- x_test.go:15: LOG: Y running N=1
- x_test.go:15: LOG: Y running N=100
- x_test.go:15: LOG: Y running N=10000
- x_test.go:15: LOG: Y running N=1000000
- x_test.go:15: LOG: Y running N=100000000
- x_test.go:15: LOG: Y running N=1000000000
---- BENCH: BenchmarkX
- x_test.go:13: LOG: X running N=1
---- BENCH: BenchmarkXX
- z_test.go:18: LOG: XX running N=1
+ TestXX: z_test.go:10: LOG: XX running
+ BenchmarkX: x_test.go:13: LOG: X running N=1
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=1
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=100
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=10000
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=100000000
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000000
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=1
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=100
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=10000
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=100000000
+ BenchmarkX/Y: x_test.go:15: LOG: Y running N=1000000000
+ BenchmarkXX: z_test.go:18: LOG: XX running N=1
`
have := strings.Join(lines, "")
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index a3277a6c3f0..b5d6ddca173 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -44,6 +44,9 @@ var (
BuildWork bool // -work flag
BuildX bool // -x flag
+ ModCacheRW bool // -modcacherw flag
+ ModFile string // -modfile flag
+
CmdName string // "build", "install", "list", "mod tidy", etc.
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index 421f1bab758..500e3e0da6d 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -108,7 +108,7 @@ var (
)
func init() {
- work.AddBuildFlags(CmdGet, work.OmitModFlag)
+ work.AddBuildFlags(CmdGet, work.OmitModFlag|work.OmitModCommonFlags)
CmdGet.Run = runGet // break init loop
CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "")
}
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index dfb89d4910b..1dc892cb325 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -21,8 +21,8 @@ http://swig.org/. When running go build, any file with a .swig
extension will be passed to SWIG. Any file with a .swigcxx extension
will be passed to SWIG with the -c++ option.
-When either cgo or SWIG is used, go build will pass any .c, .m, .s,
-or .S files to the C compiler, and any .cc, .cpp, .cxx files to the C++
+When either cgo or SWIG is used, go build will pass any .c, .m, .s, .S
+or .sx files to the C compiler, and any .cc, .cpp, .cxx files to the C++
compiler. The CC or CXX environment variables may be set to determine
the C or C++ compiler, respectively, to use.
`,
@@ -645,7 +645,7 @@ the extension of the file name. These extensions are:
.m
Objective-C source files. Only useful with cgo, and always
compiled with the OS-native compiler.
- .s, .S
+ .s, .S, .sx
Assembler source files.
If the package uses cgo or SWIG, these will be assembled with the
OS-native assembler (typically gcc (sic)); otherwise they
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index d8c75776bba..b393c67ddb2 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -384,9 +384,22 @@ func runList(cmd *base.Command, args []string) {
if modload.Init(); !modload.Enabled() {
base.Fatalf("go list -m: not using modules")
}
+
+ modload.InitMod() // Parses go.mod and sets cfg.BuildMod.
if cfg.BuildMod == "vendor" {
- base.Fatalf("go list -m: can't list modules with -mod=vendor\n\tuse -mod=mod or -mod=readonly to ignore the vendor directory")
+ for _, arg := range args {
+ // In vendor mode, the module graph is incomplete: it contains only the
+ // explicit module dependencies and the modules that supply packages in
+ // the import graph. Reject queries that imply more information than that.
+ if arg == "all" {
+ base.Fatalf("go list -m: can't compute 'all' using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")
+ }
+ if strings.Contains(arg, "...") {
+ base.Fatalf("go list -m: can't match module patterns using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")
+ }
+ }
}
+
modload.LoadBuildList()
mods := modload.ListModules(args, *listU, *listVersions)
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go
index 0d432e95490..3a86d8ac063 100644
--- a/src/cmd/go/internal/modcmd/download.go
+++ b/src/cmd/go/internal/modcmd/download.go
@@ -14,6 +14,7 @@ import (
"cmd/go/internal/modload"
"cmd/go/internal/module"
"cmd/go/internal/par"
+ "cmd/go/internal/work"
)
var cmdDownload = &base.Command{
@@ -53,6 +54,8 @@ var downloadJSON = cmdDownload.Flag.Bool("json", false, "")
func init() {
cmdDownload.Run = runDownload // break init cycle
+
+ work.AddModCommonFlags(cmdDownload)
}
type moduleJSON struct {
diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go
index 1be8b7cb2fb..97cc0fa02ff 100644
--- a/src/cmd/go/internal/modcmd/edit.go
+++ b/src/cmd/go/internal/modcmd/edit.go
@@ -12,7 +12,6 @@ import (
"fmt"
"io/ioutil"
"os"
- "path/filepath"
"strings"
"cmd/go/internal/base"
@@ -20,6 +19,7 @@ import (
"cmd/go/internal/modfile"
"cmd/go/internal/modload"
"cmd/go/internal/module"
+ "cmd/go/internal/work"
)
var cmdEdit = &base.Command{
@@ -130,6 +130,7 @@ func init() {
cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
+ work.AddModCommonFlags(cmdEdit)
base.AddBuildFlagsNX(&cmdEdit.Flag)
}
@@ -157,7 +158,7 @@ func runEdit(cmd *base.Command, args []string) {
if len(args) == 1 {
gomod = args[0]
} else {
- gomod = filepath.Join(modload.ModRoot(), "go.mod")
+ gomod = modload.ModFilePath()
}
if *editModule != "" {
diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go
index 8fcb84f2801..5dbbf332fba 100644
--- a/src/cmd/go/internal/modcmd/graph.go
+++ b/src/cmd/go/internal/modcmd/graph.go
@@ -16,6 +16,7 @@ import (
"cmd/go/internal/modload"
"cmd/go/internal/module"
"cmd/go/internal/par"
+ "cmd/go/internal/work"
)
var cmdGraph = &base.Command{
@@ -30,6 +31,10 @@ path@version, except for the main module, which has no @version suffix.
Run: runGraph,
}
+func init() {
+ work.AddModCommonFlags(cmdGraph)
+}
+
func runGraph(cmd *base.Command, args []string) {
if len(args) > 0 {
base.Fatalf("go mod graph: graph takes no arguments")
diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go
index b94453bab0c..714ff2e205a 100644
--- a/src/cmd/go/internal/modcmd/init.go
+++ b/src/cmd/go/internal/modcmd/init.go
@@ -9,6 +9,7 @@ package modcmd
import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
+ "cmd/go/internal/work"
"os"
"strings"
)
@@ -27,6 +28,10 @@ To override this guess, supply the module path as an argument.
Run: runInit,
}
+func init() {
+ work.AddModCommonFlags(cmdInit)
+}
+
func runInit(cmd *base.Command, args []string) {
modload.CmdModInit = true
if len(args) > 1 {
@@ -38,7 +43,8 @@ func runInit(cmd *base.Command, args []string) {
if os.Getenv("GO111MODULE") == "off" {
base.Fatalf("go mod init: modules disabled by GO111MODULE=off; see 'go help modules'")
}
- if _, err := os.Stat("go.mod"); err == nil {
+ modFilePath := modload.ModFilePath()
+ if _, err := os.Stat(modFilePath); err == nil {
base.Fatalf("go mod init: go.mod already exists")
}
if strings.Contains(modload.CmdModModule, "@") {
diff --git a/src/cmd/go/internal/modcmd/mod.go b/src/cmd/go/internal/modcmd/mod.go
index f150cc9728a..17505221587 100644
--- a/src/cmd/go/internal/modcmd/mod.go
+++ b/src/cmd/go/internal/modcmd/mod.go
@@ -5,7 +5,10 @@
// Package modcmd implements the ``go mod'' command.
package modcmd
-import "cmd/go/internal/base"
+import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+)
var CmdMod = &base.Command{
UsageLine: "go mod",
@@ -29,3 +32,7 @@ See 'go help modules' for an overview of module functionality.
cmdWhy,
},
}
+
+func addModFlags(cmd *base.Command) {
+ cmd.Flag.StringVar(&cfg.ModFile, "modfile", "", "")
+}
diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go
index 789e9366085..1f5a18e05e8 100644
--- a/src/cmd/go/internal/modcmd/tidy.go
+++ b/src/cmd/go/internal/modcmd/tidy.go
@@ -15,6 +15,7 @@ import (
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/module"
+ "cmd/go/internal/work"
)
var cmdTidy = &base.Command{
@@ -35,6 +36,7 @@ to standard error.
func init() {
cmdTidy.Run = runTidy // break init cycle
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
+ work.AddModCommonFlags(cmdTidy)
}
func runTidy(cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
index bb1cecdbf55..71246b2f689 100644
--- a/src/cmd/go/internal/modcmd/vendor.go
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -20,6 +20,7 @@ import (
"cmd/go/internal/modload"
"cmd/go/internal/module"
"cmd/go/internal/semver"
+ "cmd/go/internal/work"
)
var cmdVendor = &base.Command{
@@ -38,6 +39,7 @@ modules and packages to standard error.
func init() {
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
+ work.AddModCommonFlags(cmdVendor)
}
func runVendor(cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
index 81fc44dc97a..72f16a793be 100644
--- a/src/cmd/go/internal/modcmd/verify.go
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -16,6 +16,7 @@ import (
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/module"
+ "cmd/go/internal/work"
)
var cmdVerify = &base.Command{
@@ -32,13 +33,17 @@ non-zero status.
Run: runVerify,
}
+func init() {
+ work.AddModCommonFlags(cmdVerify)
+}
+
func runVerify(cmd *base.Command, args []string) {
if len(args) != 0 {
// NOTE(rsc): Could take a module pattern.
base.Fatalf("go mod verify: verify takes no arguments")
}
// Checks go mod expected behavior
- if !modload.Enabled() {
+ if !modload.Enabled() || !modload.HasModRoot() {
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
} else {
diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go
index 03e0a039bc5..93d64dcb59f 100644
--- a/src/cmd/go/internal/modcmd/why.go
+++ b/src/cmd/go/internal/modcmd/why.go
@@ -8,6 +8,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/module"
+ "cmd/go/internal/work"
"fmt"
"strings"
)
@@ -54,6 +55,7 @@ var (
func init() {
cmdWhy.Run = runWhy // break init cycle
+ work.AddModCommonFlags(cmdWhy)
}
func runWhy(cmd *base.Command, args []string) {
diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go
index a4e50d692a3..5867288c966 100644
--- a/src/cmd/go/internal/modfetch/codehost/codehost.go
+++ b/src/cmd/go/internal/modfetch/codehost/codehost.go
@@ -73,11 +73,10 @@ type Repo interface {
// ReadZip downloads a zip file for the subdir subdirectory
// of the given revision to a new file in a given temporary directory.
// It should refuse to read more than maxSize bytes.
- // It returns a ReadCloser for a streamed copy of the zip file,
- // along with the actual subdirectory (possibly shorter than subdir)
- // contained in the zip file. All files in the zip file are expected to be
+ // It returns a ReadCloser for a streamed copy of the zip file.
+ // All files in the zip file are expected to be
// nested in a single top-level directory, whose name is not specified.
- ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error)
+ 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.
diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go
index 64d4573c710..4a08f8ded61 100644
--- a/src/cmd/go/internal/modfetch/codehost/git.go
+++ b/src/cmd/go/internal/modfetch/codehost/git.go
@@ -795,7 +795,7 @@ func (r *gitRepo) DescendsFrom(rev, tag string) (bool, error) {
return false, err
}
-func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error) {
// TODO: Use maxSize or drop it.
args := []string{}
if subdir != "" {
@@ -803,17 +803,17 @@ func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
}
info, err := r.Stat(rev) // download rev into local git repo
if err != nil {
- return nil, "", err
+ return nil, err
}
unlock, err := r.mu.Lock()
if err != nil {
- return nil, "", err
+ return nil, err
}
defer unlock()
if err := ensureGitAttributes(r.dir); err != nil {
- return nil, "", err
+ return nil, err
}
// Incredibly, git produces different archives depending on whether
@@ -824,12 +824,12 @@ func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
archive, err := Run(r.dir, "git", "-c", "core.autocrlf=input", "-c", "core.eol=lf", "archive", "--format=zip", "--prefix=prefix/", info.Name, args)
if err != nil {
if bytes.Contains(err.(*RunError).Stderr, []byte("did not match any files")) {
- return nil, "", os.ErrNotExist
+ return nil, os.ErrNotExist
}
- return nil, "", err
+ return nil, err
}
- return ioutil.NopCloser(bytes.NewReader(archive)), "", nil
+ return ioutil.NopCloser(bytes.NewReader(archive)), nil
}
// ensureGitAttributes makes sure export-subst and export-ignore features are
diff --git a/src/cmd/go/internal/modfetch/codehost/git_test.go b/src/cmd/go/internal/modfetch/codehost/git_test.go
index da9e7050407..39c904f92cd 100644
--- a/src/cmd/go/internal/modfetch/codehost/git_test.go
+++ b/src/cmd/go/internal/modfetch/codehost/git_test.go
@@ -246,12 +246,11 @@ func TestReadFile(t *testing.T) {
}
var readZipTests = []struct {
- repo string
- rev string
- subdir string
- actualSubdir string
- err string
- files map[string]uint64
+ repo string
+ rev string
+ subdir string
+ err string
+ files map[string]uint64
}{
{
repo: gitrepo1,
@@ -408,7 +407,7 @@ func TestReadZip(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- rc, actualSubdir, err := r.ReadZip(tt.rev, tt.subdir, 100000)
+ rc, err := r.ReadZip(tt.rev, tt.subdir, 100000)
if err != nil {
if tt.err == "" {
t.Fatalf("ReadZip: unexpected error %v", err)
@@ -425,9 +424,6 @@ func TestReadZip(t *testing.T) {
if tt.err != "" {
t.Fatalf("ReadZip: no error, wanted %v", tt.err)
}
- if actualSubdir != tt.actualSubdir {
- t.Fatalf("ReadZip: actualSubdir = %q, want %q", actualSubdir, tt.actualSubdir)
- }
zipdata, err := ioutil.ReadAll(rc)
if err != nil {
t.Fatal(err)
diff --git a/src/cmd/go/internal/modfetch/codehost/shell.go b/src/cmd/go/internal/modfetch/codehost/shell.go
index 7b813c37401..835bc53c0dd 100644
--- a/src/cmd/go/internal/modfetch/codehost/shell.go
+++ b/src/cmd/go/internal/modfetch/codehost/shell.go
@@ -109,7 +109,7 @@ func main() {
if subdir == "-" {
subdir = ""
}
- rc, _, err := repo.ReadZip(f[1], subdir, 10<<20)
+ rc, err := repo.ReadZip(f[1], subdir, 10<<20)
if err != nil {
fmt.Fprintf(os.Stderr, "?%s\n", err)
continue
diff --git a/src/cmd/go/internal/modfetch/codehost/vcs.go b/src/cmd/go/internal/modfetch/codehost/vcs.go
index 48238f176c6..c9f77bf3b2d 100644
--- a/src/cmd/go/internal/modfetch/codehost/vcs.go
+++ b/src/cmd/go/internal/modfetch/codehost/vcs.go
@@ -417,14 +417,14 @@ func (r *vcsRepo) DescendsFrom(rev, tag string) (bool, error) {
return false, vcsErrorf("DescendsFrom not implemented")
}
-func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error) {
if r.cmd.readZip == nil {
- return nil, "", vcsErrorf("ReadZip not implemented for %s", r.cmd.vcs)
+ return nil, vcsErrorf("ReadZip not implemented for %s", r.cmd.vcs)
}
unlock, err := r.mu.Lock()
if err != nil {
- return nil, "", err
+ return nil, err
}
defer unlock()
@@ -433,7 +433,7 @@ func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
}
f, err := ioutil.TempFile("", "go-readzip-*.zip")
if err != nil {
- return nil, "", err
+ return nil, err
}
if r.cmd.vcs == "fossil" {
// If you run
@@ -454,9 +454,9 @@ func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
if err != nil {
f.Close()
os.Remove(f.Name())
- return nil, "", err
+ return nil, err
}
- return &deleteCloser{f}, "", nil
+ return &deleteCloser{f}, nil
}
// deleteCloser is a file that gets deleted on Close.
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
index 588f7a8d67c..600b2e75c3e 100644
--- a/src/cmd/go/internal/modfetch/coderepo.go
+++ b/src/cmd/go/internal/modfetch/coderepo.go
@@ -780,19 +780,16 @@ func (r *codeRepo) Zip(dst io.Writer, version string) error {
}
}
- rev, dir, _, err := r.findDir(version)
+ rev, subdir, _, err := r.findDir(version)
if err != nil {
return err
}
- dl, actualDir, err := r.code.ReadZip(rev, dir, codehost.MaxZipFile)
+ dl, err := r.code.ReadZip(rev, subdir, codehost.MaxZipFile)
if err != nil {
return err
}
defer dl.Close()
- if actualDir != "" && !hasPathPrefix(dir, actualDir) {
- return fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.modPath, rev, dir, actualDir)
- }
- subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
+ subdir = strings.Trim(subdir, "/")
// Spool to local file.
f, err := ioutil.TempFile("", "go-codehost-")
diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go
index 8f792a77687..7d20f66041a 100644
--- a/src/cmd/go/internal/modfetch/fetch.go
+++ b/src/cmd/go/internal/modfetch/fetch.go
@@ -125,9 +125,11 @@ func download(mod module.Version, dir string) (err error) {
return err
}
- // Make dir read-only only *after* renaming it.
- // os.Rename was observed to fail for read-only directories on macOS.
- makeDirsReadOnly(dir)
+ if !cfg.ModCacheRW {
+ // Make dir read-only only *after* renaming it.
+ // os.Rename was observed to fail for read-only directories on macOS.
+ makeDirsReadOnly(dir)
+ }
return nil
}
diff --git a/src/cmd/go/internal/modfile/gopkgin.go b/src/cmd/go/internal/modfile/gopkgin.go
deleted file mode 100644
index c94b3848a0e..00000000000
--- a/src/cmd/go/internal/modfile/gopkgin.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO: Figure out what gopkg.in should do.
-
-package modfile
-
-import "strings"
-
-// ParseGopkgIn splits gopkg.in import paths into their constituent parts
-func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) {
- if !strings.HasPrefix(path, "gopkg.in/") {
- return
- }
- f := strings.Split(path, "/")
- if len(f) >= 2 {
- if elem, v, ok := dotV(f[1]); ok {
- root = strings.Join(f[:2], "/")
- repo = "github.com/go-" + elem + "/" + elem
- major = v
- subdir = strings.Join(f[2:], "/")
- return root, repo, major, subdir, true
- }
- }
- if len(f) >= 3 {
- if elem, v, ok := dotV(f[2]); ok {
- root = strings.Join(f[:3], "/")
- repo = "github.com/" + f[1] + "/" + elem
- major = v
- subdir = strings.Join(f[3:], "/")
- return root, repo, major, subdir, true
- }
- }
- return
-}
-
-func dotV(name string) (elem, v string, ok bool) {
- i := len(name) - 1
- for i >= 0 && '0' <= name[i] && name[i] <= '9' {
- i--
- }
- if i <= 2 || i+1 >= len(name) || name[i-1] != '.' || name[i] != 'v' || name[i+1] == '0' && len(name) != i+2 {
- return "", "", false
- }
- return name[:i-1], name[i:], true
-}
diff --git a/src/cmd/go/internal/modfile/print.go b/src/cmd/go/internal/modfile/print.go
index cefc43b141c..3bbea38529f 100644
--- a/src/cmd/go/internal/modfile/print.go
+++ b/src/cmd/go/internal/modfile/print.go
@@ -12,6 +12,7 @@ import (
"strings"
)
+// Format returns a go.mod file as a byte slice, formatted in standard style.
func Format(f *FileSyntax) []byte {
pr := &printer{}
pr.file(f)
diff --git a/src/cmd/go/internal/modfile/read.go b/src/cmd/go/internal/modfile/read.go
index 1d81ff1ab7a..bfa90a5a644 100644
--- a/src/cmd/go/internal/modfile/read.go
+++ b/src/cmd/go/internal/modfile/read.go
@@ -17,7 +17,8 @@ import (
"unicode/utf8"
)
-// A Position describes the position between two bytes of input.
+// A Position describes an arbitrary source position in a file, including the
+// file, line, column, and byte offset.
type Position struct {
Line int // line in input (starting at 1)
LineRune int // rune in line (starting at 1)
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go
index 4105c47ba76..acbebb6d662 100644
--- a/src/cmd/go/internal/modload/build.go
+++ b/src/cmd/go/internal/modload/build.go
@@ -119,14 +119,8 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
info.GoVersion = loaded.goVersion[m.Path]
}
- if cfg.BuildMod == "vendor" {
- // The vendor directory doesn't contain enough information to reconstruct
- // anything more about the module.
- return info
- }
-
- // complete fills in the extra fields in m.
- complete := func(m *modinfo.ModulePublic) {
+ // completeFromModCache fills in the extra fields in m using the module cache.
+ completeFromModCache := func(m *modinfo.ModulePublic) {
if m.Version != "" {
if q, err := Query(m.Path, m.Version, "", nil); err != nil {
m.Error = &modinfo.ModuleError{Err: err.Error()}
@@ -152,13 +146,21 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
}
if !fromBuildList {
- complete(info)
+ completeFromModCache(info) // Will set m.Error in vendor mode.
return info
}
r := Replacement(m)
if r.Path == "" {
- complete(info)
+ if cfg.BuildMod == "vendor" {
+ // It's tempting to fill in the "Dir" field to point within the vendor
+ // directory, but that would be misleading: the vendor directory contains
+ // a flattened package tree, not complete modules, and it can even
+ // interleave packages from different modules if one module path is a
+ // prefix of the other.
+ } else {
+ completeFromModCache(info)
+ }
return info
}
@@ -177,10 +179,13 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
} else {
info.Replace.Dir = filepath.Join(ModRoot(), r.Path)
}
+ info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
+ }
+ if cfg.BuildMod != "vendor" {
+ completeFromModCache(info.Replace)
+ info.Dir = info.Replace.Dir
+ info.GoMod = info.Replace.GoMod
}
- complete(info.Replace)
- info.Dir = info.Replace.Dir
- info.GoMod = filepath.Join(info.Dir, "go.mod")
return info
}
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index cda6c93652d..984fbaf1f1e 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -34,7 +34,6 @@ import (
)
var (
- cwd string // TODO(bcmills): Is this redundant with base.Cwd?
mustUseModules = false
initialized bool
@@ -92,6 +91,9 @@ func Init() {
}
initialized = true
+ // Keep in sync with WillBeEnabled. We perform extra validation here, and
+ // there are lots of diagnostics and side effects, so we can't use
+ // WillBeEnabled directly.
env := cfg.Getenv("GO111MODULE")
switch env {
default:
@@ -132,18 +134,15 @@ func Init() {
os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
}
- var err error
- cwd, err = os.Getwd()
- if err != nil {
- base.Fatalf("go: %v", err)
- }
-
if CmdModInit {
// Running 'go mod init': go.mod will be created in current directory.
- modRoot = cwd
+ modRoot = base.Cwd
} else {
- modRoot = findModuleRoot(cwd)
+ modRoot = findModuleRoot(base.Cwd)
if modRoot == "" {
+ if cfg.ModFile != "" {
+ base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
+ }
if !mustUseModules {
// GO111MODULE is 'auto', and we can't find a module root.
// Stay in GOPATH mode.
@@ -159,6 +158,9 @@ func Init() {
fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
}
}
+ if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {
+ base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)
+ }
// We're in module mode. Install the hooks to make it work.
@@ -217,7 +219,7 @@ func Init() {
//
// See golang.org/issue/32027.
} else {
- modfetch.GoSumFile = filepath.Join(modRoot, "go.sum")
+ modfetch.GoSumFile = strings.TrimSuffix(ModFilePath(), ".mod") + ".sum"
search.SetModRoot(modRoot)
}
}
@@ -233,6 +235,54 @@ func init() {
}
}
+// WillBeEnabled checks whether modules should be enabled but does not
+// initialize modules by installing hooks. If Init has already been called,
+// WillBeEnabled returns the same result as Enabled.
+//
+// This function is needed to break a cycle. The main package needs to know
+// whether modules are enabled in order to install the module or GOPATH version
+// of 'go get', but Init reads the -modfile flag in 'go get', so it shouldn't
+// be called until the command is installed and flags are parsed. Instead of
+// calling Init and Enabled, the main package can call this function.
+func WillBeEnabled() bool {
+ if modRoot != "" || mustUseModules {
+ return true
+ }
+ if initialized {
+ return false
+ }
+
+ // Keep in sync with Init. Init does extra validation and prints warnings or
+ // exits, so it can't call this function directly.
+ env := cfg.Getenv("GO111MODULE")
+ switch env {
+ case "on":
+ return true
+ case "auto", "":
+ break
+ default:
+ return false
+ }
+
+ if CmdModInit {
+ // Running 'go mod init': go.mod will be created in current directory.
+ return true
+ }
+ if modRoot := findModuleRoot(base.Cwd); modRoot == "" {
+ // GO111MODULE is 'auto', and we can't find a module root.
+ // Stay in GOPATH mode.
+ return false
+ } else if search.InDir(modRoot, os.TempDir()) == "." {
+ // If you create /tmp/go.mod for experimenting,
+ // then any tests that create work directories under /tmp
+ // will find it and get modules when they're not expecting them.
+ // It's a bit of a peculiar thing to disallow but quite mysterious
+ // when it happens. See golang.org/issue/26708.
+ return false
+ }
+ return true
+}
+
// Enabled reports whether modules are (or must be) enabled.
// If modules are enabled but there is no main module, Enabled returns true
// and then the first use of module information will call die
@@ -259,6 +309,20 @@ func HasModRoot() bool {
return modRoot != ""
}
+// ModFilePath returns the effective path of the go.mod file. Normally, this
+// "go.mod" in the directory returned by ModRoot, but the -modfile flag may
+// change its location. ModFilePath calls base.Fatalf if there is no main
+// module, even if -modfile is set.
+func ModFilePath() string {
+ if !HasModRoot() {
+ die()
+ }
+ if cfg.ModFile != "" {
+ return cfg.ModFile
+ }
+ return filepath.Join(modRoot, "go.mod")
+}
+
// printStackInDie causes die to print a stack trace.
//
// It is enabled by the testgo tag, and helps to diagnose paths that
@@ -272,24 +336,25 @@ func die() {
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
- if cwd != "" {
- if dir, name := findAltConfig(cwd); dir != "" {
- rel, err := filepath.Rel(cwd, dir)
- if err != nil {
- rel = dir
- }
- cdCmd := ""
- if rel != "." {
- cdCmd = fmt.Sprintf("cd %s && ", rel)
- }
- base.Fatalf("go: cannot find main module, but found %s in %s\n\tto create a module there, run:\n\t%sgo mod init", name, dir, cdCmd)
+ if dir, name := findAltConfig(base.Cwd); dir != "" {
+ rel, err := filepath.Rel(base.Cwd, dir)
+ if err != nil {
+ rel = dir
}
+ cdCmd := ""
+ if rel != "." {
+ cdCmd = fmt.Sprintf("cd %s && ", rel)
+ }
+ base.Fatalf("go: cannot find main module, but found %s in %s\n\tto create a module there, run:\n\t%sgo mod init", name, dir, cdCmd)
}
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
// InitMod sets Target and, if there is a main module, parses the initial build
// list from its go.mod file, creating and populating that file if needed.
+//
+// As a side-effect, InitMod sets a default for cfg.BuildMod if it does not
+// already have an explicit value.
func InitMod() {
if len(buildList) > 0 {
return
@@ -311,7 +376,7 @@ func InitMod() {
return
}
- gomod := filepath.Join(modRoot, "go.mod")
+ gomod := ModFilePath()
data, err := renameio.ReadFile(gomod)
if err != nil {
base.Fatalf("go: %v", err)
@@ -367,7 +432,7 @@ func AllowMissingModuleImports() {
func modFileToBuildList() {
Target = modFile.Module.Mod
targetPrefix = Target.Path
- if rel := search.InDir(cwd, cfg.GOROOTsrc); rel != "" {
+ if rel := search.InDir(base.Cwd, cfg.GOROOTsrc); rel != "" {
targetInGorootSrc = true
if Target.Path == "std" {
targetPrefix = ""
@@ -581,6 +646,9 @@ var altConfigs = []string{
}
func findModuleRoot(dir string) (root string) {
+ if dir == "" {
+ panic("dir not set")
+ }
dir = filepath.Clean(dir)
// Look for enclosing go.mod.
@@ -598,6 +666,9 @@ func findModuleRoot(dir string) (root string) {
}
func findAltConfig(dir string) (root, name string) {
+ if dir == "" {
+ panic("dir not set")
+ }
dir = filepath.Clean(dir)
for {
for _, name := range altConfigs {
@@ -801,7 +872,7 @@ func WriteGoMod() {
unlock := modfetch.SideLock()
defer unlock()
- file := filepath.Join(modRoot, "go.mod")
+ file := ModFilePath()
old, err := renameio.ReadFile(file)
if !bytes.Equal(old, modFileData) {
if bytes.Equal(old, new) {
@@ -819,7 +890,6 @@ func WriteGoMod() {
// want to run concurrent commands, they need to start with a complete,
// consistent module definition.
base.Fatalf("go: updates to go.mod needed, but contents have changed")
-
}
if err := renameio.WriteFile(file, new, 0666); err != nil {
diff --git a/src/cmd/go/internal/modload/list.go b/src/cmd/go/internal/modload/list.go
index 6c0b3945cb8..cd162f88759 100644
--- a/src/cmd/go/internal/modload/list.go
+++ b/src/cmd/go/internal/modload/list.go
@@ -11,6 +11,7 @@ import (
"strings"
"cmd/go/internal/base"
+ "cmd/go/internal/cfg"
"cmd/go/internal/modinfo"
"cmd/go/internal/module"
"cmd/go/internal/par"
@@ -124,10 +125,20 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
}
continue
}
- mods = append(mods, &modinfo.ModulePublic{
- Path: arg,
- Error: modinfoError(arg, "", errors.New("not a known dependency")),
- })
+ if cfg.BuildMod == "vendor" {
+ // In vendor mode, we can't determine whether a missing module is “a
+ // known dependency” because the module graph is incomplete.
+ // Give a more explicit error message.
+ mods = append(mods, &modinfo.ModulePublic{
+ Path: arg,
+ Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")),
+ })
+ } else {
+ mods = append(mods, &modinfo.ModulePublic{
+ Path: arg,
+ Error: modinfoError(arg, "", errors.New("not a known dependency")),
+ })
+ }
} else {
fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
}
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 5f6fd672ba8..5f28d7cf14a 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -95,7 +95,7 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
for _, pkg := range pkgs {
dir := pkg
if !filepath.IsAbs(dir) {
- dir = filepath.Join(cwd, pkg)
+ dir = filepath.Join(base.Cwd, pkg)
} else {
dir = filepath.Clean(dir)
}
@@ -211,11 +211,17 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
// One last pass to finalize wildcards.
updateMatches(matches, false)
+ checkMultiplePaths()
+ WriteGoMod()
- // A given module path may be used as itself or as a replacement for another
- // module, but not both at the same time. Otherwise, the aliasing behavior is
- // too subtle (see https://golang.org/issue/26607), and we don't want to
- // commit to a specific behavior at this point.
+ return matches
+}
+
+// checkMultiplePaths verifies that a given module path is used as itself
+// or as a replacement for another module, but not both at the same time.
+//
+// (See https://golang.org/issue/26607 and https://golang.org/issue/34650.)
+func checkMultiplePaths() {
firstPath := make(map[module.Version]string, len(buildList))
for _, mod := range buildList {
src := mod
@@ -229,9 +235,6 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
}
}
base.ExitIfErrors()
- WriteGoMod()
-
- return matches
}
// pathInModuleCache returns the import path of the directory dir,
@@ -318,7 +321,7 @@ func DirImportPath(dir string) string {
}
if !filepath.IsAbs(dir) {
- dir = filepath.Join(cwd, dir)
+ dir = filepath.Join(base.Cwd, dir)
} else {
dir = filepath.Clean(dir)
}
@@ -383,6 +386,7 @@ func loadAll(testAll bool) []string {
}
all := TargetPackages("...")
loaded.load(func() []string { return all })
+ checkMultiplePaths()
WriteGoMod()
var paths []string
diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go
index 33ab4ae36eb..ef3835bfa4e 100644
--- a/src/cmd/go/internal/search/search.go
+++ b/src/cmd/go/internal/search/search.go
@@ -361,10 +361,10 @@ func ImportPathsQuiet(patterns []string) []*Match {
return out
}
-// CleanPatterns returns the patterns to use for the given
-// command line. It canonicalizes the patterns but does not
-// evaluate any matches. It preserves text after '@' for commands
-// that accept versions.
+// CleanPatterns returns the patterns to use for the given command line. It
+// canonicalizes the patterns but does not evaluate any matches. For patterns
+// that are not local or absolute paths, it preserves text after '@' to avoid
+// modifying version queries.
func CleanPatterns(patterns []string) []string {
if len(patterns) == 0 {
return []string{"."}
@@ -372,7 +372,9 @@ func CleanPatterns(patterns []string) []string {
var out []string
for _, a := range patterns {
var p, v string
- if i := strings.IndexByte(a, '@'); i < 0 {
+ if build.IsLocalImport(a) || filepath.IsAbs(a) {
+ p = a
+ } else if i := strings.IndexByte(a, '@'); i < 0 {
p = a
} else {
p = a[:i]
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 6264593c345..45dd165ce02 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -102,6 +102,16 @@ and test commands:
-mod mode
module download mode to use: readonly or vendor.
See 'go help modules' for more.
+ -modcacherw
+ leave newly-created directories in the module cache read-write
+ instead of making them read-only.
+ -modfile file
+ in module aware mode, read (and possibly write) an alternate go.mod
+ file instead of the one in the module root directory. A file named
+ "go.mod" must still be present in order to determine the module root
+ directory, but it is not accessed. When -modfile is specified, an
+ alternate go.sum file is also used: its path is derived from the
+ -modfile flag by trimming the ".mod" extension and appending ".sum".
-pkgdir dir
install and load all packages from dir instead of the usual locations.
For example, when building with a non-standard configuration,
@@ -221,9 +231,10 @@ type BuildFlagMask int
const (
DefaultBuildFlags BuildFlagMask = 0
OmitModFlag BuildFlagMask = 1 << iota
+ OmitModCommonFlags
)
-// addBuildFlags adds the flags common to the build, clean, get,
+// AddBuildFlags adds the flags common to the build, clean, get,
// install, list, run, and test commands.
func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.BoolVar(&cfg.BuildA, "a", false, "")
@@ -240,6 +251,9 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
if mask&OmitModFlag == 0 {
cmd.Flag.StringVar(&cfg.BuildMod, "mod", "", "")
}
+ if mask&OmitModCommonFlags == 0 {
+ AddModCommonFlags(cmd)
+ }
cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "")
cmd.Flag.Var(&load.BuildLdflags, "ldflags", "")
cmd.Flag.BoolVar(&cfg.BuildLinkshared, "linkshared", false, "")
@@ -255,6 +269,13 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
}
+// AddModCommonFlags adds the module-related flags common to build commands
+// and 'go mod' subcommands.
+func AddModCommonFlags(cmd *base.Command) {
+ cmd.Flag.BoolVar(&cfg.ModCacheRW, "modcacherw", false, "")
+ cmd.Flag.StringVar(&cfg.ModFile, "modfile", "", "")
+}
+
// tagsFlag is the implementation of the -tags flag.
type tagsFlag []string
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index f4ae0e11c1c..ba3168a2c88 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -249,12 +249,20 @@ func buildModeInit() {
case "":
// ok
case "readonly", "vendor", "mod":
- if load.ModLookup == nil && !inGOFLAGS("-mod") {
+ if !cfg.ModulesEnabled && !inGOFLAGS("-mod") {
base.Fatalf("build flag -mod=%s only valid when using modules", cfg.BuildMod)
}
default:
base.Fatalf("-mod=%s not supported (can be '', 'mod', 'readonly', or 'vendor')", cfg.BuildMod)
}
+ if !cfg.ModulesEnabled {
+ if cfg.ModCacheRW && !inGOFLAGS("-modcacherw") {
+ base.Fatalf("build flag -modcacherw only valid when using modules")
+ }
+ if cfg.ModFile != "" && !inGOFLAGS("-mod") {
+ base.Fatalf("build flag -modfile only valid when using modules")
+ }
+ }
}
func inGOFLAGS(flag string) bool {
diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go
index 3a5deae4513..d02630155bf 100644
--- a/src/cmd/go/internal/work/security.go
+++ b/src/cmd/go/internal/work/security.go
@@ -43,6 +43,7 @@ var re = lazyregexp.New
var validCompilerFlags = []*lazyregexp.Regexp{
re(`-D([A-Za-z_].*)`),
+ re(`-U([A-Za-z_]*)`),
re(`-F([^@\-].*)`),
re(`-I([^@\-].*)`),
re(`-O`),
@@ -51,6 +52,7 @@ var validCompilerFlags = []*lazyregexp.Regexp{
re(`-W([^@,]+)`), // -Wall but not -Wa,-foo.
re(`-Wa,-mbig-obj`),
re(`-Wp,-D([A-Za-z_].*)`),
+ re(`-Wp, -U([A-Za-z_]*)`),
re(`-ansi`),
re(`-f(no-)?asynchronous-unwind-tables`),
re(`-f(no-)?blocks`),
@@ -127,6 +129,7 @@ var validCompilerFlags = []*lazyregexp.Regexp{
var validCompilerFlagsWithNextArg = []string{
"-arch",
"-D",
+ "-U",
"-I",
"-framework",
"-isysroot",
diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go
index 8bf164bf082..3a02db1d04a 100644
--- a/src/cmd/go/internal/work/security_test.go
+++ b/src/cmd/go/internal/work/security_test.go
@@ -12,6 +12,7 @@ import (
var goodCompilerFlags = [][]string{
{"-DFOO"},
{"-Dfoo=bar"},
+ {"-Ufoo"},
{"-F/Qt"},
{"-I/"},
{"-I/etc/passwd"},
@@ -67,6 +68,7 @@ var goodCompilerFlags = [][]string{
var badCompilerFlags = [][]string{
{"-D@X"},
{"-D-X"},
+ {"-Ufoo=bar"},
{"-F@dir"},
{"-F-dir"},
{"-I@dir"},
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 73da736882f..4882375f4e4 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -91,7 +91,7 @@ func main() {
}
if args[0] == "get" || args[0] == "help" {
- if modload.Init(); !modload.Enabled() {
+ if !modload.WillBeEnabled() {
// Replace module-aware get with GOPATH get if appropriate.
*modget.CmdGet = *get.CmdGet
}
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index 5e50dd14c78..362a10fa86f 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -117,6 +117,7 @@ func (ts *testScript) setup() {
"GOSUMDB=" + testSumDBVerifierKey,
"GONOPROXY=",
"GONOSUMDB=",
+ "PWD=" + ts.cd,
tempEnvName() + "=" + filepath.Join(ts.workdir, "tmp"),
"devnull=" + os.DevNull,
"goversion=" + goVersion(ts),
@@ -414,6 +415,7 @@ func (ts *testScript) cmdCd(neg bool, args []string) {
ts.fatalf("%s is not a directory", dir)
}
ts.cd = dir
+ ts.envMap["PWD"] = dir
fmt.Fprintf(&ts.log, "%s\n", ts.cd)
}
@@ -502,9 +504,6 @@ func (ts *testScript) doCmdCmp(args []string, env, quiet bool) {
// cp copies files, maybe eventually directories.
func (ts *testScript) cmdCp(neg bool, args []string) {
- if neg {
- ts.fatalf("unsupported: ! cp")
- }
if len(args) < 2 {
ts.fatalf("usage: cp src... dst")
}
@@ -543,7 +542,14 @@ func (ts *testScript) cmdCp(neg bool, args []string) {
if dstDir {
targ = filepath.Join(dst, filepath.Base(src))
}
- ts.check(ioutil.WriteFile(targ, data, mode))
+ err := ioutil.WriteFile(targ, data, mode)
+ if neg {
+ if err == nil {
+ ts.fatalf("unexpected command success")
+ }
+ } else {
+ ts.check(err)
+ }
}
}
diff --git a/src/cmd/go/testdata/mod/not-rsc.io_quote_v0.1.0-nomod.txt b/src/cmd/go/testdata/mod/not-rsc.io_quote_v0.1.0-nomod.txt
new file mode 100644
index 00000000000..efff08826ad
--- /dev/null
+++ b/src/cmd/go/testdata/mod/not-rsc.io_quote_v0.1.0-nomod.txt
@@ -0,0 +1,59 @@
+Constructed by hand.
+(derived from rsc.io/quote@e7a685a342, but without an explicit go.mod file.)
+
+-- .mod --
+module "not-rsc.io/quote"
+-- .info --
+{"Version":"v0.1.0-nomod","Time":"2018-02-14T00:51:33Z"}
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+// Hello returns a greeting.
+func Hello() string {
+ return "Hello, world."
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+ // See http://www.oocities.org/nodotus/hbglass.html.
+ return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+ return "Don't communicate by sharing memory, share memory by communicating."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+ hello := "Hello, world."
+ if out := Hello(); out != hello {
+ t.Errorf("Hello() = %q, want %q", out, hello)
+ }
+}
+
+func TestGlass(t *testing.T) {
+ glass := "I can eat glass and it doesn't hurt me."
+ if out := Glass(); out != glass {
+ t.Errorf("Glass() = %q, want %q", out, glass)
+ }
+}
+
+func TestGo(t *testing.T) {
+ go1 := "Don't communicate by sharing memory. Share memory by communicating."
+ if out := Go(); out != go1 {
+ t.Errorf("Go() = %q, want %q", out, go1)
+ }
+}
diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README
index 46444d84d8f..ec886b18a15 100644
--- a/src/cmd/go/testdata/script/README
+++ b/src/cmd/go/testdata/script/README
@@ -107,7 +107,7 @@ The commands are:
Like cmp, but environment variables are substituted in the file contents
before the comparison. For example, $GOOS is replaced by the target GOOS.
-- cp src... dst
+- [!] cp src... dst
Copy the listed files to the target file or existing directory.
src can include "stdout" or "stderr" to use the standard output or standard error
from the most recent exec or go command.
diff --git a/src/cmd/go/testdata/script/list_ambiguous_path.txt b/src/cmd/go/testdata/script/list_ambiguous_path.txt
index 9f1aa37be83..bdb7ffb077a 100644
--- a/src/cmd/go/testdata/script/list_ambiguous_path.txt
+++ b/src/cmd/go/testdata/script/list_ambiguous_path.txt
@@ -22,8 +22,9 @@ stderr 'package ./foo.go/b.go: cannot find package "."'
# Multiple patterns for Go files with a typo. This should
# treat the wrong pattern as if it were a non-existint file.
! go list ./foo.go/a.go ./foo.go/b.go
+[plan9] stderr 'stat ./foo.go/b.go: ''./foo.go/b.go'' does not exist'
[windows] stderr './foo.go/b.go: The system cannot find the file specified'
-[!windows] stderr './foo.go/b.go: no such file or directory'
+[!plan9] [!windows] stderr './foo.go/b.go: no such file or directory'
-- a.go --
package main
diff --git a/src/cmd/go/testdata/script/mod_cache_rw.txt b/src/cmd/go/testdata/script/mod_cache_rw.txt
new file mode 100644
index 00000000000..b4a3a456e8d
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_cache_rw.txt
@@ -0,0 +1,49 @@
+# Regression test for golang.org/issue/31481.
+
+env GO111MODULE=on
+
+# golang.org/issue/31481: an explicit flag should make directories in the module
+# cache writable in order to work around the historical inability of 'rm -rf' to
+# forcibly remove files in unwritable directories.
+go get -modcacherw -d rsc.io/quote@v1.5.2
+cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
+
+# After adding an extraneous file, 'go mod verify' should fail.
+! go mod verify
+
+# However, files within those directories should still be read-only to avoid
+# accidental mutations.
+[!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
+
+# If all 'go' commands ran with the flag, the system's 'rm' binary
+# should be able to remove the module cache if the '-rf' flags are set.
+[!windows] [exec:rm] exec rm -rf $GOPATH/pkg/mod
+[!windows] [!exec:rm] go clean -modcache
+[windows] [exec:rmdir] exec rmdir /s /q $GOPATH\pkg\mod
+[windows] [!exec:rmdir] go clean -modcache
+! exists $GOPATH/pkg/mod
+
+# The directories in the module cache should by default be unwritable,
+# so that tests and tools will not accidentally add extraneous files to them.
+# Windows does not respect FILE_ATTRIBUTE_READONLY on directories, according
+# to MSDN, so there we disable testing whether the directory itself is
+# unwritable.
+go get -d rsc.io/quote@latest
+[!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
+[!windows] [!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
+! exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
+
+
+# Repeat part of the test with 'go mod download' instead of 'go get' to verify
+# -modcacherw is supported on 'go mod' subcommands.
+go clean -modcache
+go mod download -modcacherw rsc.io/quote
+cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
+! go mod verify
+[!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
+
+
+-- $WORK/extraneous.txt --
+module oops
+-- go.mod --
+module golang.org/issue/31481
diff --git a/src/cmd/go/testdata/script/mod_convert_vendor_json.txt b/src/cmd/go/testdata/script/mod_convert_vendor_json.txt
index 47d111d4c13..df6db36574f 100644
--- a/src/cmd/go/testdata/script/mod_convert_vendor_json.txt
+++ b/src/cmd/go/testdata/script/mod_convert_vendor_json.txt
@@ -10,7 +10,7 @@ stderr '\s*cd \.\. && go mod init'
# The command we suggested should succeed.
cd ..
go mod init
-go list -m all
+go list -m
stdout '^m$'
-- $WORK/test/vendor/vendor.json --
diff --git a/src/cmd/go/testdata/script/mod_convert_vendor_manifest.txt b/src/cmd/go/testdata/script/mod_convert_vendor_manifest.txt
index 68edb9dc292..8b6a1414be2 100644
--- a/src/cmd/go/testdata/script/mod_convert_vendor_manifest.txt
+++ b/src/cmd/go/testdata/script/mod_convert_vendor_manifest.txt
@@ -10,7 +10,7 @@ stderr '\s*cd \.\. && go mod init'
# The command we suggested should succeed.
cd ..
go mod init
-go list -m all
+go list -m
stdout '^m$'
-- $WORK/test/vendor/manifest --
diff --git a/src/cmd/go/testdata/script/mod_fs_patterns.txt b/src/cmd/go/testdata/script/mod_fs_patterns.txt
index fd7de130024..4911fbb6138 100644
--- a/src/cmd/go/testdata/script/mod_fs_patterns.txt
+++ b/src/cmd/go/testdata/script/mod_fs_patterns.txt
@@ -1,7 +1,6 @@
-# File system pattern searches should skip sub-modules and vendor directories.
-
env GO111MODULE=on
+# File system pattern searches should skip sub-modules and vendor directories.
cd x
# all packages
@@ -40,6 +39,24 @@ stderr '^can.t load package: package ./nonexist: cannot find package "." in:\n\t
! stderr 'import lookup disabled'
stderr 'can.t load package: package ./go.mod: cannot find package'
+
+# File system paths and patterns should allow the '@' character.
+cd ../@at
+go list $PWD
+stdout '^at$'
+go list $PWD/...
+stdout '^at$'
+
+# The '@' character is not allowed in directory paths that are part of
+# a package path.
+cd ../badat/bad@
+! go list .
+stderr 'directory . outside available modules'
+! go list $PWD
+stderr 'directory . outside available modules'
+! go list $PWD/...
+stderr 'directory . outside available modules'
+
-- x/go.mod --
module m
@@ -64,3 +81,17 @@ package z
-- x/y/z/w/w.go --
package w
+
+-- @at/go.mod --
+module at
+
+go 1.14
+-- @at/at.go --
+package at
+
+-- badat/go.mod --
+module badat
+
+go 1.14
+-- badat/bad@/bad.go --
+package bad
diff --git a/src/cmd/go/testdata/script/mod_getmode_vendor.txt b/src/cmd/go/testdata/script/mod_getmode_vendor.txt
index 21fec5b85f9..430bf1ef444 100644
--- a/src/cmd/go/testdata/script/mod_getmode_vendor.txt
+++ b/src/cmd/go/testdata/script/mod_getmode_vendor.txt
@@ -11,10 +11,17 @@ stdout '^rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote$'
stdout '^golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text[\\/]language$'
! go list -mod=vendor -m rsc.io/quote@latest
-stderr 'go list -m: can''t list modules with -mod=vendor\n\tuse -mod=mod or -mod=readonly to ignore the vendor directory'
+stderr 'go list -m: rsc.io/quote@latest: module lookup disabled by -mod=vendor'
! go get -mod=vendor -u
stderr 'flag provided but not defined: -mod'
+# Since we don't have a complete module graph, 'go list -m' queries
+# that require the complete graph should fail with a useful error.
+! go list -mod=vendor -m all
+stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+! go list -mod=vendor -m ...
+stderr 'go list -m: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+
-- go.mod --
module x
diff --git a/src/cmd/go/testdata/script/mod_outside.txt b/src/cmd/go/testdata/script/mod_outside.txt
index 815745e8bff..6f569ca351a 100644
--- a/src/cmd/go/testdata/script/mod_outside.txt
+++ b/src/cmd/go/testdata/script/mod_outside.txt
@@ -82,6 +82,10 @@ stderr 'cannot find main module'
stderr 'cannot find main module'
+# 'go mod download' without arguments should report an error.
+! go mod download
+stderr 'no modules specified'
+
# 'go mod download' should download exactly the requested module without dependencies.
rm -r $GOPATH/pkg/mod/cache/download/example.com
go mod download example.com/printversion@v1.0.0
@@ -92,15 +96,16 @@ exists $GOPATH/pkg/mod/cache/download/example.com/printversion/@v/v1.0.0.zip
! go mod download all
stderr 'go: cannot match "all": working directory is not part of a module'
+
# 'go mod vendor' should fail: it starts by clearing the existing vendor
# directory, and we don't know where that is.
! go mod vendor
stderr 'cannot find main module'
-# 'go mod verify' should succeed: we have no modules to verify.
-go mod verify
-stdout 'all modules verified'
-! stderr .
+
+# 'go mod verify' should fail: we have no modules to verify.
+! go mod verify
+stderr 'cannot find main module'
# 'go get' without arguments implicitly operates on the main module, and thus
diff --git a/src/cmd/go/testdata/script/mod_replace.txt b/src/cmd/go/testdata/script/mod_replace.txt
index e4301b50d0d..c21f1720021 100644
--- a/src/cmd/go/testdata/script/mod_replace.txt
+++ b/src/cmd/go/testdata/script/mod_replace.txt
@@ -45,6 +45,13 @@ go mod edit -replace=rsc.io/quote/v3=./local/rsc.io/quote/v3
! go get -d rsc.io/quote/v3/missing-package
stderr 'module rsc.io/quote/v3@upgrade found \(v3.0.0, replaced by ./local/rsc.io/quote/v3\), but does not contain package'
+# The reported Dir and GoMod for a replaced module should be accurate.
+cp go.mod.orig go.mod
+go mod edit -replace=rsc.io/quote/v3=not-rsc.io/quote@v0.1.0-nomod
+go mod download
+go list -m -f '{{.Path}} {{.Version}} {{.Dir}} {{.GoMod}}{{with .Replace}} => {{.Path}} {{.Version}} {{.Dir}} {{.GoMod}}{{end}}' rsc.io/quote/v3
+stdout '^rsc.io/quote/v3 v3.0.0 '$GOPATH'[/\\]pkg[/\\]mod[/\\]not-rsc.io[/\\]quote@v0.1.0-nomod '$GOPATH'[/\\]pkg[/\\]mod[/\\]cache[/\\]download[/\\]not-rsc.io[/\\]quote[/\\]@v[/\\]v0.1.0-nomod.mod => not-rsc.io/quote v0.1.0-nomod '$GOPATH'[/\\]pkg[/\\]mod[/\\]not-rsc.io[/\\]quote@v0.1.0-nomod '$GOPATH'[/\\]pkg[/\\]mod[/\\]cache[/\\]download[/\\]not-rsc.io[/\\]quote[/\\]@v[/\\]v0.1.0-nomod.mod$'
+
-- go.mod --
module quoter
diff --git a/src/cmd/go/testdata/script/mod_tidy_replace.txt b/src/cmd/go/testdata/script/mod_tidy_replace.txt
index d5c22530944..c3158f8610e 100644
--- a/src/cmd/go/testdata/script/mod_tidy_replace.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_replace.txt
@@ -47,6 +47,12 @@ grep 'rsc.io/sampler v1.2.0' go.mod
cd outside
go list -m all
stdout 'rsc.io/sampler v1.3.0'
+cd ..
+
+# The same module can't be used as two different paths.
+cd multiple-paths
+! go mod tidy
+stderr 'rsc.io/quote/v3@v3.0.0 used for two different module paths \(not-rsc.io/quote/v3 and rsc.io/quote/v3\)'
-- go.mod --
module example.com/tidy
@@ -109,3 +115,23 @@ package b
module golang.org/issue/30166/b
require golang.org/issue/30166/a v0.0.0
+-- multiple-paths/main.go --
+package main
+
+import (
+ "fmt"
+ "rsc.io/quote/v3"
+)
+
+func main() {
+ fmt.Println(quote.GoV3())
+}
+-- multiple-paths/go.mod --
+module quoter
+
+require (
+ rsc.io/quote/v3 v3.0.0
+ not-rsc.io/quote/v3 v3.0.0
+)
+
+replace not-rsc.io/quote/v3 => rsc.io/quote/v3 v3.0.0
diff --git a/src/cmd/go/testdata/script/mod_vendor.txt b/src/cmd/go/testdata/script/mod_vendor.txt
index 9b716906e59..bb3e634b3a3 100644
--- a/src/cmd/go/testdata/script/mod_vendor.txt
+++ b/src/cmd/go/testdata/script/mod_vendor.txt
@@ -32,19 +32,28 @@ stdout 'src[\\/]x'
go list -mod=vendor -f {{.Dir}} x
stdout 'src[\\/]vendor[\\/]x'
-# 'go list -mod=vendor -m' does not have enough information to list modules
-# accurately, and should fail.
-! go list -mod=vendor -f {{.Dir}} -m x
-stderr 'can''t list modules with -mod=vendor\n\tuse -mod=mod or -mod=readonly to ignore the vendor directory'
+# 'go list -mod=vendor -m' should successfully list vendored modules,
+# but should not provide a module directory because no directory contains
+# the complete module.
+go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m x
+stdout '^v1.0.0 $'
+
+# 'go list -mod=vendor -m' on a transitive dependency that does not
+# provide vendored packages should give a helpful error rather than
+# 'not a known dependency'.
+! go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m diamondright
+stderr 'go list -m: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
# 'go list -mod=mod' should report packages outside the import graph,
# but 'go list -mod=vendor' should error out for them.
go list -mod=mod -f {{.Dir}} w
stdout 'src[\\/]w'
-
! go list -mod=vendor -f {{.Dir}} w
stderr 'src[\\/]vendor[\\/]w'
+go list -mod=mod -f {{.Dir}} diamondright
+stdout 'src[\\/]diamondright'
+
# Test dependencies should not be copied.
! exists vendor/x/testdata
! exists vendor/a/foo/bar/b/ignored.go
@@ -79,6 +88,8 @@ go fmt -mod=vendor ./...
-- go.mod --
module m
+go 1.13
+
require (
a v1.0.0
diamondroot v0.0.0
@@ -264,10 +275,11 @@ require (
-- diamondroot/x.go --
package diamondroot
-import (
- _ "diamondleft"
- _ "diamondright"
-)
+import _ "diamondleft"
+-- diamondroot/unused/unused.go --
+package unused
+
+import _ "diamondright"
-- diamondleft/go.mod --
module diamondleft
diff --git a/src/cmd/go/testdata/script/mod_vendor_auto.txt b/src/cmd/go/testdata/script/mod_vendor_auto.txt
index 7abe833f57c..873644b4383 100644
--- a/src/cmd/go/testdata/script/mod_vendor_auto.txt
+++ b/src/cmd/go/testdata/script/mod_vendor_auto.txt
@@ -17,10 +17,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]printversion$'
stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$'
! go list -m all
-stderr 'can''t list modules with -mod=vendor'
+stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
! go list -m -f '{{.Dir}}' all
-stderr 'can''t list modules with -mod=vendor'
+stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
# An explicit -mod=mod should force the vendor directory to be ignored.
env GOFLAGS=-mod=mod
@@ -103,6 +103,15 @@ stdout '^'$WORK'[/\\]auto$'
stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]printversion$'
stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$'
+# ...but 'go list -m' should continue to fail, this time without
+# referring to a -mod default that the user didn't set.
+! go list -m all
+stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+
+! go list -m -f '{{.Dir}}' all
+stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)'
+
+
# 'go mod init' should work if there is already a GOPATH-mode vendor directory
# present. If there are no module dependencies, -mod=vendor should be used by
# default and should not fail the consistency check even though no module
diff --git a/src/cmd/go/testdata/script/mod_vendor_replace.txt b/src/cmd/go/testdata/script/mod_vendor_replace.txt
index a251daa6c14..0c1c1d22f5b 100644
--- a/src/cmd/go/testdata/script/mod_vendor_replace.txt
+++ b/src/cmd/go/testdata/script/mod_vendor_replace.txt
@@ -21,6 +21,16 @@ stdout '.*[/\\]vendor[/\\]rsc.io[/\\]quote[/\\]v3'
! stderr 'finding'
! stderr 'lookup disabled'
+# 'go list' should provide the original replacement directory as the module's
+# replacement path.
+go list -mod=vendor -f '{{with .Module}}{{with .Replace}}{{.Path}}{{end}}{{end}}' rsc.io/quote/v3
+stdout '.*[/\\]not-rsc.io[/\\]quote[/\\]v3'
+
+# The same module can't be used as two different paths.
+cd multiple-paths
+! go mod vendor
+stderr 'rsc.io/quote/v3@v3.0.0 used for two different module paths \(not-rsc.io/quote/v3 and rsc.io/quote/v3\)'
+
-- go.mod --
module example.com/replace
@@ -37,3 +47,20 @@ module not-rsc.io/quote/v3
-- local/not-rsc.io/quote/v3/quote.go --
package quote
+
+-- multiple-paths/main.go --
+package main
+import (
+ "fmt"
+ "rsc.io/quote/v3"
+)
+func main() {
+ fmt.Println(quote.GoV3())
+}
+-- multiple-paths/go.mod --
+module quoter
+require (
+ rsc.io/quote/v3 v3.0.0
+ not-rsc.io/quote/v3 v3.0.0
+)
+replace not-rsc.io/quote/v3 => rsc.io/quote/v3 v3.0.0
diff --git a/src/cmd/go/testdata/script/modfile_flag.txt b/src/cmd/go/testdata/script/modfile_flag.txt
new file mode 100644
index 00000000000..46a169fc427
--- /dev/null
+++ b/src/cmd/go/testdata/script/modfile_flag.txt
@@ -0,0 +1,67 @@
+# Tests the behavior of the -modfile flag in commands that support it.
+# The go.mod file exists but should not be read or written.
+# Same with go.sum.
+
+env GOFLAGS=-modfile=go.alt.mod
+cp go.mod go.mod.orig
+cp go.sum go.sum.orig
+
+
+# go mod init should create a new file, even though go.mod already exists.
+go mod init example.com/m
+grep example.com/m go.alt.mod
+
+# go mod edit should operate on the alternate file
+go mod edit -require rsc.io/quote@v1.5.2
+grep rsc.io/quote go.alt.mod
+
+# other 'go mod' commands should work. 'go mod vendor' is tested later.
+go mod download rsc.io/quote
+go mod graph
+stdout rsc.io/quote
+go mod tidy
+grep rsc.io/quote go.alt.sum
+go mod verify
+go mod why rsc.io/quote
+
+
+# 'go list' and other commands with build flags should work.
+# They should update the alternate go.mod when a dependency is missing.
+go mod edit -droprequire rsc.io/quote
+go list .
+grep rsc.io/quote go.alt.mod
+go build -n .
+go test -n .
+go get -d rsc.io/quote
+
+
+# 'go mod vendor' should work.
+go mod vendor
+exists vendor
+
+# Automatic vendoring should be broken by editing an explicit requirement
+# in the alternate go.mod file.
+go mod edit -require rsc.io/quote@v1.5.1
+! go list .
+go list -mod=mod
+
+
+# The original files should not have been modified.
+cmp go.mod go.mod.orig
+cmp go.sum go.sum.orig
+
+
+# If the altnernate mod file does not have a ".mod" suffix, an error
+# should be reported.
+cp go.alt.mod goaltmod
+! go mod tidy -modfile=goaltmod
+stderr '-modfile=goaltmod: file does not have .mod extension'
+
+-- go.mod --
+ʕ◔ϖ◔ʔ
+-- go.sum --
+ʕ◔ϖ◔ʔ
+-- use.go --
+package use
+
+import _ "rsc.io/quote"
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 2e94d552252..76c6f261a31 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -409,10 +409,11 @@ type FuncInfo struct {
dwarfAbsFnSym *LSym
dwarfDebugLinesSym *LSym
- GCArgs *LSym
- GCLocals *LSym
- GCRegs *LSym
- StackObjects *LSym
+ GCArgs *LSym
+ GCLocals *LSym
+ GCRegs *LSym
+ StackObjects *LSym
+ OpenCodedDeferInfo *LSym
FuncInfoSym *LSym
}
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index f28fa65e95c..b1969522182 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -419,6 +419,9 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
// to a PLT, so make sure the GOT pointer is loaded into BX.
// RegTo2 is set on the replacement call insn to stop it being
// processed when it is in turn passed to progedit.
+ //
+ // We disable open-coded defers in buildssa() on 386 ONLY with shared
+ // libraries because of this extra code added before deferreturn calls.
if ctxt.Arch.Family == sys.AMD64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 {
return
}
@@ -660,8 +663,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
markedPrologue = true
}
- deltasp := autoffset
-
if bpsize > 0 {
// Save caller's BP
p = obj.Appendp(p, newprog)
@@ -806,7 +807,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p = end
}
- for ; p != nil; p = p.Link {
+ var deltasp int32
+ for p = cursym.Func.Text; p != nil; p = p.Link {
pcsize := ctxt.Arch.RegSize
switch p.From.Name {
case obj.NAME_AUTO:
@@ -863,6 +865,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.Spadj = -2
continue
+ case AADJSP:
+ p.Spadj = int32(p.From.Offset)
+ deltasp += int32(p.From.Offset)
+ continue
+
case obj.ARET:
// do nothing
}
diff --git a/src/cmd/internal/objabi/funcdata.go b/src/cmd/internal/objabi/funcdata.go
index addbd2ac88b..08b75eb9fed 100644
--- a/src/cmd/internal/objabi/funcdata.go
+++ b/src/cmd/internal/objabi/funcdata.go
@@ -15,11 +15,12 @@ const (
PCDATA_StackMapIndex = 1
PCDATA_InlTreeIndex = 2
- FUNCDATA_ArgsPointerMaps = 0
- FUNCDATA_LocalsPointerMaps = 1
- FUNCDATA_RegPointerMaps = 2
- FUNCDATA_StackObjects = 3
- FUNCDATA_InlTree = 4
+ FUNCDATA_ArgsPointerMaps = 0
+ FUNCDATA_LocalsPointerMaps = 1
+ FUNCDATA_RegPointerMaps = 2
+ FUNCDATA_StackObjects = 3
+ FUNCDATA_InlTree = 4
+ FUNCDATA_OpenCodedDeferInfo = 5
// ArgsSizeUnknown is set in Func.argsize to mark all functions
// whose argument size is unknown (C vararg functions, and
diff --git a/src/cmd/internal/objabi/funcid.go b/src/cmd/internal/objabi/funcid.go
index c13c3cb4589..487f0098307 100644
--- a/src/cmd/internal/objabi/funcid.go
+++ b/src/cmd/internal/objabi/funcid.go
@@ -85,6 +85,12 @@ func GetFuncID(name, file string) FuncID {
return FuncID_panicwrap
case "runtime.handleAsyncEvent":
return FuncID_handleAsyncEvent
+ case "runtime.deferreturn":
+ // Don't show in the call stack (used when invoking defer functions)
+ return FuncID_wrapper
+ case "runtime.runOpenDeferFrame":
+ // Don't show in the call stack (used when invoking defer functions)
+ return FuncID_wrapper
}
if file == "" {
return FuncID_wrapper
diff --git a/src/cmd/internal/objabi/stack.go b/src/cmd/internal/objabi/stack.go
index 62ab0398a66..7320dbf365f 100644
--- a/src/cmd/internal/objabi/stack.go
+++ b/src/cmd/internal/objabi/stack.go
@@ -18,7 +18,7 @@ const (
)
// Initialize StackGuard and StackLimit according to target system.
-var StackGuard = 880*stackGuardMultiplier() + StackSystem
+var StackGuard = 896*stackGuardMultiplier() + StackSystem
var StackLimit = StackGuard - StackSystem - StackSmall
// stackGuardMultiplier returns a multiplier to apply to the default
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 63dcb22d987..4017ea1c79c 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -504,6 +504,11 @@ func (ctxt *Link) loadlib() {
if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
hostArchive(ctxt, p)
}
+ // Link libmsvcrt.a to resolve '__acrt_iob_func' symbol
+ // (see https://golang.org/issue/23649 for details).
+ if p := ctxt.findLibPath("libmsvcrt.a"); p != "none" {
+ hostArchive(ctxt, p)
+ }
// TODO: maybe do something similar to peimporteddlls to collect all lib names
// and try link them all to final exe just like libmingwex.a and libmingw32.a:
/*
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 5cfd1006fe8..008adfbc970 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -11,6 +11,7 @@ import (
"cmd/internal/sys"
"cmd/link/internal/sym"
"encoding/binary"
+ "fmt"
"log"
"os"
"path/filepath"
@@ -255,13 +256,23 @@ func (ctxt *Link) pclntab() {
}
if r.Type.IsDirectJump() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
if ctxt.Arch.Family == sys.Wasm {
- deferreturn = lastWasmAddr
+ deferreturn = lastWasmAddr - 1
} else {
// Note: the relocation target is in the call instruction, but
// is not necessarily the whole instruction (for instance, on
// x86 the relocation applies to bytes [1:5] of the 5 byte call
// instruction).
deferreturn = uint32(r.Off)
+ switch ctxt.Arch.Family {
+ case sys.AMD64, sys.I386:
+ deferreturn--
+ case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64, sys.RISCV64:
+ // no change
+ case sys.S390X:
+ deferreturn -= 2
+ default:
+ panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family))
+ }
}
break // only need one
}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 98305c851e1..f36f6e9740a 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -498,7 +498,8 @@ func (ctxt *Link) symtab() {
case strings.HasPrefix(s.Name, "gcargs."),
strings.HasPrefix(s.Name, "gclocals."),
strings.HasPrefix(s.Name, "gclocals·"),
- strings.HasPrefix(s.Name, "inltree."):
+ strings.HasPrefix(s.Name, "inltree."),
+ strings.HasSuffix(s.Name, ".opendefer"):
s.Type = sym.SGOFUNC
s.Attr |= sym.AttrNotInSymbolTable
s.Outer = symgofunc
diff --git a/src/cmd/link/internal/loadpe/ldpe.go b/src/cmd/link/internal/loadpe/ldpe.go
index f08e1241a71..a41a7901a9a 100644
--- a/src/cmd/link/internal/loadpe/ldpe.go
+++ b/src/cmd/link/internal/loadpe/ldpe.go
@@ -445,9 +445,26 @@ func readpesym(arch *sys.Arch, syms *sym.Symbols, f *pe.File, pesym *pe.COFFSymb
name = sectsyms[f.Sections[pesym.SectionNumber-1]].Name
} else {
name = symname
- name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
- if arch.Family == sys.I386 && name[0] == '_' {
- name = name[1:] // _Name => Name
+ switch arch.Family {
+ case sys.AMD64:
+ if name == "__imp___acrt_iob_func" {
+ // Do not rename __imp___acrt_iob_func into __acrt_iob_func,
+ // becasue __imp___acrt_iob_func symbol is real
+ // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for detials).
+ } else {
+ name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
+ }
+ case sys.I386:
+ if name == "__imp____acrt_iob_func" {
+ // Do not rename __imp____acrt_iob_func into ___acrt_iob_func,
+ // becasue __imp____acrt_iob_func symbol is real
+ // (see commit b295099 from git://git.code.sf.net/p/mingw-w64/mingw-w64 for detials).
+ } else {
+ name = strings.TrimPrefix(name, "__imp_") // __imp_Name => Name
+ }
+ if name[0] == '_' {
+ name = name[1:] // _Name => Name
+ }
}
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
index d41c4e97e32..6e7a76e8c8b 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
@@ -79,7 +79,6 @@ var (
asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
- asmArchAmd64p32 = asmArch{name: "amd64p32", bigEndian: false, stack: "SP", lr: false}
asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
@@ -94,7 +93,6 @@ var (
&asmArchArm,
&asmArchArm64,
&asmArchAmd64,
- &asmArchAmd64p32,
&asmArchMips,
&asmArchMipsLE,
&asmArchMips64,
@@ -635,9 +633,6 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
case "amd64.LEAQ":
dst = 8
addr = true
- case "amd64p32.LEAL":
- dst = 4
- addr = true
default:
switch fn.arch.name {
case "386", "amd64":
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
index f0d7e44c651..b5015380551 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
@@ -766,8 +766,8 @@ var printVerbs = []printVerb{
{'U', "-#", argRune | argInt},
{'v', allFlags, anyType},
{'w', allFlags, argError},
- {'x', sharpNumFlag, argRune | argInt | argString | argPointer},
- {'X', sharpNumFlag, argRune | argInt | argString | argPointer},
+ {'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
+ {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex},
}
// okPrintfArg compares the formatState to the arguments actually present,
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go
index b984ab6c2da..be98143461e 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/validate.go
@@ -9,13 +9,10 @@ import (
// Validate reports an error if any of the analyzers are misconfigured.
// Checks include:
// that the name is a valid identifier;
-// that analyzer names are unique;
// that the Requires graph is acyclic;
// that analyzer fact types are unique;
// that each fact type is a pointer.
func Validate(analyzers []*Analyzer) error {
- names := make(map[string]bool)
-
// Map each fact type to its sole generating analyzer.
factTypes := make(map[reflect.Type]*Analyzer)
@@ -39,10 +36,6 @@ func Validate(analyzers []*Analyzer) error {
if !validIdent(a.Name) {
return fmt.Errorf("invalid analyzer name %q", a)
}
- if names[a.Name] {
- return fmt.Errorf("duplicate analyzer name %q", a)
- }
- names[a.Name] = true
if a.Doc == "" {
return fmt.Errorf("analyzer %q is undocumented", a)
diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt
index 6ed3e16e2a7..12f8740f4da 100644
--- a/src/cmd/vendor/modules.txt
+++ b/src/cmd/vendor/modules.txt
@@ -31,7 +31,7 @@ golang.org/x/crypto/ssh/terminal
## explicit
golang.org/x/sys/unix
golang.org/x/sys/windows
-# golang.org/x/tools v0.0.0-20190925211824-e4ea94538f5b
+# golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5
## explicit
golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/internal/analysisflags
diff --git a/src/cmd/vet/testdata/print/print.go b/src/cmd/vet/testdata/print/print.go
index 7a4783aee4f..fca594925f7 100644
--- a/src/cmd/vet/testdata/print/print.go
+++ b/src/cmd/vet/testdata/print/print.go
@@ -81,8 +81,8 @@ func PrintfTests() {
fmt.Printf("%T %T", 3, i)
fmt.Printf("%U %U", 3, i)
fmt.Printf("%v %v", 3, i)
- fmt.Printf("%x %x %x %x", 3, i, "hi", s)
- fmt.Printf("%X %X %X %X", 3, i, "hi", s)
+ fmt.Printf("%x %x %x %x %x %x %x", 3, i, "hi", s, x, c, fslice)
+ fmt.Printf("%X %X %X %X %X %X %X", 3, i, "hi", s, x, c, fslice)
fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
fmt.Printf("%s", &stringerv)
fmt.Printf("%v", &stringerv)
@@ -125,7 +125,6 @@ func PrintfTests() {
fmt.Printf("%t", 23) // ERROR "Printf format %t has arg 23 of wrong type int"
fmt.Printf("%U", x) // ERROR "Printf format %U has arg x of wrong type float64"
fmt.Printf("%x", nil) // ERROR "Printf format %x has arg nil of wrong type untyped nil"
- fmt.Printf("%X", 2.3) // ERROR "Printf format %X has arg 2.3 of wrong type float64"
fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type .*print.ptrStringer"
fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type .*print.ptrStringer"
fmt.Printf("%s", embeddedStringerv) // ERROR "Printf format %s has arg embeddedStringerv of wrong type .*print.embeddedStringer"
diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go
index 6081ab20f03..01c234e6062 100644
--- a/src/crypto/tls/handshake_test.go
+++ b/src/crypto/tls/handshake_test.go
@@ -17,6 +17,7 @@ import (
"net"
"os"
"os/exec"
+ "runtime"
"strconv"
"strings"
"sync"
@@ -243,19 +244,29 @@ func localServer(l net.Listener) {
}
}
+var isConnRefused = func(err error) bool { return false }
+
func localPipe(t testing.TB) (net.Conn, net.Conn) {
localListener.mu.Lock()
defer localListener.mu.Unlock()
addr := localListener.addr
+ var err error
Dialing:
// We expect a rare mismatch, but probably not 5 in a row.
for i := 0; i < 5; i++ {
tooSlow := time.NewTimer(1 * time.Second)
defer tooSlow.Stop()
- c1, err := net.Dial(addr.Network(), addr.String())
+ var c1 net.Conn
+ c1, err = net.Dial(addr.Network(), addr.String())
if err != nil {
+ if runtime.GOOS == "dragonfly" && isConnRefused(err) {
+ // golang.org/issue/29583: Dragonfly sometimes returned a spurious
+ // ECONNREFUSED.
+ <-tooSlow.C
+ continue
+ }
t.Fatalf("localPipe: %v", err)
}
if localFlakes == 2 && i == 0 {
@@ -279,7 +290,7 @@ Dialing:
}
}
- t.Fatalf("localPipe: failed to connect")
+ t.Fatalf("localPipe: failed to connect: %v", err)
panic("unreachable")
}
diff --git a/src/crypto/tls/handshake_unix_test.go b/src/crypto/tls/handshake_unix_test.go
new file mode 100644
index 00000000000..72718544ae6
--- /dev/null
+++ b/src/crypto/tls/handshake_unix_test.go
@@ -0,0 +1,18 @@
+// Copyright 2019 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.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package tls
+
+import (
+ "errors"
+ "syscall"
+)
+
+func init() {
+ isConnRefused = func(err error) bool {
+ return errors.Is(err, syscall.ECONNREFUSED)
+ }
+}
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
index c8bad642f0e..358fca4705b 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -578,13 +578,13 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
}
if now.Before(c.NotBefore) {
return CertificateInvalidError{
- Cert: c,
+ Cert: c,
Reason: Expired,
Detail: fmt.Sprintf("current time %s is before %s", now.Format(time.RFC3339), c.NotBefore.Format(time.RFC3339)),
}
} else if now.After(c.NotAfter) {
return CertificateInvalidError{
- Cert: c,
+ Cert: c,
Reason: Expired,
Detail: fmt.Sprintf("current time %s is after %s", now.Format(time.RFC3339), c.NotAfter.Format(time.RFC3339)),
}
diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go
index c0371f3e784..a0028be0e57 100644
--- a/src/database/sql/fakedb_test.go
+++ b/src/database/sql/fakedb_test.go
@@ -10,7 +10,6 @@ import (
"errors"
"fmt"
"io"
- "log"
"reflect"
"sort"
"strconv"
@@ -20,8 +19,6 @@ import (
"time"
)
-var _ = log.Printf
-
// fakeDriver is a fake database that implements Go's driver.Driver
// interface, just for testing.
//
diff --git a/src/fmt/scan.go b/src/fmt/scan.go
index 0dab2c98f75..8cab0180ee8 100644
--- a/src/fmt/scan.go
+++ b/src/fmt/scan.go
@@ -940,6 +940,15 @@ const (
uintptrBits = 32 << (^uintptr(0) >> 63)
)
+// scanPercent scans a literal percent character.
+func (s *ss) scanPercent() {
+ s.SkipSpace()
+ s.notEOF()
+ if !s.accept("%") {
+ s.errorString("missing literal %")
+ }
+}
+
// scanOne scans a single value, deriving the scanner from the type of the argument.
func (s *ss) scanOne(verb rune, arg interface{}) {
s.buf = s.buf[:0]
@@ -1203,6 +1212,10 @@ func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err erro
if c != 'c' {
s.SkipSpace()
}
+ if c == '%' {
+ s.scanPercent()
+ continue // Do not consume an argument.
+ }
s.argLimit = s.limit
if f := s.count + s.maxWid; f < s.argLimit {
s.argLimit = f
diff --git a/src/fmt/scan_test.go b/src/fmt/scan_test.go
index b14a6f5deb7..1cc469ce361 100644
--- a/src/fmt/scan_test.go
+++ b/src/fmt/scan_test.go
@@ -318,13 +318,15 @@ var scanfTests = []ScanfTest{
{"%2s", "sssss", &xVal, Xs("ss")},
// Fixed bugs
- {"%d\n", "27\n", &intVal, 27}, // ok
- {"%d\n", "28 \n", &intVal, 28}, // was: "unexpected newline"
- {"%v", "0", &intVal, 0}, // was: "EOF"; 0 was taken as base prefix and not counted.
- {"%v", "0", &uintVal, uint(0)}, // was: "EOF"; 0 was taken as base prefix and not counted.
- {"%c", " ", &uintVal, uint(' ')}, // %c must accept a blank.
- {"%c", "\t", &uintVal, uint('\t')}, // %c must accept any space.
- {"%c", "\n", &uintVal, uint('\n')}, // %c must accept any space.
+ {"%d\n", "27\n", &intVal, 27}, // ok
+ {"%d\n", "28 \n", &intVal, 28}, // was: "unexpected newline"
+ {"%v", "0", &intVal, 0}, // was: "EOF"; 0 was taken as base prefix and not counted.
+ {"%v", "0", &uintVal, uint(0)}, // was: "EOF"; 0 was taken as base prefix and not counted.
+ {"%c", " ", &uintVal, uint(' ')}, // %c must accept a blank.
+ {"%c", "\t", &uintVal, uint('\t')}, // %c must accept any space.
+ {"%c", "\n", &uintVal, uint('\n')}, // %c must accept any space.
+ {"%d%%", "23%\n", &uintVal, uint(23)}, // %% matches literal %.
+ {"%%%d", "%23\n", &uintVal, uint(23)}, // %% matches literal %.
// space handling
{"%d", "27", &intVal, 27},
@@ -467,6 +469,9 @@ var multiTests = []ScanfMultiTest{
{"X%d", "10X", args(&intVal), nil, "input does not match format"},
{"%d%", "42%", args(&intVal), args(42), "missing verb: % at end of format string"},
{"%d% ", "42%", args(&intVal), args(42), "too few operands for format '% '"}, // Slightly odd error, but correct.
+ {"%%%d", "xxx 42", args(&intVal), args(42), "missing literal %"},
+ {"%%%d", "x42", args(&intVal), args(42), "missing literal %"},
+ {"%%%d", "42", args(&intVal), args(42), "missing literal %"},
// Bad UTF-8: should see every byte.
{"%c%c%c", "\xc2X\xc2", args(&r1, &r2, &r3), args(utf8.RuneError, 'X', utf8.RuneError), ""},
diff --git a/src/go.mod b/src/go.mod
index 8d8c89b5fb6..984ec1e572a 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -4,7 +4,7 @@ go 1.14
require (
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
- golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
+ golang.org/x/net v0.0.0-20191021124707-24d2ffbea1e8
golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 // indirect
golang.org/x/text v0.3.2 // indirect
)
diff --git a/src/go.sum b/src/go.sum
index e358118e4cb..89a846d79de 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -2,8 +2,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191021124707-24d2ffbea1e8 h1:L4W1teiyF4Jl6VuapLNV/LYho36udiBQsfbNu7eRMeo=
+golang.org/x/net v0.0.0-20191021124707-24d2ffbea1e8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 h1:2WjIC11WRITGlVWmyLXKjzIVj1ZwoWZ//tadeUUV6/o=
diff --git a/src/go/build/build.go b/src/go/build/build.go
index deeda35c2ac..c763db4f86a 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -773,7 +773,7 @@ Found:
}
var badGoError error
- var Sfiles []string // files with ".S" (capital S)
+ var Sfiles []string // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems)
var firstFile, firstCommentFile string
imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position)
@@ -827,7 +827,7 @@ Found:
case ".s":
p.SFiles = append(p.SFiles, name)
continue
- case ".S":
+ case ".S", ".sx":
Sfiles = append(Sfiles, name)
continue
case ".swig":
@@ -967,7 +967,7 @@ Found:
p.TestImports, p.TestImportPos = cleanImports(testImported)
p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
- // add the .S files only if we are using cgo
+ // add the .S/.sx files only if we are using cgo
// (which means gcc will compile them).
// The standard assemblers expect .s files.
if len(p.CgoFiles) > 0 {
@@ -1274,7 +1274,7 @@ func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binary
}
switch ext {
- case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".swig", ".swigcxx":
+ case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".sx", ".swig", ".swigcxx":
// tentatively okay - read to make sure
case ".syso":
// binary, no reading
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index ba16b652246..3a468d096bc 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -1446,7 +1446,6 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
switch t := unparen(x).(type) {
case *ast.ParenExpr:
panic("unreachable")
- case *ast.UnaryExpr:
case *ast.ArrayType:
if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis {
p.error(len.Pos(), "expected array length, found '...'")
@@ -2439,8 +2438,18 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
var body *ast.BlockStmt
if p.tok == token.LBRACE {
body = p.parseBody(scope)
+ p.expectSemi()
+ } else if p.tok == token.SEMICOLON {
+ p.next()
+ if p.tok == token.LBRACE {
+ // opening { of function declaration on next line
+ p.error(p.pos, "unexpected semicolon or newline before {")
+ body = p.parseBody(scope)
+ p.expectSemi()
+ }
+ } else {
+ p.expectSemi()
}
- p.expectSemi()
decl := &ast.FuncDecl{
Doc: doc,
diff --git a/src/go/parser/testdata/issue34946.src b/src/go/parser/testdata/issue34946.src
new file mode 100644
index 00000000000..6bb15e10c77
--- /dev/null
+++ b/src/go/parser/testdata/issue34946.src
@@ -0,0 +1,22 @@
+// Copyright 2019 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.
+
+// Test case for issue 34946: Better synchronization of
+// parser for function declarations that start their
+// body's opening { on a new line.
+
+package p
+
+// accept Allman/BSD-style declaration but complain
+// (implicit semicolon between signature and body)
+func _() int
+{ /* ERROR "unexpected semicolon or newline before {" */
+ { return 0 }
+}
+
+func _() {}
+
+func _(); { /* ERROR "unexpected semicolon or newline before {" */ }
+
+func _() {}
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 83d40939a87..5c0e611c519 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -311,19 +311,29 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
}
case *Named:
+ // don't touch the type if it is from a different package or the Universe scope
+ // (doing so would lead to a race condition - was issue #35049)
+ if t.obj.pkg != check.pkg {
+ return valid
+ }
+
// don't report a 2nd error if we already know the type is invalid
// (e.g., if a cycle was detected earlier, via Checker.underlying).
if t.underlying == Typ[Invalid] {
t.info = invalid
return invalid
}
+
switch t.info {
case unknown:
t.info = marked
- t.info = check.validType(t.orig, append(path, t.obj))
+ t.info = check.validType(t.orig, append(path, t.obj)) // only types of current package added to path
case marked:
// cycle detected
for i, tn := range path {
+ if t.obj.pkg != check.pkg {
+ panic("internal error: type cycle via package-external type")
+ }
if tn == t.obj {
check.cycleError(path[i:])
t.info = invalid
diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go
index e90071c67cf..49a03ac1e1b 100644
--- a/src/internal/reflectlite/type.go
+++ b/src/internal/reflectlite/type.go
@@ -629,7 +629,7 @@ func (t *funcType) in() []*rtype {
if t.inCount == 0 {
return nil
}
- return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.inCount]
+ return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.inCount:t.inCount]
}
func (t *funcType) out() []*rtype {
@@ -641,7 +641,7 @@ func (t *funcType) out() []*rtype {
if outCount == 0 {
return nil
}
- return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "outCount > 0"))[t.inCount : t.inCount+outCount]
+ return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "outCount > 0"))[t.inCount : t.inCount+outCount : t.inCount+outCount]
}
// add returns p+x.
diff --git a/src/internal/singleflight/singleflight_test.go b/src/internal/singleflight/singleflight_test.go
index 5e6f1b328e0..6404a1775a0 100644
--- a/src/internal/singleflight/singleflight_test.go
+++ b/src/internal/singleflight/singleflight_test.go
@@ -28,7 +28,7 @@ func TestDo(t *testing.T) {
func TestDoErr(t *testing.T) {
var g Group
- someErr := errors.New("Some error")
+ someErr := errors.New("some error")
v, err, _ := g.Do("key", func() (interface{}, error) {
return nil, someErr
})
diff --git a/src/internal/syscall/windows/registry/registry_test.go b/src/internal/syscall/windows/registry/registry_test.go
index c77329cb1e4..7fba960be4a 100644
--- a/src/internal/syscall/windows/registry/registry_test.go
+++ b/src/internal/syscall/windows/registry/registry_test.go
@@ -522,57 +522,75 @@ func TestValues(t *testing.T) {
deleteValues(t, k)
}
+// These are known to be broken due to Windows bugs. See https://golang.org/issue/35084
+var blackListedKeys = map[string]bool{
+ `HKCU\Software\Microsoft\Windows\CurrentVersion\Group Policy\`: true,
+ `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\`: true,
+ `HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Group Policy\`: true,
+ `HKLM\SYSTEM\ControlSet001\`: true,
+ `HKLM\SYSTEM\ControlSet002\`: true,
+ `HKLM\SYSTEM\CurrentControlSet\`: true,
+ `HKLM\SYSTEM\DriverDatabase\`: true,
+ `HKU\`: true, // Rather unfortunate, but SIDs are hard to predict.
+}
+
func walkKey(t *testing.T, k registry.Key, kname string) {
+ if blackListedKeys[kname+`\`] {
+ return
+ }
names, err := k.ReadValueNames(-1)
if err != nil {
- t.Fatalf("reading value names of %s failed: %v", kname, err)
+ t.Fatalf("reading value names of %#q failed: %v", kname+`\`, err)
}
for _, name := range names {
+ if blackListedKeys[kname+`\`+name] {
+ continue
+ }
_, valtype, err := k.GetValue(name, nil)
if err != nil {
- t.Fatalf("reading value type of %s of %s failed: %v", name, kname, err)
+ t.Fatalf("reading value type of %#q in %#q failed: %v", name, kname+`\`, err)
}
switch valtype {
case registry.NONE:
case registry.SZ:
_, _, err := k.GetStringValue(name)
if err != nil {
- t.Error(err)
+ t.Errorf("getting %#q string value in %#q failed: %v", name, kname+`\`, err)
}
case registry.EXPAND_SZ:
s, _, err := k.GetStringValue(name)
if err != nil {
- t.Error(err)
+ t.Errorf("getting %#q expand string value in %#q failed: %v", name, kname+`\`, err)
}
_, err = registry.ExpandString(s)
if err != nil {
- t.Error(err)
+ t.Errorf("expanding %#q value in %#q failed: %v", name, kname+`\`, err)
}
case registry.DWORD, registry.QWORD:
_, _, err := k.GetIntegerValue(name)
if err != nil {
- t.Error(err)
+ t.Errorf("getting %#q integer value in %#q failed: %v", name, kname+`\`, err)
}
case registry.BINARY:
_, _, err := k.GetBinaryValue(name)
if err != nil {
- t.Error(err)
+ t.Errorf("getting %#q binary value in %#q failed: %v", name, kname+`\`, err)
}
case registry.MULTI_SZ:
_, _, err := k.GetStringsValue(name)
if err != nil {
- t.Error(err)
+ t.Errorf("getting %#q strings value in %#q failed: %v", name, kname+`\`, err)
}
case registry.FULL_RESOURCE_DESCRIPTOR, registry.RESOURCE_LIST, registry.RESOURCE_REQUIREMENTS_LIST:
// TODO: not implemented
default:
- t.Fatalf("value type %d of %s of %s failed: %v", valtype, name, kname, err)
+ t.Fatalf("%#q in %#q has unknown value type %d", name, kname+`\`, valtype)
}
}
names, err = k.ReadSubKeyNames(-1)
if err != nil {
- t.Fatalf("reading sub-keys of %s failed: %v", kname, err)
+ t.Fatalf("reading sub-keys of %#q failed: %v", kname+`\`, err)
}
for _, name := range names {
func() {
@@ -582,7 +600,7 @@ func walkKey(t *testing.T, k registry.Key, kname string) {
// ignore error, if we are not allowed to access this key
return
}
- t.Fatalf("opening sub-keys %s of %s failed: %v", name, kname, err)
+ t.Fatalf("opening sub-keys %#q in %#q failed: %v", name, kname+`\`, err)
}
defer subk.Close()
@@ -595,11 +613,11 @@ func TestWalkFullRegistry(t *testing.T) {
if testing.Short() {
t.Skip("skipping long running test in short mode")
}
- walkKey(t, registry.CLASSES_ROOT, "CLASSES_ROOT")
- walkKey(t, registry.CURRENT_USER, "CURRENT_USER")
- walkKey(t, registry.LOCAL_MACHINE, "LOCAL_MACHINE")
- walkKey(t, registry.USERS, "USERS")
- walkKey(t, registry.CURRENT_CONFIG, "CURRENT_CONFIG")
+ walkKey(t, registry.CLASSES_ROOT, "HKCR")
+ walkKey(t, registry.CURRENT_USER, "HKCU")
+ walkKey(t, registry.LOCAL_MACHINE, "HKLM")
+ walkKey(t, registry.USERS, "HKU")
+ walkKey(t, registry.CURRENT_CONFIG, "HKCC")
}
func TestExpandString(t *testing.T) {
diff --git a/src/internal/syscall/windows/registry/value.go b/src/internal/syscall/windows/registry/value.go
index 71d4e15bab1..f8431d2c0f2 100644
--- a/src/internal/syscall/windows/registry/value.go
+++ b/src/internal/syscall/windows/registry/value.go
@@ -108,7 +108,7 @@ func (k Key) GetStringValue(name string) (val string, valtype uint32, err error)
if len(data) == 0 {
return "", typ, nil
}
- u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:]
+ u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
return syscall.UTF16ToString(u), typ, nil
}
diff --git a/src/io/io.go b/src/io/io.go
index 2010770e6a4..9cc3086c19b 100644
--- a/src/io/io.go
+++ b/src/io/io.go
@@ -368,6 +368,9 @@ func Copy(dst Writer, src Reader) (written int64, err error) {
// provided buffer (if one is required) rather than allocating a
// temporary one. If buf is nil, one is allocated; otherwise if it has
// zero length, CopyBuffer panics.
+//
+// If either src implements WriterTo or dst implements ReaderFrom,
+// buf will not be used to perform the copy.
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
if buf != nil && len(buf) == 0 {
panic("empty buffer in io.CopyBuffer")
diff --git a/src/io/ioutil/ioutil_test.go b/src/io/ioutil/ioutil_test.go
index c297847b4e4..4945c65445d 100644
--- a/src/io/ioutil/ioutil_test.go
+++ b/src/io/ioutil/ioutil_test.go
@@ -5,7 +5,9 @@
package ioutil
import (
+ "bytes"
"os"
+ "path/filepath"
"testing"
)
@@ -63,6 +65,35 @@ func TestWriteFile(t *testing.T) {
os.Remove(filename) // ignore error
}
+func TestReadOnlyWriteFile(t *testing.T) {
+ if os.Getuid() == 0 {
+ t.Skipf("Root can write to read-only files anyway, so skip the read-only test.")
+ }
+
+ // We don't want to use TempFile directly, since that opens a file for us as 0600.
+ tempDir, err := TempDir("", t.Name())
+ defer os.RemoveAll(tempDir)
+ filename := filepath.Join(tempDir, "blurp.txt")
+
+ shmorp := []byte("shmorp")
+ florp := []byte("florp")
+ err = WriteFile(filename, shmorp, 0444)
+ if err != nil {
+ t.Fatalf("WriteFile %s: %v", filename, err)
+ }
+ err = WriteFile(filename, florp, 0444)
+ if err == nil {
+ t.Fatalf("Expected an error when writing to read-only file %s", filename)
+ }
+ got, err := ReadFile(filename)
+ if err != nil {
+ t.Fatalf("ReadFile %s: %v", filename, err)
+ }
+ if !bytes.Equal(got, shmorp) {
+ t.Fatalf("want %s, got %s", shmorp, got)
+ }
+}
+
func TestReadDir(t *testing.T) {
dirname := "rumpelstilzchen"
_, err := ReadDir(dirname)
diff --git a/src/log/syslog/syslog_test.go b/src/log/syslog/syslog_test.go
index dd1a1c4988a..8df8ebbf585 100644
--- a/src/log/syslog/syslog_test.go
+++ b/src/log/syslog/syslog_test.go
@@ -134,9 +134,6 @@ func startServer(n, la string, done chan<- string) (addr string, sock io.Closer,
}
func TestWithSimulated(t *testing.T) {
- if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
- t.Skipf("sysctl is not supported on iOS")
- }
t.Parallel()
msg := "Test 123"
var transport []string
@@ -275,9 +272,6 @@ func check(t *testing.T, in, out string) {
}
func TestWrite(t *testing.T) {
- if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
- t.Skipf("sysctl is not supported on iOS")
- }
t.Parallel()
tests := []struct {
pri Priority
diff --git a/src/math/all_test.go b/src/math/all_test.go
index 208c8233e0d..e8fa2b8b66a 100644
--- a/src/math/all_test.go
+++ b/src/math/all_test.go
@@ -2005,6 +2005,64 @@ var logbBC = []float64{
1023,
}
+// Test cases were generated with Berkeley TestFloat-3e/testfloat_gen.
+// http://www.jhauser.us/arithmetic/TestFloat.html.
+// The default rounding mode is selected (nearest/even), and exception flags are ignored.
+var fmaC = []struct{ x, y, z, want float64 }{
+ // Large exponent spread
+ {-3.999999999999087, -1.1123914289620494e-16, -7.999877929687506, -7.999877929687505},
+ {-262112.0000004768, -0.06251525855623184, 1.1102230248837136e-16, 16385.99945072085},
+ {-6.462348523533467e-27, -2.3763644720331857e-211, 4.000000000931324, 4.000000000931324},
+
+ // Effective addition
+ {-2.0000000037252907, 6.7904383376e-313, -3.3951933161e-313, -1.697607001654e-312},
+ {-0.12499999999999999, 512.007568359375, -1.4193627164960366e-16, -64.00094604492188},
+ {-2.7550648847397148e-39, -3.4028301595800694e+38, 0.9960937495343386, 1.9335955376735676},
+ {5.723369164769208e+24, 3.8149300927159385e-06, 1.84489958778182e+19, 4.028324913621874e+19},
+ {-0.4843749999990904, -3.6893487872543293e+19, 9.223653786709391e+18, 2.7093936974938993e+19},
+ {-3.8146972665201165e-06, 4.2949672959999385e+09, -2.2204460489938386e-16, -16384.000003844263},
+ {6.98156394130982e-309, -1.1072962560000002e+09, -4.4414561548793455e-308, -7.73065965765153e-300},
+
+ // Effective subtraction
+ {5e-324, 4.5, -2e-323, 0},
+ {5e-324, 7, -3.5e-323, 0},
+ {5e-324, 0.5000000000000001, -5e-324, Copysign(0, -1)},
+ {-2.1240680525e-314, -1.233647078189316e+308, -0.25781249999954525, -0.25780987964919844},
+ {8.579992955364441e-308, 0.6037391876780558, -4.4501307410480706e-308, 7.29947236107098e-309},
+ {-4.450143471986689e-308, -0.9960937499927239, -4.450419332475649e-308, -1.7659233458788e-310},
+ {1.4932076393918112, -2.2248022430460833e-308, 4.449875571054211e-308, 1.127783865601762e-308},
+
+ // Overflow
+ {-2.288020632214759e+38, -8.98846570988901e+307, 1.7696041796300924e+308, Inf(0)},
+ {1.4888652783208255e+308, -9.007199254742012e+15, -6.807282911929205e+38, Inf(-1)},
+ {9.142703268902826e+192, -1.3504889569802838e+296, -1.9082200803806996e-89, Inf(-1)},
+
+ // Finite x and y, but non-finite z.
+ {31.99218749627471, -1.7976930544991702e+308, Inf(0), Inf(0)},
+ {-1.7976931281784667e+308, -2.0009765625002265, Inf(-1), Inf(-1)},
+
+ // Special
+ {0, 0, 0, 0},
+ {-1.1754226043408471e-38, NaN(), Inf(0), NaN()},
+ {0, 0, 2.22507385643494e-308, 2.22507385643494e-308},
+ {-8.65697792e+09, NaN(), -7.516192799999999e+09, NaN()},
+ {-0.00012207403779029757, 3.221225471996093e+09, NaN(), NaN()},
+ {Inf(-1), 0.1252441407414153, -1.387184532981584e-76, Inf(-1)},
+ {Inf(0), 1.525878907671432e-05, -9.214364835452549e+18, Inf(0)},
+
+ // Random
+ {0.1777916152213626, -32.000015266239636, -2.2204459148334633e-16, -5.689334401293007},
+ {-2.0816681711722314e-16, -0.4997558592585846, -0.9465627129124969, -0.9465627129124968},
+ {-1.9999997615814211, 1.8518819259933516e+19, 16.874999999999996, -3.703763410463646e+19},
+ {-0.12499994039717421, 32767.99999976135, -2.0752587082923246e+19, -2.075258708292325e+19},
+ {7.705600568510257e-34, -1.801432979000528e+16, -0.17224197722973714, -0.17224197722973716},
+ {3.8988133103758913e-308, -0.9848632812499999, 3.893879244098556e-308, 5.40811742605814e-310},
+ {-0.012651981190687427, 6.911985574912436e+38, 6.669240527007144e+18, -8.745031148409496e+36},
+ {4.612811918325842e+18, 1.4901161193847641e-08, 2.6077032311277997e-08, 6.873625395187494e+10},
+ {-9.094947033611148e-13, 4.450691014249257e-308, 2.086006742350485e-308, 2.086006742346437e-308},
+ {-7.751454006381804e-05, 5.588653777189071e-308, -2.2207280111272877e-308, -2.2211612130544025e-308},
+}
+
func tolerance(a, b, e float64) bool {
// Multiplying by e here can underflow denormal values to zero.
// Check a==b so that at least if a and b are small and identical
@@ -2995,6 +3053,15 @@ func TestYn(t *testing.T) {
}
}
+func TestFma(t *testing.T) {
+ for _, c := range fmaC {
+ got := Fma(c.x, c.y, c.z)
+ if !alike(got, c.want) {
+ t.Errorf("Fma(%g,%g,%g) == %g; want %g", c.x, c.y, c.z, got, c.want)
+ }
+ }
+}
+
// Check that math functions of high angle values
// return accurate results. [Since (vf[i] + large) - large != vf[i],
// testing for Trig(vf[i] + large) == Trig(vf[i]), where large is
@@ -3725,3 +3792,11 @@ func BenchmarkFloat32frombits(b *testing.B) {
}
GlobalF = float64(x)
}
+
+func BenchmarkFma(b *testing.B) {
+ x := 0.0
+ for i := 0; i < b.N; i++ {
+ x = Fma(E, Pi, x)
+ }
+ GlobalF = x
+}
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index 22d7a6cac06..3b602320759 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -463,7 +463,8 @@ func (z nat) mul(x, y nat) nat {
// be a larger valid threshold contradicting the assumption about k.
//
if k < n || m != n {
- var t nat
+ tp := getNat(3 * k)
+ t := *tp
// add x0*y1*b
x0 := x0.norm()
@@ -484,6 +485,8 @@ func (z nat) mul(x, y nat) nat {
t = t.mul(xi, y1)
addAt(z, t, i+k)
}
+
+ putNat(tp)
}
return z.norm()
@@ -495,7 +498,9 @@ func (z nat) mul(x, y nat) nat {
// The (non-normalized) result is placed in z.
func basicSqr(z, x nat) {
n := len(x)
- t := make(nat, 2*n) // temporary variable to hold the products
+ tp := getNat(2 * n)
+ t := *tp // temporary variable to hold the products
+ t.clear()
z[1], z[0] = mulWW(x[0], x[0]) // the initial square
for i := 1; i < n; i++ {
d := x[i]
@@ -506,6 +511,7 @@ func basicSqr(z, x nat) {
}
t[2*n-1] = shlVU(t[1:2*n-1], t[1:2*n-1], 1) // double the j < i products
addVV(z, z, t) // combine the result
+ putNat(tp)
}
// karatsubaSqr squares x and leaves the result in z.
@@ -592,7 +598,8 @@ func (z nat) sqr(x nat) nat {
z[2*k:].clear()
if k < n {
- var t nat
+ tp := getNat(2 * k)
+ t := *tp
x0 := x0.norm()
x1 := x[k:]
t = t.mul(x0, x1)
@@ -600,6 +607,7 @@ func (z nat) sqr(x nat) nat {
addAt(z, t, k) // z = 2*x1*x0*b + x0^2
t = t.sqr(x1)
addAt(z, t, 2*k) // z = x1^2*b^2 + 2*x1*x0*b + x0^2
+ putNat(tp)
}
return z.norm()
diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go
index 3c794954dc3..bb5e14b5fa4 100644
--- a/src/math/big/nat_test.go
+++ b/src/math/big/nat_test.go
@@ -206,6 +206,29 @@ func BenchmarkMul(b *testing.B) {
}
}
+func benchmarkNatMul(b *testing.B, nwords int) {
+ x := rndNat(nwords)
+ y := rndNat(nwords)
+ var z nat
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ z.mul(x, y)
+ }
+}
+
+var mulBenchSizes = []int{10, 100, 1000, 10000, 100000}
+
+func BenchmarkNatMul(b *testing.B) {
+ for _, n := range mulBenchSizes {
+ if isRaceBuilder && n > 1e3 {
+ continue
+ }
+ b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
+ benchmarkNatMul(b, n)
+ })
+ }
+}
+
func TestNLZ(t *testing.T) {
var x Word = _B >> 1
for i := 0; i <= _W; i++ {
@@ -681,7 +704,11 @@ func benchmarkNatSqr(b *testing.B, nwords int) {
}
}
-var sqrBenchSizes = []int{1, 2, 3, 5, 8, 10, 20, 30, 50, 80, 100, 200, 300, 500, 800, 1000}
+var sqrBenchSizes = []int{
+ 1, 2, 3, 5, 8, 10, 20, 30, 50, 80,
+ 100, 200, 300, 500, 800,
+ 1000, 10000, 100000,
+}
func BenchmarkNatSqr(b *testing.B) {
for _, n := range sqrBenchSizes {
diff --git a/src/math/big/rat.go b/src/math/big/rat.go
index 841ed3c7843..d35cd4cbd10 100644
--- a/src/math/big/rat.go
+++ b/src/math/big/rat.go
@@ -22,7 +22,9 @@ import (
// of Rats are not supported and may lead to errors.
type Rat struct {
// To make zero values for Rat work w/o initialization,
- // a zero value of b (len(b) == 0) acts like b == 1.
+ // a zero value of b (len(b) == 0) acts like b == 1. At
+ // the earliest opportunity (when an assignment to the Rat
+ // is made), such uninitialized denominators are set to 1.
// a.neg determines the sign of the Rat, b.neg is ignored.
a, b Int
}
@@ -297,6 +299,7 @@ func (x *Rat) Float64() (f float64, exact bool) {
}
// SetFrac sets z to a/b and returns z.
+// If b == 0, SetFrac panics.
func (z *Rat) SetFrac(a, b *Int) *Rat {
z.a.neg = a.neg != b.neg
babs := b.abs
@@ -312,11 +315,12 @@ func (z *Rat) SetFrac(a, b *Int) *Rat {
}
// SetFrac64 sets z to a/b and returns z.
+// If b == 0, SetFrac64 panics.
func (z *Rat) SetFrac64(a, b int64) *Rat {
- z.a.SetInt64(a)
if b == 0 {
panic("division by zero")
}
+ z.a.SetInt64(a)
if b < 0 {
b = -b
z.a.neg = !z.a.neg
@@ -328,21 +332,21 @@ func (z *Rat) SetFrac64(a, b int64) *Rat {
// SetInt sets z to x (by making a copy of x) and returns z.
func (z *Rat) SetInt(x *Int) *Rat {
z.a.Set(x)
- z.b.abs = z.b.abs[:0]
+ z.b.abs = z.b.abs.setWord(1)
return z
}
// SetInt64 sets z to x and returns z.
func (z *Rat) SetInt64(x int64) *Rat {
z.a.SetInt64(x)
- z.b.abs = z.b.abs[:0]
+ z.b.abs = z.b.abs.setWord(1)
return z
}
// SetUint64 sets z to x and returns z.
func (z *Rat) SetUint64(x uint64) *Rat {
z.a.SetUint64(x)
- z.b.abs = z.b.abs[:0]
+ z.b.abs = z.b.abs.setWord(1)
return z
}
@@ -352,6 +356,9 @@ func (z *Rat) Set(x *Rat) *Rat {
z.a.Set(&x.a)
z.b.Set(&x.b)
}
+ if len(z.b.abs) == 0 {
+ z.b.abs = z.b.abs.setWord(1)
+ }
return z
}
@@ -370,20 +377,13 @@ func (z *Rat) Neg(x *Rat) *Rat {
}
// Inv sets z to 1/x and returns z.
+// If x == 0, Inv panics.
func (z *Rat) Inv(x *Rat) *Rat {
if len(x.a.abs) == 0 {
panic("division by zero")
}
z.Set(x)
- a := z.b.abs
- if len(a) == 0 {
- a = a.set(natOne) // materialize numerator (a is part of z!)
- }
- b := z.a.abs
- if b.cmp(natOne) == 0 {
- b = b[:0] // normalize denominator
- }
- z.a.abs, z.b.abs = a, b // sign doesn't change
+ z.a.abs, z.b.abs = z.b.abs, z.a.abs
return z
}
@@ -411,14 +411,19 @@ func (x *Rat) Num() *Int {
}
// Denom returns the denominator of x; it is always > 0.
-// The result is a reference to x's denominator; it
+// The result is a reference to x's denominator, unless
+// x is an uninitialized (zero value) Rat, in which case
+// the result is a new Int of value 1. (To initialize x,
+// any operation that sets x will do, including x.Set(x).)
+// If the result is a reference to x's denominator it
// may change if a new value is assigned to x, and vice versa.
-// If x's denominator is 1, Denom may materialize the denominator, thereby
-// modifying x.
func (x *Rat) Denom() *Int {
x.b.neg = false // the result is always >= 0
if len(x.b.abs) == 0 {
- x.b.abs = x.b.abs.set(natOne) // materialize denominator (see issue #33792)
+ // Note: If this proves problematic, we could
+ // panic instead and require the Rat to
+ // be explicitly initialized.
+ return &Int{abs: nat{1}}
}
return &x.b
}
@@ -426,25 +431,20 @@ func (x *Rat) Denom() *Int {
func (z *Rat) norm() *Rat {
switch {
case len(z.a.abs) == 0:
- // z == 0 - normalize sign and denominator
+ // z == 0; normalize sign and denominator
z.a.neg = false
- z.b.abs = z.b.abs[:0]
+ fallthrough
case len(z.b.abs) == 0:
- // z is normalized int - nothing to do
- case z.b.abs.cmp(natOne) == 0:
- // z is int - normalize denominator
- z.b.abs = z.b.abs[:0]
+ // z is integer; normalize denominator
+ z.b.abs = z.b.abs.setWord(1)
default:
+ // z is fraction; normalize numerator and denominator
neg := z.a.neg
z.a.neg = false
z.b.neg = false
if f := NewInt(0).lehmerGCD(nil, nil, &z.a, &z.b); f.Cmp(intOne) != 0 {
z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs)
z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
- if z.b.abs.cmp(natOne) == 0 {
- // z is int - normalize denominator
- z.b.abs = z.b.abs[:0]
- }
}
z.a.neg = neg
}
@@ -456,6 +456,8 @@ func (z *Rat) norm() *Rat {
// returns z.
func mulDenom(z, x, y nat) nat {
switch {
+ case len(x) == 0 && len(y) == 0:
+ return z.setWord(1)
case len(x) == 0:
return z.set(y)
case len(y) == 0:
@@ -511,10 +513,14 @@ func (z *Rat) Sub(x, y *Rat) *Rat {
// Mul sets z to the product x*y and returns z.
func (z *Rat) Mul(x, y *Rat) *Rat {
if x == y {
- // a squared Rat is positive and can't be reduced
+ // a squared Rat is positive and can't be reduced (no need to call norm())
z.a.neg = false
z.a.abs = z.a.abs.sqr(x.a.abs)
- z.b.abs = z.b.abs.sqr(x.b.abs)
+ if len(x.b.abs) == 0 {
+ z.b.abs = z.b.abs.setWord(1)
+ } else {
+ z.b.abs = z.b.abs.sqr(x.b.abs)
+ }
return z
}
z.a.Mul(&x.a, &y.a)
@@ -523,7 +529,7 @@ func (z *Rat) Mul(x, y *Rat) *Rat {
}
// Quo sets z to the quotient x/y and returns z.
-// If y == 0, a division-by-zero run-time panic occurs.
+// If y == 0, Quo panics.
func (z *Rat) Quo(x, y *Rat) *Rat {
if len(y.a.abs) == 0 {
panic("division by zero")
diff --git a/src/math/big/rat_test.go b/src/math/big/rat_test.go
index 35bc85c8cdc..02569c1b16a 100644
--- a/src/math/big/rat_test.go
+++ b/src/math/big/rat_test.go
@@ -329,18 +329,40 @@ func TestIssue3521(t *testing.T) {
t.Errorf("0) got %s want %s", zero.Denom(), one)
}
- // 1a) a zero value remains zero independent of denominator
- x := new(Rat)
- x.Denom().Set(new(Int).Neg(b))
- if x.Cmp(zero) != 0 {
- t.Errorf("1a) got %s want %s", x, zero)
+ // 1a) the denominator of an (uninitialized) zero value is not shared with the value
+ s := &zero.b
+ d := zero.Denom()
+ if d == s {
+ t.Errorf("1a) got %s (%p) == %s (%p) want different *Int values", d, d, s, s)
}
- // 1b) a zero value may have a denominator != 0 and != 1
+ // 1b) the denominator of an (uninitialized) value is a new 1 each time
+ d1 := zero.Denom()
+ d2 := zero.Denom()
+ if d1 == d2 {
+ t.Errorf("1b) got %s (%p) == %s (%p) want different *Int values", d1, d1, d2, d2)
+ }
+
+ // 1c) the denominator of an initialized zero value is shared with the value
+ x := new(Rat)
+ x.Set(x) // initialize x (any operation that sets x explicitly will do)
+ s = &x.b
+ d = x.Denom()
+ if d != s {
+ t.Errorf("1c) got %s (%p) != %s (%p) want identical *Int values", d, d, s, s)
+ }
+
+ // 1d) a zero value remains zero independent of denominator
+ x.Denom().Set(new(Int).Neg(b))
+ if x.Cmp(zero) != 0 {
+ t.Errorf("1d) got %s want %s", x, zero)
+ }
+
+ // 1e) a zero value may have a denominator != 0 and != 1
x.Num().Set(a)
qab := new(Rat).SetFrac(a, b)
if x.Cmp(qab) != 0 {
- t.Errorf("1b) got %s want %s", x, qab)
+ t.Errorf("1e) got %s want %s", x, qab)
}
// 2a) an integral value becomes a fraction depending on denominator
diff --git a/src/math/fma.go b/src/math/fma.go
new file mode 100644
index 00000000000..76249229b2b
--- /dev/null
+++ b/src/math/fma.go
@@ -0,0 +1,169 @@
+// Copyright 2019 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 math
+
+import "math/bits"
+
+func zero(x uint64) uint64 {
+ if x == 0 {
+ return 1
+ }
+ return 0
+ // branchless:
+ // return ((x>>1 | x&1) - 1) >> 63
+}
+
+func nonzero(x uint64) uint64 {
+ if x != 0 {
+ return 1
+ }
+ return 0
+ // branchless:
+ // return 1 - ((x>>1|x&1)-1)>>63
+}
+
+func shl(u1, u2 uint64, n uint) (r1, r2 uint64) {
+ r1 = u1<>(64-n) | u2<<(n-64)
+ r2 = u2 << n
+ return
+}
+
+func shr(u1, u2 uint64, n uint) (r1, r2 uint64) {
+ r2 = u2>>n | u1<<(64-n) | u1>>(n-64)
+ r1 = u1 >> n
+ return
+}
+
+// shrcompress compresses the bottom n+1 bits of the two-word
+// value into a single bit. the result is equal to the value
+// shifted to the right by n, except the result's 0th bit is
+// set to the bitwise OR of the bottom n+1 bits.
+func shrcompress(u1, u2 uint64, n uint) (r1, r2 uint64) {
+ // TODO: Performance here is really sensitive to the
+ // order/placement of these branches. n == 0 is common
+ // enough to be in the fast path. Perhaps more measurement
+ // needs to be done to find the optimal order/placement?
+ switch {
+ case n == 0:
+ return u1, u2
+ case n == 64:
+ return 0, u1 | nonzero(u2)
+ case n >= 128:
+ return 0, nonzero(u1 | u2)
+ case n < 64:
+ r1, r2 = shr(u1, u2, n)
+ r2 |= nonzero(u2 & (1<> 63)
+ exp = int32(b>>52) & mask
+ mantissa = b & fracMask
+
+ if exp == 0 {
+ // Normalize value if subnormal.
+ shift := uint(bits.LeadingZeros64(mantissa) - 11)
+ mantissa <<= shift
+ exp = 1 - int32(shift)
+ } else {
+ // Add implicit 1 bit
+ mantissa |= 1 << 52
+ }
+ return
+}
+
+// Fma returns x * y + z, computed with only one rounding.
+func Fma(x, y, z float64) float64 {
+ bx, by, bz := Float64bits(x), Float64bits(y), Float64bits(z)
+
+ // Inf or NaN or zero involved. At most one rounding will occur.
+ if x == 0.0 || y == 0.0 || z == 0.0 || bx&uvinf == uvinf || by&uvinf == uvinf {
+ return x*y + z
+ }
+ // Handle non-finite z separately. Evaluating x*y+z where
+ // x and y are finite, but z is infinite, should always result in z.
+ if bz&uvinf == uvinf {
+ return z
+ }
+
+ // Inputs are (sub)normal.
+ // Split x, y, z into sign, exponent, mantissa.
+ xs, xe, xm := split(bx)
+ ys, ye, ym := split(by)
+ zs, ze, zm := split(bz)
+
+ // Compute product p = x*y as sign, exponent, two-word mantissa.
+ // Start with exponent. "is normal" bit isn't subtracted yet.
+ pe := xe + ye - bias + 1
+
+ // pm1:pm2 is the double-word mantissa for the product p.
+ // Shift left to leave top bit in product. Effectively
+ // shifts the 106-bit product to the left by 21.
+ pm1, pm2 := bits.Mul64(xm<<10, ym<<11)
+ zm1, zm2 := zm<<10, uint64(0)
+ ps := xs ^ ys // product sign
+
+ // normalize to 62nd bit
+ is62zero := uint((^pm1 >> 62) & 1)
+ pm1, pm2 = shl(pm1, pm2, is62zero)
+ pe -= int32(is62zero)
+
+ // Swap addition operands so |p| >= |z|
+ if pe < ze || (pe == ze && (pm1 < zm1 || (pm1 == zm1 && pm2 < zm2))) {
+ ps, pe, pm1, pm2, zs, ze, zm1, zm2 = zs, ze, zm1, zm2, ps, pe, pm1, pm2
+ }
+
+ // Align significands
+ zm1, zm2 = shrcompress(zm1, zm2, uint(pe-ze))
+
+ // Compute resulting significands, normalizing if necessary.
+ var m, c uint64
+ if ps == zs {
+ // Adding (pm1:pm2) + (zm1:zm2)
+ pm2, c = bits.Add64(pm2, zm2, 0)
+ pm1, _ = bits.Add64(pm1, zm1, c)
+ pe -= int32(^pm1 >> 63)
+ pm1, m = shrcompress(pm1, pm2, uint(64+pm1>>63))
+ } else {
+ // Subtracting (pm1:pm2) - (zm1:zm2)
+ // TODO: should we special-case cancellation?
+ pm2, c = bits.Sub64(pm2, zm2, 0)
+ pm1, _ = bits.Sub64(pm1, zm1, c)
+ nz := lz(pm1, pm2)
+ pe -= nz
+ m, pm2 = shl(pm1, pm2, uint(nz-1))
+ m |= nonzero(pm2)
+ }
+
+ // Round and break ties to even
+ if pe > 1022+bias || pe == 1022+bias && (m+1<<9)>>63 == 1 {
+ // rounded value overflows exponent range
+ return Float64frombits(uint64(ps)<<63 | uvinf)
+ }
+ if pe < 0 {
+ n := uint(-pe)
+ m = m>>n | nonzero(m&(1<> 10) & ^zero((m&(1<<10-1))^1<<9)
+ pe &= -int32(nonzero(m))
+ return Float64frombits(uint64(ps)<<63 + uint64(pe)<<52 + m)
+}
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 5a8d0e09cad..4569703d129 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -639,9 +639,11 @@ func TestDialerLocalAddr(t *testing.T) {
}
c, err := d.Dial(tt.network, addr)
if err == nil && tt.error != nil || err != nil && tt.error == nil {
- // On Darwin this occasionally times out.
- // We don't know why. Issue #22019.
- if runtime.GOOS == "darwin" && tt.error == nil && os.IsTimeout(err) {
+ // A suspected kernel bug in macOS 10.12 occasionally results in
+ // timeout errors when dialing address ::1. The errors have not
+ // been observed on newer versions of the OS, so we don't plan to work
+ // around them. See https://golang.org/issue/22019.
+ if tt.raddr == "::1" && os.Getenv("GO_BUILDER_NAME") == "darwin-amd64-10_12" && os.IsTimeout(err) {
t.Logf("ignoring timeout error on Darwin; see https://golang.org/issue/22019")
} else {
t.Errorf("%s %v->%s: got %v; want %v", tt.network, tt.laddr, tt.raddr, err, tt.error)
diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go
index d61d77839d3..c3877d70712 100644
--- a/src/net/http/clientserver_test.go
+++ b/src/net/http/clientserver_test.go
@@ -76,6 +76,12 @@ var optQuietLog = func(ts *httptest.Server) {
ts.Config.ErrorLog = quietLog
}
+func optWithServerLog(lg *log.Logger) func(*httptest.Server) {
+ return func(ts *httptest.Server) {
+ ts.Config.ErrorLog = lg
+ }
+}
+
func newClientServerTest(t *testing.T, h2 bool, h Handler, opts ...interface{}) *clientServerTest {
cst := &clientServerTest{
t: t,
diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go
index 91ff544e79f..5c572d6dc5a 100644
--- a/src/net/http/cookie.go
+++ b/src/net/http/cookie.go
@@ -353,6 +353,7 @@ func sanitizeCookieName(n string) string {
return cookieNameSanitizer.Replace(n)
}
+// sanitizeCookieValue produces a suitable cookie-value from v.
// https://tools.ietf.org/html/rfc6265#section-4.1.1
// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
@@ -360,8 +361,8 @@ func sanitizeCookieName(n string) string {
// ; whitespace DQUOTE, comma, semicolon,
// ; and backslash
// We loosen this as spaces and commas are common in cookie values
-// but we produce a quoted cookie-value in when value starts or ends
-// with a comma or space.
+// but we produce a quoted cookie-value if and only if v contains
+// commas or spaces.
// See https://golang.org/issue/7243 for the discussion.
func sanitizeCookieValue(v string) string {
v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
diff --git a/src/net/http/httptest/example_test.go b/src/net/http/httptest/example_test.go
index e3d392130e0..54e77dbb84c 100644
--- a/src/net/http/httptest/example_test.go
+++ b/src/net/http/httptest/example_test.go
@@ -55,6 +55,28 @@ func ExampleServer() {
// Output: Hello, client
}
+func ExampleServer_hTTP2() {
+ ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, "Hello, %s", r.Proto)
+ }))
+ ts.EnableHTTP2 = true
+ ts.StartTLS()
+ defer ts.Close()
+
+ res, err := ts.Client().Get(ts.URL)
+ if err != nil {
+ log.Fatal(err)
+ }
+ greeting, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s", greeting)
+
+ // Output: Hello, HTTP/2.0
+}
+
func ExampleNewTLSServer() {
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, client")
diff --git a/src/net/http/httptest/server.go b/src/net/http/httptest/server.go
index b4e2e9266e6..65165d9eb32 100644
--- a/src/net/http/httptest/server.go
+++ b/src/net/http/httptest/server.go
@@ -27,6 +27,11 @@ type Server struct {
URL string // base URL of form http://ipaddr:port with no trailing slash
Listener net.Listener
+ // EnableHTTP2 controls whether HTTP/2 is enabled
+ // on the server. It must be set between calling
+ // NewUnstartedServer and calling Server.StartTLS.
+ EnableHTTP2 bool
+
// TLS is the optional TLS configuration, populated with a new config
// after TLS is started. If set on an unstarted server before StartTLS
// is called, existing fields are copied into the new config.
@@ -151,7 +156,11 @@ func (s *Server) StartTLS() {
s.TLS = new(tls.Config)
}
if s.TLS.NextProtos == nil {
- s.TLS.NextProtos = []string{"http/1.1"}
+ nextProtos := []string{"http/1.1"}
+ if s.EnableHTTP2 {
+ nextProtos = []string{"h2"}
+ }
+ s.TLS.NextProtos = nextProtos
}
if len(s.TLS.Certificates) == 0 {
s.TLS.Certificates = []tls.Certificate{cert}
@@ -166,6 +175,7 @@ func (s *Server) StartTLS() {
TLSClientConfig: &tls.Config{
RootCAs: certpool,
},
+ ForceAttemptHTTP2: s.EnableHTTP2,
}
s.Listener = tls.NewListener(s.Listener, s.TLS)
s.URL = "https://" + s.Listener.Addr().String()
diff --git a/src/net/http/httptest/server_test.go b/src/net/http/httptest/server_test.go
index 8ab50cdb0ab..0aad15c5ed2 100644
--- a/src/net/http/httptest/server_test.go
+++ b/src/net/http/httptest/server_test.go
@@ -202,3 +202,39 @@ func TestServerZeroValueClose(t *testing.T) {
ts.Close() // tests that it doesn't panic
}
+
+func TestTLSServerWithHTTP2(t *testing.T) {
+ modes := []struct {
+ name string
+ wantProto string
+ }{
+ {"http1", "HTTP/1.1"},
+ {"http2", "HTTP/2.0"},
+ }
+
+ for _, tt := range modes {
+ t.Run(tt.name, func(t *testing.T) {
+ cst := NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("X-Proto", r.Proto)
+ }))
+
+ switch tt.name {
+ case "http2":
+ cst.EnableHTTP2 = true
+ cst.StartTLS()
+ default:
+ cst.Start()
+ }
+
+ defer cst.Close()
+
+ res, err := cst.Client().Get(cst.URL)
+ if err != nil {
+ t.Fatalf("Failed to make request: %v", err)
+ }
+ if g, w := res.Header.Get("X-Proto"), tt.wantProto; g != w {
+ t.Fatalf("X-Proto header mismatch:\n\tgot: %q\n\twant: %q", g, w)
+ }
+ })
+ }
+}
diff --git a/src/net/http/pprof/pprof.go b/src/net/http/pprof/pprof.go
index 35b3285a080..a237f58609c 100644
--- a/src/net/http/pprof/pprof.go
+++ b/src/net/http/pprof/pprof.go
@@ -20,6 +20,9 @@
// log.Println(http.ListenAndServe("localhost:6060", nil))
// }()
//
+// If you are not using DefaultServeMux, you will have to register handlers
+// with the mux you are using.
+//
// Then use the pprof tool to look at the heap profile:
//
// go tool pprof http://localhost:6060/debug/pprof/heap
diff --git a/src/net/http/request.go b/src/net/http/request.go
index 1fdd8a4fc70..72261a1bd5e 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -22,6 +22,7 @@ import (
"net/http/httptrace"
"net/textproto"
"net/url"
+ urlpkg "net/url"
"strconv"
"strings"
"sync"
@@ -850,7 +851,7 @@ func NewRequestWithContext(ctx context.Context, method, url string, body io.Read
if ctx == nil {
return nil, errors.New("net/http: nil Context")
}
- u, err := parseURL(url) // Just url.Parse (url is shadowed for godoc).
+ u, err := urlpkg.Parse(url)
if err != nil {
return nil, err
}
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index d060aa4732d..4c53c95edad 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -29,7 +29,9 @@ import (
"net/url"
"os"
"os/exec"
+ "path/filepath"
"reflect"
+ "regexp"
"runtime"
"runtime/debug"
"sort"
@@ -6266,6 +6268,97 @@ func testContentEncodingNoSniffing(t *testing.T, h2 bool) {
}
}
+// Issue 30803: ensure that TimeoutHandler logs spurious
+// WriteHeader calls, for consistency with other Handlers.
+func TestTimeoutHandlerSuperfluousLogs(t *testing.T) {
+ setParallel(t)
+ defer afterTest(t)
+
+ pc, curFile, _, _ := runtime.Caller(0)
+ curFileBaseName := filepath.Base(curFile)
+ testFuncName := runtime.FuncForPC(pc).Name()
+
+ timeoutMsg := "timed out here!"
+ maxTimeout := 200 * time.Millisecond
+
+ tests := []struct {
+ name string
+ sleepTime time.Duration
+ wantResp string
+ }{
+ {
+ name: "return before timeout",
+ sleepTime: 0,
+ wantResp: "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n",
+ },
+ {
+ name: "return after timeout",
+ sleepTime: maxTimeout * 2,
+ wantResp: fmt.Sprintf("HTTP/1.1 503 Service Unavailable\r\nContent-Length: %d\r\n\r\n%s",
+ len(timeoutMsg), timeoutMsg),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var lastSpuriousLine int32
+
+ sh := HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(404)
+ w.WriteHeader(404)
+ w.WriteHeader(404)
+ w.WriteHeader(404)
+ _, _, line, _ := runtime.Caller(0)
+ atomic.StoreInt32(&lastSpuriousLine, int32(line))
+
+ <-time.After(tt.sleepTime)
+ })
+
+ logBuf := new(bytes.Buffer)
+ srvLog := log.New(logBuf, "", 0)
+ th := TimeoutHandler(sh, maxTimeout, timeoutMsg)
+ cst := newClientServerTest(t, h1Mode /* the test is protocol-agnostic */, th, optWithServerLog(srvLog))
+ defer cst.close()
+
+ res, err := cst.c.Get(cst.ts.URL)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ // Deliberately removing the "Date" header since it is highly ephemeral
+ // and will cause failure if we try to match it exactly.
+ res.Header.Del("Date")
+ res.Header.Del("Content-Type")
+
+ // Match the response.
+ blob, _ := httputil.DumpResponse(res, true)
+ if g, w := string(blob), tt.wantResp; g != w {
+ t.Errorf("Response mismatch\nGot\n%q\n\nWant\n%q", g, w)
+ }
+
+ // Given 4 w.WriteHeader calls, only the first one is valid
+ // and the rest should be reported as the 3 spurious logs.
+ logEntries := strings.Split(strings.TrimSpace(logBuf.String()), "\n")
+ if g, w := len(logEntries), 3; g != w {
+ blob, _ := json.MarshalIndent(logEntries, "", " ")
+ t.Fatalf("Server logs count mismatch\ngot %d, want %d\n\nGot\n%s\n", g, w, blob)
+ }
+
+ // Now ensure that the regexes match exactly.
+ // "http: superfluous response.WriteHeader call from .func\d.\d (:lastSpuriousLine-[1, 3]"
+ for i, logEntry := range logEntries {
+ wantLine := atomic.LoadInt32(&lastSpuriousLine) - 3 + int32(i)
+ pat := fmt.Sprintf("^http: superfluous response.WriteHeader call from %s.func\\d+.\\d+ \\(%s:%d\\)$",
+ testFuncName, curFileBaseName, wantLine)
+ re := regexp.MustCompile(pat)
+ if !re.MatchString(logEntry) {
+ t.Errorf("Log entry mismatch\n\t%s\ndoes not match\n\t%s", logEntry, pat)
+ }
+ }
+ })
+ }
+}
+
// fetchWireResponse is a helper for dialing to host,
// sending http1ReqBody as the payload and retrieving
// the response as it was sent on the wire.
diff --git a/src/net/http/server.go b/src/net/http/server.go
index 5a006c6a678..ff93e59bc06 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -19,6 +19,7 @@ import (
"net"
"net/textproto"
"net/url"
+ urlpkg "net/url"
"os"
"path"
"runtime"
@@ -2065,8 +2066,7 @@ func StripPrefix(prefix string, h Handler) Handler {
// Setting the Content-Type header to any value, including nil,
// disables that behavior.
func Redirect(w ResponseWriter, r *Request, url string, code int) {
- // parseURL is just url.Parse (url is shadowed for godoc).
- if u, err := parseURL(url); err == nil {
+ if u, err := urlpkg.Parse(url); err == nil {
// If url was relative, make its path absolute by
// combining with request path.
// The client would probably do this for us,
@@ -2120,10 +2120,6 @@ func Redirect(w ResponseWriter, r *Request, url string, code int) {
}
}
-// parseURL is just url.Parse. It exists only so that url.Parse can be called
-// in places where url is shadowed for godoc. See https://golang.org/cl/49930.
-var parseURL = url.Parse
-
var htmlReplacer = strings.NewReplacer(
"&", "&",
"<", "<",
@@ -3227,8 +3223,9 @@ func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) {
r = r.WithContext(ctx)
done := make(chan struct{})
tw := &timeoutWriter{
- w: w,
- h: make(Header),
+ w: w,
+ h: make(Header),
+ req: r,
}
panicChan := make(chan interface{}, 1)
go func() {
@@ -3268,6 +3265,7 @@ type timeoutWriter struct {
w ResponseWriter
h Header
wbuf bytes.Buffer
+ req *Request
mu sync.Mutex
timedOut bool
@@ -3294,24 +3292,32 @@ func (tw *timeoutWriter) Write(p []byte) (int, error) {
return 0, ErrHandlerTimeout
}
if !tw.wroteHeader {
- tw.writeHeader(StatusOK)
+ tw.writeHeaderLocked(StatusOK)
}
return tw.wbuf.Write(p)
}
-func (tw *timeoutWriter) WriteHeader(code int) {
+func (tw *timeoutWriter) writeHeaderLocked(code int) {
checkWriteHeaderCode(code)
- tw.mu.Lock()
- defer tw.mu.Unlock()
- if tw.timedOut || tw.wroteHeader {
+
+ switch {
+ case tw.timedOut:
return
+ case tw.wroteHeader:
+ if tw.req != nil {
+ caller := relevantCaller()
+ logf(tw.req, "http: superfluous response.WriteHeader call from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line)
+ }
+ default:
+ tw.wroteHeader = true
+ tw.code = code
}
- tw.writeHeader(code)
}
-func (tw *timeoutWriter) writeHeader(code int) {
- tw.wroteHeader = true
- tw.code = code
+func (tw *timeoutWriter) WriteHeader(code int) {
+ tw.mu.Lock()
+ defer tw.mu.Unlock()
+ tw.writeHeaderLocked(code)
}
// onceCloseListener wraps a net.Listener, protecting it from
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index af48eaa9f05..c2880a04cfb 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -469,10 +469,12 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
if isHTTP {
for k, vv := range req.Header {
if !httpguts.ValidHeaderFieldName(k) {
+ req.closeBody()
return nil, fmt.Errorf("net/http: invalid header field name %q", k)
}
for _, v := range vv {
if !httpguts.ValidHeaderFieldValue(v) {
+ req.closeBody()
return nil, fmt.Errorf("net/http: invalid header field value %q for key %v", v, k)
}
}
@@ -492,6 +494,7 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
return nil, &badStringError{"unsupported protocol scheme", scheme}
}
if req.Method != "" && !validMethod(req.Method) {
+ req.closeBody()
return nil, fmt.Errorf("net/http: invalid method %q", req.Method)
}
if req.URL.Host == "" {
@@ -537,10 +540,15 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
if err == nil {
return resp, nil
}
- if http2isNoCachedConnError(err) {
+
+ // Failed. Clean up and determine whether to retry.
+
+ _, isH2DialError := pconn.alt.(http2erringRoundTripper)
+ if http2isNoCachedConnError(err) || isH2DialError {
t.removeIdleConn(pconn)
t.decConnsPerHost(pconn.cacheKey)
- } else if !pconn.shouldRetryRequest(req, err) {
+ }
+ if !pconn.shouldRetryRequest(req, err) {
// Issue 16465: return underlying net.Conn.Read error from peek,
// as we've historically done.
if e, ok := err.(transportReadFromServerError); ok {
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index d7eef0d94cc..f76530b8faa 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -5719,3 +5719,173 @@ func TestInvalidHeaderResponse(t *testing.T) {
t.Errorf(`bad "Foo " header value: %q, want %q`, v, "bar")
}
}
+
+type bodyCloser bool
+
+func (bc *bodyCloser) Close() error {
+ *bc = true
+ return nil
+}
+func (bc *bodyCloser) Read(b []byte) (n int, err error) {
+ return 0, io.EOF
+}
+
+// Issue 35015: ensure that Transport closes the body on any error
+// with an invalid request, as promised by Client.Do docs.
+func TestTransportClosesBodyOnInvalidRequests(t *testing.T) {
+ cst := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ t.Errorf("Should not have been invoked")
+ }))
+ defer cst.Close()
+
+ u, _ := url.Parse(cst.URL)
+
+ tests := []struct {
+ name string
+ req *Request
+ wantErr string
+ }{
+ {
+ name: "invalid method",
+ req: &Request{
+ Method: " ",
+ URL: u,
+ },
+ wantErr: "invalid method",
+ },
+ {
+ name: "nil URL",
+ req: &Request{
+ Method: "GET",
+ },
+ wantErr: "nil Request.URL",
+ },
+ {
+ name: "invalid header key",
+ req: &Request{
+ Method: "GET",
+ Header: Header{"💡": {"emoji"}},
+ URL: u,
+ },
+ wantErr: "invalid header field name",
+ },
+ {
+ name: "invalid header value",
+ req: &Request{
+ Method: "POST",
+ Header: Header{"key": {"\x19"}},
+ URL: u,
+ },
+ wantErr: "invalid header field value",
+ },
+ {
+ name: "non HTTP(s) scheme",
+ req: &Request{
+ Method: "POST",
+ URL: &url.URL{Scheme: "faux"},
+ },
+ wantErr: "unsupported protocol scheme",
+ },
+ {
+ name: "no Host in URL",
+ req: &Request{
+ Method: "POST",
+ URL: &url.URL{Scheme: "http"},
+ },
+ wantErr: "no Host",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var bc bodyCloser
+ req := tt.req
+ req.Body = &bc
+ _, err := DefaultClient.Do(tt.req)
+ if err == nil {
+ t.Fatal("Expected an error")
+ }
+ if !bc {
+ t.Fatal("Expected body to have been closed")
+ }
+ if g, w := err.Error(), tt.wantErr; !strings.Contains(g, w) {
+ t.Fatalf("Error mismatch\n\t%q\ndoes not contain\n\t%q", g, w)
+ }
+ })
+ }
+}
+
+// breakableConn is a net.Conn wrapper with a Write method
+// that will fail when its brokenState is true.
+type breakableConn struct {
+ net.Conn
+ *brokenState
+}
+
+type brokenState struct {
+ sync.Mutex
+ broken bool
+}
+
+func (w *breakableConn) Write(b []byte) (n int, err error) {
+ w.Lock()
+ defer w.Unlock()
+ if w.broken {
+ return 0, errors.New("some write error")
+ }
+ return w.Conn.Write(b)
+}
+
+// Issue 34978: don't cache a broken HTTP/2 connection
+func TestDontCacheBrokenHTTP2Conn(t *testing.T) {
+ cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {}), optQuietLog)
+ defer cst.close()
+
+ var brokenState brokenState
+
+ cst.tr.Dial = func(netw, addr string) (net.Conn, error) {
+ c, err := net.Dial(netw, addr)
+ if err != nil {
+ t.Errorf("unexpected Dial error: %v", err)
+ return nil, err
+ }
+ return &breakableConn{c, &brokenState}, err
+ }
+
+ const numReqs = 5
+ var gotConns uint32 // atomic
+ for i := 1; i <= numReqs; i++ {
+ brokenState.Lock()
+ brokenState.broken = false
+ brokenState.Unlock()
+
+ // doBreak controls whether we break the TCP connection after the TLS
+ // handshake (before the HTTP/2 handshake). We test a few failures
+ // in a row followed by a final success.
+ doBreak := i != numReqs
+
+ ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
+ GotConn: func(info httptrace.GotConnInfo) {
+ atomic.AddUint32(&gotConns, 1)
+ },
+ TLSHandshakeDone: func(cfg tls.ConnectionState, err error) {
+ brokenState.Lock()
+ defer brokenState.Unlock()
+ if doBreak {
+ brokenState.broken = true
+ }
+ },
+ })
+ req, err := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = cst.c.Do(req)
+ if doBreak != (err != nil) {
+ t.Errorf("for iteration %d, doBreak=%v; unexpected error %v", i, doBreak, err)
+ }
+ }
+ if got, want := atomic.LoadUint32(&gotConns), 1; int(got) != want {
+ t.Errorf("GotConn calls = %v; want %v", got, want)
+ }
+}
diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index 4163fa9d9a9..b2ef21e8ac2 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -8,7 +8,6 @@ package net
import (
"fmt"
- "internal/testenv"
"reflect"
"runtime"
"testing"
@@ -51,20 +50,7 @@ func ipv6LinkLocalUnicastAddr(ifi *Interface) string {
return ""
}
-func condSkipInterfaceTest(t *testing.T) {
- t.Helper()
- switch runtime.GOOS {
- case "darwin":
- if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
- t.Skipf("sysctl is not supported on iOS")
- }
- case "dragonfly":
- testenv.SkipFlaky(t, 34368)
- }
-}
-
func TestInterfaces(t *testing.T) {
- condSkipInterfaceTest(t)
ift, err := Interfaces()
if err != nil {
t.Fatal(err)
@@ -96,7 +82,6 @@ func TestInterfaces(t *testing.T) {
}
func TestInterfaceAddrs(t *testing.T) {
- condSkipInterfaceTest(t)
ift, err := Interfaces()
if err != nil {
t.Fatal(err)
@@ -116,7 +101,6 @@ func TestInterfaceAddrs(t *testing.T) {
}
func TestInterfaceUnicastAddrs(t *testing.T) {
- condSkipInterfaceTest(t)
ift, err := Interfaces()
if err != nil {
t.Fatal(err)
@@ -144,7 +128,6 @@ func TestInterfaceUnicastAddrs(t *testing.T) {
}
func TestInterfaceMulticastAddrs(t *testing.T) {
- condSkipInterfaceTest(t)
ift, err := Interfaces()
if err != nil {
t.Fatal(err)
diff --git a/src/net/server_test.go b/src/net/server_test.go
index 1608bebb00c..2673b87718c 100644
--- a/src/net/server_test.go
+++ b/src/net/server_test.go
@@ -56,71 +56,79 @@ func TestTCPServer(t *testing.T) {
const N = 3
for i, tt := range tcpServerTests {
- if !testableListenArgs(tt.snet, tt.saddr, tt.taddr) {
- t.Logf("skipping %s test", tt.snet+" "+tt.saddr+"<-"+tt.taddr)
- continue
- }
+ t.Run(tt.snet+" "+tt.saddr+"<-"+tt.taddr, func(t *testing.T) {
+ if !testableListenArgs(tt.snet, tt.saddr, tt.taddr) {
+ t.Skip("not testable")
+ }
- ln, err := Listen(tt.snet, tt.saddr)
- if err != nil {
- if perr := parseDialError(err); perr != nil {
- t.Error(perr)
- }
- t.Fatal(err)
- }
-
- var lss []*localServer
- var tpchs []chan error
- defer func() {
- for _, ls := range lss {
- ls.teardown()
- }
- }()
- for i := 0; i < N; i++ {
- ls, err := (&streamListener{Listener: ln}).newLocalServer()
- if err != nil {
- t.Fatal(err)
- }
- lss = append(lss, ls)
- tpchs = append(tpchs, make(chan error, 1))
- }
- for i := 0; i < N; i++ {
- ch := tpchs[i]
- handler := func(ls *localServer, ln Listener) { transponder(ln, ch) }
- if err := lss[i].buildup(handler); err != nil {
- t.Fatal(err)
- }
- }
-
- var trchs []chan error
- for i := 0; i < N; i++ {
- _, port, err := SplitHostPort(lss[i].Listener.Addr().String())
- if err != nil {
- t.Fatal(err)
- }
- d := Dialer{Timeout: someTimeout}
- c, err := d.Dial(tt.tnet, JoinHostPort(tt.taddr, port))
+ ln, err := Listen(tt.snet, tt.saddr)
if err != nil {
if perr := parseDialError(err); perr != nil {
t.Error(perr)
}
t.Fatal(err)
}
- defer c.Close()
- trchs = append(trchs, make(chan error, 1))
- go transceiver(c, []byte("TCP SERVER TEST"), trchs[i])
- }
- for _, ch := range trchs {
- for err := range ch {
- t.Errorf("#%d: %v", i, err)
+ var lss []*localServer
+ var tpchs []chan error
+ defer func() {
+ for _, ls := range lss {
+ ls.teardown()
+ }
+ }()
+ for i := 0; i < N; i++ {
+ ls, err := (&streamListener{Listener: ln}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ lss = append(lss, ls)
+ tpchs = append(tpchs, make(chan error, 1))
}
- }
- for _, ch := range tpchs {
- for err := range ch {
- t.Errorf("#%d: %v", i, err)
+ for i := 0; i < N; i++ {
+ ch := tpchs[i]
+ handler := func(ls *localServer, ln Listener) { transponder(ln, ch) }
+ if err := lss[i].buildup(handler); err != nil {
+ t.Fatal(err)
+ }
}
- }
+
+ var trchs []chan error
+ for i := 0; i < N; i++ {
+ _, port, err := SplitHostPort(lss[i].Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ d := Dialer{Timeout: someTimeout}
+ c, err := d.Dial(tt.tnet, JoinHostPort(tt.taddr, port))
+ if err != nil {
+ if perr := parseDialError(err); perr != nil {
+ t.Error(perr)
+ }
+ if tt.taddr == "::1" && os.Getenv("GO_BUILDER_NAME") == "darwin-amd64-10_12" && os.IsTimeout(err) {
+ // A suspected kernel bug in macOS 10.12 occasionally results in
+ // "i/o timeout" errors when dialing address ::1. The errors have not
+ // been observed on newer versions of the OS, so we don't plan to work
+ // around them. See https://golang.org/issue/32919.
+ t.Skipf("skipping due to error on known-flaky macOS 10.12 builder: %v", err)
+ }
+ t.Fatal(err)
+ }
+ defer c.Close()
+ trchs = append(trchs, make(chan error, 1))
+ go transceiver(c, []byte("TCP SERVER TEST"), trchs[i])
+ }
+
+ for _, ch := range trchs {
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+ for _, ch := range tpchs {
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+ })
}
}
diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go
index 60a8d0228b3..19d2111743e 100644
--- a/src/os/exec/exec_test.go
+++ b/src/os/exec/exec_test.go
@@ -453,13 +453,13 @@ var testedAlreadyLeaked = false
// basefds returns the number of expected file descriptors
// to be present in a process at start.
-// stdin, stdout, stderr, epoll/kqueue, maybe testlog
+// stdin, stdout, stderr, epoll/kqueue, epoll/kqueue pipe, maybe testlog
func basefds() uintptr {
n := os.Stderr.Fd() + 1
// The poll (epoll/kqueue) descriptor can be numerically
// either between stderr and the testlog-fd, or after
// testlog-fd.
- if poll.IsPollDescriptor(n) {
+ for poll.IsPollDescriptor(n) {
n++
}
for _, arg := range os.Args {
diff --git a/src/os/os_test.go b/src/os/os_test.go
index ae6bad1fee9..93ac7adfa1b 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -1515,9 +1515,6 @@ func testWindowsHostname(t *testing.T, hostname string) {
}
func TestHostname(t *testing.T) {
- if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
- t.Skipf("sysctl is not supported on iOS")
- }
hostname, err := Hostname()
if err != nil {
t.Fatal(err)
diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go
index d50e595d84d..7aa3d7805b2 100644
--- a/src/os/signal/signal_test.go
+++ b/src/os/signal/signal_test.go
@@ -469,3 +469,52 @@ func atomicStopTestProgram() {
os.Exit(0)
}
+
+func TestTime(t *testing.T) {
+ // Test that signal works fine when we are in a call to get time,
+ // which on some platforms is using VDSO. See issue #34391.
+ dur := 3 * time.Second
+ if testing.Short() {
+ dur = 100 * time.Millisecond
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ done := make(chan bool)
+ finished := make(chan bool)
+ go func() {
+ sig := make(chan os.Signal, 1)
+ Notify(sig, syscall.SIGUSR1)
+ defer Stop(sig)
+ Loop:
+ for {
+ select {
+ case <-sig:
+ case <-done:
+ break Loop
+ }
+ }
+ finished <- true
+ }()
+ go func() {
+ Loop:
+ for {
+ select {
+ case <-done:
+ break Loop
+ default:
+ syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
+ runtime.Gosched()
+ }
+ }
+ finished <- true
+ }()
+ t0 := time.Now()
+ for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
+ } // hammering on getting time
+ close(done)
+ <-finished
+ <-finished
+ // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
+ // into subsequent TestSignal() causing failure.
+ // Sleep for a while to reduce the possibility of the failure.
+ time.Sleep(10 * time.Millisecond)
+}
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index 1c785701100..de426b58a8a 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -36,11 +36,14 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr,
if ft.kind&kindGCProg != 0 {
panic("can't handle gc programs")
}
- gcdata := (*[1000]byte)(unsafe.Pointer(ft.gcdata))
- for i := uintptr(0); i < ft.ptrdata/ptrSize; i++ {
- gc = append(gc, gcdata[i/8]>>(i%8)&1)
- }
ptrs = ft.ptrdata != 0
+ if ptrs {
+ nptrs := ft.ptrdata / ptrSize
+ gcdata := ft.gcSlice(0, (nptrs+7)/8)
+ for i := uintptr(0); i < nptrs; i++ {
+ gc = append(gc, gcdata[i/8]>>(i%8)&1)
+ }
+ }
return
}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 231b38b898a..06ca09576d3 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1008,7 +1008,7 @@ func (t *funcType) in() []*rtype {
if t.inCount == 0 {
return nil
}
- return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.inCount]
+ return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.inCount:t.inCount]
}
func (t *funcType) out() []*rtype {
@@ -1020,7 +1020,7 @@ func (t *funcType) out() []*rtype {
if outCount == 0 {
return nil
}
- return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "outCount > 0"))[t.inCount : t.inCount+outCount]
+ return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "outCount > 0"))[t.inCount : t.inCount+outCount : t.inCount+outCount]
}
// add returns p+x.
@@ -2178,34 +2178,12 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
base := bucketSize / ptrSize
if ktyp.ptrdata != 0 {
- if ktyp.kind&kindGCProg != 0 {
- panic("reflect: unexpected GC program in MapOf")
- }
- kmask := (*[16]byte)(unsafe.Pointer(ktyp.gcdata))
- for i := uintptr(0); i < ktyp.ptrdata/ptrSize; i++ {
- if (kmask[i/8]>>(i%8))&1 != 0 {
- for j := uintptr(0); j < bucketSize; j++ {
- word := base + j*ktyp.size/ptrSize + i
- mask[word/8] |= 1 << (word % 8)
- }
- }
- }
+ emitGCMask(mask, base, ktyp, bucketSize)
}
base += bucketSize * ktyp.size / ptrSize
if etyp.ptrdata != 0 {
- if etyp.kind&kindGCProg != 0 {
- panic("reflect: unexpected GC program in MapOf")
- }
- emask := (*[16]byte)(unsafe.Pointer(etyp.gcdata))
- for i := uintptr(0); i < etyp.ptrdata/ptrSize; i++ {
- if (emask[i/8]>>(i%8))&1 != 0 {
- for j := uintptr(0); j < bucketSize; j++ {
- word := base + j*etyp.size/ptrSize + i
- mask[word/8] |= 1 << (word % 8)
- }
- }
- }
+ emitGCMask(mask, base, etyp, bucketSize)
}
base += bucketSize * etyp.size / ptrSize
base += overflowPad / ptrSize
@@ -2236,6 +2214,55 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
return b
}
+func (t *rtype) gcSlice(begin, end uintptr) []byte {
+ return (*[1 << 30]byte)(unsafe.Pointer(t.gcdata))[begin:end:end]
+}
+
+// emitGCMask writes the GC mask for [n]typ into out, starting at bit
+// offset base.
+func emitGCMask(out []byte, base uintptr, typ *rtype, n uintptr) {
+ if typ.kind&kindGCProg != 0 {
+ panic("reflect: unexpected GC program")
+ }
+ ptrs := typ.ptrdata / ptrSize
+ words := typ.size / ptrSize
+ mask := typ.gcSlice(0, (ptrs+7)/8)
+ for j := uintptr(0); j < ptrs; j++ {
+ if (mask[j/8]>>(j%8))&1 != 0 {
+ for i := uintptr(0); i < n; i++ {
+ k := base + i*words + j
+ out[k/8] |= 1 << (k % 8)
+ }
+ }
+ }
+}
+
+// appendGCProg appends the GC program for the first ptrdata bytes of
+// typ to dst and returns the extended slice.
+func appendGCProg(dst []byte, typ *rtype) []byte {
+ if typ.kind&kindGCProg != 0 {
+ // Element has GC program; emit one element.
+ n := uintptr(*(*uint32)(unsafe.Pointer(typ.gcdata)))
+ prog := typ.gcSlice(4, 4+n-1)
+ return append(dst, prog...)
+ }
+
+ // Element is small with pointer mask; use as literal bits.
+ ptrs := typ.ptrdata / ptrSize
+ mask := typ.gcSlice(0, (ptrs+7)/8)
+
+ // Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
+ for ; ptrs > 120; ptrs -= 120 {
+ dst = append(dst, 120)
+ dst = append(dst, mask[:15]...)
+ mask = mask[15:]
+ }
+
+ dst = append(dst, byte(ptrs))
+ dst = append(dst, mask...)
+ return dst
+}
+
// SliceOf returns the slice type with element type t.
// For example, if t represents int, SliceOf(t) represents []int.
func SliceOf(t Type) Type {
@@ -2666,25 +2693,7 @@ func StructOf(fields []StructField) Type {
off = ft.offset()
}
- elemGC := (*[1 << 30]byte)(unsafe.Pointer(ft.typ.gcdata))[:]
- elemPtrs := ft.typ.ptrdata / ptrSize
- if ft.typ.kind&kindGCProg == 0 {
- // Element is small with pointer mask; use as literal bits.
- mask := elemGC
- // Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
- var n uintptr
- for n = elemPtrs; n > 120; n -= 120 {
- prog = append(prog, 120)
- prog = append(prog, mask[:15]...)
- mask = mask[15:]
- }
- prog = append(prog, byte(n))
- prog = append(prog, mask[:(n+7)/8]...)
- } else {
- // Element has GC program; emit one element.
- elemProg := elemGC[4 : 4+*(*uint32)(unsafe.Pointer(&elemGC[0]))-1]
- prog = append(prog, elemProg...)
- }
+ prog = appendGCProg(prog, ft.typ)
off += ft.typ.ptrdata
}
prog = append(prog, 0)
@@ -2850,42 +2859,16 @@ func ArrayOf(count int, elem Type) Type {
// Create direct pointer mask by turning each 1 bit in elem
// into count 1 bits in larger mask.
mask := make([]byte, (array.ptrdata/ptrSize+7)/8)
- elemMask := (*[1 << 30]byte)(unsafe.Pointer(typ.gcdata))[:]
- elemWords := typ.size / ptrSize
- for j := uintptr(0); j < typ.ptrdata/ptrSize; j++ {
- if (elemMask[j/8]>>(j%8))&1 != 0 {
- for i := uintptr(0); i < array.len; i++ {
- k := i*elemWords + j
- mask[k/8] |= 1 << (k % 8)
- }
- }
- }
+ emitGCMask(mask, 0, typ, array.len)
array.gcdata = &mask[0]
default:
// Create program that emits one element
// and then repeats to make the array.
prog := []byte{0, 0, 0, 0} // will be length of prog
- elemGC := (*[1 << 30]byte)(unsafe.Pointer(typ.gcdata))[:]
- elemPtrs := typ.ptrdata / ptrSize
- if typ.kind&kindGCProg == 0 {
- // Element is small with pointer mask; use as literal bits.
- mask := elemGC
- // Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
- var n uintptr
- for n = elemPtrs; n > 120; n -= 120 {
- prog = append(prog, 120)
- prog = append(prog, mask[:15]...)
- mask = mask[15:]
- }
- prog = append(prog, byte(n))
- prog = append(prog, mask[:(n+7)/8]...)
- } else {
- // Element has GC program; emit one element.
- elemProg := elemGC[4 : 4+*(*uint32)(unsafe.Pointer(&elemGC[0]))-1]
- prog = append(prog, elemProg...)
- }
+ prog = appendGCProg(prog, typ)
// Pad from ptrdata to size.
+ elemPtrs := typ.ptrdata / ptrSize
elemWords := typ.size / ptrSize
if elemPtrs < elemWords {
// Emit literal 0 bit, then repeat as needed.
diff --git a/src/runtime/callers_test.go b/src/runtime/callers_test.go
index fcfd10deffe..eee1d5c867f 100644
--- a/src/runtime/callers_test.go
+++ b/src/runtime/callers_test.go
@@ -188,3 +188,32 @@ func TestCallersDivZeroPanic(t *testing.T) {
t.Fatal("did not see divide-by-sizer panic")
}
}
+
+func TestCallersDeferNilFuncPanic(t *testing.T) {
+ // Make sure we don't have any extra frames on the stack. We cut off the check
+ // at runtime.sigpanic, because non-open-coded defers (which may be used in
+ // non-opt or race checker mode) include an extra 'jmpdefer' frame (which is
+ // where the nil pointer deref happens). We could consider hiding jmpdefer in
+ // tracebacks.
+ state := 1
+ want := []string{"runtime.Callers", "runtime_test.TestCallersDeferNilFuncPanic.func1",
+ "runtime.gopanic", "runtime.panicmem", "runtime.sigpanic"}
+
+ defer func() {
+ if r := recover(); r == nil {
+ t.Fatal("did not panic")
+ }
+ pcs := make([]uintptr, 20)
+ pcs = pcs[:runtime.Callers(0, pcs)]
+ testCallersEqual(t, pcs, want)
+ if state == 1 {
+ t.Fatal("nil defer func panicked at defer time rather than function exit time")
+ }
+
+ }()
+ var f func()
+ defer f()
+ // Use the value of 'state' to make sure nil defer func f causes panic at
+ // function exit, rather than at the defer statement.
+ state = 2
+}
diff --git a/src/runtime/checkptr.go b/src/runtime/checkptr.go
index a6d33c5af10..3c6a40206fc 100644
--- a/src/runtime/checkptr.go
+++ b/src/runtime/checkptr.go
@@ -6,34 +6,50 @@ package runtime
import "unsafe"
-type ptrAlign struct {
+type ptrAlignError struct {
ptr unsafe.Pointer
elem *_type
+ n uintptr
}
-func checkptrAlignment(p unsafe.Pointer, elem *_type) {
- // Check that (*T)(p) is appropriately aligned.
+func (e ptrAlignError) RuntimeError() {}
+
+func (e ptrAlignError) Error() string {
+ return "runtime error: unsafe pointer conversion"
+}
+
+func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
+ // Check that (*[n]elem)(p) is appropriately aligned.
// TODO(mdempsky): What about fieldAlign?
if uintptr(p)&(uintptr(elem.align)-1) != 0 {
- panic(ptrAlign{p, elem})
+ panic(ptrAlignError{p, elem, n})
}
- // Check that (*T)(p) doesn't straddle multiple heap objects.
- if elem.size != 1 && checkptrBase(p) != checkptrBase(add(p, elem.size-1)) {
- panic(ptrAlign{p, elem})
+ // Check that (*[n]elem)(p) doesn't straddle multiple heap objects.
+ if size := n * elem.size; size > 1 && checkptrBase(p) != checkptrBase(add(p, size-1)) {
+ panic(ptrAlignError{p, elem, n})
}
}
-type ptrArith struct {
+type ptrArithError struct {
ptr unsafe.Pointer
originals []unsafe.Pointer
}
+func (e ptrArithError) RuntimeError() {}
+
+func (e ptrArithError) Error() string {
+ return "runtime error: unsafe pointer arithmetic"
+}
+
func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
if 0 < uintptr(p) && uintptr(p) < minLegalPointer {
- panic(ptrArith{p, originals})
+ panic(ptrArithError{p, originals})
}
+ // Check that if the computed pointer p points into a heap
+ // object, then one of the original pointers must have pointed
+ // into the same object.
base := checkptrBase(p)
if base == 0 {
return
@@ -45,12 +61,44 @@ func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
}
}
- panic(ptrArith{p, originals})
+ panic(ptrArithError{p, originals})
}
+// checkptrBase returns the base address for the allocation containing
+// the address p.
+//
+// Importantly, if p1 and p2 point into the same variable, then
+// checkptrBase(p1) == checkptrBase(p2). However, the converse/inverse
+// is not necessarily true as allocations can have trailing padding,
+// and multiple variables may be packed into a single allocation.
func checkptrBase(p unsafe.Pointer) uintptr {
- base, _, _ := findObject(uintptr(p), 0, 0)
- // TODO(mdempsky): If base == 0, then check if p points to the
- // stack or a global variable.
- return base
+ // stack
+ if gp := getg(); gp.stack.lo <= uintptr(p) && uintptr(p) < gp.stack.hi {
+ // TODO(mdempsky): Walk the stack to identify the
+ // specific stack frame or even stack object that p
+ // points into.
+ //
+ // In the mean time, use "1" as a pseudo-address to
+ // represent the stack. This is an invalid address on
+ // all platforms, so it's guaranteed to be distinct
+ // from any of the addresses we might return below.
+ return 1
+ }
+
+ // heap (must check after stack because of #35068)
+ if base, _, _ := findObject(uintptr(p), 0, 0); base != 0 {
+ return base
+ }
+
+ // data or bss
+ for _, datap := range activeModules() {
+ if datap.data <= uintptr(p) && uintptr(p) < datap.edata {
+ return datap.data
+ }
+ if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss {
+ return datap.bss
+ }
+ }
+
+ return 0
}
diff --git a/src/runtime/cpuflags.go b/src/runtime/cpuflags.go
index 1565afb93a5..94f9331d15a 100644
--- a/src/runtime/cpuflags.go
+++ b/src/runtime/cpuflags.go
@@ -23,6 +23,9 @@ var (
// TODO: deprecate these; use internal/cpu directly.
x86HasPOPCNT bool
x86HasSSE41 bool
+ x86HasFMA bool
+
+ armHasVFPv4 bool
arm64HasATOMICS bool
)
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index 7be52f499c3..ad1f29b2546 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -435,6 +435,14 @@ func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
}
func TestNetpollDeadlock(t *testing.T) {
+ if os.Getenv("GO_BUILDER_NAME") == "darwin-amd64-10_12" {
+ // A suspected kernel bug in macOS 10.12 occasionally results in
+ // an apparent deadlock when dialing localhost. The errors have not
+ // been observed on newer versions of the OS, so we don't plan to work
+ // around them. See https://golang.org/issue/22019.
+ testenv.SkipFlaky(t, 22019)
+ }
+
t.Parallel()
output := runTestProg(t, "testprognet", "NetpollDeadlock")
want := "done\n"
diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go
index ce227feebd7..4be4962f907 100644
--- a/src/runtime/crash_unix_test.go
+++ b/src/runtime/crash_unix_test.go
@@ -18,6 +18,7 @@ import (
"strings"
"syscall"
"testing"
+ "unsafe"
)
// sigquit is the signal to send to kill a hanging testdata program.
@@ -33,6 +34,29 @@ func init() {
}
}
+func TestBadOpen(t *testing.T) {
+ // make sure we get the correct error code if open fails. Same for
+ // read/write/close on the resulting -1 fd. See issue 10052.
+ nonfile := []byte("/notreallyafile")
+ fd := runtime.Open(&nonfile[0], 0, 0)
+ if fd != -1 {
+ t.Errorf("open(%q)=%d, want -1", nonfile, fd)
+ }
+ var buf [32]byte
+ r := runtime.Read(-1, unsafe.Pointer(&buf[0]), int32(len(buf)))
+ if got, want := r, -int32(syscall.EBADF); got != want {
+ t.Errorf("read()=%d, want %d", got, want)
+ }
+ w := runtime.Write(^uintptr(0), unsafe.Pointer(&buf[0]), int32(len(buf)))
+ if got, want := w, -int32(syscall.EBADF); got != want {
+ t.Errorf("write()=%d, want %d", got, want)
+ }
+ c := runtime.Close(-1)
+ if c != -1 {
+ t.Errorf("close()=%d, want -1", c)
+ }
+}
+
func TestCrashDumpsAllThreads(t *testing.T) {
if *flagQuick {
t.Skip("-quick")
diff --git a/src/runtime/defer_test.go b/src/runtime/defer_test.go
index 0d3e8e9d632..d830fc591f0 100644
--- a/src/runtime/defer_test.go
+++ b/src/runtime/defer_test.go
@@ -15,11 +15,11 @@ import (
// unconditional panic (hence no return from the function)
func TestUnconditionalPanic(t *testing.T) {
defer func() {
- if recover() == nil {
+ if recover() != "testUnconditional" {
t.Fatal("expected unconditional panic")
}
}()
- panic("panic should be recovered")
+ panic("testUnconditional")
}
var glob int = 3
@@ -30,7 +30,7 @@ func TestOpenAndNonOpenDefers(t *testing.T) {
for {
// Non-open defer because in a loop
defer func(n int) {
- if recover() == nil {
+ if recover() != "testNonOpenDefer" {
t.Fatal("expected testNonOpen panic")
}
}(3)
@@ -45,7 +45,7 @@ func TestOpenAndNonOpenDefers(t *testing.T) {
//go:noinline
func testOpen(t *testing.T, arg int) {
defer func(n int) {
- if recover() == nil {
+ if recover() != "testOpenDefer" {
t.Fatal("expected testOpen panic")
}
}(4)
@@ -61,7 +61,7 @@ func TestNonOpenAndOpenDefers(t *testing.T) {
for {
// Non-open defer because in a loop
defer func(n int) {
- if recover() == nil {
+ if recover() != "testNonOpenDefer" {
t.Fatal("expected testNonOpen panic")
}
}(3)
@@ -80,7 +80,7 @@ func TestConditionalDefers(t *testing.T) {
list = make([]int, 0, 10)
defer func() {
- if recover() == nil {
+ if recover() != "testConditional" {
t.Fatal("expected panic")
}
want := []int{4, 2, 1}
@@ -106,7 +106,7 @@ func testConditionalDefers(n int) {
defer doappend(4)
}
}
- panic("test")
+ panic("testConditional")
}
// Test that there is no compile-time or run-time error if an open-coded defer
@@ -174,3 +174,52 @@ func TestRecoverMatching(t *testing.T) {
}()
panic("panic1")
}
+
+type nonSSAable [128]byte
+
+type bigStruct struct {
+ x, y, z, w, p, q int64
+}
+
+func mknonSSAable() nonSSAable {
+ globint1++
+ return nonSSAable{0, 0, 0, 0, 5}
+}
+
+var globint1, globint2 int
+
+//go:noinline
+func sideeffect(n int64) int64 {
+ globint2++
+ return n
+}
+
+// Test that nonSSAable arguments to defer are handled correctly and only evaluated once.
+func TestNonSSAableArgs(t *testing.T) {
+ globint1 = 0
+ globint2 = 0
+ var save1 byte
+ var save2 int64
+
+ defer func() {
+ if globint1 != 1 {
+ t.Fatal(fmt.Sprintf("globint1: wanted: 1, got %v", globint1))
+ }
+ if save1 != 5 {
+ t.Fatal(fmt.Sprintf("save1: wanted: 5, got %v", save1))
+ }
+ if globint2 != 1 {
+ t.Fatal(fmt.Sprintf("globint2: wanted: 1, got %v", globint2))
+ }
+ if save2 != 2 {
+ t.Fatal(fmt.Sprintf("save2: wanted: 2, got %v", save2))
+ }
+ }()
+
+ defer func(n nonSSAable) {
+ save1 = n[4]
+ }(mknonSSAable())
+ defer func(b bigStruct) {
+ save2 = b.y
+ }(bigStruct{1, 2, 3, 4, 5, sideeffect(6)})
+}
diff --git a/src/runtime/defs1_linux.go b/src/runtime/defs1_linux.go
index e136d96e78d..4085d6f4182 100644
--- a/src/runtime/defs1_linux.go
+++ b/src/runtime/defs1_linux.go
@@ -21,6 +21,7 @@ import "C"
const (
O_RDONLY = C.O_RDONLY
+ O_NONBLOCK = C.O_NONBLOCK
O_CLOEXEC = C.O_CLOEXEC
SA_RESTORER = C.SA_RESTORER
)
diff --git a/src/runtime/defs1_netbsd_386.go b/src/runtime/defs1_netbsd_386.go
index 3eae12eed0e..a4548e6f062 100644
--- a/src/runtime/defs1_netbsd_386.go
+++ b/src/runtime/defs1_netbsd_386.go
@@ -6,6 +6,11 @@ package runtime
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x400000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs1_netbsd_amd64.go b/src/runtime/defs1_netbsd_amd64.go
index 51d55c91f93..4b0e79ebb6f 100644
--- a/src/runtime/defs1_netbsd_amd64.go
+++ b/src/runtime/defs1_netbsd_amd64.go
@@ -6,6 +6,11 @@ package runtime
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x400000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs1_netbsd_arm.go b/src/runtime/defs1_netbsd_arm.go
index fadb3415b33..2b5d5990d36 100644
--- a/src/runtime/defs1_netbsd_arm.go
+++ b/src/runtime/defs1_netbsd_arm.go
@@ -6,6 +6,11 @@ package runtime
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x400000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs1_netbsd_arm64.go b/src/runtime/defs1_netbsd_arm64.go
index 41b7aaca6cc..740dc77658f 100644
--- a/src/runtime/defs1_netbsd_arm64.go
+++ b/src/runtime/defs1_netbsd_arm64.go
@@ -6,6 +6,11 @@ package runtime
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x400000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs1_solaris_amd64.go b/src/runtime/defs1_solaris_amd64.go
index 14b5c7949e4..ee6c45e5242 100644
--- a/src/runtime/defs1_solaris_amd64.go
+++ b/src/runtime/defs1_solaris_amd64.go
@@ -8,6 +8,7 @@ const (
_EBADF = 0x9
_EFAULT = 0xe
_EAGAIN = 0xb
+ _EBUSY = 0x10
_ETIME = 0x3e
_ETIMEDOUT = 0x91
_EWOULDBLOCK = 0xb
@@ -100,7 +101,9 @@ const (
_POLLHUP = 0x10
_POLLERR = 0x8
- _PORT_SOURCE_FD = 0x4
+ _PORT_SOURCE_FD = 0x4
+ _PORT_SOURCE_ALERT = 0x5
+ _PORT_ALERT_UPDATE = 0x2
)
type semt struct {
diff --git a/src/runtime/defs2_linux.go b/src/runtime/defs2_linux.go
index b08c0dafe12..87e19c1598a 100644
--- a/src/runtime/defs2_linux.go
+++ b/src/runtime/defs2_linux.go
@@ -61,7 +61,7 @@ const (
MADV_DONTNEED = C.MADV_DONTNEED
MADV_FREE = C.MADV_FREE
MADV_HUGEPAGE = C.MADV_HUGEPAGE
- MADV_NOHUGEPAGE = C.MADV_HNOUGEPAGE
+ MADV_NOHUGEPAGE = C.MADV_NOHUGEPAGE
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
diff --git a/src/runtime/defs_aix.go b/src/runtime/defs_aix.go
index bc5101f35d0..a8924133c51 100644
--- a/src/runtime/defs_aix.go
+++ b/src/runtime/defs_aix.go
@@ -123,7 +123,8 @@ const (
_ITIMER_VIRTUAL = C.ITIMER_VIRTUAL
_ITIMER_PROF = C.ITIMER_PROF
- _O_RDONLY = C.O_RDONLY
+ _O_RDONLY = C.O_RDONLY
+ _O_NONBLOCK = C.O_NONBLOCK
_SS_DISABLE = C.SS_DISABLE
_SI_USER = C.SI_USER
diff --git a/src/runtime/defs_aix_ppc64.go b/src/runtime/defs_aix_ppc64.go
index dccc3a59264..a53fcc59336 100644
--- a/src/runtime/defs_aix_ppc64.go
+++ b/src/runtime/defs_aix_ppc64.go
@@ -80,7 +80,8 @@ const (
_ITIMER_VIRTUAL = 0x1
_ITIMER_PROF = 0x2
- _O_RDONLY = 0x0
+ _O_RDONLY = 0x0
+ _O_NONBLOCK = 0x4
_SS_DISABLE = 0x2
_SI_USER = 0x0
diff --git a/src/runtime/defs_darwin.go b/src/runtime/defs_darwin.go
index 0cd133f6e02..de1489f0326 100644
--- a/src/runtime/defs_darwin.go
+++ b/src/runtime/defs_darwin.go
@@ -30,6 +30,7 @@ import "C"
const (
EINTR = C.EINTR
EFAULT = C.EFAULT
+ EAGAIN = C.EAGAIN
ETIMEDOUT = C.ETIMEDOUT
PROT_NONE = C.PROT_NONE
diff --git a/src/runtime/defs_darwin_386.go b/src/runtime/defs_darwin_386.go
index 83928e78415..a78f54bcf56 100644
--- a/src/runtime/defs_darwin_386.go
+++ b/src/runtime/defs_darwin_386.go
@@ -8,6 +8,7 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
_ETIMEDOUT = 0x3c
_PROT_NONE = 0x0
diff --git a/src/runtime/defs_darwin_amd64.go b/src/runtime/defs_darwin_amd64.go
index 45c34a8fc04..cbc26bfcffa 100644
--- a/src/runtime/defs_darwin_amd64.go
+++ b/src/runtime/defs_darwin_amd64.go
@@ -8,6 +8,7 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
_ETIMEDOUT = 0x3c
_PROT_NONE = 0x0
diff --git a/src/runtime/defs_darwin_arm.go b/src/runtime/defs_darwin_arm.go
index 5e2af978a7b..199886aad1f 100644
--- a/src/runtime/defs_darwin_arm.go
+++ b/src/runtime/defs_darwin_arm.go
@@ -10,6 +10,7 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
_ETIMEDOUT = 0x3c
_PROT_NONE = 0x0
diff --git a/src/runtime/defs_darwin_arm64.go b/src/runtime/defs_darwin_arm64.go
index f673eb7b24b..2f466045d4f 100644
--- a/src/runtime/defs_darwin_arm64.go
+++ b/src/runtime/defs_darwin_arm64.go
@@ -8,6 +8,7 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
_ETIMEDOUT = 0x3c
_PROT_NONE = 0x0
diff --git a/src/runtime/defs_freebsd.go b/src/runtime/defs_freebsd.go
index 53c1508eb79..e196dff0769 100644
--- a/src/runtime/defs_freebsd.go
+++ b/src/runtime/defs_freebsd.go
@@ -47,6 +47,11 @@ const (
const (
EINTR = C.EINTR
EFAULT = C.EFAULT
+ EAGAIN = C.EAGAIN
+ ENOSYS = C.ENOSYS
+
+ O_NONBLOCK = C.O_NONBLOCK
+ O_CLOEXEC = C.O_CLOEXEC
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
diff --git a/src/runtime/defs_freebsd_386.go b/src/runtime/defs_freebsd_386.go
index c4d5c897d3e..6294fc32d4c 100644
--- a/src/runtime/defs_freebsd_386.go
+++ b/src/runtime/defs_freebsd_386.go
@@ -15,6 +15,11 @@ const (
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x100000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs_freebsd_amd64.go b/src/runtime/defs_freebsd_amd64.go
index 89d36c270db..840c710eeb9 100644
--- a/src/runtime/defs_freebsd_amd64.go
+++ b/src/runtime/defs_freebsd_amd64.go
@@ -15,6 +15,11 @@ const (
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x100000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs_freebsd_arm.go b/src/runtime/defs_freebsd_arm.go
index cc8c924c371..3307c8bbae3 100644
--- a/src/runtime/defs_freebsd_arm.go
+++ b/src/runtime/defs_freebsd_arm.go
@@ -15,6 +15,11 @@ const (
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x100000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs_linux.go b/src/runtime/defs_linux.go
index 2d810136d98..7b140633866 100644
--- a/src/runtime/defs_linux.go
+++ b/src/runtime/defs_linux.go
@@ -37,6 +37,7 @@ const (
EINTR = C.EINTR
EAGAIN = C.EAGAIN
ENOMEM = C.ENOMEM
+ ENOSYS = C.ENOSYS
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
@@ -50,7 +51,7 @@ const (
MADV_DONTNEED = C.MADV_DONTNEED
MADV_FREE = C.MADV_FREE
MADV_HUGEPAGE = C.MADV_HUGEPAGE
- MADV_NOHUGEPAGE = C.MADV_HNOUGEPAGE
+ MADV_NOHUGEPAGE = C.MADV_NOHUGEPAGE
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
diff --git a/src/runtime/defs_linux_386.go b/src/runtime/defs_linux_386.go
index e2fcbcac718..ba349845cfd 100644
--- a/src/runtime/defs_linux_386.go
+++ b/src/runtime/defs_linux_386.go
@@ -7,6 +7,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -78,8 +79,9 @@ const (
_ITIMER_VIRTUAL = 0x1
_ITIMER_PROF = 0x2
- _O_RDONLY = 0x0
- _O_CLOEXEC = 0x80000
+ _O_RDONLY = 0x0
+ _O_NONBLOCK = 0x800
+ _O_CLOEXEC = 0x80000
_EPOLLIN = 0x1
_EPOLLOUT = 0x4
diff --git a/src/runtime/defs_linux_amd64.go b/src/runtime/defs_linux_amd64.go
index ddad7fddd44..9eb5646ca32 100644
--- a/src/runtime/defs_linux_amd64.go
+++ b/src/runtime/defs_linux_amd64.go
@@ -7,6 +7,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -143,8 +144,9 @@ type epollevent struct {
// cgo -cdefs defs_linux.go defs1_linux.go
const (
- _O_RDONLY = 0x0
- _O_CLOEXEC = 0x80000
+ _O_RDONLY = 0x0
+ _O_NONBLOCK = 0x800
+ _O_CLOEXEC = 0x80000
)
type usigset struct {
diff --git a/src/runtime/defs_linux_arm.go b/src/runtime/defs_linux_arm.go
index 9d10d664e17..d24e0a9d6e6 100644
--- a/src/runtime/defs_linux_arm.go
+++ b/src/runtime/defs_linux_arm.go
@@ -5,6 +5,7 @@ const (
_EINTR = 0x4
_ENOMEM = 0xc
_EAGAIN = 0xb
+ _ENOSYS = 0x26
_PROT_NONE = 0
_PROT_READ = 0x1
@@ -71,6 +72,7 @@ const (
_ITIMER_PROF = 0x2
_ITIMER_VIRTUAL = 0x1
_O_RDONLY = 0
+ _O_NONBLOCK = 0x800
_O_CLOEXEC = 0x80000
_EPOLLIN = 0x1
diff --git a/src/runtime/defs_linux_arm64.go b/src/runtime/defs_linux_arm64.go
index b325a229a1d..182887d8d5e 100644
--- a/src/runtime/defs_linux_arm64.go
+++ b/src/runtime/defs_linux_arm64.go
@@ -7,6 +7,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -144,8 +145,9 @@ type epollevent struct {
// ../cmd/cgo/cgo -cdefs defs_linux.go defs1_linux.go defs2_linux.go
const (
- _O_RDONLY = 0x0
- _O_CLOEXEC = 0x80000
+ _O_RDONLY = 0x0
+ _O_NONBLOCK = 0x800
+ _O_CLOEXEC = 0x80000
)
type usigset struct {
diff --git a/src/runtime/defs_linux_mips64x.go b/src/runtime/defs_linux_mips64x.go
index a52d0d40cf2..0fb53d57370 100644
--- a/src/runtime/defs_linux_mips64x.go
+++ b/src/runtime/defs_linux_mips64x.go
@@ -7,6 +7,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x59
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -145,6 +146,7 @@ type epollevent struct {
const (
_O_RDONLY = 0x0
+ _O_NONBLOCK = 0x80
_O_CLOEXEC = 0x80000
_SA_RESTORER = 0
)
diff --git a/src/runtime/defs_linux_mipsx.go b/src/runtime/defs_linux_mipsx.go
index f3a1dd0cf0d..9315ba9346a 100644
--- a/src/runtime/defs_linux_mipsx.go
+++ b/src/runtime/defs_linux_mipsx.go
@@ -11,6 +11,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x59
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -143,6 +144,7 @@ type epollevent struct {
const (
_O_RDONLY = 0x0
+ _O_NONBLOCK = 0x80
_O_CLOEXEC = 0x80000
_SA_RESTORER = 0
)
diff --git a/src/runtime/defs_linux_ppc64.go b/src/runtime/defs_linux_ppc64.go
index f4389937217..90b1dc1ff9d 100644
--- a/src/runtime/defs_linux_ppc64.go
+++ b/src/runtime/defs_linux_ppc64.go
@@ -7,6 +7,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -145,6 +146,7 @@ type epollevent struct {
const (
_O_RDONLY = 0x0
+ _O_NONBLOCK = 0x800
_O_CLOEXEC = 0x80000
_SA_RESTORER = 0
)
diff --git a/src/runtime/defs_linux_ppc64le.go b/src/runtime/defs_linux_ppc64le.go
index f4389937217..90b1dc1ff9d 100644
--- a/src/runtime/defs_linux_ppc64le.go
+++ b/src/runtime/defs_linux_ppc64le.go
@@ -7,6 +7,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -145,6 +146,7 @@ type epollevent struct {
const (
_O_RDONLY = 0x0
+ _O_NONBLOCK = 0x800
_O_CLOEXEC = 0x80000
_SA_RESTORER = 0
)
diff --git a/src/runtime/defs_linux_s390x.go b/src/runtime/defs_linux_s390x.go
index 19b99b5bdfb..fa289d531c0 100644
--- a/src/runtime/defs_linux_s390x.go
+++ b/src/runtime/defs_linux_s390x.go
@@ -8,6 +8,7 @@ const (
_EINTR = 0x4
_EAGAIN = 0xb
_ENOMEM = 0xc
+ _ENOSYS = 0x26
_PROT_NONE = 0x0
_PROT_READ = 0x1
@@ -138,6 +139,7 @@ type epollevent struct {
const (
_O_RDONLY = 0x0
+ _O_NONBLOCK = 0x800
_O_CLOEXEC = 0x80000
_SA_RESTORER = 0
)
diff --git a/src/runtime/defs_netbsd.go b/src/runtime/defs_netbsd.go
index 41aa07af98e..3f5ce5adcac 100644
--- a/src/runtime/defs_netbsd.go
+++ b/src/runtime/defs_netbsd.go
@@ -32,6 +32,11 @@ import "C"
const (
EINTR = C.EINTR
EFAULT = C.EFAULT
+ EAGAIN = C.EAGAIN
+ ENOSYS = C.ENOSYS
+
+ O_NONBLOCK = C.O_NONBLOCK
+ O_CLOEXEC = C.O_CLOEXEC
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
diff --git a/src/runtime/defs_openbsd.go b/src/runtime/defs_openbsd.go
index a328d25db3e..4774e36c923 100644
--- a/src/runtime/defs_openbsd.go
+++ b/src/runtime/defs_openbsd.go
@@ -28,6 +28,11 @@ import "C"
const (
EINTR = C.EINTR
EFAULT = C.EFAULT
+ EAGAIN = C.EAGAIN
+ ENOSYS = C.ENOSYS
+
+ O_NONBLOCK = C.O_NONBLOCK
+ O_CLOEXEC = C.O_CLOEXEC
PROT_NONE = C.PROT_NONE
PROT_READ = C.PROT_READ
diff --git a/src/runtime/defs_openbsd_386.go b/src/runtime/defs_openbsd_386.go
index 0e59a0591a7..35f2e53fcf0 100644
--- a/src/runtime/defs_openbsd_386.go
+++ b/src/runtime/defs_openbsd_386.go
@@ -8,6 +8,11 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x10000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs_openbsd_amd64.go b/src/runtime/defs_openbsd_amd64.go
index 5cefac5858a..c187a98ae0c 100644
--- a/src/runtime/defs_openbsd_amd64.go
+++ b/src/runtime/defs_openbsd_amd64.go
@@ -8,6 +8,11 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x10000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs_openbsd_arm.go b/src/runtime/defs_openbsd_arm.go
index b187e9776f5..170bb3876c4 100644
--- a/src/runtime/defs_openbsd_arm.go
+++ b/src/runtime/defs_openbsd_arm.go
@@ -8,6 +8,11 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x10000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs_openbsd_arm64.go b/src/runtime/defs_openbsd_arm64.go
index 6b9d60110ab..8b8d5cddf2e 100644
--- a/src/runtime/defs_openbsd_arm64.go
+++ b/src/runtime/defs_openbsd_arm64.go
@@ -5,6 +5,11 @@ import "unsafe"
const (
_EINTR = 0x4
_EFAULT = 0xe
+ _EAGAIN = 0x23
+ _ENOSYS = 0x4e
+
+ _O_NONBLOCK = 0x4
+ _O_CLOEXEC = 0x10000
_PROT_NONE = 0x0
_PROT_READ = 0x1
diff --git a/src/runtime/defs_solaris.go b/src/runtime/defs_solaris.go
index b8ef12a1459..f42adebee37 100644
--- a/src/runtime/defs_solaris.go
+++ b/src/runtime/defs_solaris.go
@@ -38,6 +38,7 @@ const (
EBADF = C.EBADF
EFAULT = C.EFAULT
EAGAIN = C.EAGAIN
+ EBUSY = C.EBUSY
ETIME = C.ETIME
ETIMEDOUT = C.ETIMEDOUT
EWOULDBLOCK = C.EWOULDBLOCK
@@ -129,7 +130,9 @@ const (
POLLHUP = C.POLLHUP
POLLERR = C.POLLERR
- PORT_SOURCE_FD = C.PORT_SOURCE_FD
+ PORT_SOURCE_FD = C.PORT_SOURCE_FD
+ PORT_SOURCE_ALERT = C.PORT_SOURCE_ALERT
+ PORT_ALERT_UPDATE = C.PORT_ALERT_UPDATE
)
type SemT C.sem_t
diff --git a/src/runtime/export_linux_test.go b/src/runtime/export_linux_test.go
index c73f2f33d1f..b7c901f238f 100644
--- a/src/runtime/export_linux_test.go
+++ b/src/runtime/export_linux_test.go
@@ -10,6 +10,9 @@ import "unsafe"
var NewOSProc0 = newosproc0
var Mincore = mincore
+var Add = add
+
+type EpollEvent epollevent
func Epollctl(epfd, op, fd int32, ev unsafe.Pointer) int32 {
return epollctl(epfd, op, fd, (*epollevent)(ev))
diff --git a/src/runtime/export_nbpipe_test.go b/src/runtime/export_nbpipe_test.go
new file mode 100644
index 00000000000..cf7863566a9
--- /dev/null
+++ b/src/runtime/export_nbpipe_test.go
@@ -0,0 +1,12 @@
+// Copyright 2019 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.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
+
+package runtime
+
+var NonblockingPipe = nonblockingPipe
+var Pipe = pipe
+var SetNonblock = setNonblock
+var Closeonexec = closeonexec
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index e4a7faf9659..f5b44a29a00 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -35,6 +35,8 @@ var Atoi = atoi
var Atoi32 = atoi32
var Nanotime = nanotime
+var NetpollBreak = netpollBreak
+var Usleep = usleep
var PhysHugePageSize = physHugePageSize
@@ -51,6 +53,12 @@ func LFStackPop(head *uint64) *LFNode {
return (*LFNode)(unsafe.Pointer((*lfstack)(head).pop()))
}
+func Netpoll(delta int64) {
+ systemstack(func() {
+ netpoll(delta)
+ })
+}
+
func GCMask(x interface{}) (ret []byte) {
systemstack(func() {
ret = getgcmask(x)
diff --git a/src/runtime/funcdata.h b/src/runtime/funcdata.h
index d9a35c51a06..0fb50ddfba5 100644
--- a/src/runtime/funcdata.h
+++ b/src/runtime/funcdata.h
@@ -17,6 +17,7 @@
#define FUNCDATA_RegPointerMaps 2
#define FUNCDATA_StackObjects 3
#define FUNCDATA_InlTree 4
+#define FUNCDATA_OpenCodedDeferInfo 5 /* info for func with open-coded defers */
// Pseudo-assembly statements.
diff --git a/src/runtime/lock_futex.go b/src/runtime/lock_futex.go
index d2828b138ab..92873f2daca 100644
--- a/src/runtime/lock_futex.go
+++ b/src/runtime/lock_futex.go
@@ -230,7 +230,7 @@ func notetsleepg(n *note, ns int64) bool {
return ok
}
-func beforeIdle() bool {
+func beforeIdle(int64) bool {
return false
}
diff --git a/src/runtime/lock_js.go b/src/runtime/lock_js.go
index 23f17080f26..51cbe606071 100644
--- a/src/runtime/lock_js.go
+++ b/src/runtime/lock_js.go
@@ -111,6 +111,8 @@ func notetsleepg(n *note, ns int64) bool {
gopark(nil, nil, waitReasonSleep, traceEvNone, 1)
clearTimeoutEvent(id) // note might have woken early, clear timeout
+ clearIdleID()
+
mp = acquirem()
delete(notes, n)
delete(notesWithTimeout, n)
@@ -148,10 +150,25 @@ var isHandlingEvent = false
var nextEventIsAsync = false
var returnedEventHandler *g
+// The timeout event started by beforeIdle.
+var idleID int32
+
// beforeIdle gets called by the scheduler if no goroutine is awake.
// If we are not already handling an event, then we pause for an async event.
// If an event handler returned, we resume it and it will pause the execution.
-func beforeIdle() bool {
+func beforeIdle(delay int64) bool {
+ if delay > 0 {
+ if delay < 1e6 {
+ delay = 1
+ } else if delay < 1e15 {
+ delay = delay / 1e6
+ } else {
+ // An arbitrary cap on how long to wait for a timer.
+ // 1e9 ms == ~11.5 days.
+ delay = 1e9
+ }
+ idleID = scheduleTimeoutEvent(delay)
+ }
if !isHandlingEvent {
nextEventIsAsync = true
pause(getcallersp() - 16)
@@ -164,6 +181,14 @@ func beforeIdle() bool {
return false
}
+// clearIdleID clears our record of the timeout started by beforeIdle.
+func clearIdleID() {
+ if idleID != 0 {
+ clearTimeoutEvent(idleID)
+ idleID = 0
+ }
+}
+
// pause sets SP to newsp and pauses the execution of Go's WebAssembly code until an event is triggered.
func pause(newsp uintptr)
@@ -189,6 +214,8 @@ func handleEvent() {
eventHandler()
+ clearIdleID()
+
// wait until all goroutines are idle
returnedEventHandler = getg()
gopark(nil, nil, waitReasonZero, traceEvNone, 1)
diff --git a/src/runtime/lock_sema.go b/src/runtime/lock_sema.go
index 9507d46f41c..af9517d7440 100644
--- a/src/runtime/lock_sema.go
+++ b/src/runtime/lock_sema.go
@@ -289,7 +289,7 @@ func notetsleepg(n *note, ns int64) bool {
return ok
}
-func beforeIdle() bool {
+func beforeIdle(int64) bool {
return false
}
diff --git a/src/runtime/mgcscavenge.go b/src/runtime/mgcscavenge.go
index 0273c8d234a..0a67f74150f 100644
--- a/src/runtime/mgcscavenge.go
+++ b/src/runtime/mgcscavenge.go
@@ -268,8 +268,7 @@ func scavengeSleep(ns int64) bool {
// because we can't close over any variables without
// failing escape analysis.
now := nanotime()
- scavenge.timer.when = now + ns
- startTimer(scavenge.timer)
+ resetTimer(scavenge.timer, now+ns)
// Mark ourself as asleep and go to sleep.
scavenge.parked = true
diff --git a/src/runtime/nbpipe_pipe.go b/src/runtime/nbpipe_pipe.go
new file mode 100644
index 00000000000..822b2944dbf
--- /dev/null
+++ b/src/runtime/nbpipe_pipe.go
@@ -0,0 +1,19 @@
+// Copyright 2019 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.
+
+// +build aix darwin dragonfly
+
+package runtime
+
+func nonblockingPipe() (r, w int32, errno int32) {
+ r, w, errno = pipe()
+ if errno != 0 {
+ return -1, -1, errno
+ }
+ closeonexec(r)
+ setNonblock(r)
+ closeonexec(w)
+ setNonblock(w)
+ return r, w, errno
+}
diff --git a/src/runtime/nbpipe_pipe2.go b/src/runtime/nbpipe_pipe2.go
new file mode 100644
index 00000000000..f4c862cbff9
--- /dev/null
+++ b/src/runtime/nbpipe_pipe2.go
@@ -0,0 +1,25 @@
+// Copyright 2019 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.
+
+// +build freebsd linux netbsd openbsd
+
+package runtime
+
+func pipe() (r, w int32, errno int32)
+func pipe2(flags int32) (r, w int32, errno int32)
+
+func nonblockingPipe() (r, w int32, errno int32) {
+ r, w, errno = pipe2(_O_NONBLOCK | _O_CLOEXEC)
+ if errno == -_ENOSYS {
+ r, w, errno = pipe()
+ if errno != 0 {
+ return -1, -1, errno
+ }
+ closeonexec(r)
+ setNonblock(r)
+ closeonexec(w)
+ setNonblock(w)
+ }
+ return r, w, errno
+}
diff --git a/src/runtime/nbpipe_test.go b/src/runtime/nbpipe_test.go
new file mode 100644
index 00000000000..bd0d578234b
--- /dev/null
+++ b/src/runtime/nbpipe_test.go
@@ -0,0 +1,93 @@
+// Copyright 2019 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.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd
+
+package runtime_test
+
+import (
+ "runtime"
+ "syscall"
+ "testing"
+ "unsafe"
+)
+
+func TestNonblockingPipe(t *testing.T) {
+ t.Parallel()
+
+ // NonblockingPipe is the test name for nonblockingPipe.
+ r, w, errno := runtime.NonblockingPipe()
+ if errno != 0 {
+ t.Fatal(syscall.Errno(errno))
+ }
+ defer func() {
+ runtime.Close(r)
+ runtime.Close(w)
+ }()
+
+ checkIsPipe(t, r, w)
+ checkNonblocking(t, r, "reader")
+ checkCloseonexec(t, r, "reader")
+ checkNonblocking(t, w, "writer")
+ checkCloseonexec(t, w, "writer")
+}
+
+func checkIsPipe(t *testing.T, r, w int32) {
+ bw := byte(42)
+ if n := runtime.Write(uintptr(w), unsafe.Pointer(&bw), 1); n != 1 {
+ t.Fatalf("Write(w, &b, 1) == %d, expected 1", n)
+ }
+ var br byte
+ if n := runtime.Read(r, unsafe.Pointer(&br), 1); n != 1 {
+ t.Fatalf("Read(r, &b, 1) == %d, expected 1", n)
+ }
+ if br != bw {
+ t.Errorf("pipe read %d, expected %d", br, bw)
+ }
+}
+
+func checkNonblocking(t *testing.T, fd int32, name string) {
+ t.Helper()
+ flags, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_GETFL, 0)
+ if errno != 0 {
+ t.Errorf("fcntl(%s, F_GETFL) failed: %v", name, syscall.Errno(errno))
+ } else if flags&syscall.O_NONBLOCK == 0 {
+ t.Errorf("O_NONBLOCK not set in %s flags %#x", name, flags)
+ }
+}
+
+func checkCloseonexec(t *testing.T, fd int32, name string) {
+ t.Helper()
+ flags, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_GETFD, 0)
+ if errno != 0 {
+ t.Errorf("fcntl(%s, F_GETFD) failed: %v", name, syscall.Errno(errno))
+ } else if flags&syscall.FD_CLOEXEC == 0 {
+ t.Errorf("FD_CLOEXEC not set in %s flags %#x", name, flags)
+ }
+}
+
+func TestSetNonblock(t *testing.T) {
+ t.Parallel()
+
+ r, w, errno := runtime.Pipe()
+ if errno != 0 {
+ t.Fatal(syscall.Errno(errno))
+ }
+ defer func() {
+ runtime.Close(r)
+ runtime.Close(w)
+ }()
+
+ checkIsPipe(t, r, w)
+
+ runtime.SetNonblock(r)
+ runtime.SetNonblock(w)
+ checkNonblocking(t, r, "reader")
+ checkNonblocking(t, w, "writer")
+
+ runtime.Closeonexec(r)
+ runtime.Closeonexec(w)
+ checkCloseonexec(t, r, "reader")
+ checkCloseonexec(t, w, "writer")
+}
diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go
index adb072db38b..939b27061e3 100644
--- a/src/runtime/netpoll.go
+++ b/src/runtime/netpoll.go
@@ -12,12 +12,26 @@ import (
)
// Integrated network poller (platform-independent part).
-// A particular implementation (epoll/kqueue) must define the following functions:
-// func netpollinit() // to initialize the poller
-// func netpollopen(fd uintptr, pd *pollDesc) int32 // to arm edge-triggered notifications
-// and associate fd with pd.
-// An implementation must call the following function to denote that the pd is ready.
-// func netpollready(gpp **g, pd *pollDesc, mode int32)
+// A particular implementation (epoll/kqueue/port/AIX/Windows)
+// must define the following functions:
+//
+// func netpollinit()
+// Initialize the poller. Only called once.
+//
+// func netpollopen(fd uintptr, pd *pollDesc) int32
+// Arm edge-triggered notifications for fd. The pd argument is to pass
+// back to netpollready when fd is ready. Return an errno value.
+//
+// func netpoll(delta int64) gList
+// Poll the network. If delta < 0, block indefinitely. If delta == 0,
+// poll without blocking. If delta > 0, block for up to delta nanoseconds.
+// Return a list of goroutines built by calling netpollready.
+//
+// func netpollBreak()
+// Wake up the network poller, assumed to be blocked in netpoll.
+//
+// func netpollIsPollDescriptor(fd uintptr) bool
+// Reports whether fd is a file descriptor used by the poller.
// pollDesc contains 2 binary semaphores, rg and wg, to park reader and writer
// goroutines respectively. The semaphore can be in the following states:
@@ -86,8 +100,13 @@ var (
//go:linkname poll_runtime_pollServerInit internal/poll.runtime_pollServerInit
func poll_runtime_pollServerInit() {
- netpollinit()
- atomic.Store(&netpollInited, 1)
+ netpollGenericInit()
+}
+
+func netpollGenericInit() {
+ if atomic.Cas(&netpollInited, 0, 1) {
+ netpollinit()
+ }
}
func netpollinited() bool {
@@ -99,14 +118,7 @@ func netpollinited() bool {
// poll_runtime_isPollServerDescriptor reports whether fd is a
// descriptor being used by netpoll.
func poll_runtime_isPollServerDescriptor(fd uintptr) bool {
- fds := netpolldescriptor()
- if GOOS != "aix" {
- return fd == fds
- } else {
- // AIX have a pipe in its netpoll implementation.
- // Therefore, two fd are returned by netpolldescriptor using a mask.
- return fd == fds&0xFFFF || fd == (fds>>16)&0xFFFF
- }
+ return netpollIsPollDescriptor(fd)
}
//go:linkname poll_runtime_pollOpen internal/poll.runtime_pollOpen
@@ -232,13 +244,12 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
if pd.rt.f == nil {
if pd.rd > 0 {
pd.rt.f = rtf
- pd.rt.when = pd.rd
// Copy current seq into the timer arg.
// Timer func will check the seq against current descriptor seq,
// if they differ the descriptor was reused or timers were reset.
pd.rt.arg = pd
pd.rt.seq = pd.rseq
- addtimer(&pd.rt)
+ resettimer(&pd.rt, pd.rd)
}
} else if pd.rd != rd0 || combo != combo0 {
pd.rseq++ // invalidate current timers
@@ -252,10 +263,9 @@ func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
if pd.wt.f == nil {
if pd.wd > 0 && !combo {
pd.wt.f = netpollWriteDeadline
- pd.wt.when = pd.wd
pd.wt.arg = pd
pd.wt.seq = pd.wseq
- addtimer(&pd.wt)
+ resettimer(&pd.wt, pd.wd)
}
} else if pd.wd != wd0 || combo != combo0 {
pd.wseq++ // invalidate current timers
@@ -316,8 +326,13 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
}
}
-// make pd ready, newly runnable goroutines (if any) are added to toRun.
-// May run during STW, so write barriers are not allowed.
+// netpollready is called by the platform-specific netpoll function.
+// It declares that the fd associated with pd is ready for I/O.
+// The toRun argument is used to build a list of goroutines to return
+// from netpoll. The mode argument is 'r', 'w', or 'r'+'w' to indicate
+// whether the fd is ready for reading or writing or both.
+//
+// This may run while the world is stopped, so write barriers are not allowed.
//go:nowritebarrier
func netpollready(toRun *gList, pd *pollDesc, mode int32) {
var rg, wg *g
diff --git a/src/runtime/netpoll_aix.go b/src/runtime/netpoll_aix.go
index 08a9e42dd2e..e1512f826cb 100644
--- a/src/runtime/netpoll_aix.go
+++ b/src/runtime/netpoll_aix.go
@@ -21,12 +21,6 @@ func poll(pfds *pollfd, npfds uintptr, timeout uintptr) (int32, int32) {
return int32(r), int32(err)
}
-//go:nosplit
-func fcntl(fd, cmd int32, arg uintptr) int32 {
- r, _ := syscall3(&libc_fcntl, uintptr(fd), uintptr(cmd), arg)
- return int32(r)
-}
-
// pollfd represents the poll structure for AIX operating system.
type pollfd struct {
fd int32
@@ -38,7 +32,6 @@ const _POLLIN = 0x0001
const _POLLOUT = 0x0002
const _POLLHUP = 0x2000
const _POLLERR = 0x4000
-const _O_NONBLOCK = 0x4
var (
pfds []pollfd
@@ -51,22 +44,13 @@ var (
)
func netpollinit() {
- var p [2]int32
-
// Create the pipe we use to wakeup poll.
- if err := pipe(&p[0]); err < 0 {
+ r, w, errno := nonblockingPipe()
+ if errno != 0 {
throw("netpollinit: failed to create pipe")
}
- rdwake = p[0]
- wrwake = p[1]
-
- fl := uintptr(fcntl(rdwake, _F_GETFL, 0))
- fcntl(rdwake, _F_SETFL, fl|_O_NONBLOCK)
- fcntl(rdwake, _F_SETFD, _FD_CLOEXEC)
-
- fl = uintptr(fcntl(wrwake, _F_GETFL, 0))
- fcntl(wrwake, _F_SETFL, fl|_O_NONBLOCK)
- fcntl(wrwake, _F_SETFD, _FD_CLOEXEC)
+ rdwake = r
+ wrwake = w
// Pre-allocate array of pollfd structures for poll.
pfds = make([]pollfd, 1, 128)
@@ -79,12 +63,8 @@ func netpollinit() {
pds[0] = nil
}
-func netpolldescriptor() uintptr {
- // Both fd must be returned
- if rdwake > 0xFFFF || wrwake > 0xFFFF {
- throw("netpolldescriptor: invalid fd number")
- }
- return uintptr(rdwake<<16 | wrwake)
+func netpollIsPollDescriptor(fd uintptr) bool {
+ return fd == uintptr(rdwake) || fd == uintptr(wrwake)
}
// netpollwakeup writes on wrwake to wakeup poll before any changes.
@@ -148,6 +128,11 @@ func netpollarm(pd *pollDesc, mode int) {
unlock(&mtxset)
}
+// netpollBreak interrupts an epollwait.
+func netpollBreak() {
+ netpollwakeup()
+}
+
// netpoll checks for ready network connections.
// Returns list of goroutines that become runnable.
// delay < 0: blocks indefinitely
@@ -192,8 +177,13 @@ retry:
}
// Check if some descriptors need to be changed
if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
- var b [1]byte
- for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
+ if delay != 0 {
+ // A netpollwakeup could be picked up by a
+ // non-blocking poll. Only clear the wakeup
+ // if blocking.
+ var b [1]byte
+ for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
+ }
}
// Do not look at the other fds in this case as the mode may have changed
// XXX only additions of flags are made, so maybe it is ok
diff --git a/src/runtime/netpoll_epoll.go b/src/runtime/netpoll_epoll.go
index 73dfb4561ea..b9dc18c939e 100644
--- a/src/runtime/netpoll_epoll.go
+++ b/src/runtime/netpoll_epoll.go
@@ -20,24 +20,40 @@ func closeonexec(fd int32)
var (
epfd int32 = -1 // epoll descriptor
+
+ netpollBreakRd, netpollBreakWr uintptr // for netpollBreak
)
func netpollinit() {
epfd = epollcreate1(_EPOLL_CLOEXEC)
- if epfd >= 0 {
- return
- }
- epfd = epollcreate(1024)
- if epfd >= 0 {
+ if epfd < 0 {
+ epfd = epollcreate(1024)
+ if epfd < 0 {
+ println("runtime: epollcreate failed with", -epfd)
+ throw("runtime: netpollinit failed")
+ }
closeonexec(epfd)
- return
}
- println("runtime: epollcreate failed with", -epfd)
- throw("runtime: netpollinit failed")
+ r, w, errno := nonblockingPipe()
+ if errno != 0 {
+ println("runtime: pipe failed with", -errno)
+ throw("runtime: pipe failed")
+ }
+ ev := epollevent{
+ events: _EPOLLIN,
+ }
+ *(**uintptr)(unsafe.Pointer(&ev.data)) = &netpollBreakRd
+ errno = epollctl(epfd, _EPOLL_CTL_ADD, r, &ev)
+ if errno != 0 {
+ println("runtime: epollctl failed with", -errno)
+ throw("runtime: epollctl failed")
+ }
+ netpollBreakRd = uintptr(r)
+ netpollBreakWr = uintptr(w)
}
-func netpolldescriptor() uintptr {
- return uintptr(epfd)
+func netpollIsPollDescriptor(fd uintptr) bool {
+ return fd == uintptr(epfd) || fd == netpollBreakRd || fd == netpollBreakWr
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
@@ -56,6 +72,25 @@ func netpollarm(pd *pollDesc, mode int) {
throw("runtime: unused")
}
+// netpollBreak interrupts an epollwait.
+func netpollBreak() {
+ for {
+ var b byte
+ n := write(netpollBreakWr, unsafe.Pointer(&b), 1)
+ if n == 1 {
+ break
+ }
+ if n == -_EINTR {
+ continue
+ }
+ if n == -_EAGAIN {
+ return
+ }
+ println("runtime: netpollBreak write failed with", -n)
+ throw("runtime: netpollBreak write failed")
+ }
+}
+
// netpoll checks for ready network connections.
// Returns list of goroutines that become runnable.
// delay < 0: blocks indefinitely
@@ -100,6 +135,22 @@ retry:
if ev.events == 0 {
continue
}
+
+ if *(**uintptr)(unsafe.Pointer(&ev.data)) == &netpollBreakRd {
+ if ev.events != _EPOLLIN {
+ println("runtime: netpoll: break fd ready for", ev.events)
+ throw("runtime: netpoll: break fd ready for something unexpected")
+ }
+ if delay != 0 {
+ // netpollBreak could be picked up by a
+ // nonblocking poll. Only read the byte
+ // if blocking.
+ var tmp [16]byte
+ read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp)))
+ }
+ continue
+ }
+
var mode int32
if ev.events&(_EPOLLIN|_EPOLLRDHUP|_EPOLLHUP|_EPOLLERR) != 0 {
mode += 'r'
diff --git a/src/runtime/netpoll_fake.go b/src/runtime/netpoll_fake.go
index 071d87ad50a..b2af3b89b2c 100644
--- a/src/runtime/netpoll_fake.go
+++ b/src/runtime/netpoll_fake.go
@@ -12,8 +12,8 @@ package runtime
func netpollinit() {
}
-func netpolldescriptor() uintptr {
- return ^uintptr(0)
+func netpollIsPollDescriptor(fd uintptr) bool {
+ return false
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
@@ -27,6 +27,9 @@ func netpollclose(fd uintptr) int32 {
func netpollarm(pd *pollDesc, mode int) {
}
+func netpollBreak() {
+}
+
func netpoll(delay int64) gList {
return gList{}
}
diff --git a/src/runtime/netpoll_kqueue.go b/src/runtime/netpoll_kqueue.go
index ce8da73d1e4..22902aa7869 100644
--- a/src/runtime/netpoll_kqueue.go
+++ b/src/runtime/netpoll_kqueue.go
@@ -12,6 +12,8 @@ import "unsafe"
var (
kq int32 = -1
+
+ netpollBreakRd, netpollBreakWr uintptr // for netpollBreak
)
func netpollinit() {
@@ -21,10 +23,27 @@ func netpollinit() {
throw("runtime: netpollinit failed")
}
closeonexec(kq)
+ r, w, errno := nonblockingPipe()
+ if errno != 0 {
+ println("runtime: pipe failed with", -errno)
+ throw("runtime: pipe failed")
+ }
+ ev := keventt{
+ filter: _EVFILT_READ,
+ flags: _EV_ADD,
+ }
+ *(*uintptr)(unsafe.Pointer(&ev.ident)) = uintptr(r)
+ n := kevent(kq, &ev, 1, nil, 0, nil)
+ if n < 0 {
+ println("runtime: kevent failed with", -n)
+ throw("runtime: kevent failed")
+ }
+ netpollBreakRd = uintptr(r)
+ netpollBreakWr = uintptr(w)
}
-func netpolldescriptor() uintptr {
- return uintptr(kq)
+func netpollIsPollDescriptor(fd uintptr) bool {
+ return fd == uintptr(kq) || fd == netpollBreakRd || fd == netpollBreakWr
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
@@ -57,6 +76,22 @@ func netpollarm(pd *pollDesc, mode int) {
throw("runtime: unused")
}
+// netpollBreak interrupts an epollwait.
+func netpollBreak() {
+ for {
+ var b byte
+ n := write(netpollBreakWr, unsafe.Pointer(&b), 1)
+ if n == 1 || n == -_EAGAIN {
+ break
+ }
+ if n == -_EINTR {
+ continue
+ }
+ println("runtime: netpollBreak write failed with", -n)
+ throw("runtime: netpollBreak write failed")
+ }
+}
+
// netpoll checks for ready network connections.
// Returns list of goroutines that become runnable.
// delay < 0: blocks indefinitely
@@ -98,6 +133,22 @@ retry:
var toRun gList
for i := 0; i < int(n); i++ {
ev := &events[i]
+
+ if uintptr(ev.ident) == netpollBreakRd {
+ if ev.filter != _EVFILT_READ {
+ println("runtime: netpoll: break fd ready for", ev.filter)
+ throw("runtime: netpoll: break fd ready for something unexpected")
+ }
+ if delay != 0 {
+ // netpollBreak could be picked up by a
+ // nonblocking poll. Only read the byte
+ // if blocking.
+ var tmp [16]byte
+ read(int32(netpollBreakRd), noescape(unsafe.Pointer(&tmp[0])), int32(len(tmp)))
+ }
+ continue
+ }
+
var mode int32
switch ev.filter {
case _EVFILT_READ:
diff --git a/src/runtime/netpoll_solaris.go b/src/runtime/netpoll_solaris.go
index ad41ab5af21..fac4829ed1b 100644
--- a/src/runtime/netpoll_solaris.go
+++ b/src/runtime/netpoll_solaris.go
@@ -71,17 +71,20 @@ import "unsafe"
//go:cgo_import_dynamic libc_port_associate port_associate "libc.so"
//go:cgo_import_dynamic libc_port_dissociate port_dissociate "libc.so"
//go:cgo_import_dynamic libc_port_getn port_getn "libc.so"
+//go:cgo_import_dynamic libc_port_alert port_alert "libc.so"
//go:linkname libc_port_create libc_port_create
//go:linkname libc_port_associate libc_port_associate
//go:linkname libc_port_dissociate libc_port_dissociate
//go:linkname libc_port_getn libc_port_getn
+//go:linkname libc_port_alert libc_port_alert
var (
libc_port_create,
libc_port_associate,
libc_port_dissociate,
- libc_port_getn libcFunc
+ libc_port_getn,
+ libc_port_alert libcFunc
)
func errno() int32 {
@@ -108,6 +111,10 @@ func port_getn(port int32, evs *portevent, max uint32, nget *uint32, timeout *ti
return int32(sysvicall5(&libc_port_getn, uintptr(port), uintptr(unsafe.Pointer(evs)), uintptr(max), uintptr(unsafe.Pointer(nget)), uintptr(unsafe.Pointer(timeout))))
}
+func port_alert(port int32, flags, events uint32, user uintptr) int32 {
+ return int32(sysvicall4(&libc_port_alert, uintptr(port), uintptr(flags), uintptr(events), user))
+}
+
var portfd int32 = -1
func netpollinit() {
@@ -121,8 +128,8 @@ func netpollinit() {
throw("runtime: netpollinit failed")
}
-func netpolldescriptor() uintptr {
- return uintptr(portfd)
+func netpollIsPollDescriptor(fd uintptr) bool {
+ return fd == uintptr(portfd)
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
@@ -178,6 +185,21 @@ func netpollarm(pd *pollDesc, mode int) {
unlock(&pd.lock)
}
+// netpollBreak interrupts a port_getn wait.
+func netpollBreak() {
+ // Use port_alert to put portfd into alert mode.
+ // This will wake up all threads sleeping in port_getn on portfd,
+ // and cause their calls to port_getn to return immediately.
+ // Further, until portfd is taken out of alert mode,
+ // all calls to port_getn will return immediately.
+ if port_alert(portfd, _PORT_ALERT_UPDATE, _POLLHUP, uintptr(unsafe.Pointer(&portfd))) < 0 {
+ if e := errno(); e != _EBUSY {
+ println("runtime: port_alert failed with", e)
+ throw("runtime: netpoll: port_alert failed")
+ }
+ }
+}
+
// netpoll checks for ready network connections.
// Returns list of goroutines that become runnable.
// delay < 0: blocks indefinitely
@@ -224,6 +246,24 @@ retry:
for i := 0; i < int(n); i++ {
ev := &events[i]
+ if ev.portev_source == _PORT_SOURCE_ALERT {
+ if ev.portev_events != _POLLHUP || unsafe.Pointer(ev.portev_user) != unsafe.Pointer(&portfd) {
+ throw("runtime: netpoll: bad port_alert wakeup")
+ }
+ if delay != 0 {
+ // Now that a blocking call to netpoll
+ // has seen the alert, take portfd
+ // back out of alert mode.
+ // See the comment in netpollBreak.
+ if port_alert(portfd, 0, 0, 0) < 0 {
+ e := errno()
+ println("runtime: port_alert failed with", e)
+ throw("runtime: netpoll: port_alert failed")
+ }
+ }
+ continue
+ }
+
if ev.portev_events == 0 {
continue
}
diff --git a/src/runtime/netpoll_stub.go b/src/runtime/netpoll_stub.go
index 3437a274915..ab92b0424e0 100644
--- a/src/runtime/netpoll_stub.go
+++ b/src/runtime/netpoll_stub.go
@@ -6,13 +6,34 @@
package runtime
+import "runtime/internal/atomic"
+
+var netpollInited uint32
var netpollWaiters uint32
+var netpollStubLock mutex
+var netpollNote note
+var netpollBroken uint32
+
+func netpollGenericInit() {
+}
+
+func netpollBreak() {
+ if atomic.Cas(&netpollBroken, 0, 1) {
+ notewakeup(&netpollNote)
+ }
+}
+
// Polls for ready network connections.
// Returns list of goroutines that become runnable.
func netpoll(delay int64) gList {
// Implementation for platforms that do not support
// integrated network poller.
+ if delay != 0 {
+ noteclear(&netpollNote)
+ atomic.Store(&netpollBroken, 0)
+ notetsleep(&netpollNote, delay)
+ }
return gList{}
}
diff --git a/src/runtime/netpoll_windows.go b/src/runtime/netpoll_windows.go
index fde413677a9..ced52cbd3a3 100644
--- a/src/runtime/netpoll_windows.go
+++ b/src/runtime/netpoll_windows.go
@@ -41,8 +41,8 @@ func netpollinit() {
}
}
-func netpolldescriptor() uintptr {
- return iocphandle
+func netpollIsPollDescriptor(fd uintptr) bool {
+ return fd == iocphandle
}
func netpollopen(fd uintptr, pd *pollDesc) int32 {
@@ -61,6 +61,13 @@ func netpollarm(pd *pollDesc, mode int) {
throw("runtime: unused")
}
+func netpollBreak() {
+ if stdcall4(_PostQueuedCompletionStatus, iocphandle, 0, 0, 0) == 0 {
+ println("runtime: netpoll: PostQueuedCompletionStatus failed (errno=", getlasterror(), ")")
+ throw("runtime: netpoll: PostQueuedCompletionStatus failed")
+ }
+}
+
// netpoll checks for ready network connections.
// Returns list of goroutines that become runnable.
// delay < 0: blocks indefinitely
@@ -112,12 +119,20 @@ func netpoll(delay int64) gList {
mp.blocked = false
for i = 0; i < n; i++ {
op = entries[i].op
- errno = 0
- qty = 0
- if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
- errno = int32(getlasterror())
+ if op != nil {
+ errno = 0
+ qty = 0
+ if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
+ errno = int32(getlasterror())
+ }
+ handlecompletion(&toRun, op, errno, qty)
+ } else {
+ if delay == 0 {
+ // Forward the notification to the
+ // blocked poller.
+ netpollBreak()
+ }
}
- handlecompletion(&toRun, op, errno, qty)
}
} else {
op = nil
@@ -139,6 +154,14 @@ func netpoll(delay int64) gList {
// dequeued failed IO packet, so report that
}
mp.blocked = false
+ if op == nil {
+ if delay == 0 {
+ // Forward the notification to the
+ // blocked poller.
+ netpollBreak()
+ }
+ return gList{}
+ }
handlecompletion(&toRun, op, errno, qty)
}
return toRun
diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go
index b79fa08dc2f..7f69d6d1e37 100644
--- a/src/runtime/os2_aix.go
+++ b/src/runtime/os2_aix.go
@@ -399,16 +399,23 @@ func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
// Check the validity of g because without a g during
// newosproc0.
if _g_ != nil {
- r, _ := syscall3(&libc_write, uintptr(fd), uintptr(p), uintptr(n))
+ r, errno := syscall3(&libc_write, uintptr(fd), uintptr(p), uintptr(n))
+ if int32(r) < 0 {
+ return -int32(errno)
+ }
return int32(r)
}
+ // Note that in this case we can't return a valid errno value.
return write2(fd, uintptr(p), n)
}
//go:nosplit
func read(fd int32, p unsafe.Pointer, n int32) int32 {
- r, _ := syscall3(&libc_read, uintptr(fd), uintptr(p), uintptr(n))
+ r, errno := syscall3(&libc_read, uintptr(fd), uintptr(p), uintptr(n))
+ if int32(r) < 0 {
+ return -int32(errno)
+ }
return int32(r)
}
@@ -425,9 +432,10 @@ func closefd(fd int32) int32 {
}
//go:nosplit
-func pipe(fd *int32) int32 {
- r, _ := syscall1(&libc_pipe, uintptr(unsafe.Pointer(fd)))
- return int32(r)
+func pipe() (r, w int32, errno int32) {
+ var p [2]int32
+ _, err := syscall1(&libc_pipe, uintptr(noescape(unsafe.Pointer(&p[0]))))
+ return p[0], p[1], int32(err)
}
// mmap calls the mmap system call.
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index cdec190de52..4ac191fab8e 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -447,7 +447,11 @@ func raiseproc(sig uint32) /* int32 */ {
//go:nosplit
func read(fd int32, buf unsafe.Pointer, nbyte int32) int32 {
- return int32(sysvicall3(&libc_read, uintptr(fd), uintptr(buf), uintptr(nbyte)))
+ r1, err := sysvicall3Err(&libc_read, uintptr(fd), uintptr(buf), uintptr(nbyte))
+ if c := int32(r1); c >= 0 {
+ return c
+ }
+ return -int32(err)
}
//go:nosplit
@@ -511,7 +515,11 @@ func walltime1() (sec int64, nsec int32) {
//go:nosplit
func write1(fd uintptr, buf unsafe.Pointer, nbyte int32) int32 {
- return int32(sysvicall3(&libc_write, uintptr(fd), uintptr(buf), uintptr(nbyte)))
+ r1, err := sysvicall3Err(&libc_write, fd, uintptr(buf), uintptr(nbyte))
+ if c := int32(r1); c >= 0 {
+ return c
+ }
+ return -int32(err)
}
func osyield1()
diff --git a/src/runtime/os_aix.go b/src/runtime/os_aix.go
index 84d86e5ff12..855ae6ff469 100644
--- a/src/runtime/os_aix.go
+++ b/src/runtime/os_aix.go
@@ -357,3 +357,20 @@ func setupSystemConf() {
cpu.HWCap2 |= cpu.PPC_FEATURE2_ARCH_3_00
}
}
+
+//go:nosplit
+func fcntl(fd, cmd int32, arg uintptr) int32 {
+ r, _ := syscall3(&libc_fcntl, uintptr(fd), uintptr(cmd), arg)
+ return int32(r)
+}
+
+//go:nosplit
+func closeonexec(fd int32) {
+ fcntl(fd, _F_SETFD, _FD_CLOEXEC)
+}
+
+//go:nosplit
+func setNonblock(fd int32) {
+ flags := fcntl(fd, _F_GETFL, 0)
+ fcntl(fd, _F_SETFL, uintptr(flags|_O_NONBLOCK))
+}
diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go
index 4fda7ea8060..3266b2623a5 100644
--- a/src/runtime/os_dragonfly.go
+++ b/src/runtime/os_dragonfly.go
@@ -54,6 +54,9 @@ func kqueue() int32
//go:noescape
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32
func closeonexec(fd int32)
+func setNonblock(fd int32)
+
+func pipe() (r, w int32, errno int32)
const stackSystem = 0
diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go
index cbb72cf55e6..183d8ab9c7c 100644
--- a/src/runtime/os_freebsd.go
+++ b/src/runtime/os_freebsd.go
@@ -39,6 +39,7 @@ func kqueue() int32
//go:noescape
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32
func closeonexec(fd int32)
+func setNonblock(fd int32)
// From FreeBSD's
const (
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
index d4a9bd4ff5f..b1ddf53dd1d 100644
--- a/src/runtime/os_linux.go
+++ b/src/runtime/os_linux.go
@@ -372,6 +372,8 @@ func raiseproc(sig uint32)
func sched_getaffinity(pid, len uintptr, buf *byte) int32
func osyield()
+func setNonblock(fd int32)
+
//go:nosplit
//go:nowritebarrierrec
func setsig(i uint32, fn uintptr) {
diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go
index da024cd309b..3cb9411a9cd 100644
--- a/src/runtime/os_netbsd.go
+++ b/src/runtime/os_netbsd.go
@@ -24,8 +24,6 @@ const (
// From
_LWP_DETACHED = 0x00000040
-
- _EAGAIN = 35
)
type mOS struct {
@@ -73,6 +71,7 @@ func kqueue() int32
//go:noescape
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32
func closeonexec(fd int32)
+func setNonblock(fd int32)
const (
_ESRCH = 3
diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go
index 2d6334ec863..351a99f7e9f 100644
--- a/src/runtime/os_openbsd.go
+++ b/src/runtime/os_openbsd.go
@@ -61,10 +61,10 @@ func kqueue() int32
//go:noescape
func kevent(kq int32, ch *keventt, nch int32, ev *keventt, nev int32, ts *timespec) int32
func closeonexec(fd int32)
+func setNonblock(fd int32)
const (
_ESRCH = 3
- _EAGAIN = 35
_EWOULDBLOCK = _EAGAIN
_ENOTSUP = 91
diff --git a/src/runtime/os_solaris.go b/src/runtime/os_solaris.go
index 989edb5dcdf..d6c09156bd5 100644
--- a/src/runtime/os_solaris.go
+++ b/src/runtime/os_solaris.go
@@ -122,6 +122,16 @@ func sysvicall2(fn *libcFunc, a1, a2 uintptr) uintptr {
//go:nosplit
func sysvicall3(fn *libcFunc, a1, a2, a3 uintptr) uintptr {
+ r1, _ := sysvicall3Err(fn, a1, a2, a3)
+ return r1
+}
+
+//go:nosplit
+//go:cgo_unsafe_args
+
+// sysvicall3Err returns both the system call result and the errno value.
+// This is used by sysicall3 and write1.
+func sysvicall3Err(fn *libcFunc, a1, a2, a3 uintptr) (r1, err uintptr) {
// Leave caller's PC/SP around for traceback.
gp := getg()
var mp *m
@@ -146,7 +156,7 @@ func sysvicall3(fn *libcFunc, a1, a2, a3 uintptr) uintptr {
if mp != nil {
mp.libcallsp = 0
}
- return libcall.r1
+ return libcall.r1, libcall.err
}
//go:nosplit
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index 34d0627fcbf..764db6edb0c 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -34,6 +34,7 @@ const (
//go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
@@ -80,6 +81,7 @@ var (
_GetThreadContext,
_LoadLibraryW,
_LoadLibraryA,
+ _PostQueuedCompletionStatus,
_QueryPerformanceCounter,
_QueryPerformanceFrequency,
_ResumeThread,
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 5f33cd7c0c3..bdfe117e459 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -10,6 +10,19 @@ import (
"unsafe"
)
+// We have two different ways of doing defers. The older way involves creating a
+// defer record at the time that a defer statement is executing and adding it to a
+// defer chain. This chain is inspected by the deferreturn call at all function
+// exits in order to run the appropriate defer calls. A cheaper way (which we call
+// open-coded defers) is used for functions in which no defer statements occur in
+// loops. In that case, we simply store the defer function/arg information into
+// specific stack slots at the point of each defer statement, as well as setting a
+// bit in a bitmask. At each function exit, we add inline code to directly make
+// the appropriate defer calls based on the bitmask and fn/arg information stored
+// on the stack. During panic/Goexit processing, the appropriate defer calls are
+// made using extra funcdata info that indicates the exact stack slots that
+// contain the bitmask and defer fn/args.
+
// Check to make sure we can really generate a panic. If the panic
// was generated from the runtime, or from inside malloc, then convert
// to a throw of msg.
@@ -263,19 +276,24 @@ func deferprocStack(d *_defer) {
// are initialized here.
d.started = false
d.heap = false
+ d.openDefer = false
d.sp = getcallersp()
d.pc = getcallerpc()
+ d.framepc = 0
+ d.varp = 0
// The lines below implement:
// d.panic = nil
+ // d.fp = nil
// d.link = gp._defer
// gp._defer = d
- // But without write barriers. The first two are writes to
+ // But without write barriers. The first three are writes to
// the stack so they don't need a write barrier, and furthermore
// are to uninitialized memory, so they must not use a write barrier.
- // The third write does not require a write barrier because we
+ // The fourth write does not require a write barrier because we
// explicitly mark all the defer structures, so we don't need to
// keep track of pointers to them with a write barrier.
*(*uintptr)(unsafe.Pointer(&d._panic)) = 0
+ *(*uintptr)(unsafe.Pointer(&d.fd)) = 0
*(*uintptr)(unsafe.Pointer(&d.link)) = uintptr(unsafe.Pointer(gp._defer))
*(*uintptr)(unsafe.Pointer(&gp._defer)) = uintptr(unsafe.Pointer(d))
@@ -463,8 +481,12 @@ func freedefer(d *_defer) {
// started causing a nosplit stack overflow via typedmemmove.
d.siz = 0
d.started = false
+ d.openDefer = false
d.sp = 0
d.pc = 0
+ d.framepc = 0
+ d.varp = 0
+ d.fd = nil
// d._panic and d.fn must be nil already.
// If not, we would have called freedeferpanic or freedeferfn above,
// both of which throw.
@@ -493,9 +515,11 @@ func freedeferfn() {
// to have been called by the caller of deferreturn at the point
// just before deferreturn was called. The effect is that deferreturn
// is called again and again until there are no more deferred functions.
-// Cannot split the stack because we reuse the caller's frame to
-// call the deferred function.
-
+//
+// Declared as nosplit, because the function should not be preempted once we start
+// modifying the caller's frame in order to reuse the frame to call the deferred
+// function.
+//
// The single argument isn't actually used - it just has its address
// taken so it can be matched against pending defers.
//go:nosplit
@@ -509,6 +533,15 @@ func deferreturn(arg0 uintptr) {
if d.sp != sp {
return
}
+ if d.openDefer {
+ done := runOpenDeferFrame(gp, d)
+ if !done {
+ throw("unfinished open-coded defers in deferreturn")
+ }
+ gp._defer = d.link
+ freedefer(d)
+ return
+ }
// Moving arguments around.
//
@@ -544,6 +577,8 @@ func Goexit() {
// This code is similar to gopanic, see that implementation
// for detailed comments.
gp := getg()
+ addOneOpenDeferFrame(gp, getcallerpc(), unsafe.Pointer(getcallersp()))
+
for {
d := gp._defer
if d == nil {
@@ -554,13 +589,26 @@ func Goexit() {
d._panic.aborted = true
d._panic = nil
}
- d.fn = nil
- gp._defer = d.link
- freedefer(d)
- continue
+ if !d.openDefer {
+ d.fn = nil
+ gp._defer = d.link
+ freedefer(d)
+ continue
+ }
}
d.started = true
- reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
+ if d.openDefer {
+ done := runOpenDeferFrame(gp, d)
+ if !done {
+ // We should always run all defers in the frame,
+ // since there is no panic associated with this
+ // defer that can be recovered.
+ throw("unfinished open-coded defers in Goexit")
+ }
+ addOneOpenDeferFrame(gp, 0, nil)
+ } else {
+ reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
+ }
if gp._defer != d {
throw("bad defer entry in Goexit")
}
@@ -607,6 +655,177 @@ func printpanics(p *_panic) {
print("\n")
}
+// addOneOpenDeferFrame scans the stack for the first frame (if any) with
+// open-coded defers and if it finds one, adds a single record to the defer chain
+// for that frame. If sp is non-nil, it starts the stack scan from the frame
+// specified by sp. If sp is nil, it uses the sp from the current defer record
+// (which has just been finished). Hence, it continues the stack scan from the
+// frame of the defer that just finished. It skips any frame that already has an
+// open-coded _defer record, which would have been been created from a previous
+// (unrecovered) panic.
+//
+// Note: All entries of the defer chain (including this new open-coded entry) have
+// their pointers (including sp) adjusted properly if the stack moves while
+// running deferred functions. Also, it is safe to pass in the sp arg (which is
+// the direct result of calling getcallersp()), because all pointer variables
+// (including arguments) are adjusted as needed during stack copies.
+func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) {
+ var prevDefer *_defer
+ if sp == nil {
+ prevDefer = gp._defer
+ pc = prevDefer.framepc
+ sp = unsafe.Pointer(prevDefer.sp)
+ }
+ systemstack(func() {
+ gentraceback(pc, uintptr(sp), 0, gp, 0, nil, 0x7fffffff,
+ func(frame *stkframe, unused unsafe.Pointer) bool {
+ if prevDefer != nil && prevDefer.sp == frame.sp {
+ // Skip the frame for the previous defer that
+ // we just finished (and was used to set
+ // where we restarted the stack scan)
+ return true
+ }
+ f := frame.fn
+ fd := funcdata(f, _FUNCDATA_OpenCodedDeferInfo)
+ if fd == nil {
+ return true
+ }
+ // Insert the open defer record in the
+ // chain, in order sorted by sp.
+ d := gp._defer
+ var prev *_defer
+ for d != nil {
+ dsp := d.sp
+ if frame.sp < dsp {
+ break
+ }
+ if frame.sp == dsp {
+ if !d.openDefer {
+ throw("duplicated defer entry")
+ }
+ return true
+ }
+ prev = d
+ d = d.link
+ }
+ if frame.fn.deferreturn == 0 {
+ throw("missing deferreturn")
+ }
+
+ maxargsize, _ := readvarintUnsafe(fd)
+ d1 := newdefer(int32(maxargsize))
+ d1.openDefer = true
+ d1._panic = nil
+ // These are the pc/sp to set after we've
+ // run a defer in this frame that did a
+ // recover. We return to a special
+ // deferreturn that runs any remaining
+ // defers and then returns from the
+ // function.
+ d1.pc = frame.fn.entry + uintptr(frame.fn.deferreturn)
+ d1.varp = frame.varp
+ d1.fd = fd
+ // Save the SP/PC associated with current frame,
+ // so we can continue stack trace later if needed.
+ d1.framepc = frame.pc
+ d1.sp = frame.sp
+ d1.link = d
+ if prev == nil {
+ gp._defer = d1
+ } else {
+ prev.link = d1
+ }
+ // Stop stack scanning after adding one open defer record
+ return false
+ },
+ nil, 0)
+ })
+}
+
+// readvarintUnsafe reads the uint32 in varint format starting at fd, and returns the
+// uint32 and a pointer to the byte following the varint.
+//
+// There is a similar function runtime.readvarint, which takes a slice of bytes,
+// rather than an unsafe pointer. These functions are duplicated, because one of
+// the two use cases for the functions would get slower if the functions were
+// combined.
+func readvarintUnsafe(fd unsafe.Pointer) (uint32, unsafe.Pointer) {
+ var r uint32
+ var shift int
+ for {
+ b := *(*uint8)((unsafe.Pointer(fd)))
+ fd = add(fd, unsafe.Sizeof(b))
+ if b < 128 {
+ return r + uint32(b)< 28 {
+ panic("Bad varint")
+ }
+ }
+}
+
+// runOpenDeferFrame runs the active open-coded defers in the frame specified by
+// d. It normally processes all active defers in the frame, but stops immediately
+// if a defer does a successful recover. It returns true if there are no
+// remaining defers to run in the frame.
+func runOpenDeferFrame(gp *g, d *_defer) bool {
+ done := true
+ fd := d.fd
+
+ // Skip the maxargsize
+ _, fd = readvarintUnsafe(fd)
+ deferBitsOffset, fd := readvarintUnsafe(fd)
+ nDefers, fd := readvarintUnsafe(fd)
+ deferBits := *(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset)))
+
+ for i := int(nDefers) - 1; i >= 0; i-- {
+ // read the funcdata info for this defer
+ var argWidth, closureOffset, nArgs uint32
+ argWidth, fd = readvarintUnsafe(fd)
+ closureOffset, fd = readvarintUnsafe(fd)
+ nArgs, fd = readvarintUnsafe(fd)
+ if deferBits&(1<= number of busy P's, block.
// This is necessary to prevent excessive CPU consumption
// when GOMAXPROCS>>1 but the program parallelism is low.
@@ -2287,11 +2290,48 @@ top:
goto top
}
stealRunNextG := i > 2 // first look for ready queues with more than 1 g
- if gp := runqsteal(_p_, allp[enum.position()], stealRunNextG); gp != nil {
+ p2 := allp[enum.position()]
+ if _p_ == p2 {
+ continue
+ }
+ if gp := runqsteal(_p_, p2, stealRunNextG); gp != nil {
return gp, false
}
+
+ // Consider stealing timers from p2.
+ // This call to checkTimers is the only place where
+ // we hold a lock on a different P's timers.
+ // Lock contention can be a problem here, so avoid
+ // grabbing the lock if p2 is running and not marked
+ // for preemption. If p2 is running and not being
+ // preempted we assume it will handle its own timers.
+ if i > 2 && shouldStealTimers(p2) {
+ tnow, w, ran := checkTimers(p2, now)
+ now = tnow
+ if w != 0 && (pollUntil == 0 || w < pollUntil) {
+ pollUntil = w
+ }
+ if ran {
+ // Running the timers may have
+ // made an arbitrary number of G's
+ // ready and added them to this P's
+ // local run queue. That invalidates
+ // the assumption of runqsteal
+ // that is always has room to add
+ // stolen G's. So check now if there
+ // is a local G to run.
+ if gp, inheritTime := runqget(_p_); gp != nil {
+ return gp, inheritTime
+ }
+ ranTimer = true
+ }
+ }
}
}
+ if ranTimer {
+ // Running a timer may have made some goroutine ready.
+ goto top
+ }
stop:
@@ -2308,10 +2348,16 @@ stop:
return gp, false
}
+ delta := int64(-1)
+ if pollUntil != 0 {
+ // checkTimers ensures that polluntil > now.
+ delta = pollUntil - now
+ }
+
// wasm only:
// If a callback returned and no other goroutine is awake,
// then pause execution until a callback was triggered.
- if beforeIdle() {
+ if beforeIdle(delta) {
// At least one goroutine got woken.
goto top
}
@@ -2399,15 +2445,27 @@ stop:
}
// poll network
- if netpollinited() && atomic.Load(&netpollWaiters) > 0 && atomic.Xchg64(&sched.lastpoll, 0) != 0 {
+ if netpollinited() && (atomic.Load(&netpollWaiters) > 0 || pollUntil != 0) && atomic.Xchg64(&sched.lastpoll, 0) != 0 {
+ atomic.Store64(&sched.pollUntil, uint64(pollUntil))
if _g_.m.p != 0 {
throw("findrunnable: netpoll with p")
}
if _g_.m.spinning {
throw("findrunnable: netpoll with spinning")
}
- list := netpoll(-1) // block until new work is available
+ if faketime != 0 {
+ // When using fake time, just poll.
+ delta = 0
+ }
+ list := netpoll(delta) // block until new work is available
+ atomic.Store64(&sched.pollUntil, 0)
atomic.Store64(&sched.lastpoll, uint64(nanotime()))
+ if faketime != 0 && list.empty() {
+ // Using fake time and nothing is ready; stop M.
+ // When all M's stop, checkdead will call timejump.
+ stopm()
+ goto top
+ }
lock(&sched.lock)
_p_ = pidleget()
unlock(&sched.lock)
@@ -2430,6 +2488,11 @@ stop:
}
goto top
}
+ } else if pollUntil != 0 && netpollinited() {
+ pollerPollUntil := int64(atomic.Load64(&sched.pollUntil))
+ if pollerPollUntil == 0 || pollerPollUntil > pollUntil {
+ netpollBreak()
+ }
}
stopm()
goto top
@@ -2456,6 +2519,22 @@ func pollWork() bool {
return false
}
+// wakeNetPoller wakes up the thread sleeping in the network poller,
+// if there is one, and if it isn't going to wake up anyhow before
+// the when argument.
+func wakeNetPoller(when int64) {
+ if atomic.Load64(&sched.lastpoll) == 0 {
+ // In findrunnable we ensure that when polling the pollUntil
+ // field is either zero or the time to which the current
+ // poll is expected to run. This can have a spurious wakeup
+ // but should never miss a wakeup.
+ pollerPollUntil := int64(atomic.Load64(&sched.pollUntil))
+ if pollerPollUntil == 0 || pollerPollUntil > when {
+ netpollBreak()
+ }
+ }
+}
+
func resetspinning() {
_g_ := getg()
if !_g_.m.spinning {
@@ -2524,10 +2603,20 @@ top:
gcstopm()
goto top
}
- if _g_.m.p.ptr().runSafePointFn != 0 {
+ pp := _g_.m.p.ptr()
+ if pp.runSafePointFn != 0 {
runSafePointFn()
}
+ // Sanity check: if we are spinning, the run queue should be empty.
+ // Check this before calling checkTimers, as that might call
+ // goready to put a ready goroutine on the local run queue.
+ if _g_.m.spinning && (pp.runnext != 0 || pp.runqhead != pp.runqtail) {
+ throw("schedule: spinning with local work")
+ }
+
+ checkTimers(pp, 0)
+
var gp *g
var inheritTime bool
@@ -2559,9 +2648,8 @@ top:
}
if gp == nil {
gp, inheritTime = runqget(_g_.m.p.ptr())
- if gp != nil && _g_.m.spinning {
- throw("schedule: spinning with local work")
- }
+ // We can see gp != nil here even if the M is spinning,
+ // if checkTimers added a local goroutine via goready.
}
if gp == nil {
gp, inheritTime = findrunnable() // blocks until work is available
@@ -2622,6 +2710,60 @@ func dropg() {
setGNoWB(&_g_.m.curg, nil)
}
+// checkTimers runs any timers for the P that are ready.
+// If now is not 0 it is the current time.
+// It returns the current time or 0 if it is not known,
+// and the time when the next timer should run or 0 if there is no next timer,
+// and reports whether it ran any timers.
+// If the time when the next timer should run is not 0,
+// it is always larger than the returned time.
+// We pass now in and out to avoid extra calls of nanotime.
+//go:yeswritebarrierrec
+func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) {
+ lock(&pp.timersLock)
+
+ adjusttimers(pp)
+
+ rnow = now
+ if len(pp.timers) > 0 {
+ if rnow == 0 {
+ rnow = nanotime()
+ }
+ for len(pp.timers) > 0 {
+ if tw := runtimer(pp, rnow); tw != 0 {
+ if tw > 0 {
+ pollUntil = tw
+ }
+ break
+ }
+ ran = true
+ }
+ }
+
+ unlock(&pp.timersLock)
+
+ return rnow, pollUntil, ran
+}
+
+// shouldStealTimers reports whether we should try stealing the timers from p2.
+// We don't steal timers from a running P that is not marked for preemption,
+// on the assumption that it will run its own timers. This reduces
+// contention on the timers lock.
+func shouldStealTimers(p2 *p) bool {
+ if p2.status != _Prunning {
+ return true
+ }
+ mp := p2.m.ptr()
+ if mp == nil || mp.locks > 0 {
+ return false
+ }
+ gp := mp.curg
+ if gp == nil || gp.atomicstatus != _Grunning || !gp.preempt {
+ return false
+ }
+ return true
+}
+
func parkunlock_c(gp *g, lock unsafe.Pointer) bool {
unlock((*mutex)(lock))
return true
@@ -3280,6 +3422,9 @@ func malg(stacksize int32) *g {
})
newg.stackguard0 = newg.stack.lo + _StackGuard
newg.stackguard1 = ^uintptr(0)
+ // Clear the bottom word of the stack. We record g
+ // there on gsignal stack during VDSO on ARM and ARM64.
+ *(*uintptr)(unsafe.Pointer(newg.stack.lo)) = 0
}
return newg
}
@@ -3987,6 +4132,13 @@ func (pp *p) destroy() {
globrunqputhead(pp.runnext.ptr())
pp.runnext = 0
}
+ if len(pp.timers) > 0 {
+ plocal := getg().m.p.ptr()
+ // The world is stopped so we don't need to hold timersLock.
+ moveTimers(plocal, pp.timers)
+ pp.timers = nil
+ pp.adjustTimers = 0
+ }
// If there's a background worker, make it runnable and put
// it on the global queue so it can clean itself up.
if gp := pp.gcBgMarkWorker.ptr(); gp != nil {
@@ -4019,6 +4171,21 @@ func (pp *p) destroy() {
gfpurge(pp)
traceProcFree(pp)
if raceenabled {
+ if pp.timerRaceCtx != 0 {
+ // The race detector code uses a callback to fetch
+ // the proc context, so arrange for that callback
+ // to see the right thing.
+ // This hack only works because we are the only
+ // thread running.
+ mp := getg().m
+ phold := mp.p.ptr()
+ mp.p.set(pp)
+
+ racectxend(pp.timerRaceCtx)
+ pp.timerRaceCtx = 0
+
+ mp.p.set(phold)
+ }
raceprocdestroy(pp.raceprocctx)
pp.raceprocctx = 0
}
@@ -4285,23 +4452,51 @@ func checkdead() {
}
// Maybe jump time forward for playground.
- gp := timejump()
- if gp != nil {
- casgstatus(gp, _Gwaiting, _Grunnable)
- globrunqput(gp)
- _p_ := pidleget()
- if _p_ == nil {
- throw("checkdead: no p for timer")
+ if oldTimers {
+ gp := timejumpOld()
+ if gp != nil {
+ casgstatus(gp, _Gwaiting, _Grunnable)
+ globrunqput(gp)
+ _p_ := pidleget()
+ if _p_ == nil {
+ throw("checkdead: no p for timer")
+ }
+ mp := mget()
+ if mp == nil {
+ // There should always be a free M since
+ // nothing is running.
+ throw("checkdead: no m for timer")
+ }
+ mp.nextp.set(_p_)
+ notewakeup(&mp.park)
+ return
}
- mp := mget()
- if mp == nil {
- // There should always be a free M since
- // nothing is running.
- throw("checkdead: no m for timer")
+ } else {
+ _p_ := timejump()
+ if _p_ != nil {
+ for pp := &sched.pidle; *pp != 0; pp = &(*pp).ptr().link {
+ if (*pp).ptr() == _p_ {
+ *pp = _p_.link
+ break
+ }
+ }
+ mp := mget()
+ if mp == nil {
+ // There should always be a free M since
+ // nothing is running.
+ throw("checkdead: no m for timer")
+ }
+ mp.nextp.set(_p_)
+ notewakeup(&mp.park)
+ return
+ }
+ }
+
+ // There are no goroutines running, so we can look at the P's.
+ for _, _p_ := range allp {
+ if len(_p_.timers) > 0 {
+ return
}
- mp.nextp.set(_p_)
- notewakeup(&mp.park)
- return
}
getg().m.throwing = -1 // do not dump full stacks
@@ -4391,6 +4586,12 @@ func sysmon() {
incidlelocked(1)
}
}
+ if timeSleepUntil() < now {
+ // There are timers that should have already run,
+ // perhaps because there is an unpreemptible P.
+ // Try to start an M to run them.
+ startm(nil, false)
+ }
// retake P's blocked in syscalls
// and preempt long running G's
if retake(now) != 0 {
diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go
index 6e6272e80a2..3a1bf91fa5d 100644
--- a/src/runtime/proc_test.go
+++ b/src/runtime/proc_test.go
@@ -981,3 +981,42 @@ func TestPreemptionAfterSyscall(t *testing.T) {
func TestGetgThreadSwitch(t *testing.T) {
runtime.RunGetgThreadSwitchTest()
}
+
+// TestNetpollBreak tests that netpollBreak can break a netpoll.
+// This test is not particularly safe since the call to netpoll
+// will pick up any stray files that are ready, but it should work
+// OK as long it is not run in parallel.
+func TestNetpollBreak(t *testing.T) {
+ if runtime.GOMAXPROCS(0) == 1 {
+ t.Skip("skipping: GOMAXPROCS=1")
+ }
+
+ // Make sure that netpoll is initialized.
+ time.Sleep(1)
+
+ start := time.Now()
+ c := make(chan bool, 2)
+ go func() {
+ c <- true
+ runtime.Netpoll(10 * time.Second.Nanoseconds())
+ c <- true
+ }()
+ <-c
+ // Loop because the break might get eaten by the scheduler.
+ // Break twice to break both the netpoll we started and the
+ // scheduler netpoll.
+loop:
+ for {
+ runtime.Usleep(100)
+ runtime.NetpollBreak()
+ runtime.NetpollBreak()
+ select {
+ case <-c:
+ break loop
+ default:
+ }
+ }
+ if dur := time.Since(start); dur > 5*time.Second {
+ t.Errorf("netpollBreak did not interrupt netpoll: slept for: %v", dur)
+ }
+}
diff --git a/src/runtime/race.go b/src/runtime/race.go
index d2fc6a3c478..d11dc9b5bfe 100644
--- a/src/runtime/race.go
+++ b/src/runtime/race.go
@@ -459,6 +459,11 @@ func racegoend() {
racecall(&__tsan_go_end, getg().racectx, 0, 0, 0)
}
+//go:nosplit
+func racectxend(racectx uintptr) {
+ racecall(&__tsan_go_end, racectx, 0, 0, 0)
+}
+
//go:nosplit
func racewriterangepc(addr unsafe.Pointer, sz, callpc, pc uintptr) {
_g_ := getg()
@@ -506,6 +511,14 @@ func raceacquireg(gp *g, addr unsafe.Pointer) {
racecall(&__tsan_acquire, gp.racectx, uintptr(addr), 0, 0)
}
+//go:nosplit
+func raceacquirectx(racectx uintptr, addr unsafe.Pointer) {
+ if !isvalidaddr(addr) {
+ return
+ }
+ racecall(&__tsan_acquire, racectx, uintptr(addr), 0, 0)
+}
+
//go:nosplit
func racerelease(addr unsafe.Pointer) {
racereleaseg(getg(), addr)
diff --git a/src/runtime/race0.go b/src/runtime/race0.go
index f1d37062317..6f26afa854f 100644
--- a/src/runtime/race0.go
+++ b/src/runtime/race0.go
@@ -29,6 +29,7 @@ func racereadrangepc(addr unsafe.Pointer, sz, callerpc, pc uintptr) { th
func racewriterangepc(addr unsafe.Pointer, sz, callerpc, pc uintptr) { throw("race") }
func raceacquire(addr unsafe.Pointer) { throw("race") }
func raceacquireg(gp *g, addr unsafe.Pointer) { throw("race") }
+func raceacquirectx(racectx uintptr, addr unsafe.Pointer) { throw("race") }
func racerelease(addr unsafe.Pointer) { throw("race") }
func racereleaseg(gp *g, addr unsafe.Pointer) { throw("race") }
func racereleasemerge(addr unsafe.Pointer) { throw("race") }
@@ -38,3 +39,4 @@ func racemalloc(p unsafe.Pointer, sz uintptr) { th
func racefree(p unsafe.Pointer, sz uintptr) { throw("race") }
func racegostart(pc uintptr) uintptr { throw("race"); return 0 }
func racegoend() { throw("race") }
+func racectxend(racectx uintptr) { throw("race") }
diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s
index 4ed9533bfba..758d5432034 100644
--- a/src/runtime/race_amd64.s
+++ b/src/runtime/race_amd64.s
@@ -416,9 +416,11 @@ rest:
// Set g = g0.
get_tls(R12)
MOVQ g(R12), R13
- MOVQ g_m(R13), R13
- MOVQ m_g0(R13), R14
- MOVQ R14, g(R12) // g = m->g0
+ MOVQ g_m(R13), R14
+ MOVQ m_g0(R14), R15
+ CMPQ R13, R15
+ JEQ noswitch // branch if already on g0
+ MOVQ R15, g(R12) // g = m->g0
PUSHQ RARG1 // func arg
PUSHQ RARG0 // func arg
CALL runtime·racecallback(SB)
@@ -430,6 +432,7 @@ rest:
MOVQ g_m(R13), R13
MOVQ m_curg(R13), R14
MOVQ R14, g(R12) // g = m->curg
+ret:
// Restore callee-saved registers.
POPQ R15
POPQ R14
@@ -440,3 +443,12 @@ rest:
POPQ BP
POPQ BX
RET
+
+noswitch:
+ // already on g0
+ PUSHQ RARG1 // func arg
+ PUSHQ RARG0 // func arg
+ CALL runtime·racecallback(SB)
+ POPQ R12
+ POPQ R12
+ JMP ret
diff --git a/src/runtime/race_arm64.s b/src/runtime/race_arm64.s
index 00a67e86021..46224f8d73c 100644
--- a/src/runtime/race_arm64.s
+++ b/src/runtime/race_arm64.s
@@ -448,7 +448,10 @@ rest:
// restore R0
MOVD R13, R0
MOVD g_m(g), R13
- MOVD m_g0(R13), g
+ MOVD m_g0(R13), R14
+ CMP R14, g
+ BEQ noswitch // branch if already on g0
+ MOVD R14, g
MOVD R0, 8(RSP) // func arg
MOVD R1, 16(RSP) // func arg
@@ -457,6 +460,7 @@ rest:
// All registers are smashed after Go code, reload.
MOVD g_m(g), R13
MOVD m_curg(R13), g // g = m->curg
+ret:
// Restore callee-saved registers.
MOVD 0(RSP), LR
LDP 24(RSP), (R19, R20)
@@ -467,5 +471,12 @@ rest:
ADD $112, RSP
JMP (LR)
+noswitch:
+ // already on g0
+ MOVD R0, 8(RSP) // func arg
+ MOVD R1, 16(RSP) // func arg
+ BL runtime·racecallback(SB)
+ JMP ret
+
// tls_g, g value for each thread in TLS
GLOBL runtime·tls_g+0(SB), TLSBSS+DUPOK, $8
diff --git a/src/runtime/race_ppc64le.s b/src/runtime/race_ppc64le.s
index 0486bb338be..7421d539ca5 100644
--- a/src/runtime/race_ppc64le.s
+++ b/src/runtime/race_ppc64le.s
@@ -507,20 +507,29 @@ rest:
FMOVD F30, 312(R1)
FMOVD F31, 320(R1)
+ MOVD R3, FIXED_FRAME+0(R1)
+ MOVD R4, FIXED_FRAME+8(R1)
+
MOVD runtime·tls_g(SB), R10
MOVD 0(R13)(R10*1), g
MOVD g_m(g), R7
- MOVD m_g0(R7), g // set g = m-> g0
- MOVD R3, FIXED_FRAME+0(R1)
- MOVD R4, FIXED_FRAME+8(R1)
+ MOVD m_g0(R7), R8
+ CMP g, R8
+ BEQ noswitch
+
+ MOVD R8, g // set g = m-> g0
+
BL runtime·racecallback(SB)
+
// All registers are clobbered after Go code, reload.
MOVD runtime·tls_g(SB), R10
MOVD 0(R13)(R10*1), g
MOVD g_m(g), R7
MOVD m_curg(R7), g // restore g = m->curg
+
+ret:
MOVD 328(R1), R14
MOVD 48(R1), R15
MOVD 56(R1), R16
@@ -565,5 +574,9 @@ rest:
MOVD R10, LR
RET
+noswitch:
+ BL runtime·racecallback(SB)
+ JMP ret
+
// tls_g, g value for each thread in TLS
GLOBL runtime·tls_g+0(SB), TLSBSS+DUPOK, $8
diff --git a/src/runtime/rt0_js_wasm.s b/src/runtime/rt0_js_wasm.s
index b22c46e2e95..714582a6d56 100644
--- a/src/runtime/rt0_js_wasm.s
+++ b/src/runtime/rt0_js_wasm.s
@@ -19,7 +19,7 @@ TEXT _rt0_wasm_js(SB),NOSPLIT,$0
// R0: argc (i32)
// R1: argv (i32)
TEXT wasm_export_run(SB),NOSPLIT,$0
- MOVD $runtime·wasmStack+m0Stack__size(SB), SP
+ MOVD $runtime·wasmStack+(m0Stack__size-16)(SB), SP
Get SP
Get R0 // argc
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index e810a59507a..c389b36efe4 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -66,8 +66,8 @@ func checkGdbVersion(t *testing.T) {
}
func checkGdbPython(t *testing.T) {
- if runtime.GOOS == "solaris" && testenv.Builder() != "solaris-amd64-smartosbuildlet" {
- t.Skip("skipping gdb python tests on solaris; see golang.org/issue/20821")
+ if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
+ t.Skip("skipping gdb python tests on illumos and solaris; see golang.org/issue/20821")
}
cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')")
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index dd399e00a69..c5023027be0 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -40,7 +40,7 @@ const (
// _Grunning means this goroutine may execute user code. The
// stack is owned by this goroutine. It is not on a run queue.
- // It is assigned an M and a P.
+ // It is assigned an M and a P (g.m and g.m.p are valid).
_Grunning // 2
// _Gsyscall means this goroutine is executing a system call.
@@ -598,13 +598,32 @@ type p struct {
runSafePointFn uint32 // if 1, run sched.safePointFn at next safe point
+ // Lock for timers. We normally access the timers while running
+ // on this P, but the scheduler can also do it from a different P.
+ timersLock mutex
+
+ // Actions to take at some time. This is used to implement the
+ // standard library's time package.
+ // Must hold timersLock to access.
+ timers []*timer
+
+ // Number of timerModifiedEarlier timers on P's heap.
+ // This should only be modified while holding timersLock,
+ // or while the timer status is in a transient state
+ // such as timerModifying.
+ adjustTimers uint32
+
+ // Race context used while executing timer functions.
+ timerRaceCtx uintptr
+
pad cpu.CacheLinePad
}
type schedt struct {
// accessed atomically. keep at top to ensure alignment on 32-bit systems.
- goidgen uint64
- lastpoll uint64
+ goidgen uint64
+ lastpoll uint64 // time of last network poll, 0 if currently polling
+ pollUntil uint64 // time to which current poll is sleeping
lock mutex
@@ -701,7 +720,7 @@ type _func struct {
nameoff int32 // function name
args int32 // in/out args size
- deferreturn uint32 // offset of a deferreturn block from entry, if any.
+ deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
pcsp int32
pcfile int32
@@ -774,7 +793,7 @@ func extendRandom(r []byte, n int) {
}
// A _defer holds an entry on the list of deferred calls.
-// If you add a field here, add code to clear it in freedefer.
+// If you add a field here, add code to clear it in freedefer and deferProcStack
// This struct must match the code in cmd/compile/internal/gc/reflect.go:deferstruct
// and cmd/compile/internal/gc/ssa.go:(*state).call.
// Some defers will be allocated on the stack and some on the heap.
@@ -785,11 +804,27 @@ type _defer struct {
siz int32 // includes both arguments and results
started bool
heap bool
- sp uintptr // sp at time of defer
- pc uintptr
- fn *funcval
- _panic *_panic // panic that is running defer
- link *_defer
+ // openDefer indicates that this _defer is for a frame with open-coded
+ // defers. We have only one defer record for the entire frame (which may
+ // currently have 0, 1, or more defers active).
+ openDefer bool
+ sp uintptr // sp at time of defer
+ pc uintptr // pc at time of defer
+ fn *funcval
+ _panic *_panic // panic that is running defer
+ link *_defer
+
+ // If openDefer is true, the fields below record values about the stack
+ // frame and associated function that has the open-coded defer(s). sp
+ // above will be the sp for the frame, and pc will be address of the
+ // deferreturn call in the function.
+ fd unsafe.Pointer // funcdata for the function associated with the frame
+ varp uintptr // value of varp for the stack frame
+ // framepc is the current pc associated with the stack frame. Together,
+ // with sp above (which is the sp associated with the stack frame),
+ // framepc/sp can be used as pc/sp pair to continue a stack trace via
+ // gentraceback().
+ framepc uintptr
}
// A _panic holds information about an active panic.
diff --git a/src/runtime/runtime_linux_test.go b/src/runtime/runtime_linux_test.go
index 17d6fbde467..cd59368cb2a 100644
--- a/src/runtime/runtime_linux_test.go
+++ b/src/runtime/runtime_linux_test.go
@@ -41,11 +41,11 @@ func TestLockOSThread(t *testing.T) {
}
}
-// Test that error values are negative. Use address 1 (a misaligned
-// pointer) to get -EINVAL.
+// Test that error values are negative.
+// Use a misaligned pointer to get -EINVAL.
func TestMincoreErrorSign(t *testing.T) {
var dst byte
- v := Mincore(unsafe.Pointer(uintptr(1)), 1, &dst)
+ v := Mincore(Add(unsafe.Pointer(new(int32)), 1), 1, &dst)
const EINVAL = 0x16
if v != -EINVAL {
@@ -54,7 +54,7 @@ func TestMincoreErrorSign(t *testing.T) {
}
func TestEpollctlErrorSign(t *testing.T) {
- v := Epollctl(-1, 1, -1, unsafe.Pointer(&struct{}{}))
+ v := Epollctl(-1, 1, -1, unsafe.Pointer(&EpollEvent{}))
const EBADF = 0x09
if v != -EBADF {
diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go
index ab7a03b2d16..26ae77456ae 100644
--- a/src/runtime/runtime_test.go
+++ b/src/runtime/runtime_test.go
@@ -290,32 +290,6 @@ func TestTrailingZero(t *testing.T) {
}
}
-func TestBadOpen(t *testing.T) {
- if GOOS == "windows" || GOOS == "js" {
- t.Skip("skipping OS that doesn't have open/read/write/close")
- }
- // make sure we get the correct error code if open fails. Same for
- // read/write/close on the resulting -1 fd. See issue 10052.
- nonfile := []byte("/notreallyafile")
- fd := Open(&nonfile[0], 0, 0)
- if fd != -1 {
- t.Errorf("open(\"%s\")=%d, want -1", string(nonfile), fd)
- }
- var buf [32]byte
- r := Read(-1, unsafe.Pointer(&buf[0]), int32(len(buf)))
- if r != -1 {
- t.Errorf("read()=%d, want -1", r)
- }
- w := Write(^uintptr(0), unsafe.Pointer(&buf[0]), int32(len(buf)))
- if w != -1 {
- t.Errorf("write()=%d, want -1", w)
- }
- c := Close(-1)
- if c != -1 {
- t.Errorf("close()=%d, want -1", c)
- }
-}
-
func TestAppendGrowth(t *testing.T) {
var x []int64
check := func(want int) {
diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index 3db6133af0a..cea65282e0a 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -299,6 +299,16 @@ func sigFetchG(c *sigctxt) *g {
switch GOARCH {
case "arm", "arm64":
if inVDSOPage(c.sigpc()) {
+ // Before making a VDSO call we save the g to the bottom of the
+ // signal stack. Fetch from there.
+ // TODO: in efence mode, stack is sysAlloc'd, so this wouldn't
+ // work.
+ sp := getcallersp()
+ s := spanOf(sp)
+ if s != nil && s.state == mSpanManual && s.base() < sp && sp < s.limit {
+ gp := *(**g)(unsafe.Pointer(s.base()))
+ return gp
+ }
return nil
}
}
@@ -333,43 +343,10 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
}
// If some non-Go code called sigaltstack, adjust.
- setStack := false
var gsignalStack gsignalStack
- sp := uintptr(unsafe.Pointer(&sig))
- if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi {
- if sp >= g.m.g0.stack.lo && sp < g.m.g0.stack.hi {
- // The signal was delivered on the g0 stack.
- // This can happen when linked with C code
- // using the thread sanitizer, which collects
- // signals then delivers them itself by calling
- // the signal handler directly when C code,
- // including C code called via cgo, calls a
- // TSAN-intercepted function such as malloc.
- st := stackt{ss_size: g.m.g0.stack.hi - g.m.g0.stack.lo}
- setSignalstackSP(&st, g.m.g0.stack.lo)
- setGsignalStack(&st, &gsignalStack)
- g.m.gsignal.stktopsp = getcallersp()
- setStack = true
- } else {
- var st stackt
- sigaltstack(nil, &st)
- if st.ss_flags&_SS_DISABLE != 0 {
- setg(nil)
- needm(0)
- noSignalStack(sig)
- dropm()
- }
- stsp := uintptr(unsafe.Pointer(st.ss_sp))
- if sp < stsp || sp >= stsp+st.ss_size {
- setg(nil)
- needm(0)
- sigNotOnStack(sig)
- dropm()
- }
- setGsignalStack(&st, &gsignalStack)
- g.m.gsignal.stktopsp = getcallersp()
- setStack = true
- }
+ setStack := adjustSignalStack(sig, g.m, &gsignalStack)
+ if setStack {
+ g.m.gsignal.stktopsp = getcallersp()
}
setg(g.m.gsignal)
@@ -386,6 +363,51 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
}
}
+// adjustSignalStack adjusts the current stack guard based on the
+// stack pointer that is actually in use while handling a signal.
+// We do this in case some non-Go code called sigaltstack.
+// This reports whether the stack was adjusted, and if so stores the old
+// signal stack in *gsigstack.
+//go:nosplit
+func adjustSignalStack(sig uint32, mp *m, gsigStack *gsignalStack) bool {
+ sp := uintptr(unsafe.Pointer(&sig))
+ if sp >= mp.gsignal.stack.lo && sp < mp.gsignal.stack.hi {
+ return false
+ }
+
+ if sp >= mp.g0.stack.lo && sp < mp.g0.stack.hi {
+ // The signal was delivered on the g0 stack.
+ // This can happen when linked with C code
+ // using the thread sanitizer, which collects
+ // signals then delivers them itself by calling
+ // the signal handler directly when C code,
+ // including C code called via cgo, calls a
+ // TSAN-intercepted function such as malloc.
+ st := stackt{ss_size: mp.g0.stack.hi - mp.g0.stack.lo}
+ setSignalstackSP(&st, mp.g0.stack.lo)
+ setGsignalStack(&st, gsigStack)
+ return true
+ }
+
+ var st stackt
+ sigaltstack(nil, &st)
+ if st.ss_flags&_SS_DISABLE != 0 {
+ setg(nil)
+ needm(0)
+ noSignalStack(sig)
+ dropm()
+ }
+ stsp := uintptr(unsafe.Pointer(st.ss_sp))
+ if sp < stsp || sp >= stsp+st.ss_size {
+ setg(nil)
+ needm(0)
+ sigNotOnStack(sig)
+ dropm()
+ }
+ setGsignalStack(&st, gsigStack)
+ return true
+}
+
// crashing is the number of m's we have waited for when implementing
// GOTRACEBACK=crash when a signal is received.
var crashing int32
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 271b24c58aa..93f97698993 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -91,7 +91,7 @@ const (
// The stack guard is a pointer this many bytes above the
// bottom of the stack.
- _StackGuard = 880*sys.StackGuardMultiplier + _StackSystem
+ _StackGuard = 896*sys.StackGuardMultiplier + _StackSystem
// After a stack split check the SP is allowed to be this
// many bytes below the stack guard. This saves an instruction
@@ -736,6 +736,8 @@ func adjustdefers(gp *g, adjinfo *adjustinfo) {
adjustpointer(adjinfo, unsafe.Pointer(&d.sp))
adjustpointer(adjinfo, unsafe.Pointer(&d._panic))
adjustpointer(adjinfo, unsafe.Pointer(&d.link))
+ adjustpointer(adjinfo, unsafe.Pointer(&d.varp))
+ adjustpointer(adjinfo, unsafe.Pointer(&d.fd))
}
// Adjust defer argument blocks the same way we adjust active stack frames.
@@ -1096,6 +1098,12 @@ func shrinkstack(gp *g) {
if gstatus&_Gscan == 0 {
throw("bad status in shrinkstack")
}
+ // Check for self-shrinks while in a libcall. These may have
+ // pointers into the stack disguised as uintptrs, but these
+ // code paths should all be nosplit.
+ if gp == getg().m.curg && gp.m.libcallsp != 0 {
+ throw("shrinking stack in libcall")
+ }
if debug.gcshrinkstackoff > 0 {
return
@@ -1129,9 +1137,6 @@ func shrinkstack(gp *g) {
if gp.syscallsp != 0 {
return
}
- if sys.GoosWindows != 0 && gp.m != nil && gp.m.libcallsp != 0 {
- return
- }
if stackDebug > 0 {
print("shrinking stack ", oldsize, "->", newsize, "\n")
diff --git a/src/runtime/stubs2.go b/src/runtime/stubs2.go
index cf2b1248125..4a1a5cc3d90 100644
--- a/src/runtime/stubs2.go
+++ b/src/runtime/stubs2.go
@@ -13,12 +13,17 @@ package runtime
import "unsafe"
+// read calls the read system call.
+// It returns a non-negative number of bytes written or a negative errno value.
func read(fd int32, p unsafe.Pointer, n int32) int32
+
func closefd(fd int32) int32
func exit(code int32)
func usleep(usec uint32)
+// write calls the write system call.
+// It returns a non-negative number of bytes written or a negative errno value.
//go:noescape
func write1(fd uintptr, p unsafe.Pointer, n int32) int32
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index df6e02f62ad..e99b8cf6695 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -216,11 +216,12 @@ const (
_PCDATA_StackMapIndex = 1
_PCDATA_InlTreeIndex = 2
- _FUNCDATA_ArgsPointerMaps = 0
- _FUNCDATA_LocalsPointerMaps = 1
- _FUNCDATA_RegPointerMaps = 2
- _FUNCDATA_StackObjects = 3
- _FUNCDATA_InlTree = 4
+ _FUNCDATA_ArgsPointerMaps = 0
+ _FUNCDATA_LocalsPointerMaps = 1
+ _FUNCDATA_RegPointerMaps = 2
+ _FUNCDATA_StackObjects = 3
+ _FUNCDATA_InlTree = 4
+ _FUNCDATA_OpenCodedDeferInfo = 5
_ArgsSizeUnknown = -0x80000000
)
diff --git a/src/runtime/sys_darwin_386.s b/src/runtime/sys_darwin_386.s
index e653c54f614..bea804b8ddf 100644
--- a/src/runtime/sys_darwin_386.s
+++ b/src/runtime/sys_darwin_386.s
@@ -64,6 +64,12 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
MOVL 8(CX), AX // arg 3 count
MOVL AX, 8(SP)
CALL libc_read(SB)
+ TESTL AX, AX
+ JGE noerr
+ CALL libc_error(SB)
+ MOVL (AX), AX
+ NEGL AX // caller expects negative errno value
+noerr:
MOVL BP, SP
POPL BP
RET
@@ -80,6 +86,12 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
MOVL 8(CX), AX // arg 3 count
MOVL AX, 8(SP)
CALL libc_write(SB)
+ TESTL AX, AX
+ JGE noerr
+ CALL libc_error(SB)
+ MOVL (AX), AX
+ NEGL AX // caller expects negative errno value
+noerr:
MOVL BP, SP
POPL BP
RET
diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s
index 87c8db8c82d..ea8cf1abb13 100644
--- a/src/runtime/sys_darwin_amd64.s
+++ b/src/runtime/sys_darwin_amd64.s
@@ -46,6 +46,12 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
MOVL 16(DI), DX // arg 3 count
MOVL 0(DI), DI // arg 1 fd
CALL libc_read(SB)
+ TESTL AX, AX
+ JGE noerr
+ CALL libc_error(SB)
+ MOVL (AX), AX
+ NEGL AX // caller expects negative errno value
+noerr:
POPQ BP
RET
@@ -56,6 +62,12 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
MOVL 16(DI), DX // arg 3 count
MOVQ 0(DI), DI // arg 1 fd
CALL libc_write(SB)
+ TESTL AX, AX
+ JGE noerr
+ CALL libc_error(SB)
+ MOVL (AX), AX
+ NEGL AX // caller expects negative errno value
+noerr:
POPQ BP
RET
diff --git a/src/runtime/sys_darwin_arm.s b/src/runtime/sys_darwin_arm.s
index 996f8028a36..84b0b0f5f4c 100644
--- a/src/runtime/sys_darwin_arm.s
+++ b/src/runtime/sys_darwin_arm.s
@@ -32,6 +32,13 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
MOVW 8(R0), R2 // arg 3 count
MOVW 0(R0), R0 // arg 1 fd
BL libc_write(SB)
+ MOVW $-1, R1
+ CMP R0, R1
+ BNE noerr
+ BL libc_error(SB)
+ MOVW (R0), R0
+ RSB $0, R0, R0 // caller expects negative errno value
+noerr:
RET
TEXT runtime·read_trampoline(SB),NOSPLIT,$0
@@ -39,6 +46,13 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
MOVW 8(R0), R2 // arg 3 count
MOVW 0(R0), R0 // arg 1 fd
BL libc_read(SB)
+ MOVW $-1, R1
+ CMP R0, R1
+ BNE noerr
+ BL libc_error(SB)
+ MOVW (R0), R0
+ RSB $0, R0, R0 // caller expects negative errno value
+noerr:
RET
TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s
index ac3ca74f637..8d39a0727fc 100644
--- a/src/runtime/sys_darwin_arm64.s
+++ b/src/runtime/sys_darwin_arm64.s
@@ -35,6 +35,13 @@ TEXT runtime·write_trampoline(SB),NOSPLIT,$0
MOVW 16(R0), R2 // arg 3 count
MOVW 0(R0), R0 // arg 1 fd
BL libc_write(SB)
+ MOVD $-1, R1
+ CMP R0, R1
+ BNE noerr
+ BL libc_error(SB)
+ MOVW (R0), R0
+ NEG R0, R0 // caller expects negative errno value
+noerr:
RET
TEXT runtime·read_trampoline(SB),NOSPLIT,$0
@@ -42,6 +49,13 @@ TEXT runtime·read_trampoline(SB),NOSPLIT,$0
MOVW 16(R0), R2 // arg 3 count
MOVW 0(R0), R0 // arg 1 fd
BL libc_read(SB)
+ MOVD $-1, R1
+ CMP R0, R1
+ BNE noerr
+ BL libc_error(SB)
+ MOVW (R0), R0
+ NEG R0, R0 // caller expects negative errno value
+noerr:
RET
TEXT runtime·pipe_trampoline(SB),NOSPLIT,$0
diff --git a/src/runtime/sys_dragonfly_amd64.s b/src/runtime/sys_dragonfly_amd64.s
index ab9ca3795ff..68962d9e305 100644
--- a/src/runtime/sys_dragonfly_amd64.s
+++ b/src/runtime/sys_dragonfly_amd64.s
@@ -104,10 +104,25 @@ TEXT runtime·read(SB),NOSPLIT,$-8
MOVL $3, AX
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVL $42, AX
+ SYSCALL
+ JCC pipeok
+ MOVL $-1,r+0(FP)
+ MOVL $-1,w+4(FP)
+ MOVL AX, errno+8(FP)
+ RET
+pipeok:
+ MOVL AX, r+0(FP)
+ MOVL DX, w+4(FP)
+ MOVL $0, errno+8(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-8
MOVQ fd+0(FP), DI // arg 1 fd
MOVQ p+8(FP), SI // arg 2 buf
@@ -115,7 +130,7 @@ TEXT runtime·write1(SB),NOSPLIT,$-8
MOVL $4, AX
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
@@ -371,3 +386,18 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVL $92, AX // fcntl
SYSCALL
RET
+
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVL fd+0(FP), DI // fd
+ MOVQ $3, SI // F_GETFL
+ MOVQ $0, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ MOVL fd+0(FP), DI // fd
+ MOVQ $4, SI // F_SETFL
+ MOVQ $4, DX // O_NONBLOCK
+ ORL AX, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s
index 7ff4016de3e..48f64b9f8b6 100644
--- a/src/runtime/sys_freebsd_386.s
+++ b/src/runtime/sys_freebsd_386.s
@@ -93,15 +93,41 @@ TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
JAE 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$8-12
+ MOVL $42, AX
+ INT $0x80
+ JAE ok
+ MOVL $0, r+0(FP)
+ MOVL $0, w+4(FP)
+ MOVL AX, errno+8(FP)
+ RET
+ok:
+ MOVL AX, r+0(FP)
+ MOVL DX, w+4(FP)
+ MOVL $0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$12-16
+ MOVL $542, AX
+ LEAL r+4(FP), BX
+ MOVL BX, 4(SP)
+ MOVL flags+0(FP), BX
+ MOVL BX, 8(SP)
+ INT $0x80
+ MOVL AX, errno+12(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-4
MOVL $4, AX
INT $0x80
JAE 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+12(FP)
RET
@@ -412,6 +438,23 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32
NEGL AX
RET
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$16-4
+ MOVL $92, AX // fcntl
+ MOVL fd+0(FP), BX // fd
+ MOVL BX, 4(SP)
+ MOVL $3, 8(SP) // F_GETFL
+ MOVL $0, 12(SP)
+ INT $0x80
+ MOVL fd+0(FP), BX // fd
+ MOVL BX, 4(SP)
+ MOVL $4, 8(SP) // F_SETFL
+ ORL $4, AX // O_NONBLOCK
+ MOVL AX, 12(SP)
+ MOVL $92, AX // fcntl
+ INT $0x80
+ RET
+
// func cpuset_getaffinity(level int, which int, id int64, size int, mask *byte) int32
TEXT runtime·cpuset_getaffinity(SB), NOSPLIT, $0-28
MOVL $487, AX
diff --git a/src/runtime/sys_freebsd_amd64.s b/src/runtime/sys_freebsd_amd64.s
index 6f6da480d86..d24ab1f643c 100644
--- a/src/runtime/sys_freebsd_amd64.s
+++ b/src/runtime/sys_freebsd_amd64.s
@@ -93,10 +93,34 @@ TEXT runtime·read(SB),NOSPLIT,$-8
MOVL $3, AX
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGQ AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVL $42, AX
+ SYSCALL
+ JCC ok
+ MOVL $0, r+0(FP)
+ MOVL $0, w+4(FP)
+ MOVL AX, errno+8(FP)
+ RET
+ok:
+ MOVL AX, r+0(FP)
+ MOVL DX, w+4(FP)
+ MOVL $0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-20
+ LEAQ r+8(FP), DI
+ MOVL flags+0(FP), SI
+ MOVL $542, AX
+ SYSCALL
+ MOVL AX, errno+16(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-8
MOVQ fd+0(FP), DI // arg 1 fd
MOVQ p+8(FP), SI // arg 2 buf
@@ -104,7 +128,7 @@ TEXT runtime·write1(SB),NOSPLIT,$-8
MOVL $4, AX
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGQ AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
@@ -447,6 +471,21 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
SYSCALL
RET
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVL fd+0(FP), DI // fd
+ MOVQ $3, SI // F_GETFL
+ MOVQ $0, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ MOVL fd+0(FP), DI // fd
+ MOVQ $4, SI // F_SETFL
+ MOVQ $4, DX // O_NONBLOCK
+ ORL AX, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
+
// func cpuset_getaffinity(level int, which int, id int64, size int, mask *byte) int32
TEXT runtime·cpuset_getaffinity(SB), NOSPLIT, $0-44
MOVQ level+0(FP), DI
diff --git a/src/runtime/sys_freebsd_arm.s b/src/runtime/sys_freebsd_arm.s
index 1bdc10681a7..8da36dff178 100644
--- a/src/runtime/sys_freebsd_arm.s
+++ b/src/runtime/sys_freebsd_arm.s
@@ -20,6 +20,7 @@
#define SYS_close (SYS_BASE + 6)
#define SYS_getpid (SYS_BASE + 20)
#define SYS_kill (SYS_BASE + 37)
+#define SYS_pipe (SYS_BASE + 42)
#define SYS_sigaltstack (SYS_BASE + 53)
#define SYS_munmap (SYS_BASE + 73)
#define SYS_madvise (SYS_BASE + 75)
@@ -40,6 +41,7 @@
#define SYS_thr_new (SYS_BASE + 455)
#define SYS_mmap (SYS_BASE + 477)
#define SYS_cpuset_getaffinity (SYS_BASE + 487)
+#define SYS_pipe2 (SYS_BASE + 542)
TEXT runtime·sys_umtx_op(SB),NOSPLIT,$0
MOVW addr+0(FP), R0
@@ -115,17 +117,43 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0
MOVW n+8(FP), R2 // arg 3 count
MOVW $SYS_read, R7
SWI $0
- MOVW.CS $-1, R0
+ RSB.CS $0, R0 // caller expects negative errno
MOVW R0, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVW $SYS_pipe, R7
+ SWI $0
+ BCC ok
+ MOVW $0, R1
+ MOVW R1, r+0(FP)
+ MOVW R1, w+4(FP)
+ MOVW R0, errno+8(FP)
+ RET
+ok:
+ MOVW R0, r+0(FP)
+ MOVW R1, w+4(FP)
+ MOVW $0, R1
+ MOVW R1, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-16
+ MOVW $r+4(FP), R0
+ MOVW flags+0(FP), R1
+ MOVW $SYS_pipe2, R7
+ SWI $0
+ MOVW R0, errno+12(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0
MOVW fd+0(FP), R0 // arg 1 fd
MOVW p+4(FP), R1 // arg 2 buf
MOVW n+8(FP), R2 // arg 3 count
MOVW $SYS_write, R7
SWI $0
- MOVW.CS $-1, R0
+ RSB.CS $0, R0 // caller expects negative errno
MOVW R0, ret+12(FP)
RET
@@ -371,6 +399,20 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
SWI $0
RET
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVW fd+0(FP), R0 // fd
+ MOVW $3, R1 // F_GETFL
+ MOVW $0, R2
+ MOVW $SYS_fcntl, R7
+ SWI $0
+ ORR $0x4, R0, R2 // O_NONBLOCK
+ MOVW fd+0(FP), R0 // fd
+ MOVW $4, R1 // F_SETFL
+ MOVW $SYS_fcntl, R7
+ SWI $0
+ RET
+
// TODO: this is only valid for ARMv7+
TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0
B runtime·armPublicationBarrier(SB)
diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s
index 882f31cd641..4b440b13cbf 100644
--- a/src/runtime/sys_linux_386.s
+++ b/src/runtime/sys_linux_386.s
@@ -32,6 +32,7 @@
#define SYS_getpid 20
#define SYS_access 33
#define SYS_kill 37
+#define SYS_pipe 42
#define SYS_brk 45
#define SYS_fcntl 55
#define SYS_munmap 91
@@ -58,6 +59,7 @@
#define SYS_clock_gettime 265
#define SYS_tgkill 270
#define SYS_epoll_create1 329
+#define SYS_pipe2 331
TEXT runtime·exit(SB),NOSPLIT,$0
MOVL $SYS_exit_group, AX
@@ -113,9 +115,6 @@ TEXT runtime·write1(SB),NOSPLIT,$0
MOVL p+4(FP), CX
MOVL n+8(FP), DX
INVOKE_SYSCALL
- CMPL AX, $0xfffff001
- JLS 2(PC)
- MOVL $-1, AX
MOVL AX, ret+12(FP)
RET
@@ -125,12 +124,26 @@ TEXT runtime·read(SB),NOSPLIT,$0
MOVL p+4(FP), CX
MOVL n+8(FP), DX
INVOKE_SYSCALL
- CMPL AX, $0xfffff001
- JLS 2(PC)
- MOVL $-1, AX
MOVL AX, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVL $SYS_pipe, AX
+ LEAL r+0(FP), BX
+ INVOKE_SYSCALL
+ MOVL AX, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-16
+ MOVL $SYS_pipe2, AX
+ LEAL r+4(FP), BX
+ MOVL flags+0(FP), CX
+ INVOKE_SYSCALL
+ MOVL AX, errno+12(FP)
+ RET
+
TEXT runtime·usleep(SB),NOSPLIT,$8
MOVL $0, DX
MOVL usec+0(FP), AX
@@ -695,6 +708,21 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
INVOKE_SYSCALL
RET
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVL $SYS_fcntl, AX
+ MOVL fd+0(FP), BX // fd
+ MOVL $3, CX // F_GETFL
+ MOVL $0, DX
+ INVOKE_SYSCALL
+ MOVL fd+0(FP), BX // fd
+ MOVL $4, CX // F_SETFL
+ MOVL $0x800, DX // O_NONBLOCK
+ ORL AX, DX
+ MOVL $SYS_fcntl, AX
+ INVOKE_SYSCALL
+ RET
+
// int access(const char *name, int mode)
TEXT runtime·access(SB),NOSPLIT,$0
MOVL $SYS_access, AX
diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s
index 7e8f5279cac..0728d1766e2 100644
--- a/src/runtime/sys_linux_amd64.s
+++ b/src/runtime/sys_linux_amd64.s
@@ -21,6 +21,7 @@
#define SYS_rt_sigaction 13
#define SYS_rt_sigprocmask 14
#define SYS_rt_sigreturn 15
+#define SYS_pipe 22
#define SYS_sched_yield 24
#define SYS_mincore 27
#define SYS_madvise 28
@@ -46,6 +47,7 @@
#define SYS_faccessat 269
#define SYS_epoll_pwait 281
#define SYS_epoll_create1 291
+#define SYS_pipe2 293
TEXT runtime·exit(SB),NOSPLIT,$0-4
MOVL code+0(FP), DI
@@ -95,9 +97,6 @@ TEXT runtime·write1(SB),NOSPLIT,$0-28
MOVL n+16(FP), DX
MOVL $SYS_write, AX
SYSCALL
- CMPQ AX, $0xfffffffffffff001
- JLS 2(PC)
- MOVL $-1, AX
MOVL AX, ret+24(FP)
RET
@@ -107,12 +106,26 @@ TEXT runtime·read(SB),NOSPLIT,$0-28
MOVL n+16(FP), DX
MOVL $SYS_read, AX
SYSCALL
- CMPQ AX, $0xfffffffffffff001
- JLS 2(PC)
- MOVL $-1, AX
MOVL AX, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ LEAQ r+0(FP), DI
+ MOVL $SYS_pipe, AX
+ SYSCALL
+ MOVL AX, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-20
+ LEAQ r+8(FP), DI
+ MOVL flags+0(FP), SI
+ MOVL $SYS_pipe2, AX
+ SYSCALL
+ MOVL AX, errno+16(FP)
+ RET
+
TEXT runtime·usleep(SB),NOSPLIT,$16
MOVL $0, DX
MOVL usec+0(FP), AX
@@ -682,6 +695,20 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
SYSCALL
RET
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVL fd+0(FP), DI // fd
+ MOVQ $3, SI // F_GETFL
+ MOVQ $0, DX
+ MOVL $SYS_fcntl, AX
+ SYSCALL
+ MOVL fd+0(FP), DI // fd
+ MOVQ $4, SI // F_SETFL
+ MOVQ $0x800, DX // O_NONBLOCK
+ ORL AX, DX
+ MOVL $SYS_fcntl, AX
+ SYSCALL
+ RET
// int access(const char *name, int mode)
TEXT runtime·access(SB),NOSPLIT,$0
diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s
index c7d83a6d3ac..9a9e1c92c75 100644
--- a/src/runtime/sys_linux_arm.s
+++ b/src/runtime/sys_linux_arm.s
@@ -23,6 +23,7 @@
#define SYS_close (SYS_BASE + 6)
#define SYS_getpid (SYS_BASE + 20)
#define SYS_kill (SYS_BASE + 37)
+#define SYS_pipe (SYS_BASE + 42)
#define SYS_clone (SYS_BASE + 120)
#define SYS_rt_sigreturn (SYS_BASE + 173)
#define SYS_rt_sigaction (SYS_BASE + 174)
@@ -45,6 +46,7 @@
#define SYS_epoll_ctl (SYS_BASE + 251)
#define SYS_epoll_wait (SYS_BASE + 252)
#define SYS_epoll_create1 (SYS_BASE + 357)
+#define SYS_pipe2 (SYS_BASE + 359)
#define SYS_fcntl (SYS_BASE + 55)
#define SYS_access (SYS_BASE + 33)
#define SYS_connect (SYS_BASE + 283)
@@ -81,9 +83,6 @@ TEXT runtime·write1(SB),NOSPLIT,$0
MOVW n+8(FP), R2
MOVW $SYS_write, R7
SWI $0
- MOVW $0xfffff001, R1
- CMP R1, R0
- MOVW.HI $-1, R0
MOVW R0, ret+12(FP)
RET
@@ -93,12 +92,26 @@ TEXT runtime·read(SB),NOSPLIT,$0
MOVW n+8(FP), R2
MOVW $SYS_read, R7
SWI $0
- MOVW $0xfffff001, R1
- CMP R1, R0
- MOVW.HI $-1, R0
MOVW R0, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVW $r+0(FP), R0
+ MOVW $SYS_pipe, R7
+ SWI $0
+ MOVW R0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-16
+ MOVW $r+4(FP), R0
+ MOVW flags+0(FP), R1
+ MOVW $SYS_pipe2, R7
+ SWI $0
+ MOVW R0, errno+12(FP)
+ RET
+
TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0
MOVW code+0(FP), R0
MOVW $SYS_exit_group, R7
@@ -246,7 +259,23 @@ noswitch:
CMP $0, R11
B.EQ fallback
+ // Store g on gsignal's stack, so if we receive a signal
+ // during VDSO code we can find the g.
+ // If we don't have a signal stack, we won't receive signal,
+ // so don't bother saving g.
+ MOVW m_gsignal(R5), R6 // g.m.gsignal
+ CMP $0, R6
+ BEQ 3(PC)
+ MOVW (g_stack+stack_lo)(R6), R6 // g.m.gsignal.stack.lo
+ MOVW g, (R6)
+
BL (R11)
+
+ CMP $0, R6 // R6 is unchanged by C code
+ BEQ 3(PC)
+ MOVW $0, R1
+ MOVW R1, (R6) // clear g slot
+
JMP finish
fallback:
@@ -297,7 +326,23 @@ noswitch:
CMP $0, R11
B.EQ fallback
+ // Store g on gsignal's stack, so if we receive a signal
+ // during VDSO code we can find the g.
+ // If we don't have a signal stack, we won't receive signal,
+ // so don't bother saving g.
+ MOVW m_gsignal(R5), R6 // g.m.gsignal
+ CMP $0, R6
+ BEQ 3(PC)
+ MOVW (g_stack+stack_lo)(R6), R6 // g.m.gsignal.stack.lo
+ MOVW g, (R6)
+
BL (R11)
+
+ CMP $0, R6 // R6 is unchanged by C code
+ BEQ 3(PC)
+ MOVW $0, R1
+ MOVW R1, (R6) // clear g slot
+
JMP finish
fallback:
@@ -567,6 +612,20 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
SWI $0
RET
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVW fd+0(FP), R0 // fd
+ MOVW $3, R1 // F_GETFL
+ MOVW $0, R2
+ MOVW $SYS_fcntl, R7
+ SWI $0
+ ORR $0x800, R0, R2 // O_NONBLOCK
+ MOVW fd+0(FP), R0 // fd
+ MOVW $4, R1 // F_SETFL
+ MOVW $SYS_fcntl, R7
+ SWI $0
+ RET
+
// b __kuser_get_tls @ 0xffff0fe0
TEXT runtime·read_tls_fallback(SB),NOSPLIT|NOFRAME,$0
MOVW $0xffff0fe0, R0
diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s
index bce948a91e0..a77be98739f 100644
--- a/src/runtime/sys_linux_arm64.s
+++ b/src/runtime/sys_linux_arm64.s
@@ -20,6 +20,7 @@
#define SYS_write 64
#define SYS_openat 56
#define SYS_close 57
+#define SYS_pipe2 59
#define SYS_fcntl 25
#define SYS_nanosleep 101
#define SYS_mmap 222
@@ -97,10 +98,6 @@ TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0-28
MOVW n+16(FP), R2
MOVD $SYS_write, R8
SVC
- CMN $4095, R0
- BCC done
- MOVW $-1, R0
-done:
MOVW R0, ret+24(FP)
RET
@@ -110,13 +107,27 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28
MOVW n+16(FP), R2
MOVD $SYS_read, R8
SVC
- CMN $4095, R0
- BCC done
- MOVW $-1, R0
-done:
MOVW R0, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12
+ ADD $8, RSP, R0
+ MOVW $0, R1
+ MOVW $SYS_pipe2, R8
+ SVC
+ MOVW R0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20
+ ADD $16, RSP, R0
+ MOVW flags+0(FP), R1
+ MOVW $SYS_pipe2, R8
+ SVC
+ MOVW R0, errno+16(FP)
+ RET
+
TEXT runtime·usleep(SB),NOSPLIT,$24-4
MOVWU usec+0(FP), R3
MOVD R3, R5
@@ -207,7 +218,21 @@ noswitch:
MOVW $CLOCK_REALTIME, R0
MOVD runtime·vdsoClockgettimeSym(SB), R2
CBZ R2, fallback
+
+ // Store g on gsignal's stack, so if we receive a signal
+ // during VDSO code we can find the g.
+ // If we don't have a signal stack, we won't receive signal,
+ // so don't bother saving g.
+ MOVD m_gsignal(R21), R22 // g.m.gsignal
+ CBZ R22, 3(PC)
+ MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
+ MOVD g, (R22)
+
BL (R2)
+
+ CBZ R22, 2(PC) // R22 is unchanged by C code
+ MOVD ZR, (R22) // clear g slot
+
B finish
fallback:
@@ -250,7 +275,21 @@ noswitch:
MOVW $CLOCK_MONOTONIC, R0
MOVD runtime·vdsoClockgettimeSym(SB), R2
CBZ R2, fallback
+
+ // Store g on gsignal's stack, so if we receive a signal
+ // during VDSO code we can find the g.
+ // If we don't have a signal stack, we won't receive signal,
+ // so don't bother saving g.
+ MOVD m_gsignal(R21), R22 // g.m.gsignal
+ CBZ R22, 3(PC)
+ MOVD (g_stack+stack_lo)(R22), R22 // g.m.gsignal.stack.lo
+ MOVD g, (R22)
+
BL (R2)
+
+ CBZ R22, 2(PC) // R22 is unchanged by C code
+ MOVD ZR, (R22) // clear g slot
+
B finish
fallback:
@@ -605,6 +644,21 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0
SVC
RET
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW fd+0(FP), R0 // fd
+ MOVD $3, R1 // F_GETFL
+ MOVD $0, R2
+ MOVD $SYS_fcntl, R8
+ SVC
+ MOVD $0x800, R2 // O_NONBLOCK
+ EOR R0, R2
+ MOVW fd+0(FP), R0 // fd
+ MOVD $4, R1 // F_SETFL
+ MOVD $SYS_fcntl, R8
+ SVC
+ RET
+
// int access(const char *name, int mode)
TEXT runtime·access(SB),NOSPLIT,$0-20
MOVD $AT_FDCWD, R0
diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s
index 26b619e238f..49459b0cece 100644
--- a/src/runtime/sys_linux_mips64x.s
+++ b/src/runtime/sys_linux_mips64x.s
@@ -21,7 +21,7 @@
#define SYS_close 5003
#define SYS_getpid 5038
#define SYS_kill 5060
-#define SYS_fcntl 5080
+#define SYS_fcntl 5070
#define SYS_mmap 5009
#define SYS_munmap 5011
#define SYS_setitimer 5036
@@ -46,6 +46,7 @@
#define SYS_clock_gettime 5222
#define SYS_epoll_create1 5285
#define SYS_brk 5012
+#define SYS_pipe2 5287
TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4
MOVW code+0(FP), R4
@@ -95,7 +96,7 @@ TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0-28
MOVV $SYS_write, R2
SYSCALL
BEQ R7, 2(PC)
- MOVW $-1, R2
+ SUBVU R2, R0, R2 // caller expects negative errno
MOVW R2, ret+24(FP)
RET
@@ -106,10 +107,28 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28
MOVV $SYS_read, R2
SYSCALL
BEQ R7, 2(PC)
- MOVW $-1, R2
+ SUBVU R2, R0, R2 // caller expects negative errno
MOVW R2, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12
+ MOVV $r+0(FP), R4
+ MOVV R0, R5
+ MOVV $SYS_pipe2, R2
+ SYSCALL
+ MOVW R2, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20
+ MOVV $r+8(FP), R4
+ MOVW flags+0(FP), R5
+ MOVV $SYS_pipe2, R2
+ SYSCALL
+ MOVW R2, errno+16(FP)
+ RET
+
TEXT runtime·usleep(SB),NOSPLIT,$16-4
MOVWU usec+0(FP), R3
MOVV R3, R5
@@ -454,6 +473,21 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0
SYSCALL
RET
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW fd+0(FP), R4 // fd
+ MOVV $3, R5 // F_GETFL
+ MOVV $0, R6
+ MOVV $SYS_fcntl, R2
+ SYSCALL
+ MOVW $0x80, R6 // O_NONBLOCK
+ OR R2, R6
+ MOVW fd+0(FP), R4 // fd
+ MOVV $4, R5 // F_SETFL
+ MOVV $SYS_fcntl, R2
+ SYSCALL
+ RET
+
// func sbrk0() uintptr
TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0-8
// Implemented as brk(NULL).
diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s
index 30d962c3250..3c405c264e1 100644
--- a/src/runtime/sys_linux_mipsx.s
+++ b/src/runtime/sys_linux_mipsx.s
@@ -20,6 +20,7 @@
#define SYS_close 4006
#define SYS_getpid 4020
#define SYS_kill 4037
+#define SYS_pipe 4042
#define SYS_brk 4045
#define SYS_fcntl 4055
#define SYS_mmap 4090
@@ -44,6 +45,7 @@
#define SYS_clock_gettime 4263
#define SYS_tgkill 4266
#define SYS_epoll_create1 4326
+#define SYS_pipe2 4328
TEXT runtime·exit(SB),NOSPLIT,$0-4
MOVW code+0(FP), R4
@@ -93,7 +95,7 @@ TEXT runtime·write1(SB),NOSPLIT,$0-16
MOVW $SYS_write, R2
SYSCALL
BEQ R7, 2(PC)
- MOVW $-1, R2
+ SUBU R2, R0, R2 // caller expects negative errno
MOVW R2, ret+12(FP)
RET
@@ -104,10 +106,35 @@ TEXT runtime·read(SB),NOSPLIT,$0-16
MOVW $SYS_read, R2
SYSCALL
BEQ R7, 2(PC)
- MOVW $-1, R2
+ SUBU R2, R0, R2 // caller expects negative errno
MOVW R2, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVW $SYS_pipe, R2
+ SYSCALL
+ BEQ R7, pipeok
+ MOVW $-1, R1
+ MOVW R1, r+0(FP)
+ MOVW R1, w+4(FP)
+ MOVW R2, errno+8(FP)
+ RET
+pipeok:
+ MOVW R2, r+0(FP)
+ MOVW R3, w+4(FP)
+ MOVW R0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-16
+ MOVW $r+4(FP), R4
+ MOVW flags+0(FP), R5
+ MOVW $SYS_pipe2, R2
+ SYSCALL
+ MOVW R2, errno+12(FP)
+ RET
+
TEXT runtime·usleep(SB),NOSPLIT,$28-4
MOVW usec+0(FP), R3
MOVW R3, R5
@@ -487,6 +514,21 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0-4
SYSCALL
RET
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVW fd+0(FP), R4 // fd
+ MOVW $3, R5 // F_GETFL
+ MOVW $0, R6
+ MOVW $SYS_fcntl, R2
+ SYSCALL
+ MOVW $0x80, R6 // O_NONBLOCK
+ OR R2, R6
+ MOVW fd+0(FP), R4 // fd
+ MOVW $4, R5 // F_SETFL
+ MOVW $SYS_fcntl, R2
+ SYSCALL
+ RET
+
// func sbrk0() uintptr
TEXT runtime·sbrk0(SB),NOSPLIT,$0-4
// Implemented as brk(NULL).
diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s
index cda19568758..203ce089c13 100644
--- a/src/runtime/sys_linux_ppc64x.s
+++ b/src/runtime/sys_linux_ppc64x.s
@@ -21,6 +21,7 @@
#define SYS_close 6
#define SYS_getpid 20
#define SYS_kill 37
+#define SYS_pipe 42
#define SYS_brk 45
#define SYS_fcntl 55
#define SYS_mmap 90
@@ -45,6 +46,7 @@
#define SYS_clock_gettime 246
#define SYS_tgkill 250
#define SYS_epoll_create1 315
+#define SYS_pipe2 317
TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4
MOVW code+0(FP), R3
@@ -86,7 +88,7 @@ TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0-28
MOVW n+16(FP), R5
SYSCALL $SYS_write
BVC 2(PC)
- MOVW $-1, R3
+ NEG R3 // caller expects negative errno
MOVW R3, ret+24(FP)
RET
@@ -96,10 +98,25 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28
MOVW n+16(FP), R5
SYSCALL $SYS_read
BVC 2(PC)
- MOVW $-1, R3
+ NEG R3 // caller expects negative errno
MOVW R3, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12
+ ADD $FIXED_FRAME, R1, R3
+ SYSCALL $SYS_pipe
+ MOVW R3, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20
+ ADD $FIXED_FRAME+8, R1, R3
+ MOVW flags+0(FP), R4
+ SYSCALL $SYS_pipe2
+ MOVW R3, errno+16(FP)
+ RET
+
TEXT runtime·usleep(SB),NOSPLIT,$16-4
MOVW usec+0(FP), R3
MOVD R3, R5
@@ -612,6 +629,18 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0
SYSCALL $SYS_fcntl
RET
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW fd+0(FP), R3 // fd
+ MOVD $3, R4 // F_GETFL
+ MOVD $0, R5
+ SYSCALL $SYS_fcntl
+ OR $0x800, R3, R5 // O_NONBLOCK
+ MOVW fd+0(FP), R3 // fd
+ MOVD $4, R4 // F_SETFL
+ SYSCALL $SYS_fcntl
+ RET
+
// func sbrk0() uintptr
TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0
// Implemented as brk(NULL).
diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s
index f8ef4eca322..df01271f7b5 100644
--- a/src/runtime/sys_linux_s390x.s
+++ b/src/runtime/sys_linux_s390x.s
@@ -16,6 +16,7 @@
#define SYS_close 6
#define SYS_getpid 20
#define SYS_kill 37
+#define SYS_pipe 42
#define SYS_brk 45
#define SYS_fcntl 55
#define SYS_mmap 90
@@ -39,6 +40,7 @@
#define SYS_epoll_ctl 250
#define SYS_epoll_wait 251
#define SYS_clock_gettime 260
+#define SYS_pipe2 325
#define SYS_epoll_create1 327
TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4
@@ -86,9 +88,6 @@ TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0-28
MOVW n+16(FP), R4
MOVW $SYS_write, R1
SYSCALL
- MOVD $-4095, R3
- CMPUBLT R2, R3, 2(PC)
- MOVW $-1, R2
MOVW R2, ret+24(FP)
RET
@@ -98,12 +97,26 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28
MOVW n+16(FP), R4
MOVW $SYS_read, R1
SYSCALL
- MOVD $-4095, R3
- CMPUBLT R2, R3, 2(PC)
- MOVW $-1, R2
MOVW R2, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12
+ MOVD $r+0(FP), R2
+ MOVW $SYS_pipe, R1
+ SYSCALL
+ MOVW R2, errno+8(FP)
+ RET
+
+// func pipe2() (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20
+ MOVD $r+8(FP), R2
+ MOVW flags+0(FP), R3
+ MOVW $SYS_pipe2, R1
+ SYSCALL
+ MOVW R2, errno+16(FP)
+ RET
+
TEXT runtime·usleep(SB),NOSPLIT,$16-4
MOVW usec+0(FP), R2
MOVD R2, R4
@@ -441,6 +454,21 @@ TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0
SYSCALL
RET
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW fd+0(FP), R2 // fd
+ MOVD $3, R3 // F_GETFL
+ XOR R4, R4
+ MOVW $SYS_fcntl, R1
+ SYSCALL
+ MOVD $0x800, R4 // O_NONBLOCK
+ OR R2, R4
+ MOVW fd+0(FP), R2 // fd
+ MOVD $4, R3 // F_SETFL
+ MOVW $SYS_fcntl, R1
+ SYSCALL
+ RET
+
// func sbrk0() uintptr
TEXT runtime·sbrk0(SB),NOSPLIT|NOFRAME,$0-8
// Implemented as brk(NULL).
diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s
index b8c5a92a4fb..7a542da526b 100644
--- a/src/runtime/sys_netbsd_386.s
+++ b/src/runtime/sys_netbsd_386.s
@@ -83,15 +83,41 @@ TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $SYS_read, AX
INT $0x80
JAE 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVL $42, AX
+ INT $0x80
+ JCC pipeok
+ MOVL $-1, r+0(FP)
+ MOVL $-1, w+4(FP)
+ MOVL AX, errno+8(FP)
+ RET
+pipeok:
+ MOVL AX, r+0(FP)
+ MOVL DX, w+4(FP)
+ MOVL $0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$12-16
+ MOVL $453, AX
+ LEAL r+4(FP), BX
+ MOVL BX, 4(SP)
+ MOVL flags+0(FP), BX
+ MOVL BX, 8(SP)
+ INT $0x80
+ MOVL AX, errno+12(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-4
MOVL $SYS_write, AX
INT $0x80
JAE 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+12(FP)
RET
@@ -455,3 +481,20 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32
JAE 2(PC)
NEGL AX
RET
+
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$16-4
+ MOVL $92, AX // fcntl
+ MOVL fd+0(FP), BX // fd
+ MOVL BX, 4(SP)
+ MOVL $3, 8(SP) // F_GETFL
+ MOVL $0, 12(SP)
+ INT $0x80
+ MOVL fd+0(FP), BX // fd
+ MOVL BX, 4(SP)
+ MOVL $4, 8(SP) // F_SETFL
+ ORL $4, AX // O_NONBLOCK
+ MOVL AX, 12(SP)
+ MOVL $92, AX // fcntl
+ INT $0x80
+ RET
diff --git a/src/runtime/sys_netbsd_amd64.s b/src/runtime/sys_netbsd_amd64.s
index b1b50ab4a99..4d1d36f01bf 100644
--- a/src/runtime/sys_netbsd_amd64.s
+++ b/src/runtime/sys_netbsd_amd64.s
@@ -154,10 +154,34 @@ TEXT runtime·read(SB),NOSPLIT,$-8
MOVL $SYS_read, AX
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGQ AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVL $42, AX
+ SYSCALL
+ JCC pipeok
+ MOVL $-1, r+0(FP)
+ MOVL $-1, w+4(FP)
+ MOVL AX, errno+8(FP)
+ RET
+pipeok:
+ MOVL AX, r+0(FP)
+ MOVL DX, w+4(FP)
+ MOVL $0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-20
+ LEAQ r+8(FP), DI
+ MOVL flags+0(FP), SI
+ MOVL $453, AX
+ SYSCALL
+ MOVL AX, errno+16(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-8
MOVQ fd+0(FP), DI // arg 1 - fd
MOVQ p+8(FP), SI // arg 2 - buf
@@ -165,7 +189,7 @@ TEXT runtime·write1(SB),NOSPLIT,$-8
MOVL $SYS_write, AX
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGQ AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
@@ -429,3 +453,18 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVL $SYS_fcntl, AX
SYSCALL
RET
+
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVL fd+0(FP), DI // fd
+ MOVQ $3, SI // F_GETFL
+ MOVQ $0, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ MOVL fd+0(FP), DI // fd
+ MOVQ $4, SI // F_SETFL
+ MOVQ $4, DX // O_NONBLOCK
+ ORL AX, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
diff --git a/src/runtime/sys_netbsd_arm.s b/src/runtime/sys_netbsd_arm.s
index 90f37e1a056..c8ee262d59b 100644
--- a/src/runtime/sys_netbsd_arm.s
+++ b/src/runtime/sys_netbsd_arm.s
@@ -92,16 +92,40 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0
MOVW p+4(FP), R1
MOVW n+8(FP), R2
SWI $SYS_read
- MOVW.CS $-1, R0
+ RSB.CS $0, R0 // caller expects negative errno
MOVW R0, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ SWI $0xa0002a
+ BCC pipeok
+ MOVW $-1,R2
+ MOVW R2, r+0(FP)
+ MOVW R2, w+4(FP)
+ MOVW R0, errno+8(FP)
+ RET
+pipeok:
+ MOVW $0, R2
+ MOVW R0, r+0(FP)
+ MOVW R1, w+4(FP)
+ MOVW R2, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-16
+ MOVW $r+4(FP), R0
+ MOVW flags+0(FP), R1
+ SWI $0xa001c5
+ MOVW R0, errno+12(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0
MOVW fd+0(FP), R0 // arg 1 - fd
MOVW p+4(FP), R1 // arg 2 - buf
MOVW n+8(FP), R2 // arg 3 - nbyte
SWI $SYS_write
- MOVW.CS $-1, R0
+ RSB.CS $0, R0 // caller expects negative errno
MOVW R0, ret+12(FP)
RET
@@ -385,6 +409,18 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
SWI $SYS_fcntl
RET
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVW fd+0(FP), R0 // fd
+ MOVW $3, R1 // F_GETFL
+ MOVW $0, R2
+ SWI $0xa0005c // sys_fcntl
+ ORR $0x4, R0, R2 // O_NONBLOCK
+ MOVW fd+0(FP), R0 // fd
+ MOVW $4, R1 // F_SETFL
+ SWI $0xa0005c // sys_fcntl
+ RET
+
// TODO: this is only valid for ARMv7+
TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0
B runtime·armPublicationBarrier(SB)
diff --git a/src/runtime/sys_netbsd_arm64.s b/src/runtime/sys_netbsd_arm64.s
index 55abdd52d6f..ccc34142aa3 100644
--- a/src/runtime/sys_netbsd_arm64.s
+++ b/src/runtime/sys_netbsd_arm64.s
@@ -14,6 +14,9 @@
#define CLOCK_MONOTONIC 3
#define FD_CLOEXEC 1
#define F_SETFD 2
+#define F_GETFL 3
+#define F_SETFL 4
+#define O_NONBLOCK 4
#define SYS_exit 1
#define SYS_read 3
@@ -43,6 +46,7 @@
#define SYS___clock_gettime50 427
#define SYS___nanosleep50 430
#define SYS___kevent50 435
+#define SYS_pipe2 453
#define SYS_openat 468
#define SYS____lwp_park60 478
@@ -141,18 +145,45 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0
MOVW n+16(FP), R2 // arg 3 - count
SVC $SYS_read
BCC ok
- MOVW $-1, R0
+ NEG R0, R0
ok:
MOVW R0, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12
+ MOVW $0, R0
+ SVC $SYS_pipe2
+ BCC pipeok
+ MOVW $-1,R1
+ MOVW R1, r+0(FP)
+ MOVW R1, w+4(FP)
+ NEG R0, R0
+ MOVW R0, errno+8(FP)
+ RET
+pipeok:
+ MOVW R0, r+0(FP)
+ MOVW R1, w+4(FP)
+ MOVW ZR, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20
+ ADD $8, RSP, R0
+ MOVW flags+0(FP), R1
+ SVC $SYS_pipe2
+ BCC 2(PC)
+ NEG R0, R0
+ MOVW R0, errno+16(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-8
MOVD fd+0(FP), R0 // arg 1 - fd
MOVD p+8(FP), R1 // arg 2 - buf
MOVW n+16(FP), R2 // arg 3 - nbyte
SVC $SYS_write
BCC ok
- MOVW $-1, R0
+ NEG R0, R0
ok:
MOVW R0, ret+24(FP)
RET
@@ -431,3 +462,16 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVW $FD_CLOEXEC, R2
SVC $SYS_fcntl
RET
+
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW fd+0(FP), R0 // arg 1 - fd
+ MOVD $F_GETFL, R1 // arg 2 - cmd
+ MOVD $0, R2 // arg 3
+ SVC $SYS_fcntl
+ MOVD $O_NONBLOCK, R2
+ EOR R0, R2 // arg 3 - flags
+ MOVW fd+0(FP), R0 // arg 1 - fd
+ MOVD $F_SETFL, R1 // arg 2 - cmd
+ SVC $SYS_fcntl
+ RET
diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s
index 31c837cd81d..9805a438025 100644
--- a/src/runtime/sys_openbsd_386.s
+++ b/src/runtime/sys_openbsd_386.s
@@ -46,15 +46,35 @@ TEXT runtime·read(SB),NOSPLIT,$-4
MOVL $3, AX
INT $0x80
JAE 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$8-12
+ MOVL $263, AX
+ LEAL r+0(FP), BX
+ MOVL BX, 4(SP)
+ INT $0x80
+ MOVL AX, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$12-16
+ MOVL $101, AX
+ LEAL r+4(FP), BX
+ MOVL BX, 4(SP)
+ MOVL flags+0(FP), BX
+ MOVL BX, 8(SP)
+ INT $0x80
+ MOVL AX, errno+12(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-4
MOVL $4, AX // sys_write
INT $0x80
JAE 2(PC)
- MOVL $-1, AX
+ NEGL AX // caller expects negative errno
MOVL AX, ret+12(FP)
RET
@@ -416,4 +436,21 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$32
NEGL AX
RET
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$16-4
+ MOVL $92, AX // fcntl
+ MOVL fd+0(FP), BX // fd
+ MOVL BX, 4(SP)
+ MOVL $3, 8(SP) // F_GETFL
+ MOVL $0, 12(SP)
+ INT $0x80
+ MOVL fd+0(FP), BX // fd
+ MOVL BX, 4(SP)
+ MOVL $4, 8(SP) // F_SETFL
+ ORL $4, AX // O_NONBLOCK
+ MOVL AX, 12(SP)
+ MOVL $92, AX // fcntl
+ INT $0x80
+ RET
+
GLOBL runtime·tlsoffset(SB),NOPTR,$4
diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s
index 17dd9e86a0c..66526bff0de 100644
--- a/src/runtime/sys_openbsd_amd64.s
+++ b/src/runtime/sys_openbsd_amd64.s
@@ -123,10 +123,27 @@ TEXT runtime·read(SB),NOSPLIT,$-8
MOVL $3, AX
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGQ AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ LEAQ r+0(FP), DI
+ MOVL $263, AX
+ SYSCALL
+ MOVL AX, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-20
+ LEAQ r+8(FP), DI
+ MOVL flags+0(FP), SI
+ MOVL $101, AX
+ SYSCALL
+ MOVL AX, errno+16(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT,$-8
MOVQ fd+0(FP), DI // arg 1 - fd
MOVQ p+8(FP), SI // arg 2 - buf
@@ -134,7 +151,7 @@ TEXT runtime·write1(SB),NOSPLIT,$-8
MOVL $4, AX // sys_write
SYSCALL
JCC 2(PC)
- MOVL $-1, AX
+ NEGQ AX // caller expects negative errno
MOVL AX, ret+24(FP)
RET
@@ -378,3 +395,18 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVL $92, AX // fcntl
SYSCALL
RET
+
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVL fd+0(FP), DI // fd
+ MOVQ $3, SI // F_GETFL
+ MOVQ $0, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ MOVL fd+0(FP), DI // fd
+ MOVQ $4, SI // F_SETFL
+ MOVQ $4, DX // O_NONBLOCK
+ ORL AX, DX
+ MOVL $92, AX // fcntl
+ SYSCALL
+ RET
diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s
index 69a5f4cf76a..92ab3270bee 100644
--- a/src/runtime/sys_openbsd_arm.s
+++ b/src/runtime/sys_openbsd_arm.s
@@ -55,17 +55,34 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0
MOVW n+8(FP), R2 // arg 3 - nbyte
MOVW $3, R12 // sys_read
SWI $0
- MOVW.CS $-1, R0
+ RSB.CS $0, R0 // caller expects negative errno
MOVW R0, ret+12(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT,$0-12
+ MOVW $r+0(FP), R0
+ MOVW $263, R12
+ SWI $0
+ MOVW R0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT,$0-16
+ MOVW $r+4(FP), R0
+ MOVW flags+0(FP), R1
+ MOVW $101, R12
+ SWI $0
+ MOVW R0, errno+12(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0
MOVW fd+0(FP), R0 // arg 1 - fd
MOVW p+4(FP), R1 // arg 2 - buf
MOVW n+8(FP), R2 // arg 3 - nbyte
MOVW $4, R12 // sys_write
SWI $0
- MOVW.CS $-1, R0
+ RSB.CS $0, R0 // caller expects negative errno
MOVW R0, ret+12(FP)
RET
@@ -368,6 +385,20 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
SWI $0
RET
+// func runtime·setNonblock(fd int32)
+TEXT runtime·setNonblock(SB),NOSPLIT,$0-4
+ MOVW fd+0(FP), R0 // fd
+ MOVW $3, R1 // F_GETFL
+ MOVW $0, R2
+ MOVW $92, R12
+ SWI $0
+ ORR $0x4, R0, R2 // O_NONBLOCK
+ MOVW fd+0(FP), R0 // fd
+ MOVW $4, R1 // F_SETFL
+ MOVW $92, R12
+ SWI $0
+ RET
+
TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0
B runtime·armPublicationBarrier(SB)
diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s
index e9c2a8b62c1..c8bf2d345e5 100644
--- a/src/runtime/sys_openbsd_arm64.s
+++ b/src/runtime/sys_openbsd_arm64.s
@@ -59,10 +59,32 @@ TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0
MOVD $3, R8 // sys_read
SVC
BCC 2(PC)
- MOVW $-1, R0
+ NEG R0, R0
MOVW R0, ret+24(FP)
RET
+// func pipe() (r, w int32, errno int32)
+TEXT runtime·pipe(SB),NOSPLIT|NOFRAME,$0-12
+ MOVD RSP, R0
+ MOVW $0, R1
+ MOVD $101, R8 // sys_pipe2
+ SVC
+ BCC 2(PC)
+ NEG R0, R0
+ MOVW R0, errno+8(FP)
+ RET
+
+// func pipe2(flags int32) (r, w int32, errno int32)
+TEXT runtime·pipe2(SB),NOSPLIT|NOFRAME,$0-20
+ ADD $8, RSP, R0
+ MOVW flags+0(FP), R1
+ MOVD $101, R8 // sys_pipe2
+ SVC
+ BCC 2(PC)
+ NEG R0, R0
+ MOVW R0, errno+16(FP)
+ RET
+
TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0
MOVW fd+0(FP), R0 // arg 1 - fd
MOVD p+8(FP), R1 // arg 2 - buf
@@ -70,7 +92,7 @@ TEXT runtime·write1(SB),NOSPLIT|NOFRAME,$0
MOVD $4, R8 // sys_write
SVC
BCC 2(PC)
- MOVW $-1, R0
+ NEG R0, R0
MOVW R0, ret+24(FP)
RET
@@ -394,3 +416,18 @@ TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVD $92, R8 // sys_fcntl
SVC
RET
+
+// func runtime·setNonblock(int32 fd)
+TEXT runtime·setNonblock(SB),NOSPLIT|NOFRAME,$0-4
+ MOVW fd+0(FP), R0 // arg 1 - fd
+ MOVD $3, R1 // arg 2 - cmd (F_GETFL)
+ MOVD $0, R2 // arg 3
+ MOVD $92, R8 // sys_fcntl
+ SVC
+ MOVD $0x800, R2 // O_NONBLOCK
+ EOR R0, R2 // arg 3 - flags
+ MOVW fd+0(FP), R0 // arg 1 - fd
+ MOVD $4, R1 // arg 2 - cmd (F_SETFL)
+ MOVD $92, R8 // sys_fcntl
+ SVC
+ RET
diff --git a/src/runtime/time.go b/src/runtime/time.go
index d667d11244e..fea5d6871c8 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -8,14 +8,24 @@ package runtime
import (
"internal/cpu"
+ "runtime/internal/atomic"
+ "runtime/internal/sys"
"unsafe"
)
+// Temporary scaffolding while the new timer code is added.
+const oldTimers = true
+
// Package time knows the layout of this structure.
// If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
type timer struct {
- tb *timersBucket // the bucket the timer lives in
- i int // heap index
+ tb *timersBucket // the bucket the timer lives in (oldTimers)
+ i int // heap index (oldTimers)
+
+ // If this timer is on a heap, which P's heap it is on.
+ // puintptr rather than *p to match uintptr in the versions
+ // of this struct defined in other packages. (!oldTimers)
+ pp puintptr
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
// each time calling f(arg, now) in the timer goroutine, so f must be
@@ -25,6 +35,12 @@ type timer struct {
f func(interface{}, uintptr)
arg interface{}
seq uintptr
+
+ // What to set the when field to in timerModifiedXX status. (!oldTimers)
+ nextwhen int64
+
+ // The status field holds one of the values below. (!oldTimers)
+ status uint32
}
// timersLen is the length of timers array.
@@ -69,6 +85,139 @@ type timersBucket struct {
t []*timer
}
+// Code outside this file has to be careful in using a timer value.
+//
+// The pp, status, and nextwhen fields may only be used by code in this file.
+//
+// Code that creates a new timer value can set the when, period, f,
+// arg, and seq fields.
+// A new timer value may be passed to addtimer (called by time.startTimer).
+// After doing that no fields may be touched.
+//
+// An active timer (one that has been passed to addtimer) may be
+// passed to deltimer (time.stopTimer), after which it is no longer an
+// active timer. It is an inactive timer.
+// In an inactive timer the period, f, arg, and seq fields may be modified,
+// but not the when field.
+// It's OK to just drop an inactive timer and let the GC collect it.
+// It's not OK to pass an inactive timer to addtimer.
+// Only newly allocated timer values may be passed to addtimer.
+//
+// An active timer may be passed to modtimer. No fields may be touched.
+// It remains an active timer.
+//
+// An inactive timer may be passed to resettimer to turn into an
+// active timer with an updated when field.
+// It's OK to pass a newly allocated timer value to resettimer.
+//
+// Timer operations are addtimer, deltimer, modtimer, resettimer,
+// cleantimers, adjusttimers, and runtimer.
+//
+// We don't permit calling addtimer/deltimer/modtimer/resettimer simultaneously,
+// but adjusttimers and runtimer can be called at the same time as any of those.
+//
+// Active timers live in heaps attached to P, in the timers field.
+// Inactive timers live there too temporarily, until they are removed.
+//
+// addtimer:
+// timerNoStatus -> timerWaiting
+// anything else -> panic: invalid value
+// deltimer:
+// timerWaiting -> timerDeleted
+// timerModifiedXX -> timerDeleted
+// timerNoStatus -> do nothing
+// timerDeleted -> do nothing
+// timerRemoving -> do nothing
+// timerRemoved -> do nothing
+// timerRunning -> wait until status changes
+// timerMoving -> wait until status changes
+// timerModifying -> panic: concurrent deltimer/modtimer calls
+// modtimer:
+// timerWaiting -> timerModifying -> timerModifiedXX
+// timerModifiedXX -> timerModifying -> timerModifiedYY
+// timerNoStatus -> timerWaiting
+// timerRemoved -> timerWaiting
+// timerRunning -> wait until status changes
+// timerMoving -> wait until status changes
+// timerRemoving -> wait until status changes
+// timerDeleted -> panic: concurrent modtimer/deltimer calls
+// timerModifying -> panic: concurrent modtimer calls
+// resettimer:
+// timerNoStatus -> timerWaiting
+// timerRemoved -> timerWaiting
+// timerDeleted -> timerModifying -> timerModifiedXX
+// timerRemoving -> wait until status changes
+// timerRunning -> wait until status changes
+// timerWaiting -> panic: resettimer called on active timer
+// timerMoving -> panic: resettimer called on active timer
+// timerModifiedXX -> panic: resettimer called on active timer
+// timerModifying -> panic: resettimer called on active timer
+// cleantimers (looks in P's timer heap):
+// timerDeleted -> timerRemoving -> timerRemoved
+// timerModifiedXX -> timerMoving -> timerWaiting
+// adjusttimers (looks in P's timer heap):
+// timerDeleted -> timerRemoving -> timerRemoved
+// timerModifiedXX -> timerMoving -> timerWaiting
+// runtimer (looks in P's timer heap):
+// timerNoStatus -> panic: uninitialized timer
+// timerWaiting -> timerWaiting or
+// timerWaiting -> timerRunning -> timerNoStatus or
+// timerWaiting -> timerRunning -> timerWaiting
+// timerModifying -> wait until status changes
+// timerModifiedXX -> timerMoving -> timerWaiting
+// timerDeleted -> timerRemoving -> timerRemoved
+// timerRunning -> panic: concurrent runtimer calls
+// timerRemoved -> panic: inconsistent timer heap
+// timerRemoving -> panic: inconsistent timer heap
+// timerMoving -> panic: inconsistent timer heap
+
+// Values for the timer status field.
+const (
+ // Timer has no status set yet.
+ timerNoStatus = iota
+
+ // Waiting for timer to fire.
+ // The timer is in some P's heap.
+ timerWaiting
+
+ // Running the timer function.
+ // A timer will only have this status briefly.
+ timerRunning
+
+ // The timer is deleted and should be removed.
+ // It should not be run, but it is still in some P's heap.
+ timerDeleted
+
+ // The timer is being removed.
+ // The timer will only have this status briefly.
+ timerRemoving
+
+ // The timer has been stopped.
+ // It is not in any P's heap.
+ timerRemoved
+
+ // The timer is being modified.
+ // The timer will only have this status briefly.
+ timerModifying
+
+ // The timer has been modified to an earlier time.
+ // The new when value is in the nextwhen field.
+ // The timer is in some P's heap, possibly in the wrong place.
+ timerModifiedEarlier
+
+ // The timer has been modified to the same or a later time.
+ // The new when value is in the nextwhen field.
+ // The timer is in some P's heap, possibly in the wrong place.
+ timerModifiedLater
+
+ // The timer has been modified and is being moved.
+ // The timer will only have this status briefly.
+ timerMoving
+)
+
+// maxWhen is the maximum value for timer's when field.
+const maxWhen = 1<<63 - 1
+
// Package time APIs.
// Godoc uses the comments in package time, not these.
@@ -77,6 +226,38 @@ type timersBucket struct {
// timeSleep puts the current goroutine to sleep for at least ns nanoseconds.
//go:linkname timeSleep time.Sleep
func timeSleep(ns int64) {
+ if oldTimers {
+ timeSleepOld(ns)
+ return
+ }
+
+ if ns <= 0 {
+ return
+ }
+
+ gp := getg()
+ t := gp.timer
+ if t == nil {
+ t = new(timer)
+ gp.timer = t
+ }
+ t.f = goroutineReady
+ t.arg = gp
+ t.nextwhen = nanotime() + ns
+ gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1)
+}
+
+// resetForSleep is called after the goroutine is parked for timeSleep.
+// We can't call resettimer in timeSleep itself because if this is a short
+// sleep and there are many goroutines then the P can wind up running the
+// timer function, goroutineReady, before the goroutine has been parked.
+func resetForSleep(gp *g, ut unsafe.Pointer) bool {
+ t := (*timer)(ut)
+ resettimer(t, t.nextwhen)
+ return true
+}
+
+func timeSleepOld(ns int64) {
if ns <= 0 {
return
}
@@ -97,7 +278,7 @@ func timeSleep(ns int64) {
unlock(&tb.lock)
badTimer()
}
- goparkunlock(&tb.lock, waitReasonSleep, traceEvGoSleep, 2)
+ goparkunlock(&tb.lock, waitReasonSleep, traceEvGoSleep, 3)
}
// startTimer adds t to the timer heap.
@@ -109,13 +290,22 @@ func startTimer(t *timer) {
addtimer(t)
}
-// stopTimer removes t from the timer heap if it is there.
-// It returns true if t was removed, false if t wasn't even there.
+// stopTimer stops a timer.
+// It reports whether t was stopped before being run.
//go:linkname stopTimer time.stopTimer
func stopTimer(t *timer) bool {
return deltimer(t)
}
+// resetTimer resets an inactive timer, adding it to the heap.
+//go:linkname resetTimer time.resetTimer
+func resetTimer(t *timer, when int64) {
+ if raceenabled {
+ racerelease(unsafe.Pointer(t))
+ }
+ resettimer(t, when)
+}
+
// Go runtime.
// Ready the goroutine arg.
@@ -123,7 +313,64 @@ func goroutineReady(arg interface{}, seq uintptr) {
goready(arg.(*g), 0)
}
+// addtimer adds a timer to the current P.
+// This should only be called with a newly created timer.
+// That avoids the risk of changing the when field of a timer in some P's heap,
+// which could cause the heap to become unsorted.
func addtimer(t *timer) {
+ if oldTimers {
+ addtimerOld(t)
+ return
+ }
+
+ // when must never be negative; otherwise runtimer will overflow
+ // during its delta calculation and never expire other runtime timers.
+ if t.when < 0 {
+ t.when = maxWhen
+ }
+ if t.status != timerNoStatus {
+ badTimer()
+ }
+ t.status = timerWaiting
+
+ addInitializedTimer(t)
+}
+
+// addInitializedTimer adds an initialized timer to the current P.
+func addInitializedTimer(t *timer) {
+ when := t.when
+
+ pp := getg().m.p.ptr()
+ lock(&pp.timersLock)
+ ok := cleantimers(pp) && doaddtimer(pp, t)
+ unlock(&pp.timersLock)
+ if !ok {
+ badTimer()
+ }
+
+ wakeNetPoller(when)
+}
+
+// doaddtimer adds t to the current P's heap.
+// It reports whether it saw no problems due to races.
+// The caller must have locked the timers for pp.
+func doaddtimer(pp *p, t *timer) bool {
+ // Timers rely on the network poller, so make sure the poller
+ // has started.
+ if netpollInited == 0 {
+ netpollGenericInit()
+ }
+
+ if t.pp != 0 {
+ throw("doaddtimer: P already set in timer")
+ }
+ t.pp.set(pp)
+ i := len(pp.timers)
+ pp.timers = append(pp.timers, t)
+ return siftupTimer(pp.timers, i)
+}
+
+func addtimerOld(t *timer) {
tb := t.assignBucket()
lock(&tb.lock)
ok := tb.addtimerLocked(t)
@@ -167,9 +414,106 @@ func (tb *timersBucket) addtimerLocked(t *timer) bool {
return true
}
-// Delete timer t from the heap.
-// Do not need to update the timerproc: if it wakes up early, no big deal.
+// deltimer deletes the timer t. It may be on some other P, so we can't
+// actually remove it from the timers heap. We can only mark it as deleted.
+// It will be removed in due course by the P whose heap it is on.
+// Reports whether the timer was removed before it was run.
func deltimer(t *timer) bool {
+ if oldTimers {
+ return deltimerOld(t)
+ }
+
+ for {
+ switch s := atomic.Load(&t.status); s {
+ case timerWaiting, timerModifiedLater:
+ if atomic.Cas(&t.status, s, timerDeleted) {
+ // Timer was not yet run.
+ return true
+ }
+ case timerModifiedEarlier:
+ tpp := t.pp.ptr()
+ if atomic.Cas(&t.status, s, timerModifying) {
+ atomic.Xadd(&tpp.adjustTimers, -1)
+ if !atomic.Cas(&t.status, timerModifying, timerDeleted) {
+ badTimer()
+ }
+ // Timer was not yet run.
+ return true
+ }
+ case timerDeleted, timerRemoving, timerRemoved:
+ // Timer was already run.
+ return false
+ case timerRunning, timerMoving:
+ // The timer is being run or moved, by a different P.
+ // Wait for it to complete.
+ osyield()
+ case timerNoStatus:
+ // Removing timer that was never added or
+ // has already been run. Also see issue 21874.
+ return false
+ case timerModifying:
+ // Simultaneous calls to deltimer and modtimer.
+ badTimer()
+ default:
+ badTimer()
+ }
+ }
+}
+
+// dodeltimer removes timer i from the current P's heap.
+// We are locked on the P when this is called.
+// It reports whether it saw no problems due to races.
+// The caller must have locked the timers for pp.
+func dodeltimer(pp *p, i int) bool {
+ if t := pp.timers[i]; t.pp.ptr() != pp {
+ throw("dodeltimer: wrong P")
+ } else {
+ t.pp = 0
+ }
+ last := len(pp.timers) - 1
+ if i != last {
+ pp.timers[i] = pp.timers[last]
+ }
+ pp.timers[last] = nil
+ pp.timers = pp.timers[:last]
+ ok := true
+ if i != last {
+ // Moving to i may have moved the last timer to a new parent,
+ // so sift up to preserve the heap guarantee.
+ if !siftupTimer(pp.timers, i) {
+ ok = false
+ }
+ if !siftdownTimer(pp.timers, i) {
+ ok = false
+ }
+ }
+ return ok
+}
+
+// dodeltimer0 removes timer 0 from the current P's heap.
+// We are locked on the P when this is called.
+// It reports whether it saw no problems due to races.
+// The caller must have locked the timers for pp.
+func dodeltimer0(pp *p) bool {
+ if t := pp.timers[0]; t.pp.ptr() != pp {
+ throw("dodeltimer0: wrong P")
+ } else {
+ t.pp = 0
+ }
+ last := len(pp.timers) - 1
+ if last > 0 {
+ pp.timers[0] = pp.timers[last]
+ }
+ pp.timers[last] = nil
+ pp.timers = pp.timers[:last]
+ ok := true
+ if last > 0 {
+ ok = siftdownTimer(pp.timers, 0)
+ }
+ return ok
+}
+
+func deltimerOld(t *timer) bool {
if t.tb == nil {
// t.tb can be nil if the user created a timer
// directly, without invoking startTimer e.g
@@ -217,7 +561,97 @@ func (tb *timersBucket) deltimerLocked(t *timer) (removed, ok bool) {
return true, ok
}
+// modtimer modifies an existing timer.
+// This is called by the netpoll code.
func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {
+ if oldTimers {
+ modtimerOld(t, when, period, f, arg, seq)
+ return
+ }
+
+ if when < 0 {
+ when = maxWhen
+ }
+
+ status := uint32(timerNoStatus)
+ wasRemoved := false
+loop:
+ for {
+ switch status = atomic.Load(&t.status); status {
+ case timerWaiting, timerModifiedEarlier, timerModifiedLater:
+ if atomic.Cas(&t.status, status, timerModifying) {
+ break loop
+ }
+ case timerNoStatus, timerRemoved:
+ // Timer was already run and t is no longer in a heap.
+ // Act like addtimer.
+ wasRemoved = true
+ atomic.Store(&t.status, timerWaiting)
+ break loop
+ case timerRunning, timerRemoving, timerMoving:
+ // The timer is being run or moved, by a different P.
+ // Wait for it to complete.
+ osyield()
+ case timerDeleted:
+ // Simultaneous calls to modtimer and deltimer.
+ badTimer()
+ case timerModifying:
+ // Multiple simultaneous calls to modtimer.
+ badTimer()
+ default:
+ badTimer()
+ }
+ }
+
+ t.period = period
+ t.f = f
+ t.arg = arg
+ t.seq = seq
+
+ if wasRemoved {
+ t.when = when
+ addInitializedTimer(t)
+ } else {
+ // The timer is in some other P's heap, so we can't change
+ // the when field. If we did, the other P's heap would
+ // be out of order. So we put the new when value in the
+ // nextwhen field, and let the other P set the when field
+ // when it is prepared to resort the heap.
+ t.nextwhen = when
+
+ newStatus := uint32(timerModifiedLater)
+ if when < t.when {
+ newStatus = timerModifiedEarlier
+ }
+
+ // Update the adjustTimers field. Subtract one if we
+ // are removing a timerModifiedEarlier, add one if we
+ // are adding a timerModifiedEarlier.
+ tpp := t.pp.ptr()
+ adjust := int32(0)
+ if status == timerModifiedEarlier {
+ adjust--
+ }
+ if newStatus == timerModifiedEarlier {
+ adjust++
+ }
+ if adjust != 0 {
+ atomic.Xadd(&tpp.adjustTimers, adjust)
+ }
+
+ // Set the new status of the timer.
+ if !atomic.Cas(&t.status, timerModifying, newStatus) {
+ badTimer()
+ }
+
+ // If the new status is earlier, wake up the poller.
+ if newStatus == timerModifiedEarlier {
+ wakeNetPoller(when)
+ }
+ }
+}
+
+func modtimerOld(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) {
tb := t.tb
lock(&tb.lock)
@@ -236,6 +670,66 @@ func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg in
}
}
+// resettimer resets an existing inactive timer to turn it into an active timer,
+// with a new time for when the timer should fire.
+// This should be called instead of addtimer if the timer value has been,
+// or may have been, used previously.
+func resettimer(t *timer, when int64) {
+ if oldTimers {
+ resettimerOld(t, when)
+ return
+ }
+
+ if when < 0 {
+ when = maxWhen
+ }
+
+ for {
+ switch s := atomic.Load(&t.status); s {
+ case timerNoStatus, timerRemoved:
+ atomic.Store(&t.status, timerWaiting)
+ t.when = when
+ addInitializedTimer(t)
+ return
+ case timerDeleted:
+ if atomic.Cas(&t.status, s, timerModifying) {
+ t.nextwhen = when
+ newStatus := uint32(timerModifiedLater)
+ if when < t.when {
+ newStatus = timerModifiedEarlier
+ atomic.Xadd(&t.pp.ptr().adjustTimers, 1)
+ }
+ if !atomic.Cas(&t.status, timerModifying, newStatus) {
+ badTimer()
+ }
+ if newStatus == timerModifiedEarlier {
+ wakeNetPoller(when)
+ }
+ return
+ }
+ case timerRemoving:
+ // Wait for the removal to complete.
+ osyield()
+ case timerRunning:
+ // Even though the timer should not be active,
+ // we can see timerRunning if the timer function
+ // permits some other goroutine to call resettimer.
+ // Wait until the run is complete.
+ osyield()
+ case timerWaiting, timerModifying, timerModifiedEarlier, timerModifiedLater, timerMoving:
+ // Called resettimer on active timer.
+ badTimer()
+ default:
+ badTimer()
+ }
+ }
+}
+
+func resettimerOld(t *timer, when int64) {
+ t.when = when
+ addtimer(t)
+}
+
// Timerproc runs the time-driven events.
// It sleeps until the next event in the tb heap.
// If addtimer inserts a new earlier event, it wakes timerproc early.
@@ -307,7 +801,412 @@ func timerproc(tb *timersBucket) {
}
}
-func timejump() *g {
+// cleantimers cleans up the head of the timer queue. This speeds up
+// programs that create and delete timers; leaving them in the heap
+// slows down addtimer. Reports whether no timer problems were found.
+// The caller must have locked the timers for pp.
+func cleantimers(pp *p) bool {
+ for {
+ if len(pp.timers) == 0 {
+ return true
+ }
+ t := pp.timers[0]
+ if t.pp.ptr() != pp {
+ throw("cleantimers: bad p")
+ }
+ switch s := atomic.Load(&t.status); s {
+ case timerDeleted:
+ if !atomic.Cas(&t.status, s, timerRemoving) {
+ continue
+ }
+ if !dodeltimer0(pp) {
+ return false
+ }
+ if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
+ return false
+ }
+ case timerModifiedEarlier, timerModifiedLater:
+ if !atomic.Cas(&t.status, s, timerMoving) {
+ continue
+ }
+ // Now we can change the when field.
+ t.when = t.nextwhen
+ // Move t to the right position.
+ if !dodeltimer0(pp) {
+ return false
+ }
+ if !doaddtimer(pp, t) {
+ return false
+ }
+ if s == timerModifiedEarlier {
+ atomic.Xadd(&pp.adjustTimers, -1)
+ }
+ if !atomic.Cas(&t.status, timerMoving, timerWaiting) {
+ return false
+ }
+ default:
+ // Head of timers does not need adjustment.
+ return true
+ }
+ }
+}
+
+// moveTimers moves a slice of timers to pp. The slice has been taken
+// from a different P.
+// This is currently called when the world is stopped, but it could
+// work as long as the timers for pp are locked.
+func moveTimers(pp *p, timers []*timer) {
+ for _, t := range timers {
+ loop:
+ for {
+ switch s := atomic.Load(&t.status); s {
+ case timerWaiting:
+ t.pp = 0
+ if !doaddtimer(pp, t) {
+ badTimer()
+ }
+ break loop
+ case timerModifiedEarlier, timerModifiedLater:
+ if !atomic.Cas(&t.status, s, timerMoving) {
+ continue
+ }
+ t.when = t.nextwhen
+ t.pp = 0
+ if !doaddtimer(pp, t) {
+ badTimer()
+ }
+ if !atomic.Cas(&t.status, timerMoving, timerWaiting) {
+ badTimer()
+ }
+ break loop
+ case timerDeleted:
+ if !atomic.Cas(&t.status, s, timerRemoved) {
+ continue
+ }
+ t.pp = 0
+ // We no longer need this timer in the heap.
+ break loop
+ case timerModifying:
+ // Loop until the modification is complete.
+ osyield()
+ case timerNoStatus, timerRemoved:
+ // We should not see these status values in a timers heap.
+ badTimer()
+ case timerRunning, timerRemoving, timerMoving:
+ // Some other P thinks it owns this timer,
+ // which should not happen.
+ badTimer()
+ default:
+ badTimer()
+ }
+ }
+ }
+}
+
+// adjusttimers looks through the timers in the current P's heap for
+// any timers that have been modified to run earlier, and puts them in
+// the correct place in the heap. While looking for those timers,
+// it also moves timers that have been modified to run later,
+// and removes deleted timers. The caller must have locked the timers for pp.
+func adjusttimers(pp *p) {
+ if len(pp.timers) == 0 {
+ return
+ }
+ if atomic.Load(&pp.adjustTimers) == 0 {
+ return
+ }
+ var moved []*timer
+ for i := 0; i < len(pp.timers); i++ {
+ t := pp.timers[i]
+ if t.pp.ptr() != pp {
+ throw("adjusttimers: bad p")
+ }
+ switch s := atomic.Load(&t.status); s {
+ case timerDeleted:
+ if atomic.Cas(&t.status, s, timerRemoving) {
+ if !dodeltimer(pp, i) {
+ badTimer()
+ }
+ if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
+ badTimer()
+ }
+ // Look at this heap position again.
+ i--
+ }
+ case timerModifiedEarlier, timerModifiedLater:
+ if atomic.Cas(&t.status, s, timerMoving) {
+ // Now we can change the when field.
+ t.when = t.nextwhen
+ // Take t off the heap, and hold onto it.
+ // We don't add it back yet because the
+ // heap manipulation could cause our
+ // loop to skip some other timer.
+ if !dodeltimer(pp, i) {
+ badTimer()
+ }
+ moved = append(moved, t)
+ if !atomic.Cas(&t.status, timerMoving, timerWaiting) {
+ badTimer()
+ }
+ if s == timerModifiedEarlier {
+ if n := atomic.Xadd(&pp.adjustTimers, -1); int32(n) <= 0 {
+ addAdjustedTimers(pp, moved)
+ return
+ }
+ }
+ }
+ case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving:
+ badTimer()
+ case timerWaiting:
+ // OK, nothing to do.
+ case timerModifying:
+ // Check again after modification is complete.
+ osyield()
+ i--
+ default:
+ badTimer()
+ }
+ }
+
+ if len(moved) > 0 {
+ addAdjustedTimers(pp, moved)
+ }
+}
+
+// addAdjustedTimers adds any timers we adjusted in adjusttimers
+// back to the timer heap.
+func addAdjustedTimers(pp *p, moved []*timer) {
+ for _, t := range moved {
+ loop:
+ for {
+ switch s := atomic.Load(&t.status); s {
+ case timerWaiting:
+ // This is the normal case.
+ if !doaddtimer(pp, t) {
+ badTimer()
+ }
+ break loop
+ case timerDeleted:
+ // Timer has been deleted since we adjusted it.
+ // This timer is already out of the heap.
+ if !atomic.Cas(&t.status, s, timerRemoved) {
+ badTimer()
+ }
+ break loop
+ case timerModifiedEarlier, timerModifiedLater:
+ // Timer has been modified again since
+ // we adjusted it.
+ if atomic.Cas(&t.status, s, timerMoving) {
+ t.when = t.nextwhen
+ if !doaddtimer(pp, t) {
+ badTimer()
+ }
+ if !atomic.Cas(&t.status, timerMoving, timerWaiting) {
+ badTimer()
+ }
+ if s == timerModifiedEarlier {
+ atomic.Xadd(&pp.adjustTimers, -1)
+ }
+ }
+ break loop
+ case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving:
+ badTimer()
+ case timerModifying:
+ // Wait and try again.
+ osyield()
+ continue
+ }
+ }
+ }
+}
+
+// runtimer examines the first timer in timers. If it is ready based on now,
+// it runs the timer and removes or updates it.
+// Returns 0 if it ran a timer, -1 if there are no more timers, or the time
+// when the first timer should run.
+// The caller must have locked the timers for pp.
+func runtimer(pp *p, now int64) int64 {
+ for {
+ t := pp.timers[0]
+ if t.pp.ptr() != pp {
+ throw("runtimer: bad p")
+ }
+ switch s := atomic.Load(&t.status); s {
+ case timerWaiting:
+ if t.when > now {
+ // Not ready to run.
+ return t.when
+ }
+
+ if !atomic.Cas(&t.status, s, timerRunning) {
+ continue
+ }
+ runOneTimer(pp, t, now)
+ return 0
+
+ case timerDeleted:
+ if !atomic.Cas(&t.status, s, timerRemoving) {
+ continue
+ }
+ if !dodeltimer0(pp) {
+ badTimer()
+ }
+ if !atomic.Cas(&t.status, timerRemoving, timerRemoved) {
+ badTimer()
+ }
+ if len(pp.timers) == 0 {
+ return -1
+ }
+
+ case timerModifiedEarlier, timerModifiedLater:
+ if !atomic.Cas(&t.status, s, timerMoving) {
+ continue
+ }
+ t.when = t.nextwhen
+ if !dodeltimer0(pp) {
+ badTimer()
+ }
+ if !doaddtimer(pp, t) {
+ badTimer()
+ }
+ if s == timerModifiedEarlier {
+ atomic.Xadd(&pp.adjustTimers, -1)
+ }
+ if !atomic.Cas(&t.status, timerMoving, timerWaiting) {
+ badTimer()
+ }
+
+ case timerModifying:
+ // Wait for modification to complete.
+ osyield()
+
+ case timerNoStatus, timerRemoved:
+ // Should not see a new or inactive timer on the heap.
+ badTimer()
+ case timerRunning, timerRemoving, timerMoving:
+ // These should only be set when timers are locked,
+ // and we didn't do it.
+ badTimer()
+ default:
+ badTimer()
+ }
+ }
+}
+
+// runOneTimer runs a single timer.
+// The caller must have locked the timers for pp.
+func runOneTimer(pp *p, t *timer, now int64) {
+ if raceenabled {
+ if pp.timerRaceCtx == 0 {
+ pp.timerRaceCtx = racegostart(funcPC(runtimer) + sys.PCQuantum)
+ }
+ raceacquirectx(pp.timerRaceCtx, unsafe.Pointer(t))
+ }
+
+ f := t.f
+ arg := t.arg
+ seq := t.seq
+
+ if t.period > 0 {
+ // Leave in heap but adjust next time to fire.
+ delta := t.when - now
+ t.when += t.period * (1 + -delta/t.period)
+ if !siftdownTimer(pp.timers, 0) {
+ badTimer()
+ }
+ if !atomic.Cas(&t.status, timerRunning, timerWaiting) {
+ badTimer()
+ }
+ } else {
+ // Remove from heap.
+ if !dodeltimer0(pp) {
+ badTimer()
+ }
+ if !atomic.Cas(&t.status, timerRunning, timerNoStatus) {
+ badTimer()
+ }
+ }
+
+ if raceenabled {
+ // Temporarily use the P's racectx for g0.
+ gp := getg()
+ if gp.racectx != 0 {
+ throw("runOneTimer: unexpected racectx")
+ }
+ gp.racectx = pp.timerRaceCtx
+ }
+
+ // Note that since timers are locked here, f may not call
+ // addtimer or resettimer.
+
+ f(arg, seq)
+
+ if raceenabled {
+ gp := getg()
+ gp.racectx = 0
+ }
+}
+
+func timejump() *p {
+ if faketime == 0 {
+ return nil
+ }
+
+ // Nothing is running, so we can look at all the P's.
+ // Determine a timer bucket with minimum when.
+ var (
+ minT *timer
+ minWhen int64
+ minP *p
+ )
+ for _, pp := range allp {
+ if pp.status != _Pidle && pp.status != _Pdead {
+ throw("non-idle P in timejump")
+ }
+ if len(pp.timers) == 0 {
+ continue
+ }
+ c := pp.adjustTimers
+ for _, t := range pp.timers {
+ switch s := atomic.Load(&t.status); s {
+ case timerWaiting:
+ if minT == nil || t.when < minWhen {
+ minT = t
+ minWhen = t.when
+ minP = pp
+ }
+ case timerModifiedEarlier, timerModifiedLater:
+ if minT == nil || t.nextwhen < minWhen {
+ minT = t
+ minWhen = t.nextwhen
+ minP = pp
+ }
+ if s == timerModifiedEarlier {
+ c--
+ }
+ case timerRunning, timerModifying, timerMoving:
+ badTimer()
+ }
+ // The timers are sorted, so we only have to check
+ // the first timer for each P, unless there are
+ // some timerModifiedEarlier timers. The number
+ // of timerModifiedEarlier timers is in the adjustTimers
+ // field, used to initialize c, above.
+ if c == 0 {
+ break
+ }
+ }
+ }
+
+ if minT == nil || minWhen <= faketime {
+ return nil
+ }
+
+ faketime = minWhen
+ return minP
+}
+
+func timejumpOld() *g {
if faketime == 0 {
return nil
}
@@ -350,6 +1249,52 @@ func timejumpLocked() *g {
}
func timeSleepUntil() int64 {
+ if oldTimers {
+ return timeSleepUntilOld()
+ }
+
+ next := int64(maxWhen)
+
+ for _, pp := range allp {
+ lock(&pp.timersLock)
+ c := atomic.Load(&pp.adjustTimers)
+ for _, t := range pp.timers {
+ switch s := atomic.Load(&t.status); s {
+ case timerWaiting:
+ if t.when < next {
+ next = t.when
+ }
+ case timerModifiedEarlier, timerModifiedLater:
+ if t.nextwhen < next {
+ next = t.nextwhen
+ }
+ if s == timerModifiedEarlier {
+ c--
+ }
+ }
+ // The timers are sorted, so we only have to check
+ // the first timer for each P, unless there are
+ // some timerModifiedEarlier timers. The number
+ // of timerModifiedEarlier timers is in the adjustTimers
+ // field, used to initialize c, above.
+ //
+ // We don't worry about cases like timerModifying.
+ // New timers can show up at any time,
+ // so this function is necessarily imprecise.
+ // Do a signed check here since we aren't
+ // synchronizing the read of pp.adjustTimers
+ // with the check of a timer status.
+ if int32(c) <= 0 {
+ break
+ }
+ }
+ unlock(&pp.timersLock)
+ }
+
+ return next
+}
+
+func timeSleepUntilOld() int64 {
next := int64(1<<63 - 1)
// Determine minimum sleepUntil across all the timer buckets.
diff --git a/src/sync/atomic/atomic_test.go b/src/sync/atomic/atomic_test.go
index 135f02a726a..286eadc6cdf 100644
--- a/src/sync/atomic/atomic_test.go
+++ b/src/sync/atomic/atomic_test.go
@@ -1140,6 +1140,9 @@ func hammerStoreLoadUintptr(t *testing.T, paddr unsafe.Pointer) {
StoreUintptr(addr, new)
}
+//go:nocheckptr
+// This code is just testing that LoadPointer/StorePointer operate
+// atomically; it's not actually calculating pointers.
func hammerStoreLoadPointer(t *testing.T, paddr unsafe.Pointer) {
addr := (*unsafe.Pointer)(paddr)
v := uintptr(LoadPointer(addr))
diff --git a/src/syscall/env_plan9.go b/src/syscall/env_plan9.go
index 9a8a837e7d3..e403a25e314 100644
--- a/src/syscall/env_plan9.go
+++ b/src/syscall/env_plan9.go
@@ -74,7 +74,21 @@ func Setenv(key, value string) error {
}
func Clearenv() {
- RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
+ // Creating a new environment group using rfork(RFCENVG) can race
+ // with access to files in /env (e.g. from Setenv or Getenv).
+ // Remove all environment variables in current environment group instead.
+ fd, err := open("/env", O_RDONLY)
+ if err != nil {
+ return
+ }
+ defer Close(fd)
+ files, err := readdirnames(fd)
+ if err != nil {
+ return
+ }
+ for _, key := range files {
+ Remove("/env/" + key)
+ }
}
func Unsetenv(key string) error {
diff --git a/src/syscall/getdirentries_test.go b/src/syscall/getdirentries_test.go
index 8505a0bb897..2a3419c2300 100644
--- a/src/syscall/getdirentries_test.go
+++ b/src/syscall/getdirentries_test.go
@@ -66,7 +66,11 @@ func testGetdirentries(t *testing.T, count int) {
}
data := buf[:n]
for len(data) > 0 {
- dirent := (*syscall.Dirent)(unsafe.Pointer(&data[0]))
+ // If multiple Dirents are written into buf, sometimes when we reach the final one,
+ // we have cap(buf) < Sizeof(Dirent). So use an appropriate slice to copy from data.
+ var dirent syscall.Dirent
+ copy((*[unsafe.Sizeof(dirent)]byte)(unsafe.Pointer(&dirent))[:], data)
+
data = data[dirent.Reclen:]
name := make([]byte, dirent.Namlen)
for i := 0; i < int(dirent.Namlen); i++ {
diff --git a/src/syscall/security_windows.go b/src/syscall/security_windows.go
index db80d98a084..e35c4a0c258 100644
--- a/src/syscall/security_windows.go
+++ b/src/syscall/security_windows.go
@@ -157,13 +157,15 @@ func LookupSID(system, account string) (sid *SID, domain string, accType uint32,
// String converts sid to a string format
// suitable for display, storage, or transmission.
func (sid *SID) String() (string, error) {
+ // From https://docs.microsoft.com/en-us/windows/win32/secbiomet/general-constants
+ const SecurityMaxSidSize = 68
var s *uint16
e := ConvertSidToStringSid(sid, &s)
if e != nil {
return "", e
}
defer LocalFree((Handle)(unsafe.Pointer(s)))
- return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]), nil
+ return UTF16ToString((*[SecurityMaxSidSize]uint16)(unsafe.Pointer(s))[:]), nil
}
// Len returns the length, in bytes, of a valid security identifier sid.
diff --git a/src/syscall/sockcmsg_dragonfly.go b/src/syscall/sockcmsg_dragonfly.go
new file mode 100644
index 00000000000..d217d9eed14
--- /dev/null
+++ b/src/syscall/sockcmsg_dragonfly.go
@@ -0,0 +1,16 @@
+// Copyright 2019 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 syscall
+
+// Round the length of a raw sockaddr up to align it properly.
+func cmsgAlignOf(salen int) int {
+ salign := sizeofPtr
+ if sizeofPtr == 8 && !supportsABI(_dragonflyABIChangeVersion) {
+ // 64-bit Dragonfly before the September 2019 ABI changes still requires
+ // 32-bit aligned access to network subsystem.
+ salign = 4
+ }
+ return (salen + salign - 1) & ^(salign - 1)
+}
diff --git a/src/syscall/sockcmsg_unix.go b/src/syscall/sockcmsg_unix.go
index 4eb0c0b7487..1ff97a5ae1b 100644
--- a/src/syscall/sockcmsg_unix.go
+++ b/src/syscall/sockcmsg_unix.go
@@ -9,35 +9,9 @@
package syscall
import (
- "runtime"
"unsafe"
)
-// Round the length of a raw sockaddr up to align it properly.
-func cmsgAlignOf(salen int) int {
- salign := sizeofPtr
-
- switch runtime.GOOS {
- case "aix":
- // There is no alignment on AIX.
- salign = 1
- case "darwin", "dragonfly", "illumos", "solaris":
- // NOTE: It seems like 64-bit Darwin, DragonFly BSD and
- // Solaris kernels still require 32-bit aligned access to
- // network subsystem.
- if sizeofPtr == 8 {
- salign = 4
- }
- case "netbsd", "openbsd":
- // NetBSD and OpenBSD armv7 require 64-bit alignment.
- if runtime.GOARCH == "arm" {
- salign = 8
- }
- }
-
- return (salen + salign - 1) & ^(salign - 1)
-}
-
// CmsgLen returns the value to store in the Len field of the Cmsghdr
// structure, taking into account any necessary alignment.
func CmsgLen(datalen int) int {
diff --git a/src/syscall/sockcmsg_unix_other.go b/src/syscall/sockcmsg_unix_other.go
new file mode 100644
index 00000000000..fbafbf83410
--- /dev/null
+++ b/src/syscall/sockcmsg_unix_other.go
@@ -0,0 +1,38 @@
+// Copyright 2019 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.
+
+// +build aix darwin freebsd linux netbsd openbsd solaris
+
+package syscall
+
+import (
+ "runtime"
+)
+
+// Round the length of a raw sockaddr up to align it properly.
+func cmsgAlignOf(salen int) int {
+ salign := sizeofPtr
+
+ // dragonfly needs to check ABI version at runtime, see cmsgAlignOf in
+ // sockcmsg_dragonfly.go
+ switch runtime.GOOS {
+ case "aix":
+ // There is no alignment on AIX.
+ salign = 1
+ case "darwin", "illumos", "solaris":
+ // NOTE: It seems like 64-bit Darwin, Illumos and Solaris
+ // kernels still require 32-bit aligned access to network
+ // subsystem.
+ if sizeofPtr == 8 {
+ salign = 4
+ }
+ case "netbsd", "openbsd":
+ // NetBSD and OpenBSD armv7 require 64-bit alignment.
+ if runtime.GOARCH == "arm" {
+ salign = 8
+ }
+ }
+
+ return (salen + salign - 1) & ^(salign - 1)
+}
diff --git a/src/syscall/syscall_bsd.go b/src/syscall/syscall_bsd.go
index 3d043493871..fda9d613d3b 100644
--- a/src/syscall/syscall_bsd.go
+++ b/src/syscall/syscall_bsd.go
@@ -242,7 +242,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
break
}
}
- bytes := (*[10000]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
+ bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
return sa, nil
diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_darwin.go
index 2efcf3bea94..c84547c628c 100644
--- a/src/syscall/syscall_darwin.go
+++ b/src/syscall/syscall_darwin.go
@@ -337,6 +337,7 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
//sysnb ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_ioctl
//sysnb execve(path *byte, argv **byte, envp **byte) (err error)
//sysnb exit(res int) (err error)
+//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error)
//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (val int, err error) = SYS_fcntl
//sys unlinkat(fd int, path string, flags int) (err error)
//sys openat(fd int, path string, flags int, perm uint32) (fdret int, err error)
@@ -379,8 +380,6 @@ func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
- const ptrSize = unsafe.Sizeof(uintptr(0))
-
// We store the number of entries to skip in the seek
// offset of fd. See issue #31368.
// It's not the full required semantics, but should handle the case
diff --git a/src/syscall/syscall_darwin_386.go b/src/syscall/syscall_darwin_386.go
index 46714bab57f..8c5b82da55d 100644
--- a/src/syscall/syscall_darwin_386.go
+++ b/src/syscall/syscall_darwin_386.go
@@ -22,7 +22,6 @@ func setTimeval(sec, usec int64) Timeval {
//sys Statfs(path string, stat *Statfs_t) (err error) = SYS_statfs64
//sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_fstatat64
//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
-//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error)
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint32(fd)
diff --git a/src/syscall/syscall_darwin_amd64.go b/src/syscall/syscall_darwin_amd64.go
index 43506e4531d..23a4e5f9962 100644
--- a/src/syscall/syscall_darwin_amd64.go
+++ b/src/syscall/syscall_darwin_amd64.go
@@ -22,7 +22,6 @@ func setTimeval(sec, usec int64) Timeval {
//sys Statfs(path string, stat *Statfs_t) (err error) = SYS_statfs64
//sys fstatat(fd int, path string, stat *Stat_t, flags int) (err error) = SYS_fstatat64
//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
-//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error)
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint64(fd)
diff --git a/src/syscall/syscall_darwin_arm.go b/src/syscall/syscall_darwin_arm.go
index 4ca2e300e4c..7f39cf40031 100644
--- a/src/syscall/syscall_darwin_arm.go
+++ b/src/syscall/syscall_darwin_arm.go
@@ -29,10 +29,6 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) error {
return ENOTSUP
}
-func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error {
- return ENOTSUP
-}
-
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint32(fd)
k.Filter = int16(mode)
diff --git a/src/syscall/syscall_darwin_arm64.go b/src/syscall/syscall_darwin_arm64.go
index fde48f35961..bd110f2e7f2 100644
--- a/src/syscall/syscall_darwin_arm64.go
+++ b/src/syscall/syscall_darwin_arm64.go
@@ -29,10 +29,6 @@ func ptrace(request int, pid int, addr uintptr, data uintptr) error {
return ENOTSUP
}
-func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error {
- return ENOTSUP
-}
-
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint64(fd)
k.Filter = int16(mode)
diff --git a/src/syscall/syscall_dragonfly.go b/src/syscall/syscall_dragonfly.go
index 56dd0d76e91..0988fe46088 100644
--- a/src/syscall/syscall_dragonfly.go
+++ b/src/syscall/syscall_dragonfly.go
@@ -12,7 +12,25 @@
package syscall
-import "unsafe"
+import (
+ "sync"
+ "unsafe"
+)
+
+// See version list in https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/param.h
+var (
+ osreldateOnce sync.Once
+ osreldate uint32
+)
+
+// First __DragonFly_version after September 2019 ABI changes
+// http://lists.dragonflybsd.org/pipermail/users/2019-September/358280.html
+const _dragonflyABIChangeVersion = 500705
+
+func supportsABI(ver uint32) bool {
+ osreldateOnce.Do(func() { osreldate, _ = SysctlUint32("kern.osreldate") })
+ return osreldate >= ver
+}
type SockaddrDatalink struct {
Len uint8
diff --git a/src/syscall/syscall_freebsd.go b/src/syscall/syscall_freebsd.go
index 3abc722c427..7c7b89aab90 100644
--- a/src/syscall/syscall_freebsd.go
+++ b/src/syscall/syscall_freebsd.go
@@ -381,8 +381,12 @@ func convertFromDirents11(buf []byte, old []byte) int {
dstPos := 0
srcPos := 0
for dstPos+fixedSize < len(buf) && srcPos+oldFixedSize < len(old) {
- dstDirent := (*Dirent)(unsafe.Pointer(&buf[dstPos]))
- srcDirent := (*dirent_freebsd11)(unsafe.Pointer(&old[srcPos]))
+ var dstDirent Dirent
+ var srcDirent dirent_freebsd11
+
+ // If multiple direntries are written, sometimes when we reach the final one,
+ // we may have cap of old less than size of dirent_freebsd11.
+ copy((*[unsafe.Sizeof(srcDirent)]byte)(unsafe.Pointer(&srcDirent))[:], old[srcPos:])
reclen := roundup(fixedSize+int(srcDirent.Namlen)+1, 8)
if dstPos+reclen > len(buf) {
@@ -398,6 +402,7 @@ func convertFromDirents11(buf []byte, old []byte) int {
dstDirent.Pad1 = 0
copy(dstDirent.Name[:], srcDirent.Name[:srcDirent.Namlen])
+ copy(buf[dstPos:], (*[unsafe.Sizeof(dstDirent)]byte)(unsafe.Pointer(&dstDirent))[:])
padding := buf[dstPos+fixedSize+int(dstDirent.Namlen) : dstPos+reclen]
for i := range padding {
padding[i] = 0
diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go
index ed57c93b1f5..2eba033d7c6 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -484,7 +484,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
for n < len(pp.Path) && pp.Path[n] != 0 {
n++
}
- bytes := (*[10000]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
+ bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
return sa, nil
diff --git a/src/syscall/syscall_solaris.go b/src/syscall/syscall_solaris.go
index 425f5122de1..daa4b88a71d 100644
--- a/src/syscall/syscall_solaris.go
+++ b/src/syscall/syscall_solaris.go
@@ -293,7 +293,7 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
for n < len(pp.Path) && pp.Path[n] != 0 {
n++
}
- bytes := (*[10000]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
+ bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
return sa, nil
diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index e4d78d66ada..992f6738ce2 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -312,7 +312,11 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
default:
createmode = OPEN_EXISTING
}
- h, e := CreateFile(pathp, access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
+ var attrs uint32 = FILE_ATTRIBUTE_NORMAL
+ if perm&S_IWRITE == 0 {
+ attrs = FILE_ATTRIBUTE_READONLY
+ }
+ h, e := CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
return h, e
}
@@ -765,7 +769,7 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) {
for n < len(pp.Path) && pp.Path[n] != 0 {
n++
}
- bytes := (*[10000]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
+ bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
sa.Name = string(bytes)
return sa, nil
diff --git a/src/syscall/zsyscall_darwin_386.go b/src/syscall/zsyscall_darwin_386.go
index 0ffc692116f..2c3b15f5f94 100644
--- a/src/syscall/zsyscall_darwin_386.go
+++ b/src/syscall/zsyscall_darwin_386.go
@@ -1870,6 +1870,27 @@ func libc_exit_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := syscall6(funcPC(libc_sysctl_trampoline), uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func libc_sysctl_trampoline()
+
+//go:linkname libc_sysctl libc_sysctl
+//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (val int, err error) {
r0, _, e1 := syscall(funcPC(libc_fcntl_trampoline), uintptr(fd), uintptr(cmd), uintptr(arg))
val = int(r0)
@@ -2060,24 +2081,3 @@ func libc_ptrace_trampoline()
//go:linkname libc_ptrace libc_ptrace
//go:cgo_import_dynamic libc_ptrace ptrace "/usr/lib/libSystem.B.dylib"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
- var _p0 unsafe.Pointer
- if len(mib) > 0 {
- _p0 = unsafe.Pointer(&mib[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- _, _, e1 := syscall6(funcPC(libc_sysctl_trampoline), uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_sysctl_trampoline()
-
-//go:linkname libc_sysctl libc_sysctl
-//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
diff --git a/src/syscall/zsyscall_darwin_386.s b/src/syscall/zsyscall_darwin_386.s
index 0dec5baa226..d84b46229e1 100644
--- a/src/syscall/zsyscall_darwin_386.s
+++ b/src/syscall/zsyscall_darwin_386.s
@@ -229,6 +229,8 @@ TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
JMP libc_execve(SB)
TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
JMP libc_exit(SB)
+TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+ JMP libc_sysctl(SB)
TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
JMP libc_unlinkat(SB)
TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
@@ -249,5 +251,3 @@ TEXT ·libc_fstatat64_trampoline(SB),NOSPLIT,$0-0
JMP libc_fstatat64(SB)
TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0
JMP libc_ptrace(SB)
-TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
- JMP libc_sysctl(SB)
diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go
index 942152ae39d..83214de2fbb 100644
--- a/src/syscall/zsyscall_darwin_amd64.go
+++ b/src/syscall/zsyscall_darwin_amd64.go
@@ -1870,6 +1870,27 @@ func libc_exit_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := syscall6(funcPC(libc_sysctl_trampoline), uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func libc_sysctl_trampoline()
+
+//go:linkname libc_sysctl libc_sysctl
+//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (val int, err error) {
r0, _, e1 := syscall(funcPC(libc_fcntl_trampoline), uintptr(fd), uintptr(cmd), uintptr(arg))
val = int(r0)
@@ -2060,24 +2081,3 @@ func libc_ptrace_trampoline()
//go:linkname libc_ptrace libc_ptrace
//go:cgo_import_dynamic libc_ptrace ptrace "/usr/lib/libSystem.B.dylib"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
-func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
- var _p0 unsafe.Pointer
- if len(mib) > 0 {
- _p0 = unsafe.Pointer(&mib[0])
- } else {
- _p0 = unsafe.Pointer(&_zero)
- }
- _, _, e1 := syscall6(funcPC(libc_sysctl_trampoline), uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_sysctl_trampoline()
-
-//go:linkname libc_sysctl libc_sysctl
-//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
diff --git a/src/syscall/zsyscall_darwin_amd64.s b/src/syscall/zsyscall_darwin_amd64.s
index fcb7f9c2913..23ddbe06c0d 100644
--- a/src/syscall/zsyscall_darwin_amd64.s
+++ b/src/syscall/zsyscall_darwin_amd64.s
@@ -229,6 +229,8 @@ TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
JMP libc_execve(SB)
TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
JMP libc_exit(SB)
+TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+ JMP libc_sysctl(SB)
TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
JMP libc_unlinkat(SB)
TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
@@ -249,5 +251,3 @@ TEXT ·libc_fstatat64_trampoline(SB),NOSPLIT,$0-0
JMP libc_fstatat64(SB)
TEXT ·libc_ptrace_trampoline(SB),NOSPLIT,$0-0
JMP libc_ptrace(SB)
-TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
- JMP libc_sysctl(SB)
diff --git a/src/syscall/zsyscall_darwin_arm.go b/src/syscall/zsyscall_darwin_arm.go
index 73ee205d335..2a643f209fd 100644
--- a/src/syscall/zsyscall_darwin_arm.go
+++ b/src/syscall/zsyscall_darwin_arm.go
@@ -1870,6 +1870,27 @@ func libc_exit_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := syscall6(funcPC(libc_sysctl_trampoline), uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func libc_sysctl_trampoline()
+
+//go:linkname libc_sysctl libc_sysctl
+//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (val int, err error) {
r0, _, e1 := syscall(funcPC(libc_fcntl_trampoline), uintptr(fd), uintptr(cmd), uintptr(arg))
val = int(r0)
diff --git a/src/syscall/zsyscall_darwin_arm.s b/src/syscall/zsyscall_darwin_arm.s
index 6462a198b02..c7cd83d83ea 100644
--- a/src/syscall/zsyscall_darwin_arm.s
+++ b/src/syscall/zsyscall_darwin_arm.s
@@ -229,6 +229,8 @@ TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
JMP libc_execve(SB)
TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
JMP libc_exit(SB)
+TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+ JMP libc_sysctl(SB)
TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
JMP libc_unlinkat(SB)
TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go
index bbe092cd073..0b778398692 100644
--- a/src/syscall/zsyscall_darwin_arm64.go
+++ b/src/syscall/zsyscall_darwin_arm64.go
@@ -1870,6 +1870,27 @@ func libc_exit_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
+ var _p0 unsafe.Pointer
+ if len(mib) > 0 {
+ _p0 = unsafe.Pointer(&mib[0])
+ } else {
+ _p0 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := syscall6(funcPC(libc_sysctl_trampoline), uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func libc_sysctl_trampoline()
+
+//go:linkname libc_sysctl libc_sysctl
+//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (val int, err error) {
r0, _, e1 := syscall(funcPC(libc_fcntl_trampoline), uintptr(fd), uintptr(cmd), uintptr(arg))
val = int(r0)
diff --git a/src/syscall/zsyscall_darwin_arm64.s b/src/syscall/zsyscall_darwin_arm64.s
index ec2210f1df0..7b8b3764a82 100644
--- a/src/syscall/zsyscall_darwin_arm64.s
+++ b/src/syscall/zsyscall_darwin_arm64.s
@@ -229,6 +229,8 @@ TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
JMP libc_execve(SB)
TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
JMP libc_exit(SB)
+TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
+ JMP libc_sysctl(SB)
TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
JMP libc_unlinkat(SB)
TEXT ·libc_openat_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go
index 0e348be3583..e954fc6ccbb 100644
--- a/src/testing/benchmark.go
+++ b/src/testing/benchmark.go
@@ -545,7 +545,11 @@ func (ctx *benchContext) processBench(b *B) {
for j := uint(0); j < *count; j++ {
runtime.GOMAXPROCS(procs)
benchName := benchmarkName(b.name, procs)
- fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+
+ // If it's chatty, we've already printed this information.
+ if !b.chatty {
+ fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+ }
// Recompute the running time for all but the first iteration.
if i > 0 || j > 0 {
b = &B{
@@ -569,6 +573,9 @@ func (ctx *benchContext) processBench(b *B) {
continue
}
results := r.String()
+ if b.chatty {
+ fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+ }
if *benchmarkMemory || b.showAllocResult {
results += "\t" + r.MemString()
}
@@ -627,6 +634,19 @@ func (b *B) Run(name string, f func(b *B)) bool {
// Only process sub-benchmarks, if any.
atomic.StoreInt32(&sub.hasSub, 1)
}
+
+ if b.chatty {
+ labelsOnce.Do(func() {
+ fmt.Printf("goos: %s\n", runtime.GOOS)
+ fmt.Printf("goarch: %s\n", runtime.GOARCH)
+ if b.importPath != "" {
+ fmt.Printf("pkg: %s\n", b.importPath)
+ }
+ })
+
+ fmt.Println(benchName)
+ }
+
if sub.run1() {
sub.run()
}
diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go
index cc5dd2f3cfb..abaedefde7d 100644
--- a/src/testing/sub_test.go
+++ b/src/testing/sub_test.go
@@ -434,6 +434,31 @@ func TestTRun(t *T) {
<-ch
t.Errorf("error")
},
+ }, {
+ // A chatty test should always log with fmt.Print, even if the
+ // parent test has completed.
+ // TODO(deklerk) Capture the log of fmt.Print and assert that the
+ // subtest message is not lost.
+ desc: "log in finished sub test with chatty",
+ ok: false,
+ chatty: true,
+ output: `
+ --- FAIL: log in finished sub test with chatty (N.NNs)`,
+ maxPar: 1,
+ f: func(t *T) {
+ ch := make(chan bool)
+ t.Run("sub", func(t2 *T) {
+ go func() {
+ <-ch
+ t2.Log("message1")
+ ch <- true
+ }()
+ })
+ t.Log("message2")
+ ch <- true
+ <-ch
+ t.Errorf("error")
+ },
}}
for _, tc := range testCases {
ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", ""))
@@ -521,13 +546,6 @@ func TestBRun(t *T) {
chatty: true,
output: "--- SKIP: root",
f: func(b *B) { b.SkipNow() },
- }, {
- desc: "skipping with message, chatty",
- chatty: true,
- output: `
---- SKIP: root
- sub_test.go:NNN: skipping`,
- f: func(b *B) { b.Skip("skipping") },
}, {
desc: "chatty with recursion",
chatty: true,
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 6ab9b791961..bbb10263c39 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -479,6 +479,9 @@ func (c *common) decorate(s string, skip int) string {
buf := new(strings.Builder)
// Every line is indented at least 4 spaces.
buf.WriteString(" ")
+ if c.chatty {
+ fmt.Fprintf(buf, "%s: ", c.name)
+ }
fmt.Fprintf(buf, "%s:%d: ", file, line)
lines := strings.Split(s, "\n")
if l := len(lines); l > 1 && lines[l-1] == "" {
@@ -662,9 +665,7 @@ func (c *common) log(s string) {
func (c *common) logDepth(s string, depth int) {
c.mu.Lock()
defer c.mu.Unlock()
- if !c.done {
- c.output = append(c.output, c.decorate(s, depth+1)...)
- } else {
+ if c.done {
// This test has already finished. Try and log this message
// with our parent. If we don't have a parent, panic.
for parent := c.parent; parent != nil; parent = parent.parent {
@@ -676,6 +677,12 @@ func (c *common) logDepth(s string, depth int) {
}
}
panic("Log in goroutine after " + c.name + " has completed")
+ } else {
+ if c.chatty {
+ fmt.Print(c.decorate(s, depth+1))
+ return
+ }
+ c.output = append(c.output, c.decorate(s, depth+1)...)
}
}
diff --git a/src/time/internal_test.go b/src/time/internal_test.go
index 336deb92112..3bca88e2b99 100644
--- a/src/time/internal_test.go
+++ b/src/time/internal_test.go
@@ -64,8 +64,7 @@ func CheckRuntimeTimerOverflow() {
// once more.
stopTimer(r)
t.Stop()
- r.when = 0
- startTimer(r)
+ resetTimer(r, 0)
}()
// If the test fails, we will hang here until the timeout in the testing package
diff --git a/src/time/sleep.go b/src/time/sleep.go
index 2cc908da55e..adce860b306 100644
--- a/src/time/sleep.go
+++ b/src/time/sleep.go
@@ -13,12 +13,15 @@ func Sleep(d Duration)
type runtimeTimer struct {
tb uintptr
i int
+ pp uintptr
- when int64
- period int64
- f func(interface{}, uintptr) // NOTE: must not be closure
- arg interface{}
- seq uintptr
+ when int64
+ period int64
+ f func(interface{}, uintptr) // NOTE: must not be closure
+ arg interface{}
+ seq uintptr
+ nextwhen int64
+ status uint32
}
// when is a helper function for setting the 'when' field of a runtimeTimer.
@@ -38,6 +41,7 @@ func when(d Duration) int64 {
func startTimer(*runtimeTimer)
func stopTimer(*runtimeTimer) bool
+func resetTimer(*runtimeTimer, int64)
// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
@@ -122,8 +126,7 @@ func (t *Timer) Reset(d Duration) bool {
}
w := when(d)
active := stopTimer(&t.r)
- t.r.when = w
- startTimer(&t.r)
+ resetTimer(&t.r, w)
return active
}
diff --git a/src/vendor/golang.org/x/net/dns/dnsmessage/message.go b/src/vendor/golang.org/x/net/dns/dnsmessage/message.go
index 13fbc0814e5..82bcdcc838c 100644
--- a/src/vendor/golang.org/x/net/dns/dnsmessage/message.go
+++ b/src/vendor/golang.org/x/net/dns/dnsmessage/message.go
@@ -1660,7 +1660,7 @@ func (h *ResourceHeader) fixLen(msg []byte, lenOff int, preLen int) error {
return nil
}
-// EDNS(0) wire costants.
+// EDNS(0) wire constants.
const (
edns0Version = 0
diff --git a/src/vendor/golang.org/x/net/http2/hpack/encode.go b/src/vendor/golang.org/x/net/http2/hpack/encode.go
index 1565cf2702d..97f17831fc5 100644
--- a/src/vendor/golang.org/x/net/http2/hpack/encode.go
+++ b/src/vendor/golang.org/x/net/http2/hpack/encode.go
@@ -150,7 +150,7 @@ func appendIndexed(dst []byte, i uint64) []byte {
// extended buffer.
//
// If f.Sensitive is true, "Never Indexed" representation is used. If
-// f.Sensitive is false and indexing is true, "Inremental Indexing"
+// f.Sensitive is false and indexing is true, "Incremental Indexing"
// representation is used.
func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
diff --git a/src/vendor/golang.org/x/net/route/address.go b/src/vendor/golang.org/x/net/route/address.go
index e6bfa39e93b..b71528191f6 100644
--- a/src/vendor/golang.org/x/net/route/address.go
+++ b/src/vendor/golang.org/x/net/route/address.go
@@ -222,7 +222,7 @@ func parseKernelInetAddr(af int, b []byte) (int, Addr, error) {
// the routing message boundary
l := int(b[0])
if runtime.GOOS == "darwin" {
- // On Darwn, an address in the kernel form is also
+ // On Darwin, an address in the kernel form is also
// used as a message filler.
if l == 0 || len(b) > roundup(l) {
l = roundup(l)
diff --git a/src/vendor/golang.org/x/net/route/message.go b/src/vendor/golang.org/x/net/route/message.go
index 0fa7e09f468..80c482ae94a 100644
--- a/src/vendor/golang.org/x/net/route/message.go
+++ b/src/vendor/golang.org/x/net/route/message.go
@@ -45,7 +45,7 @@ func ParseRIB(typ RIBType, b []byte) ([]Message, error) {
if len(b) < l {
return nil, errMessageTooShort
}
- if b[2] != sysRTM_VERSION {
+ if b[2] != rtmVersion {
b = b[l:]
continue
}
diff --git a/src/vendor/golang.org/x/net/route/route.go b/src/vendor/golang.org/x/net/route/route.go
index 8cb64f789b3..bba7ed7ef4e 100644
--- a/src/vendor/golang.org/x/net/route/route.go
+++ b/src/vendor/golang.org/x/net/route/route.go
@@ -73,7 +73,7 @@ type RouteMessage struct {
Version int // message version
Type int // message type
Flags int // route flags
- Index int // interface index when atatched
+ Index int // interface index when attached
ID uintptr // sender's identifier; usually process ID
Seq int // sequence number
Err error // error on requested operation
diff --git a/src/vendor/golang.org/x/net/route/route_classic.go b/src/vendor/golang.org/x/net/route/route_classic.go
index 02fa688309c..a7d3864646b 100644
--- a/src/vendor/golang.org/x/net/route/route_classic.go
+++ b/src/vendor/golang.org/x/net/route/route_classic.go
@@ -25,7 +25,7 @@ func (m *RouteMessage) marshal() ([]byte, error) {
b := make([]byte, l)
nativeEndian.PutUint16(b[:2], uint16(l))
if m.Version == 0 {
- b[2] = sysRTM_VERSION
+ b[2] = rtmVersion
} else {
b[2] = byte(m.Version)
}
diff --git a/src/vendor/golang.org/x/net/route/sys.go b/src/vendor/golang.org/x/net/route/sys.go
index 13933f9af75..a0ab3e9c73b 100644
--- a/src/vendor/golang.org/x/net/route/sys.go
+++ b/src/vendor/golang.org/x/net/route/sys.go
@@ -11,6 +11,7 @@ import "unsafe"
var (
nativeEndian binaryByteOrder
kernelAlign int
+ rtmVersion byte
wireFormats map[int]*wireFormat
)
@@ -22,6 +23,8 @@ func init() {
} else {
nativeEndian = bigEndian
}
+ // might get overridden in probeRoutingStack
+ rtmVersion = sysRTM_VERSION
kernelAlign, wireFormats = probeRoutingStack()
}
diff --git a/src/vendor/golang.org/x/net/route/sys_dragonfly.go b/src/vendor/golang.org/x/net/route/sys_dragonfly.go
index 0c14bc2b4d4..a138951f945 100644
--- a/src/vendor/golang.org/x/net/route/sys_dragonfly.go
+++ b/src/vendor/golang.org/x/net/route/sys_dragonfly.go
@@ -4,7 +4,10 @@
package route
-import "unsafe"
+import (
+ "syscall"
+ "unsafe"
+)
func (typ RIBType) parseable() bool { return true }
@@ -56,6 +59,15 @@ func probeRoutingStack() (int, map[int]*wireFormat) {
ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage
ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrDragonFlyBSD4, bodyOff: sizeofIfAnnouncemsghdrDragonFlyBSD4}
ifanm.parse = ifanm.parseInterfaceAnnounceMessage
+
+ rel, _ := syscall.SysctlUint32("kern.osreldate")
+ if rel >= 500705 {
+ // https://github.com/DragonFlyBSD/DragonFlyBSD/commit/43a373152df2d405c9940983e584e6a25e76632d
+ // but only the size of struct ifa_msghdr actually changed
+ rtmVersion = 7
+ ifam.bodyOff = sizeofIfaMsghdrDragonFlyBSD58
+ }
+
return int(unsafe.Sizeof(p)), map[int]*wireFormat{
sysRTM_ADD: rtm,
sysRTM_DELETE: rtm,
diff --git a/src/vendor/golang.org/x/net/route/zsys_dragonfly.go b/src/vendor/golang.org/x/net/route/zsys_dragonfly.go
index 8ed2d4d550c..34f0eaaa42b 100644
--- a/src/vendor/golang.org/x/net/route/zsys_dragonfly.go
+++ b/src/vendor/golang.org/x/net/route/zsys_dragonfly.go
@@ -46,8 +46,6 @@ const (
sysRTM_REDIRECT = 0x6
sysRTM_MISS = 0x7
sysRTM_LOCK = 0x8
- sysRTM_OLDADD = 0x9
- sysRTM_OLDDEL = 0xa
sysRTM_RESOLVE = 0xb
sysRTM_NEWADDR = 0xc
sysRTM_DELADDR = 0xd
@@ -89,6 +87,8 @@ const (
sizeofIfmaMsghdrDragonFlyBSD4 = 0x10
sizeofIfAnnouncemsghdrDragonFlyBSD4 = 0x18
+ sizeofIfaMsghdrDragonFlyBSD58 = 0x18
+
sizeofRtMsghdrDragonFlyBSD4 = 0x98
sizeofRtMetricsDragonFlyBSD4 = 0x70
diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt
index c0f37845b01..f91341daca2 100644
--- a/src/vendor/modules.txt
+++ b/src/vendor/modules.txt
@@ -8,7 +8,7 @@ golang.org/x/crypto/hkdf
golang.org/x/crypto/internal/chacha20
golang.org/x/crypto/internal/subtle
golang.org/x/crypto/poly1305
-# golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
+# golang.org/x/net v0.0.0-20191021124707-24d2ffbea1e8
## explicit
golang.org/x/net/dns/dnsmessage
golang.org/x/net/http/httpguts
diff --git a/test/codegen/math.go b/test/codegen/math.go
index 8aa652595b2..751406d732e 100644
--- a/test/codegen/math.go
+++ b/test/codegen/math.go
@@ -107,6 +107,16 @@ func copysign(a, b, c float64) {
sink64[3] = math.Copysign(-1, c)
}
+func fma(x, y, z float64) float64 {
+ // amd64:"VFMADD231SD"
+ // arm/6:"FMULAD"
+ // arm64:"FMADDD"
+ // s390x:"FMADD"
+ // ppc64:"FMADD"
+ // ppc64le:"FMADD"
+ return math.Fma(x, y, z)
+}
+
func fromFloat64(f64 float64) uint64 {
// amd64:"MOVQ\tX.*, [^X].*"
// arm64:"FMOVD\tF.*, R.*"
diff --git a/test/defererrcheck.go b/test/defererrcheck.go
new file mode 100644
index 00000000000..95b91da54db
--- /dev/null
+++ b/test/defererrcheck.go
@@ -0,0 +1,86 @@
+// errorcheck -0 -l -d=defer
+
+// Copyright 2019 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.
+
+// check that open-coded defers are used in expected situations
+
+package main
+
+import "fmt"
+
+var glob = 3
+
+func f1() {
+
+ for i := 0; i < 10; i++ {
+ fmt.Println("loop")
+ }
+ defer func() { // ERROR "open-coded defer"
+ fmt.Println("defer")
+ }()
+}
+
+func f2() {
+ for {
+ defer func() { // ERROR "heap-allocated defer"
+ fmt.Println("defer1")
+ }()
+ if glob > 2 {
+ break
+ }
+ }
+ defer func() { // ERROR "stack-allocated defer"
+ fmt.Println("defer2")
+ }()
+}
+
+func f3() {
+ defer func() { // ERROR "stack-allocated defer"
+ fmt.Println("defer2")
+ }()
+ for {
+ defer func() { // ERROR "heap-allocated defer"
+ fmt.Println("defer1")
+ }()
+ if glob > 2 {
+ break
+ }
+ }
+}
+
+func f4() {
+ defer func() { // ERROR "open-coded defer"
+ fmt.Println("defer")
+ }()
+label:
+ fmt.Println("goto loop")
+ if glob > 2 {
+ goto label
+ }
+}
+
+func f5() {
+label:
+ fmt.Println("goto loop")
+ defer func() { // ERROR "heap-allocated defer"
+ fmt.Println("defer")
+ }()
+ if glob > 2 {
+ goto label
+ }
+}
+
+func f6() {
+label:
+ fmt.Println("goto loop")
+ if glob > 2 {
+ goto label
+ }
+ // The current analysis doesn't end a backward goto loop, so this defer is
+ // considered to be inside a loop
+ defer func() { // ERROR "heap-allocated defer"
+ fmt.Println("defer")
+ }()
+}
diff --git a/test/fixedbugs/issue35027.go b/test/fixedbugs/issue35027.go
new file mode 100644
index 00000000000..d4b0be52c10
--- /dev/null
+++ b/test/fixedbugs/issue35027.go
@@ -0,0 +1,23 @@
+// run -gcflags=-d=checkptr
+
+// Copyright 2019 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 main
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+var s []int
+
+func main() {
+ s = []int{42}
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
+ x := *(*int)(unsafe.Pointer(h.Data))
+ if x != 42 {
+ panic(x)
+ }
+}
diff --git a/test/live.go b/test/live.go
index b6e6d93f5f8..32c397f4a99 100644
--- a/test/live.go
+++ b/test/live.go
@@ -367,16 +367,19 @@ func f24() {
m2[[2]string{"x", "y"}] = nil
}
-// defer should not cause spurious ambiguously live variables
-
+// Non-open-coded defers should not cause autotmps. (Open-coded defers do create extra autotmps).
func f25(b bool) {
- defer g25()
+ for i := 0; i < 2; i++ {
+ // Put in loop to make sure defer is not open-coded
+ defer g25()
+ }
if b {
return
}
var x string
x = g14()
printstring(x)
+ return
}
func g25()
@@ -417,7 +420,8 @@ func f27defer(b bool) {
defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
}
defer call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
- printnl()
+ printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ .autotmp_[0-9]+"
+ return // ERROR "live at call to call27: .autotmp_[0-9]+"
}
// and newproc (go) escapes to the heap
@@ -687,12 +691,12 @@ type R struct{ *T } // ERRORAUTO "live at entry to \(\*R\)\.Foo: \.this ptr" "li
// In particular, at printint r must be live.
func f41(p, q *int) (r *int) { // ERROR "live at entry to f41: p q$"
r = p
- defer func() { // ERROR "live at call to deferprocStack: q r$" "live at call to deferreturn: r$"
+ defer func() {
recover()
}()
- printint(0) // ERROR "live at call to printint: q r$"
+ printint(0) // ERROR "live at call to printint: q r .autotmp_[0-9]+$"
r = q
- return // ERROR "live at call to deferreturn: r$"
+ return // ERROR "live at call to f41.func1: r .autotmp_[0-9]+$"
}
func f42() {
diff --git a/test/nosplit.go b/test/nosplit.go
index 266e6077b1f..3b7e6059999 100644
--- a/test/nosplit.go
+++ b/test/nosplit.go
@@ -309,17 +309,17 @@ TestCases:
name := m[1]
size, _ := strconv.Atoi(m[2])
- // The limit was originally 128 but is now 752 (880-128).
+ // The limit was originally 128 but is now 768 (896-128).
// Instead of rewriting the test cases above, adjust
// the first stack frame to use up the extra bytes.
if i == 0 {
- size += (880 - 128) - 128
+ size += (896 - 128) - 128
// Noopt builds have a larger stackguard.
// See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier
// This increase is included in objabi.StackGuard
for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") {
if s == "-N" {
- size += 880
+ size += 896
}
}
}