From 7e0d66020c49ef56158346ce18dc3f538393829e Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Mon, 4 Apr 2016 18:22:29 +0200 Subject: testing: improve output This introduces a few changes - Skipped benchmarks now print a SKIP line, also if there was no output - The benchmark name is only printed if there the benchmark was not skipped or did not fail in the probe phase. It also fixes a bug of doubling a skip message in chatty mode in absense of a failure. The chatty flag is now passed in the common struct to allow for testing of the printed messages. Fixes #14799 Change-Id: Ia8eb140c2e5bb467e66b8ef20a2f98f5d95415d5 Reviewed-on: https://go-review.googlesource.com/21504 Reviewed-by: Brad Fitzpatrick Run-TryBot: Marcel van Lohuizen TryBot-Result: Gobot Gobot --- src/testing/benchmark.go | 45 ++++++++++++++++--- src/testing/sub_test.go | 110 ++++++++++++++++++++++++++++++++++++++++++++++- src/testing/testing.go | 16 ++++--- 3 files changed, 158 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index c935bc5e06..5d58b85e78 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -208,7 +208,24 @@ func (b *B) run1() bool { b.runN(1) }() <-b.signal - return !b.hasSub + if b.failed { + fmt.Fprintf(b.w, "--- FAIL: %s\n%s", b.name, b.output) + return false + } + // Only print the output if we know we are not going to proceed. + // Otherwise it is printed in processBench. + if b.hasSub || b.finished { + tag := "BENCH" + if b.skipped { + tag = "SKIP" + } + if b.chatty && (len(b.output) > 0 || b.finished) { + b.trimOutput() + fmt.Fprintf(b.w, "--- %s: %s\n%s", tag, b.name, b.output) + } + return false + } + return true } // run executes the benchmark in a separate goroutine, including all of its @@ -372,7 +389,11 @@ func runBenchmarksInternal(matchString func(pat, str string) (bool, error), benc } } main := &B{ - common: common{name: "Main"}, + common: common{ + name: "Main", + w: os.Stdout, + chatty: *chatty, + }, benchFunc: func(b *B) { for _, Benchmark := range bs { b.Run(Benchmark.Name, Benchmark.F) @@ -390,13 +411,15 @@ func (ctx *benchContext) processBench(b *B) { for i, procs := range cpuList { runtime.GOMAXPROCS(procs) benchName := benchmarkName(b.name, procs) - fmt.Printf("%-*s\t", ctx.maxLen, benchName) + fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName) // Recompute the running time for all but the first iteration. if i > 0 { b = &B{ common: common{ signal: make(chan bool), name: b.name, + w: b.w, + chatty: b.chatty, }, benchFunc: b.benchFunc, benchTime: b.benchTime, @@ -408,19 +431,19 @@ func (ctx *benchContext) processBench(b *B) { // The output could be very long here, but probably isn't. // We print it all, regardless, because we don't want to trim the reason // the benchmark failed. - fmt.Printf("--- FAIL: %s\n%s", benchName, b.output) + fmt.Fprintf(b.w, "--- FAIL: %s\n%s", benchName, b.output) continue } results := r.String() if *benchmarkMemory || b.showAllocResult { results += "\t" + r.MemString() } - fmt.Println(results) + fmt.Fprintln(b.w, results) // Unlike with tests, we ignore the -chatty flag and always print output for // benchmarks since the output generation time will skew the results. if len(b.output) > 0 { b.trimOutput() - fmt.Printf("--- BENCH: %s\n%s", benchName, b.output) + fmt.Fprintf(b.w, "--- BENCH: %s\n%s", benchName, b.output) } if p := runtime.GOMAXPROCS(-1); p != procs { fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p) @@ -453,6 +476,8 @@ func (b *B) Run(name string, f func(b *B)) bool { name: benchName, parent: &b.common, level: b.level + 1, + w: b.w, + chatty: b.chatty, }, benchFunc: f, benchTime: b.benchTime, @@ -597,9 +622,17 @@ func Benchmark(f func(b *B)) BenchmarkResult { b := &B{ common: common{ signal: make(chan bool), + w: discard{}, }, benchFunc: f, benchTime: *benchTime, } + if !b.run1() { + return BenchmarkResult{} + } return b.run() } + +type discard struct{} + +func (discard) Write(b []byte) (n int, err error) { return len(b), nil } diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 264d77c2cf..e053a3c348 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -5,8 +5,9 @@ package testing import ( - "io/ioutil" + "bytes" "regexp" + "strings" "sync/atomic" "time" ) @@ -113,11 +114,17 @@ func TestTRun(t *T) { desc string ok bool maxPar int + chatty bool + output string f func(*T) }{{ desc: "failnow skips future sequential and parallel tests at same level", ok: false, maxPar: 1, + output: ` +--- FAIL: failnow skips future sequential and parallel tests at same level (0.00s) + --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (0.00s) + `, f: func(t *T) { ranSeq := false ranPar := false @@ -149,6 +156,11 @@ func TestTRun(t *T) { desc: "failure in parallel test propagates upwards", ok: false, maxPar: 1, + output: ` +--- FAIL: failure in parallel test propagates upwards (0.00s) + --- FAIL: failure in parallel test propagates upwards/#00 (0.00s) + --- FAIL: failure in parallel test propagates upwards/#00/par (0.00s) + `, f: func(t *T) { t.Run("", func(t *T) { t.Parallel() @@ -158,6 +170,28 @@ func TestTRun(t *T) { }) }) }, + }, { + desc: "skipping without message, chatty", + ok: true, + chatty: true, + output: ` +=== RUN skipping without message, chatty +--- SKIP: skipping without message, chatty (0.00s)`, + f: func(t *T) { t.SkipNow() }, + }, { + desc: "skipping without message, not chatty", + ok: true, + f: func(t *T) { t.SkipNow() }, + }, { + desc: "skipping after error", + output: ` +--- FAIL: skipping after error (0.00s) + sub_test.go:nnn: an error + sub_test.go:nnn: skipped`, + f: func(t *T) { + t.Error("an error") + t.Skip("skipped") + }, }, { desc: "use Run to locally synchronize parallelism", ok: true, @@ -301,14 +335,23 @@ func TestTRun(t *T) { }) } }, + }, { + desc: "skip output", + ok: true, + maxPar: 4, + f: func(t *T) { + t.Skip() + }, }} for _, tc := range testCases { ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", "")) + buf := &bytes.Buffer{} root := &T{ common: common{ signal: make(chan bool), name: "Test", - w: ioutil.Discard, + w: buf, + chatty: tc.chatty, }, context: ctx, } @@ -324,6 +367,11 @@ func TestTRun(t *T) { if ctx.running != 0 || ctx.numWaiting != 0 { t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting) } + got := sanitizeLog(buf.String()) + want := sanitizeLog(tc.output) + if got != want { + t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) + } } } @@ -336,6 +384,8 @@ func TestBRun(t *T) { testCases := []struct { desc string failed bool + chatty bool + output string f func(*B) }{{ desc: "simulate sequential run of subbenchmarks.", @@ -371,7 +421,34 @@ func TestBRun(t *T) { }, { desc: "failure carried over to root", failed: true, + output: "--- FAIL: root", f: func(b *B) { b.Fail() }, + }, { + desc: "skipping without message, chatty", + chatty: true, + output: "--- SKIP: root", + f: func(b *B) { b.SkipNow() }, + }, { + desc: "skipping with message, chatty", + chatty: true, + output: ` +--- SKIP: root + sub_test.go:: skipping`, + f: func(b *B) { b.Skip("skipping") }, + }, { + desc: "skipping without message, not chatty", + f: func(b *B) { b.SkipNow() }, + }, { + desc: "skipping after error", + failed: true, + output: ` +--- FAIL: root + sub_test.go:nnn: an error + sub_test.go:nnn: skipped`, + f: func(b *B) { + b.Error("an error") + b.Skip("skipped") + }, }, { desc: "memory allocation", f: func(b *B) { @@ -398,11 +475,15 @@ func TestBRun(t *T) { }} for _, tc := range testCases { var ok bool + buf := &bytes.Buffer{} // This is almost like the Benchmark function, except that we override // the benchtime and catch the failure result of the subbenchmark. root := &B{ common: common{ signal: make(chan bool), + name: "root", + w: buf, + chatty: tc.chatty, }, benchFunc: func(b *B) { ok = b.Run("test", tc.f) }, // Use Run to catch failure. benchTime: time.Microsecond, @@ -418,5 +499,30 @@ func TestBRun(t *T) { if root.result.N != 1 { t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N) } + got := sanitizeLog(buf.String()) + want := sanitizeLog(tc.output) + if got != want { + t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) + } } } + +// sanitizeLog removes line numbers from log entries. +func sanitizeLog(s string) string { + s = strings.TrimSpace(s) + lines := strings.Split(s, "\n") + for i, line := range lines { + p := strings.IndexByte(line, ':') + if p > 0 && line[p+4] == ':' { // assuming 3-digit file positions + lines[i] = line[:p+1] + line[p+4:] + } + } + return strings.Join(lines, "\n") +} + +func TestBenchmarkOutput(t *T) { + // Ensure Benchmark initialized common.w by invoking it with an error and + // normal case. + Benchmark(func(b *B) { b.Error("do not print this output") }) + Benchmark(func(b *B) {}) +} diff --git a/src/testing/testing.go b/src/testing/testing.go index f9bb43b618..8e16db321d 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -199,6 +199,7 @@ type common struct { mu sync.RWMutex // guards output and failed output []byte // Output generated by test or benchmark. w io.Writer // For flushToParent. + chatty bool // A copy of the chatty flag. failed bool // Test or benchmark has failed. skipped bool // Test of benchmark has been skipped. finished bool @@ -265,7 +266,6 @@ func (c *common) flushToParent(format string, args ...interface{}) { defer p.mu.Unlock() fmt.Fprintf(p.w, format, args...) - fmt.Fprintln(p.w) c.mu.Lock() defer c.mu.Unlock() @@ -562,13 +562,18 @@ func (t *T) Run(name string, f func(t *T)) bool { name: testName, parent: &t.common, level: t.level + 1, + chatty: t.chatty, }, context: t.context, } t.w = indenter{&t.common} - if *chatty { - fmt.Printf("=== RUN %s\n", t.name) + if t.chatty { + // Print directly to root's io.Writer so there is no delay. + root := t.parent + for ; root.parent != nil; root = t.parent { + } + fmt.Fprintf(root.w, "=== RUN %s\n", t.name) } // Instead of reducing the running count of this test before calling the // tRunner and increasing it afterwards, we rely on tRunner keeping the @@ -690,10 +695,10 @@ func (t *T) report() { return } dstr := fmtDuration(t.duration) - format := "--- %s: %s (%s)" + format := "--- %s: %s (%s)\n" if t.Failed() { t.flushToParent(format, "FAIL", t.name, dstr) - } else if *chatty { + } else if t.chatty { if t.Skipped() { t.flushToParent(format, "SKIP", t.name, dstr) } else { @@ -716,6 +721,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT signal: make(chan bool), barrier: make(chan bool), w: os.Stdout, + chatty: *chatty, }, context: ctx, } -- cgit v1.3 From d7c699d9935ce2f99419faf81909b36409171705 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 5 Apr 2016 20:40:40 +0000 Subject: crypto/rsa, crypto/ecdsa: fail earlier on zero parameters Change-Id: Ia6ed49d5ef3a256a55e6d4eaa1b4d9f0fc447013 Reviewed-on: https://go-review.googlesource.com/21560 Reviewed-by: Robert Griesemer --- src/crypto/ecdsa/ecdsa.go | 11 ++++++++--- src/crypto/rsa/rsa.go | 5 ++++- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index 42ec92b6f9..e63bd8669e 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -23,6 +23,7 @@ import ( "crypto/elliptic" "crypto/sha512" "encoding/asn1" + "errors" "io" "math/big" ) @@ -140,6 +141,8 @@ func fermatInverse(k, N *big.Int) *big.Int { return new(big.Int).Exp(k, nMinus2, N) } +var errZeroParam = errors.New("zero parameter") + // Sign signs an arbitrary length hash (which should be the result of hashing a // larger message) using the private key, priv. It returns the signature as a // pair of integers. The security of the private key depends on the entropy of @@ -180,7 +183,9 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err // See [NSA] 3.4.1 c := priv.PublicKey.Curve N := c.Params().N - + if N.Sign() == 0 { + return nil, nil, errZeroParam + } var k, kInv *big.Int for { for { @@ -193,7 +198,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err if in, ok := priv.Curve.(invertible); ok { kInv = in.Inverse(k) } else { - kInv = fermatInverse(k, N) + kInv = fermatInverse(k, N) // N != 0 } r, _ = priv.Curve.ScalarBaseMult(k.Bytes()) @@ -207,7 +212,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err s = new(big.Int).Mul(priv.D, r) s.Add(s, e) s.Mul(s, kInv) - s.Mod(s, N) + s.Mod(s, N) // N != 0 if s.Sign() != 0 { break } diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index 3f353f891f..594305631b 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -465,6 +465,9 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er err = ErrDecryption return } + if priv.N.Sign() == 0 { + return nil, ErrDecryption + } var ir *big.Int if random != nil { @@ -490,7 +493,7 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er } } bigE := big.NewInt(int64(priv.E)) - rpowe := new(big.Int).Exp(r, bigE, priv.N) + rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0 cCopy := new(big.Int).Set(c) cCopy.Mul(cCopy, rpowe) cCopy.Mod(cCopy, priv.N) -- cgit v1.3 From cca4ddb497a2d56654b38991566e45be1ef18f4d Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 5 Apr 2016 12:48:49 -0700 Subject: cmd/compile: add comments explaining how declarations/scopes work Change-Id: I301760b015eb69ff12eee53473fdbf5e9f168413 Reviewed-on: https://go-review.googlesource.com/21542 Reviewed-by: Brad Fitzpatrick Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/gc/dcl.go | 43 +++++++++++++++++++++++++++----------- src/cmd/compile/internal/gc/go.go | 10 +++++++-- 2 files changed, 39 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index bd5a1f6f07..8553e2f1e8 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -11,6 +11,8 @@ import ( "strings" ) +// Declaration stack & operations + func dflag() bool { if Debug['d'] == 0 { return false @@ -24,8 +26,21 @@ func dflag() bool { return true } -// declaration stack & operations -func dcopy(a *Sym, b *Sym) { +// dclstack maintains a stack of shadowed symbol declarations so that +// popdcl can restore their declarations when a block scope ends. +// The stack is maintained as a linked list, using Sym's Link field. +// +// In practice, the "stack" actually ends up forming a tree: goto and label +// statements record the current state of dclstack so that checkgoto can +// validate that a goto statement does not jump over any declarations or +// into a new block scope. +// +// Finally, the Syms in this list are not "real" Syms as they don't actually +// represent object names. Sym is just a convenient type for saving shadowed +// Sym definitions, and only a subset of its fields are actually used. +var dclstack *Sym + +func dcopy(a, b *Sym) { a.Pkg = b.Pkg a.Name = b.Name a.Def = b.Def @@ -41,6 +56,8 @@ func push() *Sym { return d } +// pushdcl pushes the current declaration for symbol s (if any) so that +// it can be shadowed by a new declaration within a nested block scope. func pushdcl(s *Sym) *Sym { d := push() dcopy(d, s) @@ -50,6 +67,8 @@ func pushdcl(s *Sym) *Sym { return d } +// popdcl pops the innermost block scope and restores all symbol declarations +// to their previous state. func popdcl() { d := dclstack for ; d != nil && d.Name != ""; d = d.Link { @@ -70,6 +89,7 @@ func popdcl() { block = d.Block } +// markdcl records the start of a new block scope for declarations. func markdcl() { d := push() d.Name = "" // used as a mark in fifo @@ -104,6 +124,7 @@ func testdclstack() { } } +// redeclare emits a diagnostic about symbol s being redeclared somewhere. func redeclare(s *Sym, where string) { if s.Lastlineno == 0 { var tmp string @@ -137,6 +158,8 @@ var vargen int var declare_typegen int +// declare records that Node n declares symbol n.Sym in the specified +// declaration context. func declare(n *Node, ctxt Class) { if ctxt == PDISCARD { return @@ -318,8 +341,7 @@ func constiter(vl []*Node, t *Node, cl []*Node) []*Node { return vv } -// this generates a new name node, -// typically for labels or other one-off names. +// newname returns a new ONAME Node associated with symbol s. func newname(s *Sym) *Node { if s == nil { Fatalf("newname nil") @@ -364,17 +386,14 @@ func typenod(t *Type) *Node { return t.Nod } -// this will return an old name -// that has already been pushed on the -// declaration list. a diagnostic is -// generated if no name has been defined. +// oldname returns the Node that declares symbol s in the current scope. +// If no such Node currently exists, an ONONAME Node is returned instead. func oldname(s *Sym) *Node { n := s.Def if n == nil { - // maybe a top-level name will come along - // to give this a definition later. - // walkdef will check s->def again once - // all the input source has been processed. + // Maybe a top-level declaration will come along later to + // define s. resolve will check s.Def again once all input + // source has been processed. n = newname(s) n.Op = ONONAME n.Name.Iota = iota_ // save current iota value in const declarations diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index fdea1f2fba..f4b3dc9326 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -66,6 +66,14 @@ type Pkg struct { Syms map[string]*Sym } +// Sym represents an object name. Most commonly, this is a Go identifier naming +// an object declared within a package, but Syms are also used to name internal +// synthesized objects. +// +// As a special exception, field and method names that are exported use the Sym +// associated with localpkg instead of the package that declared them. This +// allows using Sym pointer equality to test for Go identifier uniqueness when +// handling selector expressions. type Sym struct { Flags SymFlags Link *Sym @@ -111,8 +119,6 @@ const ( SymAlgGen ) -var dclstack *Sym - // Ctype describes the constant kind of an "ideal" (untyped) constant. type Ctype int8 -- cgit v1.3 From 5ba797bd189b460854a0aea877381abcaef8105b Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 5 Apr 2016 14:20:04 -0700 Subject: cmd/compile: move a lot of declarations outside of go.go go.go is currently a grab bag of various unrelated type and variable declarations. Move a bunch of them into other more relevant source files. There are still more that can be moved, but these were the low hanging fruit with obvious homes. No code/comment changes. Just shuffling stuff around. Change-Id: I43dbe1a5b8b707709c1a3a034c693d38b8465063 Reviewed-on: https://go-review.googlesource.com/21561 Run-TryBot: Matthew Dempsky Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/const.go | 53 ++++++++++++ src/cmd/compile/internal/gc/dcl.go | 6 ++ src/cmd/compile/internal/gc/go.go | 137 ------------------------------- src/cmd/compile/internal/gc/lex.go | 12 +++ src/cmd/compile/internal/gc/popt.go | 28 +++++++ src/cmd/compile/internal/gc/reflect.go | 10 +++ src/cmd/compile/internal/gc/type.go | 14 ++++ src/cmd/compile/internal/gc/typecheck.go | 14 ++++ 8 files changed, 137 insertions(+), 137 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index 5c9a67c8b5..c7fb4d97e5 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -10,6 +10,59 @@ import ( "strings" ) +// Ctype describes the constant kind of an "ideal" (untyped) constant. +type Ctype int8 + +const ( + CTxxx Ctype = iota + + CTINT + CTRUNE + CTFLT + CTCPLX + CTSTR + CTBOOL + CTNIL +) + +type Val struct { + // U contains one of: + // bool bool when n.ValCtype() == CTBOOL + // *Mpint int when n.ValCtype() == CTINT, rune when n.ValCtype() == CTRUNE + // *Mpflt float when n.ValCtype() == CTFLT + // *Mpcplx pair of floats when n.ValCtype() == CTCPLX + // string string when n.ValCtype() == CTSTR + // *Nilval when n.ValCtype() == CTNIL + U interface{} +} + +func (v Val) Ctype() Ctype { + switch x := v.U.(type) { + default: + Fatalf("unexpected Ctype for %T", v.U) + panic("not reached") + case nil: + return 0 + case *NilVal: + return CTNIL + case bool: + return CTBOOL + case *Mpint: + if x.Rune { + return CTRUNE + } + return CTINT + case *Mpflt: + return CTFLT + case *Mpcplx: + return CTCPLX + case string: + return CTSTR + } +} + +type NilVal struct{} + // IntLiteral returns the Node's literal value as an integer. func (n *Node) IntLiteral() (x int64, ok bool) { switch { diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 8553e2f1e8..fb81545a46 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -26,6 +26,12 @@ func dflag() bool { return true } +var externdcl []*Node + +var blockgen int32 // max block number + +var block int32 // current block number + // dclstack maintains a stack of shadowed symbol declarations so that // popdcl can restore their declarations when a block scope ends. // The stack is maintained as a linked list, using Sym's Link field. diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index f4b3dc9326..4cb985b1be 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -5,7 +5,6 @@ package gc import ( - "bytes" "cmd/compile/internal/ssa" "cmd/internal/obj" ) @@ -16,44 +15,6 @@ const ( MaxStackVarSize = 10 * 1024 * 1024 ) -type Val struct { - // U contains one of: - // bool bool when n.ValCtype() == CTBOOL - // *Mpint int when n.ValCtype() == CTINT, rune when n.ValCtype() == CTRUNE - // *Mpflt float when n.ValCtype() == CTFLT - // *Mpcplx pair of floats when n.ValCtype() == CTCPLX - // string string when n.ValCtype() == CTSTR - // *Nilval when n.ValCtype() == CTNIL - U interface{} -} - -type NilVal struct{} - -func (v Val) Ctype() Ctype { - switch x := v.U.(type) { - default: - Fatalf("unexpected Ctype for %T", v.U) - panic("not reached") - case nil: - return 0 - case *NilVal: - return CTNIL - case bool: - return CTBOOL - case *Mpint: - if x.Rune { - return CTRUNE - } - return CTINT - case *Mpflt: - return CTFLT - case *Mpcplx: - return CTCPLX - case string: - return CTSTR - } -} - type Pkg struct { Name string // package name, e.g. "sys" Path string // string literal used in import statement, e.g. "runtime/internal/sys" @@ -119,35 +80,6 @@ const ( SymAlgGen ) -// Ctype describes the constant kind of an "ideal" (untyped) constant. -type Ctype int8 - -const ( - CTxxx Ctype = iota - - CTINT - CTRUNE - CTFLT - CTCPLX - CTSTR - CTBOOL - CTNIL -) - -// ChanDir is whether a channel can send, receive, or both. -type ChanDir uint8 - -func (c ChanDir) CanRecv() bool { return c&Crecv != 0 } -func (c ChanDir) CanSend() bool { return c&Csend != 0 } - -const ( - // types of channel - // must match ../../../../reflect/type.go:/ChanDir - Crecv ChanDir = 1 << 0 - Csend ChanDir = 1 << 1 - Cboth ChanDir = Crecv | Csend -) - // The Class of a variable/function describes the "storage class" // of a variable or function. During parsing, storage classes are // called declaration contexts. @@ -167,30 +99,6 @@ const ( PHEAP = 1 << 7 // an extra bit to identify an escaped variable ) -const ( - Etop = 1 << 1 // evaluated at statement level - Erv = 1 << 2 // evaluated in value context - Etype = 1 << 3 - Ecall = 1 << 4 // call-only expressions are ok - Efnstruct = 1 << 5 // multivalue function returns are ok - Eiota = 1 << 6 // iota is ok - Easgn = 1 << 7 // assigning to expression - Eindir = 1 << 8 // indirecting through expression - Eaddr = 1 << 9 // taking address of expression - Eproc = 1 << 10 // inside a go statement - Ecomplit = 1 << 11 // type in composite literal -) - -type Sig struct { - name string - pkg *Pkg - isym *Sym - tsym *Sym - type_ *Type - mtype *Type - offset int32 -} - // note this is the runtime representation // of the compilers arrays. // @@ -218,13 +126,6 @@ var sizeof_Array int // runtime sizeof(Array) // } String; var sizeof_String int // runtime sizeof(String) -// lexlineno is the line number _after_ the most recently read rune. -// In particular, it's advanced (or rewound) as newlines are read (or unread). -var lexlineno int32 - -// lineno is the line number at the start of the most recently lexed token. -var lineno int32 - var pragcgobuf string var infile string @@ -245,10 +146,6 @@ var safemode int var nolocalimports int -var lexbuf bytes.Buffer -var strbuf bytes.Buffer -var litbuf string // LLITERAL value for use in syntax error messages - var Debug [256]int var debugstr string @@ -324,8 +221,6 @@ var maxfltval [NTYPE]*Mpflt var xtop []*Node -var externdcl []*Node - var exportlist []*Node var importlist []*Node // imported functions and methods with inlinable bodies @@ -350,10 +245,6 @@ var Stksize int64 // stack size for current frame var stkptrsize int64 // prefix of stack containing pointers -var blockgen int32 // max block number - -var block int32 // current block number - var hasdefer bool // flag that curfn has defer statement var Curfn *Node @@ -410,34 +301,6 @@ var nodfp *Node var Disable_checknil int -type Flow struct { - Prog *obj.Prog // actual instruction - P1 *Flow // predecessors of this instruction: p1, - P2 *Flow // and then p2 linked though p2link. - P2link *Flow - S1 *Flow // successors of this instruction (at most two: s1 and s2). - S2 *Flow - Link *Flow // next instruction in function code - - Active int32 // usable by client - - Id int32 // sequence number in flow graph - Rpo int32 // reverse post ordering - Loop uint16 // x5 for every loop - Refset bool // diagnostic generated - - Data interface{} // for use by client -} - -type Graph struct { - Start *Flow - Num int - - // After calling flowrpo, rpo lists the flow nodes in reverse postorder, - // and each non-dead Flow node f has g->rpo[f->rpo] == f. - Rpo []*Flow -} - // interface to back end const ( diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 6f1331ca89..2dbbd9276b 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -6,6 +6,7 @@ package gc import ( "bufio" + "bytes" "cmd/internal/obj" "fmt" "io" @@ -20,6 +21,17 @@ const ( BOM = 0xFEFF ) +// lexlineno is the line number _after_ the most recently read rune. +// In particular, it's advanced (or rewound) as newlines are read (or unread). +var lexlineno int32 + +// lineno is the line number at the start of the most recently lexed token. +var lineno int32 + +var lexbuf bytes.Buffer +var strbuf bytes.Buffer +var litbuf string // LLITERAL value for use in syntax error messages + func isSpace(c rune) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } diff --git a/src/cmd/compile/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go index 41f8ba9fcc..001a715f7b 100644 --- a/src/cmd/compile/internal/gc/popt.go +++ b/src/cmd/compile/internal/gc/popt.go @@ -235,6 +235,34 @@ func fixjmp(firstp *obj.Prog) { // for every f.Data field, for use by the client. // If newData is nil, f.Data will be nil. +type Graph struct { + Start *Flow + Num int + + // After calling flowrpo, rpo lists the flow nodes in reverse postorder, + // and each non-dead Flow node f has g->rpo[f->rpo] == f. + Rpo []*Flow +} + +type Flow struct { + Prog *obj.Prog // actual instruction + P1 *Flow // predecessors of this instruction: p1, + P2 *Flow // and then p2 linked though p2link. + P2link *Flow + S1 *Flow // successors of this instruction (at most two: s1 and s2). + S2 *Flow + Link *Flow // next instruction in function code + + Active int32 // usable by client + + Id int32 // sequence number in flow graph + Rpo int32 // reverse post ordering + Loop uint16 // x5 for every loop + Refset bool // diagnostic generated + + Data interface{} // for use by client +} + var flowmark int // MaxFlowProg is the maximum size program (counted in instructions) diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 11bcd4cdc6..c069b35787 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -22,6 +22,16 @@ type itabEntry struct { var signatlist []*Node var itabs []itabEntry +type Sig struct { + name string + pkg *Pkg + isym *Sym + tsym *Sym + type_ *Type + mtype *Type + offset int32 +} + // byMethodNameAndPackagePath sorts method signatures by name, then package path. type byMethodNameAndPackagePath []*Sig diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index b89c5dbf22..05e30df271 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -75,6 +75,20 @@ const ( dddBound = -100 // arrays declared as [...]T start life with Bound=dddBound ) +// ChanDir is whether a channel can send, receive, or both. +type ChanDir uint8 + +func (c ChanDir) CanRecv() bool { return c&Crecv != 0 } +func (c ChanDir) CanSend() bool { return c&Csend != 0 } + +const ( + // types of channel + // must match ../../../../reflect/type.go:/ChanDir + Crecv ChanDir = 1 << 0 + Csend ChanDir = 1 << 1 + Cboth ChanDir = Crecv | Csend +) + // Types stores pointers to predeclared named types. // // It also stores pointers to several special types: diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 688936e926..d21552d180 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -11,6 +11,20 @@ import ( "strings" ) +const ( + Etop = 1 << 1 // evaluated at statement level + Erv = 1 << 2 // evaluated in value context + Etype = 1 << 3 + Ecall = 1 << 4 // call-only expressions are ok + Efnstruct = 1 << 5 // multivalue function returns are ok + Eiota = 1 << 6 // iota is ok + Easgn = 1 << 7 // assigning to expression + Eindir = 1 << 8 // indirecting through expression + Eaddr = 1 << 9 // taking address of expression + Eproc = 1 << 10 // inside a go statement + Ecomplit = 1 << 11 // type in composite literal +) + // type check the whole tree of an expression. // calculates expression types. // evaluates compile time constants. -- cgit v1.3 From cb284313f7e24319e7d22a551bd04ad9632db659 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 5 Apr 2016 20:01:50 +0000 Subject: net/http, net/http/httputil: start with capitals in deprecation sentences Fixes #15139 Change-Id: I73111137907e612af871b77ccf166572bf78c840 Reviewed-on: https://go-review.googlesource.com/21544 Reviewed-by: Andrew Gerrand --- src/net/http/httputil/persist.go | 10 +++++----- src/net/http/request.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/net/http/httputil/persist.go b/src/net/http/httputil/persist.go index 7874da3bec..51486e78e2 100644 --- a/src/net/http/httputil/persist.go +++ b/src/net/http/httputil/persist.go @@ -28,7 +28,7 @@ var errClosed = errors.New("i/o operation on closed connection") // Is is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: use the Server in package net/http instead. +// Deprecated: Use the Server in package net/http instead. type ServerConn struct { mu sync.Mutex // read-write protects the following fields c net.Conn @@ -45,7 +45,7 @@ type ServerConn struct { // Is is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: use the Server in package net/http instead. +// Deprecated: Use the Server in package net/http instead. func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn { if r == nil { r = bufio.NewReader(c) @@ -221,7 +221,7 @@ func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error { // Is is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: use Client or Transport in package net/http instead. +// Deprecated: Use Client or Transport in package net/http instead. type ClientConn struct { mu sync.Mutex // read-write protects the following fields c net.Conn @@ -239,7 +239,7 @@ type ClientConn struct { // Is is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: use the Client or Transport in package net/http instead. +// Deprecated: Use the Client or Transport in package net/http instead. func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn { if r == nil { r = bufio.NewReader(c) @@ -256,7 +256,7 @@ func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn { // Is is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // -// Deprecated: use the Client or Transport in package net/http instead. +// Deprecated: Use the Client or Transport in package net/http instead. func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn { cc := NewClientConn(c, r) cc.writeReq = (*http.Request).WriteProxy diff --git a/src/net/http/request.go b/src/net/http/request.go index 371d36b097..5510691912 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -249,7 +249,7 @@ type Request struct { // // For server requests, this field is not applicable. // - // Deprecated: use the Context and WithContext methods + // Deprecated: Use the Context and WithContext methods // instead. If a Request's Cancel field and context are both // set, it is undefined whether Cancel is respected. Cancel <-chan struct{} -- cgit v1.3 From 8556c76f88a6e80aafb535802be71cc79bd22c75 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 5 Apr 2016 18:22:23 +0000 Subject: runtime: minor Windows cleanup Change-Id: I9a8081ef1109469e9577c642156aa635188d8954 Reviewed-on: https://go-review.googlesource.com/21538 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman --- src/runtime/os1_windows.go | 22 +++++++++------------- src/runtime/os_windows.go | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go index 315dd9816a..a36def0ffe 100644 --- a/src/runtime/os1_windows.go +++ b/src/runtime/os1_windows.go @@ -110,28 +110,24 @@ func asmstdcall(fn unsafe.Pointer) var asmstdcallAddr unsafe.Pointer -func windowsFindfunc(name []byte, lib uintptr) stdFunction { +func windowsFindfunc(lib uintptr, name []byte) stdFunction { + if name[len(name)-1] != 0 { + throw("usage") + } f := stdcall2(_GetProcAddress, lib, uintptr(unsafe.Pointer(&name[0]))) return stdFunction(unsafe.Pointer(f)) } func loadOptionalSyscalls() { - var ( - kernel32dll = []byte("kernel32.dll\000") - addVectoredContinueHandler = []byte("AddVectoredContinueHandler\000") - getQueuedCompletionStatusEx = []byte("GetQueuedCompletionStatusEx\000") - addDllDirectory = []byte("AddDllDirectory\000") - loadLibraryExW = []byte("LoadLibraryExW\000") - ) - + var kernel32dll = []byte("kernel32.dll\000") k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) if k32 == 0 { throw("kernel32.dll not found") } - _AddDllDirectory = windowsFindfunc(addDllDirectory, k32) - _AddVectoredContinueHandler = windowsFindfunc(addVectoredContinueHandler, k32) - _GetQueuedCompletionStatusEx = windowsFindfunc(getQueuedCompletionStatusEx, k32) - _LoadLibraryExW = windowsFindfunc(loadLibraryExW, k32) + _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) + _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) + _GetQueuedCompletionStatusEx = windowsFindfunc(k32, []byte("GetQueuedCompletionStatusEx\000")) + _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) } //go:nosplit diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 8bdf5a271f..24b3b8cf29 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -10,7 +10,7 @@ type mOS struct { waitsema uintptr // semaphore for parking on locks } -type stdFunction *byte +type stdFunction unsafe.Pointer //go:linkname os_sigpipe os.sigpipe func os_sigpipe() { -- cgit v1.3 From e0307c25bebd694b98ae538065cda0681ef9ecf1 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 5 Apr 2016 15:59:55 +0000 Subject: net/http: document that Handlers shouldn't mutate Request Also, don't read from the Request.Headers in the http Server code once ServeHTTP has started. This is partially redundant with documenting that handlers shouldn't mutate request, but: the space is free due to bool packing, it's faster to do the checks once instead of N times in writeChunk, and it's a little nicer to code which previously didn't play by the unwritten rules. But I'm not going to fix all the cases. Fixes #14940 Change-Id: I612a8826b41c8682b59515081c590c512ee6949e Reviewed-on: https://go-review.googlesource.com/21530 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Andrew Gerrand --- src/net/http/server.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/net/http/server.go b/src/net/http/server.go index a2ef0ddf20..f4e697169d 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -50,6 +50,9 @@ var ( // ResponseWriter. Cautious handlers should read the Request.Body // first, and then reply. // +// Except for reading the body, handlers should not modify the +// provided Request. +// // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes // that the effect of the panic was isolated to the active request. // It recovers the panic, logs a stack trace to the server error log, @@ -306,11 +309,13 @@ func (cw *chunkWriter) close() { // A response represents the server side of an HTTP response. type response struct { - conn *conn - req *Request // request for this response - reqBody io.ReadCloser - wroteHeader bool // reply header has been (logically) written - wroteContinue bool // 100 Continue response was written + conn *conn + req *Request // request for this response + reqBody io.ReadCloser + wroteHeader bool // reply header has been (logically) written + wroteContinue bool // 100 Continue response was written + wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" + wantsClose bool // HTTP request has Connection "close" w *bufio.Writer // buffers output in chunks to chunkWriter cw chunkWriter @@ -748,6 +753,12 @@ func (c *conn) readRequest() (w *response, err error) { reqBody: req.Body, handlerHeader: make(Header), contentLength: -1, + + // We populate these ahead of time so we're not + // reading from req.Header after their Handler starts + // and maybe mutates it (Issue 14940) + wants10KeepAlive: req.wantsHttp10KeepAlive(), + wantsClose: req.wantsClose(), } if isH2Upgrade { w.closeAfterReply = true @@ -929,7 +940,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // If this was an HTTP/1.0 request with keep-alive and we sent a // Content-Length back, we can make this a keep-alive response ... - if w.req.wantsHttp10KeepAlive() && keepAlivesEnabled { + if w.wants10KeepAlive && keepAlivesEnabled { sentLength := header.get("Content-Length") != "" if sentLength && header.get("Connection") == "keep-alive" { w.closeAfterReply = false @@ -939,12 +950,12 @@ func (cw *chunkWriter) writeHeader(p []byte) { // Check for a explicit (and valid) Content-Length header. hasCL := w.contentLength != -1 - if w.req.wantsHttp10KeepAlive() && (isHEAD || hasCL) { + if w.wants10KeepAlive && (isHEAD || hasCL) { _, connectionHeaderSet := header["Connection"] if !connectionHeaderSet { setHeader.connection = "keep-alive" } - } else if !w.req.ProtoAtLeast(1, 1) || w.req.wantsClose() { + } else if !w.req.ProtoAtLeast(1, 1) || w.wantsClose { w.closeAfterReply = true } -- cgit v1.3 From fda831ed3f904c659fe41f253f75fe76528a28ee Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 5 Apr 2016 16:44:07 -0700 Subject: cmd/compile: encapsulate reads of gc.Type.Funarg Changes generated with eg and then manually checked and in some cases simplified. Passes toolstash -cmp. Change-Id: I2119f37f003368ce1884d2863b406d6ffbfe38c7 Reviewed-on: https://go-review.googlesource.com/21563 Reviewed-by: Brad Fitzpatrick Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/align.go | 4 ++-- src/cmd/compile/internal/gc/bexport.go | 2 +- src/cmd/compile/internal/gc/esc.go | 2 +- src/cmd/compile/internal/gc/export.go | 2 +- src/cmd/compile/internal/gc/fmt.go | 2 +- src/cmd/compile/internal/gc/gsubr.go | 2 +- src/cmd/compile/internal/gc/order.go | 2 +- src/cmd/compile/internal/gc/type.go | 7 ++++++- src/cmd/compile/internal/gc/typecheck.go | 8 ++++---- src/cmd/compile/internal/gc/walk.go | 2 +- 10 files changed, 19 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index b7ed9f19b9..9d5c3a550c 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -263,7 +263,7 @@ func dowidth(t *Type) { } case TSTRUCT: - if t.Funarg { + if t.IsFuncArgStruct() { Fatalf("dowidth fn struct %v", t) } w = widstruct(t, t, 0, 1) @@ -335,7 +335,7 @@ func checkwidth(t *Type) { // function arg structs should not be checked // outside of the enclosing function. - if t.Funarg { + if t.IsFuncArgStruct() { Fatalf("checkwidth %v", t) } diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 8968ce8924..f88afd2488 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -742,7 +742,7 @@ func basetypeName(t *Type) string { } func (p *exporter) paramList(params *Type, numbered bool) { - if !params.IsStruct() || !params.Funarg { + if !params.IsFuncArgStruct() { Fatalf("exporter: parameter list expected") } diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 9b8f134178..d7a63668a6 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -1435,7 +1435,7 @@ func esccall(e *EscState, n *Node, up *Node) { ll := n.List if n.List.Len() == 1 { a := n.List.First() - if a.Type.IsStruct() && a.Type.Funarg { // f(g()). + if a.Type.IsFuncArgStruct() { // f(g()) ll = e.nodeEscState(a).Escretval } } diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 9fc6e56275..6de7da0667 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -592,7 +592,7 @@ func dumpasmhdr() { case OTYPE: t := n.Type - if !t.IsStruct() || t.Map != nil || t.Funarg { + if !t.IsStruct() || t.Map != nil || t.IsFuncArgStruct() { break } fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width)) diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index ab9bad3c2a..27ccdfbdcf 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -690,7 +690,7 @@ func typefmt(t *Type, flag FmtFlag) string { } var buf bytes.Buffer - if t.Funarg { + if t.IsFuncArgStruct() { buf.WriteString("(") var flag1 FmtFlag if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index 353d90f593..a2fa5f8b31 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -541,7 +541,7 @@ func nodarg(t interface{}, fp int) *Node { switch t := t.(type) { case *Type: // entire argument struct, not just one arg - if !t.IsStruct() || !t.Funarg { + if !t.IsFuncArgStruct() { Fatalf("nodarg: bad type %v", t) } n = Nod(ONAME, nil, nil) diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 8410a236cd..3b83e3bcc0 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -324,7 +324,7 @@ func ismulticall(l Nodes) bool { // Copyret emits t1, t2, ... = n, where n is a function call, // and then returns the list t1, t2, .... func copyret(n *Node, order *Order) []*Node { - if !n.Type.IsStruct() || !n.Type.Funarg { + if !n.Type.IsFuncArgStruct() { Fatalf("copyret %v %d", n.Type, n.Left.Type.Results().NumFields()) } diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index 05e30df271..e04cfcda63 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -127,7 +127,7 @@ type Type struct { Chan ChanDir Trecur uint8 // to detect loops Printed bool - Funarg bool // on TSTRUCT and TFIELD + Funarg bool // TSTRUCT only: whether this struct represents function parameters Local bool // created in this file Deferwidth bool Broke bool // broken type definition. @@ -566,6 +566,11 @@ func (t *Type) SetNname(n *Node) { t.nname = n } +// IsFuncArgStruct reports whether t is a struct representing function parameters. +func (t *Type) IsFuncArgStruct() bool { + return t.Etype == TSTRUCT && t.Funarg +} + func (t *Type) Methods() *Fields { // TODO(mdempsky): Validate t? return &t.methods diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index d21552d180..db74a0d246 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -1607,7 +1607,7 @@ OpSwitch: // Unpack multiple-return result before type-checking. var funarg *Type - if t.IsStruct() && t.Funarg { + if t.IsFuncArgStruct() { funarg = t t = t.Field(0).Type } @@ -2159,7 +2159,7 @@ OpSwitch: } t := n.Type - if t != nil && !t.Funarg && n.Op != OTYPE { + if t != nil && !t.IsFuncArgStruct() && n.Op != OTYPE { switch t.Etype { case TFUNC, // might have TANY; wait until its called TANY, @@ -2611,7 +2611,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc if nl.Len() == 1 { n = nl.First() if n.Type != nil { - if n.Type.IsStruct() && n.Type.Funarg { + if n.Type.IsFuncArgStruct() { if !hasddd(tstruct) { n1 := tstruct.NumFields() n2 := n.Type.NumFields() @@ -3359,7 +3359,7 @@ func typecheckas2(n *Node) { } switch r.Op { case OCALLMETH, OCALLINTER, OCALLFUNC: - if !r.Type.IsStruct() || !r.Type.Funarg { + if !r.Type.IsFuncArgStruct() { break } cr = r.Type.NumFields() diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index b7edae5af4..392dae0fa9 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -1783,7 +1783,7 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini var nn []*Node // f(g()) where g has multiple return values - if r != nil && len(lr) <= 1 && r.Type.IsStruct() && r.Type.Funarg { + if r != nil && len(lr) <= 1 && r.Type.IsFuncArgStruct() { // optimization - can do block copy if eqtypenoname(r.Type, nl) { arg := nodarg(nl, fp) -- cgit v1.3 From 870d997ab47fe88c33f4dadef38d7e85eeabf17c Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 5 Apr 2016 17:24:23 +0000 Subject: net/http: keep request context during Client redirects Change-Id: I25c51280ba55120ffeaf08222f5ac5d471632d89 Reviewed-on: https://go-review.googlesource.com/21535 Reviewed-by: Andrew Gerrand Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/net/http/client.go | 1 + src/net/http/client_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'src') diff --git a/src/net/http/client.go b/src/net/http/client.go index 10f5684a79..ee0fd2cb62 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -475,6 +475,7 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo URL: u, Header: make(Header), Cancel: ireq.Cancel, + ctx: ireq.ctx, } if ireq.Method == "POST" || ireq.Method == "PUT" { req.Method = "GET" diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index e4fed26803..b9e17c5270 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -8,6 +8,7 @@ package http_test import ( "bytes" + "context" "crypto/tls" "crypto/x509" "encoding/base64" @@ -290,6 +291,33 @@ func TestClientRedirects(t *testing.T) { } } +func TestClientRedirectContext(t *testing.T) { + defer afterTest(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + Redirect(w, r, "/", StatusFound) + })) + defer ts.Close() + + ctx, cancel := context.WithCancel(context.Background()) + c := &Client{CheckRedirect: func(req *Request, via []*Request) error { + cancel() + if len(via) > 2 { + return errors.New("too many redirects") + } + return nil + }} + req, _ := NewRequest("GET", ts.URL, nil) + req = req.WithContext(ctx) + _, err := c.Do(req) + ue, ok := err.(*url.Error) + if !ok { + t.Fatalf("got error %T; want *url.Error") + } + if ue.Err != ExportErrRequestCanceled && ue.Err != ExportErrRequestCanceledConn { + t.Errorf("url.Error.Err = %v; want errRequestCanceled or errRequestCanceledConn", ue.Err) + } +} + func TestPostRedirects(t *testing.T) { defer afterTest(t) var log struct { -- cgit v1.3 From 6f2a8810b01d6332d35c9b3e3e64c13f60d31776 Mon Sep 17 00:00:00 2001 From: David Symonds Date: Wed, 6 Apr 2016 11:53:26 +1000 Subject: expvar: Ensure strings are written as valid JSON. Change-Id: I5147dbf4e85cf42cd1f32c57861e4c16d9dbd049 Reviewed-on: https://go-review.googlesource.com/21529 Reviewed-by: Brad Fitzpatrick Reviewed-by: Andrew Gerrand --- src/expvar/expvar.go | 6 ++++-- src/expvar/expvar_test.go | 10 ++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go index 1ec85006b4..b7ea433014 100644 --- a/src/expvar/expvar.go +++ b/src/expvar/expvar.go @@ -219,8 +219,10 @@ type String struct { func (v *String) String() string { v.mu.RLock() - defer v.mu.RUnlock() - return strconv.Quote(v.s) + s := v.s + v.mu.RUnlock() + b, _ := json.Marshal(s) + return string(b) } func (v *String) Set(value string) { diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go index 385fea81ad..7b1c9dfc4f 100644 --- a/src/expvar/expvar_test.go +++ b/src/expvar/expvar_test.go @@ -142,8 +142,14 @@ func TestString(t *testing.T) { t.Errorf("name.s = %q, want \"Mike\"", name.s) } - if s := name.String(); s != "\"Mike\"" { - t.Errorf("reqs.String() = %q, want \"\"Mike\"\"", s) + if s, want := name.String(), `"Mike"`; s != want { + t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want) + } + + // Make sure we produce safe JSON output. + name.Set(`<`) + if s, want := name.String(), "\"\\u003c\""; s != want { + t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want) } } -- cgit v1.3 From 5103fbfdb29278533c666163a9d56f85408224d9 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 02:51:55 +0000 Subject: runtime: merge os_linux.go into os1_linux.go Change-Id: I791c47014fe69e8529c7b2f0b9a554e47902d46c Reviewed-on: https://go-review.googlesource.com/21566 Reviewed-by: Minux Ma --- src/runtime/os1_linux.go | 29 +++++++++++++++++++++++++++++ src/runtime/os_linux.go | 36 ------------------------------------ 2 files changed, 29 insertions(+), 36 deletions(-) delete mode 100644 src/runtime/os_linux.go (limited to 'src') diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go index 726dd649fe..7d8cc7e5c4 100644 --- a/src/runtime/os1_linux.go +++ b/src/runtime/os1_linux.go @@ -9,6 +9,11 @@ import ( "unsafe" ) +type mOS struct{} + +//go:noescape +func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 + // Linux futex. // // futexsleep(uint32 *addr, uint32 val) @@ -127,6 +132,9 @@ const ( _CLONE_THREAD /* revisit - okay for now */ ) +//go:noescape +func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32 + // May run with m.p==nil, so write barriers are not allowed. //go:nowritebarrier func newosproc(mp *m, stk unsafe.Pointer) { @@ -307,6 +315,27 @@ func sigreturn() func sigtramp() func cgoSigtramp() +//go:noescape +func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 + +//go:noescape +func sigaltstack(new, old *sigaltstackt) + +//go:noescape +func setitimer(mode int32, new, old *itimerval) + +//go:noescape +func rtsigprocmask(sig uint32, new, old *sigset, size int32) + +//go:noescape +func getrlimit(kind int32, limit unsafe.Pointer) int32 +func raise(sig int32) +func raiseproc(sig int32) + +//go:noescape +func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 +func osyield() + //go:nosplit //go:nowritebarrierrec func setsig(i int32, fn uintptr, restart bool) { diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go deleted file mode 100644 index dd69743e10..0000000000 --- a/src/runtime/os_linux.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2014 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 runtime - -import "unsafe" - -type mOS struct{} - -//go:noescape -func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 - -//go:noescape -func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32 - -//go:noescape -func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 - -//go:noescape -func sigaltstack(new, old *sigaltstackt) - -//go:noescape -func setitimer(mode int32, new, old *itimerval) - -//go:noescape -func rtsigprocmask(sig uint32, new, old *sigset, size int32) - -//go:noescape -func getrlimit(kind int32, limit unsafe.Pointer) int32 -func raise(sig int32) -func raiseproc(sig int32) - -//go:noescape -func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 -func osyield() -- cgit v1.3 From 34c58065e54e0ac2d610b4a550bdba8f1db90ec6 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 02:52:17 +0000 Subject: runtime: rename os1_linux.go to os_linux.go Change-Id: I938f61763c3256a876d62aeb54ef8c25cc4fc90e Reviewed-on: https://go-review.googlesource.com/21567 Reviewed-by: Minux Ma --- src/runtime/os1_linux.go | 422 ----------------------------------------------- src/runtime/os_linux.go | 422 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+), 422 deletions(-) delete mode 100644 src/runtime/os1_linux.go create mode 100644 src/runtime/os_linux.go (limited to 'src') diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go deleted file mode 100644 index 7d8cc7e5c4..0000000000 --- a/src/runtime/os1_linux.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2009 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 runtime - -import ( - "runtime/internal/sys" - "unsafe" -) - -type mOS struct{} - -//go:noescape -func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 - -// Linux futex. -// -// futexsleep(uint32 *addr, uint32 val) -// futexwakeup(uint32 *addr) -// -// Futexsleep atomically checks if *addr == val and if so, sleeps on addr. -// Futexwakeup wakes up threads sleeping on addr. -// Futexsleep is allowed to wake up spuriously. - -const ( - _FUTEX_WAIT = 0 - _FUTEX_WAKE = 1 -) - -// Atomically, -// if(*addr == val) sleep -// Might be woken up spuriously; that's allowed. -// Don't sleep longer than ns; ns < 0 means forever. -//go:nosplit -func futexsleep(addr *uint32, val uint32, ns int64) { - var ts timespec - - // Some Linux kernels have a bug where futex of - // FUTEX_WAIT returns an internal error code - // as an errno. Libpthread ignores the return value - // here, and so can we: as it says a few lines up, - // spurious wakeups are allowed. - if ns < 0 { - futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, nil, nil, 0) - return - } - - // It's difficult to live within the no-split stack limits here. - // On ARM and 386, a 64-bit divide invokes a general software routine - // that needs more stack than we can afford. So we use timediv instead. - // But on real 64-bit systems, where words are larger but the stack limit - // is not, even timediv is too heavy, and we really need to use just an - // ordinary machine instruction. - if sys.PtrSize == 8 { - ts.set_sec(ns / 1000000000) - ts.set_nsec(int32(ns % 1000000000)) - } else { - ts.tv_nsec = 0 - ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec))))) - } - futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, unsafe.Pointer(&ts), nil, 0) -} - -// If any procs are sleeping on addr, wake up at most cnt. -//go:nosplit -func futexwakeup(addr *uint32, cnt uint32) { - ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE, cnt, nil, nil, 0) - if ret >= 0 { - return - } - - // I don't know that futex wakeup can return - // EAGAIN or EINTR, but if it does, it would be - // safe to loop and call futex again. - systemstack(func() { - print("futexwakeup addr=", addr, " returned ", ret, "\n") - }) - - *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006 -} - -func getproccount() int32 { - // This buffer is huge (8 kB) but we are on the system stack - // and there should be plenty of space (64 kB). - // Also this is a leaf, so we're not holding up the memory for long. - // See golang.org/issue/11823. - // The suggested behavior here is to keep trying with ever-larger - // buffers, but we don't have a dynamic memory allocator at the - // moment, so that's a bit tricky and seems like overkill. - const maxCPUs = 64 * 1024 - var buf [maxCPUs / (sys.PtrSize * 8)]uintptr - r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0]) - n := int32(0) - for _, v := range buf[:r/sys.PtrSize] { - for v != 0 { - n += int32(v & 1) - v >>= 1 - } - } - if n == 0 { - n = 1 - } - return n -} - -// Clone, the Linux rfork. -const ( - _CLONE_VM = 0x100 - _CLONE_FS = 0x200 - _CLONE_FILES = 0x400 - _CLONE_SIGHAND = 0x800 - _CLONE_PTRACE = 0x2000 - _CLONE_VFORK = 0x4000 - _CLONE_PARENT = 0x8000 - _CLONE_THREAD = 0x10000 - _CLONE_NEWNS = 0x20000 - _CLONE_SYSVSEM = 0x40000 - _CLONE_SETTLS = 0x80000 - _CLONE_PARENT_SETTID = 0x100000 - _CLONE_CHILD_CLEARTID = 0x200000 - _CLONE_UNTRACED = 0x800000 - _CLONE_CHILD_SETTID = 0x1000000 - _CLONE_STOPPED = 0x2000000 - _CLONE_NEWUTS = 0x4000000 - _CLONE_NEWIPC = 0x8000000 - - cloneFlags = _CLONE_VM | /* share memory */ - _CLONE_FS | /* share cwd, etc */ - _CLONE_FILES | /* share fd table */ - _CLONE_SIGHAND | /* share sig handler table */ - _CLONE_THREAD /* revisit - okay for now */ -) - -//go:noescape -func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32 - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - /* - * note: strace gets confused if we use CLONE_PTRACE here. - */ - if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", funcPC(clone), " id=", mp.id, " ostk=", &mp, "\n") - } - - // Disable signals during clone, so that the new thread starts - // with signals disabled. It will enable them in minit. - var oset sigset - rtsigprocmask(_SIG_SETMASK, &sigset_all, &oset, int32(unsafe.Sizeof(oset))) - ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) - rtsigprocmask(_SIG_SETMASK, &oset, nil, int32(unsafe.Sizeof(oset))) - - if ret < 0 { - print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n") - throw("newosproc") - } -} - -// Version of newosproc that doesn't require a valid G. -//go:nosplit -func newosproc0(stacksize uintptr, fn unsafe.Pointer) { - stack := sysAlloc(stacksize, &memstats.stacks_sys) - if stack == nil { - write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack))) - exit(1) - } - ret := clone(cloneFlags, unsafe.Pointer(uintptr(stack)+stacksize), nil, nil, fn) - if ret < 0 { - write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate))) - exit(1) - } -} - -var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n") -var failthreadcreate = []byte("runtime: failed to create new OS thread\n") - -func osinit() { - ncpu = getproccount() -} - -var urandom_dev = []byte("/dev/urandom\x00") - -func getRandomData(r []byte) { - if startupRandomData != nil { - n := copy(r, startupRandomData) - extendRandom(r, n) - return - } - fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) - n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) - closefd(fd) - extendRandom(r, int(n)) -} - -func goenvs() { - goenvs_unix() -} - -// Called to do synchronous initialization of Go code built with -// -buildmode=c-archive or -buildmode=c-shared. -// None of the Go runtime is initialized. -//go:nosplit -//go:nowritebarrierrec -func libpreinit() { - initsig(true) -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - mp.gsignal = malg(32 * 1024) // Linux wants >= 2K - mp.gsignal.m = mp -} - -//go:nosplit -func msigsave(mp *m) { - smask := &mp.sigmask - rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask))) -} - -//go:nosplit -func msigrestore(sigmask sigset) { - rtsigprocmask(_SIG_SETMASK, &sigmask, nil, int32(unsafe.Sizeof(sigmask))) -} - -//go:nosplit -func sigblock() { - rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all))) -} - -func gettid() uint32 - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - // Initialize signal handling. - _g_ := getg() - - var st sigaltstackt - sigaltstack(nil, &st) - if st.ss_flags&_SS_DISABLE != 0 { - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true - } else { - // Use existing signal stack. - stsp := uintptr(unsafe.Pointer(st.ss_sp)) - _g_.m.gsignal.stack.lo = stsp - _g_.m.gsignal.stack.hi = stsp + st.ss_size - _g_.m.gsignal.stackguard0 = stsp + _StackGuard - _g_.m.gsignal.stackguard1 = stsp + _StackGuard - _g_.m.gsignal.stackAlloc = st.ss_size - _g_.m.newSigstack = false - } - - // for debuggers, in case cgo created the thread - _g_.m.procid = uint64(gettid()) - - // restore signal mask from m.sigmask and unblock essential signals - nmask := _g_.m.sigmask - for i := range sigtable { - if sigtable[i].flags&_SigUnblock != 0 { - sigdelset(&nmask, i) - } - } - rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask))) -} - -// Called from dropm to undo the effect of an minit. -//go:nosplit -func unminit() { - if getg().m.newSigstack { - signalstack(nil) - } -} - -func memlimit() uintptr { - /* - TODO: Convert to Go when something actually uses the result. - - Rlimit rl; - extern byte runtime·text[], runtime·end[]; - uintptr used; - - if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) - return 0; - if(rl.rlim_cur >= 0x7fffffff) - return 0; - - // Estimate our VM footprint excluding the heap. - // Not an exact science: use size of binary plus - // some room for thread stacks. - used = runtime·end - runtime·text + (64<<20); - if(used >= rl.rlim_cur) - return 0; - - // If there's not at least 16 MB left, we're probably - // not going to be able to do much. Treat as no limit. - rl.rlim_cur -= used; - if(rl.rlim_cur < (16<<20)) - return 0; - - return rl.rlim_cur - used; - */ - - return 0 -} - -//#ifdef GOARCH_386 -//#define sa_handler k_sa_handler -//#endif - -func sigreturn() -func sigtramp() -func cgoSigtramp() - -//go:noescape -func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 - -//go:noescape -func sigaltstack(new, old *sigaltstackt) - -//go:noescape -func setitimer(mode int32, new, old *itimerval) - -//go:noescape -func rtsigprocmask(sig uint32, new, old *sigset, size int32) - -//go:noescape -func getrlimit(kind int32, limit unsafe.Pointer) int32 -func raise(sig int32) -func raiseproc(sig int32) - -//go:noescape -func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 -func osyield() - -//go:nosplit -//go:nowritebarrierrec -func setsig(i int32, fn uintptr, restart bool) { - var sa sigactiont - memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) - sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER - if restart { - sa.sa_flags |= _SA_RESTART - } - sigfillset(&sa.sa_mask) - // Although Linux manpage says "sa_restorer element is obsolete and - // should not be used". x86_64 kernel requires it. Only use it on - // x86. - if GOARCH == "386" || GOARCH == "amd64" { - sa.sa_restorer = funcPC(sigreturn) - } - if fn == funcPC(sighandler) { - if iscgo { - fn = funcPC(cgoSigtramp) - } else { - fn = funcPC(sigtramp) - } - } - sa.sa_handler = fn - rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) -} - -//go:nosplit -//go:nowritebarrierrec -func setsigstack(i int32) { - var sa sigactiont - if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { - throw("rt_sigaction failure") - } - if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 { - return - } - sa.sa_flags |= _SA_ONSTACK - if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 { - throw("rt_sigaction failure") - } -} - -//go:nosplit -//go:nowritebarrierrec -func getsig(i int32) uintptr { - var sa sigactiont - - memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) - if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { - throw("rt_sigaction read failure") - } - if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) { - return funcPC(sighandler) - } - return sa.sa_handler -} - -//go:nosplit -func signalstack(s *stack) { - var st sigaltstackt - if s == nil { - st.ss_flags = _SS_DISABLE - } else { - st.ss_sp = (*byte)(unsafe.Pointer(s.lo)) - st.ss_size = s.hi - s.lo - st.ss_flags = 0 - } - sigaltstack(&st, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func updatesigmask(m sigmask) { - var mask sigset - sigcopyset(&mask, m) - rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask))) -} - -func unblocksig(sig int32) { - var mask sigset - sigaddset(&mask, int(sig)) - rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask))) -} diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go new file mode 100644 index 0000000000..7d8cc7e5c4 --- /dev/null +++ b/src/runtime/os_linux.go @@ -0,0 +1,422 @@ +// Copyright 2009 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 runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +type mOS struct{} + +//go:noescape +func futex(addr unsafe.Pointer, op int32, val uint32, ts, addr2 unsafe.Pointer, val3 uint32) int32 + +// Linux futex. +// +// futexsleep(uint32 *addr, uint32 val) +// futexwakeup(uint32 *addr) +// +// Futexsleep atomically checks if *addr == val and if so, sleeps on addr. +// Futexwakeup wakes up threads sleeping on addr. +// Futexsleep is allowed to wake up spuriously. + +const ( + _FUTEX_WAIT = 0 + _FUTEX_WAKE = 1 +) + +// Atomically, +// if(*addr == val) sleep +// Might be woken up spuriously; that's allowed. +// Don't sleep longer than ns; ns < 0 means forever. +//go:nosplit +func futexsleep(addr *uint32, val uint32, ns int64) { + var ts timespec + + // Some Linux kernels have a bug where futex of + // FUTEX_WAIT returns an internal error code + // as an errno. Libpthread ignores the return value + // here, and so can we: as it says a few lines up, + // spurious wakeups are allowed. + if ns < 0 { + futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, nil, nil, 0) + return + } + + // It's difficult to live within the no-split stack limits here. + // On ARM and 386, a 64-bit divide invokes a general software routine + // that needs more stack than we can afford. So we use timediv instead. + // But on real 64-bit systems, where words are larger but the stack limit + // is not, even timediv is too heavy, and we really need to use just an + // ordinary machine instruction. + if sys.PtrSize == 8 { + ts.set_sec(ns / 1000000000) + ts.set_nsec(int32(ns % 1000000000)) + } else { + ts.tv_nsec = 0 + ts.set_sec(int64(timediv(ns, 1000000000, (*int32)(unsafe.Pointer(&ts.tv_nsec))))) + } + futex(unsafe.Pointer(addr), _FUTEX_WAIT, val, unsafe.Pointer(&ts), nil, 0) +} + +// If any procs are sleeping on addr, wake up at most cnt. +//go:nosplit +func futexwakeup(addr *uint32, cnt uint32) { + ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE, cnt, nil, nil, 0) + if ret >= 0 { + return + } + + // I don't know that futex wakeup can return + // EAGAIN or EINTR, but if it does, it would be + // safe to loop and call futex again. + systemstack(func() { + print("futexwakeup addr=", addr, " returned ", ret, "\n") + }) + + *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006 +} + +func getproccount() int32 { + // This buffer is huge (8 kB) but we are on the system stack + // and there should be plenty of space (64 kB). + // Also this is a leaf, so we're not holding up the memory for long. + // See golang.org/issue/11823. + // The suggested behavior here is to keep trying with ever-larger + // buffers, but we don't have a dynamic memory allocator at the + // moment, so that's a bit tricky and seems like overkill. + const maxCPUs = 64 * 1024 + var buf [maxCPUs / (sys.PtrSize * 8)]uintptr + r := sched_getaffinity(0, unsafe.Sizeof(buf), &buf[0]) + n := int32(0) + for _, v := range buf[:r/sys.PtrSize] { + for v != 0 { + n += int32(v & 1) + v >>= 1 + } + } + if n == 0 { + n = 1 + } + return n +} + +// Clone, the Linux rfork. +const ( + _CLONE_VM = 0x100 + _CLONE_FS = 0x200 + _CLONE_FILES = 0x400 + _CLONE_SIGHAND = 0x800 + _CLONE_PTRACE = 0x2000 + _CLONE_VFORK = 0x4000 + _CLONE_PARENT = 0x8000 + _CLONE_THREAD = 0x10000 + _CLONE_NEWNS = 0x20000 + _CLONE_SYSVSEM = 0x40000 + _CLONE_SETTLS = 0x80000 + _CLONE_PARENT_SETTID = 0x100000 + _CLONE_CHILD_CLEARTID = 0x200000 + _CLONE_UNTRACED = 0x800000 + _CLONE_CHILD_SETTID = 0x1000000 + _CLONE_STOPPED = 0x2000000 + _CLONE_NEWUTS = 0x4000000 + _CLONE_NEWIPC = 0x8000000 + + cloneFlags = _CLONE_VM | /* share memory */ + _CLONE_FS | /* share cwd, etc */ + _CLONE_FILES | /* share fd table */ + _CLONE_SIGHAND | /* share sig handler table */ + _CLONE_THREAD /* revisit - okay for now */ +) + +//go:noescape +func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32 + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + /* + * note: strace gets confused if we use CLONE_PTRACE here. + */ + if false { + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " clone=", funcPC(clone), " id=", mp.id, " ostk=", &mp, "\n") + } + + // Disable signals during clone, so that the new thread starts + // with signals disabled. It will enable them in minit. + var oset sigset + rtsigprocmask(_SIG_SETMASK, &sigset_all, &oset, int32(unsafe.Sizeof(oset))) + ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) + rtsigprocmask(_SIG_SETMASK, &oset, nil, int32(unsafe.Sizeof(oset))) + + if ret < 0 { + print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n") + throw("newosproc") + } +} + +// Version of newosproc that doesn't require a valid G. +//go:nosplit +func newosproc0(stacksize uintptr, fn unsafe.Pointer) { + stack := sysAlloc(stacksize, &memstats.stacks_sys) + if stack == nil { + write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack))) + exit(1) + } + ret := clone(cloneFlags, unsafe.Pointer(uintptr(stack)+stacksize), nil, nil, fn) + if ret < 0 { + write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate))) + exit(1) + } +} + +var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n") +var failthreadcreate = []byte("runtime: failed to create new OS thread\n") + +func osinit() { + ncpu = getproccount() +} + +var urandom_dev = []byte("/dev/urandom\x00") + +func getRandomData(r []byte) { + if startupRandomData != nil { + n := copy(r, startupRandomData) + extendRandom(r, n) + return + } + fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) + n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) + closefd(fd) + extendRandom(r, int(n)) +} + +func goenvs() { + goenvs_unix() +} + +// Called to do synchronous initialization of Go code built with +// -buildmode=c-archive or -buildmode=c-shared. +// None of the Go runtime is initialized. +//go:nosplit +//go:nowritebarrierrec +func libpreinit() { + initsig(true) +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) // Linux wants >= 2K + mp.gsignal.m = mp +} + +//go:nosplit +func msigsave(mp *m) { + smask := &mp.sigmask + rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask))) +} + +//go:nosplit +func msigrestore(sigmask sigset) { + rtsigprocmask(_SIG_SETMASK, &sigmask, nil, int32(unsafe.Sizeof(sigmask))) +} + +//go:nosplit +func sigblock() { + rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all))) +} + +func gettid() uint32 + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + // Initialize signal handling. + _g_ := getg() + + var st sigaltstackt + sigaltstack(nil, &st) + if st.ss_flags&_SS_DISABLE != 0 { + signalstack(&_g_.m.gsignal.stack) + _g_.m.newSigstack = true + } else { + // Use existing signal stack. + stsp := uintptr(unsafe.Pointer(st.ss_sp)) + _g_.m.gsignal.stack.lo = stsp + _g_.m.gsignal.stack.hi = stsp + st.ss_size + _g_.m.gsignal.stackguard0 = stsp + _StackGuard + _g_.m.gsignal.stackguard1 = stsp + _StackGuard + _g_.m.gsignal.stackAlloc = st.ss_size + _g_.m.newSigstack = false + } + + // for debuggers, in case cgo created the thread + _g_.m.procid = uint64(gettid()) + + // restore signal mask from m.sigmask and unblock essential signals + nmask := _g_.m.sigmask + for i := range sigtable { + if sigtable[i].flags&_SigUnblock != 0 { + sigdelset(&nmask, i) + } + } + rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask))) +} + +// Called from dropm to undo the effect of an minit. +//go:nosplit +func unminit() { + if getg().m.newSigstack { + signalstack(nil) + } +} + +func memlimit() uintptr { + /* + TODO: Convert to Go when something actually uses the result. + + Rlimit rl; + extern byte runtime·text[], runtime·end[]; + uintptr used; + + if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) + return 0; + if(rl.rlim_cur >= 0x7fffffff) + return 0; + + // Estimate our VM footprint excluding the heap. + // Not an exact science: use size of binary plus + // some room for thread stacks. + used = runtime·end - runtime·text + (64<<20); + if(used >= rl.rlim_cur) + return 0; + + // If there's not at least 16 MB left, we're probably + // not going to be able to do much. Treat as no limit. + rl.rlim_cur -= used; + if(rl.rlim_cur < (16<<20)) + return 0; + + return rl.rlim_cur - used; + */ + + return 0 +} + +//#ifdef GOARCH_386 +//#define sa_handler k_sa_handler +//#endif + +func sigreturn() +func sigtramp() +func cgoSigtramp() + +//go:noescape +func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 + +//go:noescape +func sigaltstack(new, old *sigaltstackt) + +//go:noescape +func setitimer(mode int32, new, old *itimerval) + +//go:noescape +func rtsigprocmask(sig uint32, new, old *sigset, size int32) + +//go:noescape +func getrlimit(kind int32, limit unsafe.Pointer) int32 +func raise(sig int32) +func raiseproc(sig int32) + +//go:noescape +func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 +func osyield() + +//go:nosplit +//go:nowritebarrierrec +func setsig(i int32, fn uintptr, restart bool) { + var sa sigactiont + memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) + sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER + if restart { + sa.sa_flags |= _SA_RESTART + } + sigfillset(&sa.sa_mask) + // Although Linux manpage says "sa_restorer element is obsolete and + // should not be used". x86_64 kernel requires it. Only use it on + // x86. + if GOARCH == "386" || GOARCH == "amd64" { + sa.sa_restorer = funcPC(sigreturn) + } + if fn == funcPC(sighandler) { + if iscgo { + fn = funcPC(cgoSigtramp) + } else { + fn = funcPC(sigtramp) + } + } + sa.sa_handler = fn + rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) +} + +//go:nosplit +//go:nowritebarrierrec +func setsigstack(i int32) { + var sa sigactiont + if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { + throw("rt_sigaction failure") + } + if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 { + return + } + sa.sa_flags |= _SA_ONSTACK + if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 { + throw("rt_sigaction failure") + } +} + +//go:nosplit +//go:nowritebarrierrec +func getsig(i int32) uintptr { + var sa sigactiont + + memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa)) + if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 { + throw("rt_sigaction read failure") + } + if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) { + return funcPC(sighandler) + } + return sa.sa_handler +} + +//go:nosplit +func signalstack(s *stack) { + var st sigaltstackt + if s == nil { + st.ss_flags = _SS_DISABLE + } else { + st.ss_sp = (*byte)(unsafe.Pointer(s.lo)) + st.ss_size = s.hi - s.lo + st.ss_flags = 0 + } + sigaltstack(&st, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func updatesigmask(m sigmask) { + var mask sigset + sigcopyset(&mask, m) + rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask))) +} + +func unblocksig(sig int32) { + var mask sigset + sigaddset(&mask, int(sig)) + rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask))) +} -- cgit v1.3 From 7f39f21c7be83b9ff59089b29fa9e723c13cafa9 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 4 Apr 2016 16:54:54 +0000 Subject: io: add ReadAtSizer interface ReadAtSizer is a common abstraction for a stateless, concurrently-readable fixed number of bytes. This interface has existed in various codebases for over 3 years (previously usually named SizeReaderAt). It is used inside Google in dl.google.com (mentioned in https://talks.golang.org/2013/oscon-dl.slide) and other packages. It is used in Camlistore, in Juju, in the Google API Go client, in github.com/nightlyone/views, and 33 other pages of Github search results. It is implemented by io.SectionReader, bytes.Reader, strings.Reader, etc. Time to finally promote this interface to the standard library and give it a standard name, blessing it as best practice. Updates #7263 Updates #14889 Change-Id: Id28c0cafa7d2d37e8887c54708b5daf1b11c83ea Reviewed-on: https://go-review.googlesource.com/21492 Reviewed-by: Rob Pike --- src/io/io.go | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/io/io.go b/src/io/io.go index 6e33192052..23401dae93 100644 --- a/src/io/io.go +++ b/src/io/io.go @@ -274,6 +274,15 @@ type RuneScanner interface { UnreadRune() error } +// ReadAtSizer is the interface that groups the basic ReadAt and Size +// methods, representing a sized data source that supports random +// access by multiple concurrent goroutines. +type ReadAtSizer interface { + ReaderAt + // Size reports the length of the data source in bytes. + Size() int64 +} + // stringWriter is the interface that wraps the WriteString method. type stringWriter interface { WriteString(s string) (n int, err error) -- cgit v1.3 From db9348b866775974b5e04eed04c03de47f1c2d46 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Sun, 20 Mar 2016 21:41:07 -0400 Subject: reflect: add s390x support Change-Id: I1f975130179cf26af67e82664310b93d43e87a75 Reviewed-on: https://go-review.googlesource.com/20944 Reviewed-by: Minux Ma Run-TryBot: Michael Munday TryBot-Result: Gobot Gobot --- src/reflect/asm_s390x.s | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/reflect/asm_s390x.s (limited to 'src') diff --git a/src/reflect/asm_s390x.s b/src/reflect/asm_s390x.s new file mode 100644 index 0000000000..e6b86cfaa9 --- /dev/null +++ b/src/reflect/asm_s390x.s @@ -0,0 +1,30 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" +#include "funcdata.h" + +// makeFuncStub is the code half of the function returned by MakeFunc. +// See the comment on the declaration of makeFuncStub in makefunc.go +// for more details. +// No arg size here, runtime pulls arg map out of the func value. +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$16 + NO_LOCAL_POINTERS + MOVD R12, 8(R15) + MOVD $argframe+0(FP), R3 + MOVD R3, 16(R15) + BL ·callReflect(SB) + RET + +// methodValueCall is the code half of the function returned by makeMethodValue. +// See the comment on the declaration of methodValueCall in makefunc.go +// for more details. +// No arg size here; runtime pulls arg map out of the func value. +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16 + NO_LOCAL_POINTERS + MOVD R12, 8(R15) + MOVD $argframe+0(FP), R3 + MOVD R3, 16(R15) + BL ·callMethod(SB) + RET -- cgit v1.3 From a2eded3421f144983c0ccb9e6c0a325fa1ba1f82 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Tue, 5 Apr 2016 23:09:39 -0400 Subject: runtime: get randomness from AT_RANDOM AUXV on linux/arm64 Fixes #15147. Change-Id: Ibfe46c747dea987787a51eb0c95ccd8c5f24f366 Reviewed-on: https://go-review.googlesource.com/21580 Run-TryBot: Minux Ma Reviewed-by: Brad Fitzpatrick --- src/runtime/os_linux_arm64.go | 26 ++++++++++++++++++++++++++ src/runtime/vdso_none.go | 1 + 2 files changed, 27 insertions(+) (limited to 'src') diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go index 3f994f128b..57184b0d3a 100644 --- a/src/runtime/os_linux_arm64.go +++ b/src/runtime/os_linux_arm64.go @@ -4,6 +4,11 @@ package runtime +import ( + "runtime/internal/sys" + "unsafe" +) + const ( _AT_NULL = 0 _AT_RANDOM = 25 // introduced in 2.6.29 @@ -11,6 +16,27 @@ const ( var randomNumber uint32 +func sysargs(argc int32, argv **byte) { + // skip over argv, envv to get to auxv + n := argc + 1 + for argv_index(argv, n) != nil { + n++ + } + n++ + auxv := (*[1 << 29]uint64)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize)) + + for i := 0; auxv[i] != _AT_NULL; i += 2 { + switch auxv[i] { + case _AT_RANDOM: // kernel provides a pointer to 16-bytes worth of random data + startupRandomData = (*[16]byte)(unsafe.Pointer(uintptr(auxv[i+1])))[:] + // the pointer provided may not be word aligned, so we must treat it + // as a byte array. + randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 | + uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24 + } + } +} + //go:nosplit func cputicks() int64 { // Currently cputicks() is used in blocking profiler and to seed fastrand1(). diff --git a/src/runtime/vdso_none.go b/src/runtime/vdso_none.go index b4e0a0e349..e14e1a4707 100644 --- a/src/runtime/vdso_none.go +++ b/src/runtime/vdso_none.go @@ -5,6 +5,7 @@ // +build !linux !amd64 // +build !linux !386 // +build !linux !arm +// +build !linux !arm64 package runtime -- cgit v1.3 From 0f08dd21831a71292dc306cfc05e28a3a9d73786 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 18 Mar 2016 19:02:52 -0400 Subject: runtime: add s390x support (modified files only) Change-Id: Ib79ad4a890994ad64edb1feb79bd242d26b5b08a Reviewed-on: https://go-review.googlesource.com/20945 Reviewed-by: Minux Ma Run-TryBot: Michael Munday TryBot-Result: Gobot Gobot --- src/runtime/cgocall.go | 6 +++--- src/runtime/extern.go | 2 +- src/runtime/gcinfo_test.go | 2 +- src/runtime/hash64.go | 2 +- src/runtime/noasm.go | 2 +- src/runtime/os1_linux_generic.go | 1 + src/runtime/os2_linux_generic.go | 1 + src/runtime/runtime-gdb_test.go | 2 +- src/runtime/unaligned1.go | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index d5248803a4..c6000bf98f 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -246,8 +246,8 @@ func cgocallbackg1() { case "386": // On 386, stack frame is three words, plus caller PC. cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize)) - case "ppc64", "ppc64le": - // On ppc64, the callback arguments are in the arguments area of + case "ppc64", "ppc64le", "s390x": + // On ppc64 and s390x, the callback arguments are in the arguments area of // cgocallback's stack frame. The stack looks like this: // +--------------------+------------------------------+ // | | ... | @@ -300,7 +300,7 @@ func unwindm(restore *bool) { switch GOARCH { default: throw("unwindm not implemented") - case "386", "amd64", "arm", "ppc64", "ppc64le": + case "386", "amd64", "arm", "ppc64", "ppc64le", "s390x": sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + sys.MinFrameSize)) case "arm64": sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16)) diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 984b0ca817..1d8304f4fc 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -227,5 +227,5 @@ func Version() string { const GOOS string = sys.TheGoos // GOARCH is the running program's architecture target: -// 386, amd64, or arm. +// 386, amd64, arm, or s390x. const GOARCH string = sys.TheGoarch diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go index edb6361642..c1c2354bf9 100644 --- a/src/runtime/gcinfo_test.go +++ b/src/runtime/gcinfo_test.go @@ -144,7 +144,7 @@ func infoBigStruct() []byte { typeScalar, typeScalar, typeScalar, typeScalar, // t int; y uint16; u uint64 typePointer, typeScalar, // i string } - case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le": + case "arm64", "amd64", "mips64", "mips64le", "ppc64", "ppc64le", "s390x": return []byte{ typePointer, // q *int typeScalar, typeScalar, typeScalar, // w byte; e [17]byte diff --git a/src/runtime/hash64.go b/src/runtime/hash64.go index fb3dba4000..d61f114475 100644 --- a/src/runtime/hash64.go +++ b/src/runtime/hash64.go @@ -6,7 +6,7 @@ // xxhash: https://code.google.com/p/xxhash/ // cityhash: https://code.google.com/p/cityhash/ -// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le +// +build amd64 amd64p32 arm64 mips64 mips64le ppc64 ppc64le s390x package runtime diff --git a/src/runtime/noasm.go b/src/runtime/noasm.go index 351e325f4f..0a8f9e6f52 100644 --- a/src/runtime/noasm.go +++ b/src/runtime/noasm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Routines that are implemented in assembly in asm_{amd64,386,arm,arm64,ppc64x}.s +// Routines that are implemented in assembly in asm_{amd64,386,arm,arm64,ppc64x,s390x}.s // +build mips64 mips64le diff --git a/src/runtime/os1_linux_generic.go b/src/runtime/os1_linux_generic.go index 2c8b743aeb..50d6d6afb4 100644 --- a/src/runtime/os1_linux_generic.go +++ b/src/runtime/os1_linux_generic.go @@ -4,6 +4,7 @@ // +build !mips64 // +build !mips64le +// +build !s390x // +build linux package runtime diff --git a/src/runtime/os2_linux_generic.go b/src/runtime/os2_linux_generic.go index 01e6c8a5ec..f1a2dd5130 100644 --- a/src/runtime/os2_linux_generic.go +++ b/src/runtime/os2_linux_generic.go @@ -4,6 +4,7 @@ // +build !mips64 // +build !mips64le +// +build !s390x // +build linux package runtime diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 110d99064f..7cfcefc2c2 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -104,7 +104,7 @@ func TestGdbPython(t *testing.T) { // stack frames on RISC architectures. canBackTrace := false switch runtime.GOARCH { - case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le": + case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le", "s390x": canBackTrace = true args = append(args, "-ex", "echo BEGIN goroutine 2 bt\n", diff --git a/src/runtime/unaligned1.go b/src/runtime/unaligned1.go index 6bd9018352..754d63b417 100644 --- a/src/runtime/unaligned1.go +++ b/src/runtime/unaligned1.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le +// +build 386 amd64 amd64p32 arm64 ppc64 ppc64le s390x package runtime -- cgit v1.3 From e095f53e9be7aadc74fddf5532296a438410df40 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 02:38:45 +0000 Subject: runtime: merge os{,2}_windows.go into os1_windows.go. A future CL will rename os1_windows.go to os_windows.go. Change-Id: I223e76002dd1e9c9d1798fb0beac02c7d3bf4812 Reviewed-on: https://go-review.googlesource.com/21564 Run-TryBot: Brad Fitzpatrick Reviewed-by: Minux Ma --- src/runtime/os1_windows.go | 40 +++++++++++++++++++++++++++++++++++++++- src/runtime/os2_windows.go | 19 ------------------- src/runtime/os_windows.go | 32 -------------------------------- 3 files changed, 39 insertions(+), 52 deletions(-) delete mode 100644 src/runtime/os2_windows.go delete mode 100644 src/runtime/os_windows.go (limited to 'src') diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go index a36def0ffe..7244706b92 100644 --- a/src/runtime/os1_windows.go +++ b/src/runtime/os1_windows.go @@ -9,6 +9,11 @@ import ( "unsafe" ) +// TODO(brainman): should not need those +const ( + _NSIG = 65 +) + //go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler%2 "kernel32.dll" //go:cgo_import_dynamic runtime._CloseHandle CloseHandle%1 "kernel32.dll" //go:cgo_import_dynamic runtime._CreateEventA CreateEventA%4 "kernel32.dll" @@ -49,6 +54,8 @@ import ( //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" +type stdFunction unsafe.Pointer + var ( // Following syscalls are available on every Windows PC. // All these variables are set by the Windows executable @@ -102,6 +109,35 @@ var ( _ stdFunction ) +// Function to be called by windows CreateThread +// to start new os thread. +func tstart_stdcall(newm *m) uint32 + +func ctrlhandler(_type uint32) uint32 + +type mOS struct { + waitsema uintptr // semaphore for parking on locks +} + +//go:linkname os_sigpipe os.sigpipe +func os_sigpipe() { + throw("too many writes on closed pipe") +} + +// Stubs so tests can link correctly. These should never be called. +func open(name *byte, mode, perm int32) int32 { + throw("unimplemented") + return -1 +} +func closefd(fd int32) int32 { + throw("unimplemented") + return -1 +} +func read(fd int32, p unsafe.Pointer, n int32) int32 { + throw("unimplemented") + return -1 +} + type sigset struct{} // Call a Windows function with stdcall conventions, @@ -171,8 +207,10 @@ const ( currentThread = ^uintptr(1) // -2 = current thread ) -// in sys_windows_386.s and sys_windows_amd64.s +// in sys_windows_386.s and sys_windows_amd64.s: func externalthreadhandler() +func getlasterror() uint32 +func setlasterror(err uint32) // When loading DLLs, we prefer to use LoadLibraryEx with // LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not diff --git a/src/runtime/os2_windows.go b/src/runtime/os2_windows.go deleted file mode 100644 index a867dfeb64..0000000000 --- a/src/runtime/os2_windows.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2009 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 runtime - -func getlasterror() uint32 -func setlasterror(err uint32) - -// Function to be called by windows CreateThread -// to start new os thread. -func tstart_stdcall(newm *m) uint32 - -func ctrlhandler(_type uint32) uint32 - -// TODO(brainman): should not need those -const ( - _NSIG = 65 -) diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go deleted file mode 100644 index 24b3b8cf29..0000000000 --- a/src/runtime/os_windows.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2014 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 runtime - -import "unsafe" - -type mOS struct { - waitsema uintptr // semaphore for parking on locks -} - -type stdFunction unsafe.Pointer - -//go:linkname os_sigpipe os.sigpipe -func os_sigpipe() { - throw("too many writes on closed pipe") -} - -// Stubs so tests can link correctly. These should never be called. -func open(name *byte, mode, perm int32) int32 { - throw("unimplemented") - return -1 -} -func closefd(fd int32) int32 { - throw("unimplemented") - return -1 -} -func read(fd int32, p unsafe.Pointer, n int32) int32 { - throw("unimplemented") - return -1 -} -- cgit v1.3 From fd2bb1e30ae2f489e05ec022a8457a680663c27d Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 02:39:47 +0000 Subject: runtime: rename os1_windows.go to os_windows.go Change-Id: I11172f3d0e28f17b812e67a4db9cfe513b8e1974 Reviewed-on: https://go-review.googlesource.com/21565 Reviewed-by: Minux Ma --- src/runtime/os1_windows.go | 737 --------------------------------------------- src/runtime/os_windows.go | 737 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 737 insertions(+), 737 deletions(-) delete mode 100644 src/runtime/os1_windows.go create mode 100644 src/runtime/os_windows.go (limited to 'src') diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go deleted file mode 100644 index 7244706b92..0000000000 --- a/src/runtime/os1_windows.go +++ /dev/null @@ -1,737 +0,0 @@ -// Copyright 2009 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 runtime - -import ( - "runtime/internal/atomic" - "unsafe" -) - -// TODO(brainman): should not need those -const ( - _NSIG = 65 -) - -//go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler%2 "kernel32.dll" -//go:cgo_import_dynamic runtime._CloseHandle CloseHandle%1 "kernel32.dll" -//go:cgo_import_dynamic runtime._CreateEventA CreateEventA%4 "kernel32.dll" -//go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll" -//go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll" -//go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll" -//go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW%5 "advapi32.dll" -//go:cgo_import_dynamic runtime._CryptGenRandom CryptGenRandom%3 "advapi32.dll" -//go:cgo_import_dynamic runtime._CryptReleaseContext CryptReleaseContext%2 "advapi32.dll" -//go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll" -//go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll" -//go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll" -//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll" -//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll" -//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll" -//go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll" -//go:cgo_import_dynamic runtime._GetQueuedCompletionStatus GetQueuedCompletionStatus%5 "kernel32.dll" -//go:cgo_import_dynamic runtime._GetStdHandle GetStdHandle%1 "kernel32.dll" -//go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" -//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._NtWaitForSingleObject NtWaitForSingleObject%3 "ntdll.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" -//go:cgo_import_dynamic runtime._SetEvent SetEvent%1 "kernel32.dll" -//go:cgo_import_dynamic runtime._SetProcessPriorityBoost SetProcessPriorityBoost%2 "kernel32.dll" -//go:cgo_import_dynamic runtime._SetThreadPriority SetThreadPriority%2 "kernel32.dll" -//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter%1 "kernel32.dll" -//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll" -//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll" -//go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll" -//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll" -//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll" -//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll" -//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll" -//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" -//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" - -type stdFunction unsafe.Pointer - -var ( - // Following syscalls are available on every Windows PC. - // All these variables are set by the Windows executable - // loader before the Go program starts. - _AddVectoredExceptionHandler, - _CloseHandle, - _CreateEventA, - _CreateIoCompletionPort, - _CreateThread, - _CreateWaitableTimerA, - _CryptAcquireContextW, - _CryptGenRandom, - _CryptReleaseContext, - _DuplicateHandle, - _ExitProcess, - _FreeEnvironmentStringsW, - _GetConsoleMode, - _GetEnvironmentStringsW, - _GetProcAddress, - _GetProcessAffinityMask, - _GetQueuedCompletionStatus, - _GetStdHandle, - _GetSystemInfo, - _GetThreadContext, - _LoadLibraryW, - _LoadLibraryA, - _NtWaitForSingleObject, - _ResumeThread, - _SetConsoleCtrlHandler, - _SetErrorMode, - _SetEvent, - _SetProcessPriorityBoost, - _SetThreadPriority, - _SetUnhandledExceptionFilter, - _SetWaitableTimer, - _SuspendThread, - _SwitchToThread, - _VirtualAlloc, - _VirtualFree, - _WSAGetOverlappedResult, - _WaitForSingleObject, - _WriteConsoleW, - _WriteFile stdFunction - - // Following syscalls are only available on some Windows PCs. - // We will load syscalls, if available, before using them. - _AddDllDirectory, - _AddVectoredContinueHandler, - _GetQueuedCompletionStatusEx, - _LoadLibraryExW, - _ stdFunction -) - -// Function to be called by windows CreateThread -// to start new os thread. -func tstart_stdcall(newm *m) uint32 - -func ctrlhandler(_type uint32) uint32 - -type mOS struct { - waitsema uintptr // semaphore for parking on locks -} - -//go:linkname os_sigpipe os.sigpipe -func os_sigpipe() { - throw("too many writes on closed pipe") -} - -// Stubs so tests can link correctly. These should never be called. -func open(name *byte, mode, perm int32) int32 { - throw("unimplemented") - return -1 -} -func closefd(fd int32) int32 { - throw("unimplemented") - return -1 -} -func read(fd int32, p unsafe.Pointer, n int32) int32 { - throw("unimplemented") - return -1 -} - -type sigset struct{} - -// Call a Windows function with stdcall conventions, -// and switch to os stack during the call. -func asmstdcall(fn unsafe.Pointer) - -var asmstdcallAddr unsafe.Pointer - -func windowsFindfunc(lib uintptr, name []byte) stdFunction { - if name[len(name)-1] != 0 { - throw("usage") - } - f := stdcall2(_GetProcAddress, lib, uintptr(unsafe.Pointer(&name[0]))) - return stdFunction(unsafe.Pointer(f)) -} - -func loadOptionalSyscalls() { - var kernel32dll = []byte("kernel32.dll\000") - k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) - if k32 == 0 { - throw("kernel32.dll not found") - } - _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) - _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) - _GetQueuedCompletionStatusEx = windowsFindfunc(k32, []byte("GetQueuedCompletionStatusEx\000")) - _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) -} - -//go:nosplit -func getLoadLibrary() uintptr { - return uintptr(unsafe.Pointer(_LoadLibraryW)) -} - -//go:nosplit -func getLoadLibraryEx() uintptr { - return uintptr(unsafe.Pointer(_LoadLibraryExW)) -} - -//go:nosplit -func getGetProcAddress() uintptr { - return uintptr(unsafe.Pointer(_GetProcAddress)) -} - -func getproccount() int32 { - var mask, sysmask uintptr - ret := stdcall3(_GetProcessAffinityMask, currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) - if ret != 0 { - n := 0 - maskbits := int(unsafe.Sizeof(mask) * 8) - for i := 0; i < maskbits; i++ { - if mask&(1<= 0x80 { - isASCII = false - break - } - } - - if !isASCII { - var m uint32 - isConsole := stdcall2(_GetConsoleMode, handle, uintptr(unsafe.Pointer(&m))) != 0 - // If this is a console output, various non-unicode code pages can be in use. - // Use the dedicated WriteConsole call to ensure unicode is printed correctly. - if isConsole { - return int32(writeConsole(handle, buf, n)) - } - } - var written uint32 - stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0) - return int32(written) -} - -var ( - utf16ConsoleBack [1000]uint16 - utf16ConsoleBackLock mutex -) - -// writeConsole writes bufLen bytes from buf to the console File. -// It returns the number of bytes written. -func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int { - const surr2 = (surrogateMin + surrogateMax + 1) / 2 - - // Do not use defer for unlock. May cause issues when printing a panic. - lock(&utf16ConsoleBackLock) - - b := (*[1 << 30]byte)(buf)[:bufLen] - s := *(*string)(unsafe.Pointer(&b)) - - utf16tmp := utf16ConsoleBack[:] - - total := len(s) - w := 0 - for len(s) > 0 { - if w >= len(utf16tmp)-2 { - writeConsoleUTF16(handle, utf16tmp[:w]) - w = 0 - } - r, n := charntorune(s) - s = s[n:] - if r < 0x10000 { - utf16tmp[w] = uint16(r) - w++ - } else { - r -= 0x10000 - utf16tmp[w] = surrogateMin + uint16(r>>10)&0x3ff - utf16tmp[w+1] = surr2 + uint16(r)&0x3ff - w += 2 - } - } - writeConsoleUTF16(handle, utf16tmp[:w]) - unlock(&utf16ConsoleBackLock) - return total -} - -// writeConsoleUTF16 is the dedicated windows calls that correctly prints -// to the console regardless of the current code page. Input is utf-16 code points. -// The handle must be a console handle. -func writeConsoleUTF16(handle uintptr, b []uint16) { - l := uint32(len(b)) - if l == 0 { - return - } - var written uint32 - stdcall5(_WriteConsoleW, - handle, - uintptr(unsafe.Pointer(&b[0])), - uintptr(l), - uintptr(unsafe.Pointer(&written)), - 0, - ) - return -} - -//go:nosplit -func semasleep(ns int64) int32 { - // store ms in ns to save stack space - if ns < 0 { - ns = _INFINITE - } else { - ns = int64(timediv(ns, 1000000, nil)) - if ns == 0 { - ns = 1 - } - } - if stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) != 0 { - return -1 // timeout - } - return 0 -} - -//go:nosplit -func semawakeup(mp *m) { - stdcall1(_SetEvent, mp.waitsema) -} - -//go:nosplit -func semacreate(mp *m) { - if mp.waitsema != 0 { - return - } - mp.waitsema = stdcall4(_CreateEventA, 0, 0, 0, 0) -} - -// May run with m.p==nil, so write barriers are not allowed. This -// function is called by newosproc0, so it is also required to -// operate without stack guards. -//go:nowritebarrierc -//go:nosplit -func newosproc(mp *m, stk unsafe.Pointer) { - const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000 - thandle := stdcall6(_CreateThread, 0, 0x20000, - funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)), - _STACK_SIZE_PARAM_IS_A_RESERVATION, 0) - if thandle == 0 { - print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")\n") - throw("runtime.newosproc") - } -} - -// Used by the C library build mode. On Linux this function would allocate a -// stack, but that's not necessary for Windows. No stack guards are present -// and the GC has not been initialized, so write barriers will fail. -//go:nowritebarrierc -//go:nosplit -func newosproc0(mp *m, stk unsafe.Pointer) { - newosproc(mp, stk) -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { -} - -//go:nosplit -func msigsave(mp *m) { -} - -//go:nosplit -func msigrestore(sigmask sigset) { -} - -//go:nosplit -func sigblock() { -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - var thandle uintptr - stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS) - atomic.Storeuintptr(&getg().m.thread, thandle) -} - -// Called from dropm to undo the effect of an minit. -//go:nosplit -func unminit() { - tp := &getg().m.thread - stdcall1(_CloseHandle, *tp) - *tp = 0 -} - -// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/ -type _KSYSTEM_TIME struct { - LowPart uint32 - High1Time int32 - High2Time int32 -} - -const ( - _INTERRUPT_TIME = 0x7ffe0008 - _SYSTEM_TIME = 0x7ffe0014 -) - -//go:nosplit -func systime(addr uintptr) int64 { - timeaddr := (*_KSYSTEM_TIME)(unsafe.Pointer(addr)) - - var t _KSYSTEM_TIME - for i := 1; i < 10000; i++ { - // these fields must be read in that order (see URL above) - t.High1Time = timeaddr.High1Time - t.LowPart = timeaddr.LowPart - t.High2Time = timeaddr.High2Time - if t.High1Time == t.High2Time { - return int64(t.High1Time)<<32 | int64(t.LowPart) - } - if (i % 100) == 0 { - osyield() - } - } - systemstack(func() { - throw("interrupt/system time is changing too fast") - }) - return 0 -} - -//go:nosplit -func unixnano() int64 { - return (systime(_SYSTEM_TIME) - 116444736000000000) * 100 -} - -//go:nosplit -func nanotime() int64 { - return systime(_INTERRUPT_TIME) * 100 -} - -// Calling stdcall on os stack. -// May run during STW, so write barriers are not allowed. -//go:nowritebarrier -//go:nosplit -func stdcall(fn stdFunction) uintptr { - gp := getg() - mp := gp.m - mp.libcall.fn = uintptr(unsafe.Pointer(fn)) - - if mp.profilehz != 0 { - // leave pc/sp for cpu profiler - mp.libcallg.set(gp) - mp.libcallpc = getcallerpc(unsafe.Pointer(&fn)) - // sp must be the last, because once async cpu profiler finds - // all three values to be non-zero, it will use them - mp.libcallsp = getcallersp(unsafe.Pointer(&fn)) - } - asmcgocall(asmstdcallAddr, unsafe.Pointer(&mp.libcall)) - mp.libcallsp = 0 - return mp.libcall.r1 -} - -//go:nosplit -func stdcall0(fn stdFunction) uintptr { - mp := getg().m - mp.libcall.n = 0 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&fn))) // it's unused but must be non-nil, otherwise crashes - return stdcall(fn) -} - -//go:nosplit -func stdcall1(fn stdFunction, a0 uintptr) uintptr { - mp := getg().m - mp.libcall.n = 1 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) - return stdcall(fn) -} - -//go:nosplit -func stdcall2(fn stdFunction, a0, a1 uintptr) uintptr { - mp := getg().m - mp.libcall.n = 2 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) - return stdcall(fn) -} - -//go:nosplit -func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr { - mp := getg().m - mp.libcall.n = 3 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) - return stdcall(fn) -} - -//go:nosplit -func stdcall4(fn stdFunction, a0, a1, a2, a3 uintptr) uintptr { - mp := getg().m - mp.libcall.n = 4 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) - return stdcall(fn) -} - -//go:nosplit -func stdcall5(fn stdFunction, a0, a1, a2, a3, a4 uintptr) uintptr { - mp := getg().m - mp.libcall.n = 5 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) - return stdcall(fn) -} - -//go:nosplit -func stdcall6(fn stdFunction, a0, a1, a2, a3, a4, a5 uintptr) uintptr { - mp := getg().m - mp.libcall.n = 6 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) - return stdcall(fn) -} - -//go:nosplit -func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr { - mp := getg().m - mp.libcall.n = 7 - mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) - return stdcall(fn) -} - -// in sys_windows_386.s and sys_windows_amd64.s -func onosstack(fn unsafe.Pointer, arg uint32) -func usleep2(usec uint32) -func switchtothread() - -var usleep2Addr unsafe.Pointer -var switchtothreadAddr unsafe.Pointer - -//go:nosplit -func osyield() { - onosstack(switchtothreadAddr, 0) -} - -//go:nosplit -func usleep(us uint32) { - // Have 1us units; want 100ns units. - onosstack(usleep2Addr, 10*us) -} - -func ctrlhandler1(_type uint32) uint32 { - var s uint32 - - switch _type { - case _CTRL_C_EVENT, _CTRL_BREAK_EVENT: - s = _SIGINT - default: - return 0 - } - - if sigsend(s) { - return 1 - } - exit(2) // SIGINT, SIGTERM, etc - return 0 -} - -// in sys_windows_386.s and sys_windows_amd64.s -func profileloop() - -var profiletimer uintptr - -func profilem(mp *m) { - var r *context - rbuf := make([]byte, unsafe.Sizeof(*r)+15) - - tls := &mp.tls[0] - gp := *((**g)(unsafe.Pointer(tls))) - - // align Context to 16 bytes - r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15)) - r.contextflags = _CONTEXT_CONTROL - stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r))) - sigprof(r.ip(), r.sp(), 0, gp, mp) -} - -func profileloop1(param uintptr) uint32 { - stdcall2(_SetThreadPriority, currentThread, _THREAD_PRIORITY_HIGHEST) - - for { - stdcall2(_WaitForSingleObject, profiletimer, _INFINITE) - first := (*m)(atomic.Loadp(unsafe.Pointer(&allm))) - for mp := first; mp != nil; mp = mp.alllink { - thread := atomic.Loaduintptr(&mp.thread) - // Do not profile threads blocked on Notes, - // this includes idle worker threads, - // idle timer thread, idle heap scavenger, etc. - if thread == 0 || mp.profilehz == 0 || mp.blocked { - continue - } - stdcall1(_SuspendThread, thread) - if mp.profilehz != 0 && !mp.blocked { - profilem(mp) - } - stdcall1(_ResumeThread, thread) - } - } -} - -var cpuprofilerlock mutex - -func resetcpuprofiler(hz int32) { - lock(&cpuprofilerlock) - if profiletimer == 0 { - timer := stdcall3(_CreateWaitableTimerA, 0, 0, 0) - atomic.Storeuintptr(&profiletimer, timer) - thread := stdcall6(_CreateThread, 0, 0, funcPC(profileloop), 0, 0, 0) - stdcall2(_SetThreadPriority, thread, _THREAD_PRIORITY_HIGHEST) - stdcall1(_CloseHandle, thread) - } - unlock(&cpuprofilerlock) - - ms := int32(0) - due := ^int64(^uint64(1 << 63)) - if hz > 0 { - ms = 1000 / hz - if ms == 0 { - ms = 1 - } - due = int64(ms) * -10000 - } - stdcall6(_SetWaitableTimer, profiletimer, uintptr(unsafe.Pointer(&due)), uintptr(ms), 0, 0, 0) - atomic.Store((*uint32)(unsafe.Pointer(&getg().m.profilehz)), uint32(hz)) -} - -func memlimit() uintptr { - return 0 -} diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go new file mode 100644 index 0000000000..7244706b92 --- /dev/null +++ b/src/runtime/os_windows.go @@ -0,0 +1,737 @@ +// Copyright 2009 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 runtime + +import ( + "runtime/internal/atomic" + "unsafe" +) + +// TODO(brainman): should not need those +const ( + _NSIG = 65 +) + +//go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler%2 "kernel32.dll" +//go:cgo_import_dynamic runtime._CloseHandle CloseHandle%1 "kernel32.dll" +//go:cgo_import_dynamic runtime._CreateEventA CreateEventA%4 "kernel32.dll" +//go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll" +//go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll" +//go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll" +//go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW%5 "advapi32.dll" +//go:cgo_import_dynamic runtime._CryptGenRandom CryptGenRandom%3 "advapi32.dll" +//go:cgo_import_dynamic runtime._CryptReleaseContext CryptReleaseContext%2 "advapi32.dll" +//go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll" +//go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll" +//go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetQueuedCompletionStatus GetQueuedCompletionStatus%5 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetStdHandle GetStdHandle%1 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 "kernel32.dll" +//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._NtWaitForSingleObject NtWaitForSingleObject%3 "ntdll.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" +//go:cgo_import_dynamic runtime._SetEvent SetEvent%1 "kernel32.dll" +//go:cgo_import_dynamic runtime._SetProcessPriorityBoost SetProcessPriorityBoost%2 "kernel32.dll" +//go:cgo_import_dynamic runtime._SetThreadPriority SetThreadPriority%2 "kernel32.dll" +//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter%1 "kernel32.dll" +//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll" +//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll" +//go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll" +//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll" +//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll" +//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll" +//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll" +//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" +//go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" + +type stdFunction unsafe.Pointer + +var ( + // Following syscalls are available on every Windows PC. + // All these variables are set by the Windows executable + // loader before the Go program starts. + _AddVectoredExceptionHandler, + _CloseHandle, + _CreateEventA, + _CreateIoCompletionPort, + _CreateThread, + _CreateWaitableTimerA, + _CryptAcquireContextW, + _CryptGenRandom, + _CryptReleaseContext, + _DuplicateHandle, + _ExitProcess, + _FreeEnvironmentStringsW, + _GetConsoleMode, + _GetEnvironmentStringsW, + _GetProcAddress, + _GetProcessAffinityMask, + _GetQueuedCompletionStatus, + _GetStdHandle, + _GetSystemInfo, + _GetThreadContext, + _LoadLibraryW, + _LoadLibraryA, + _NtWaitForSingleObject, + _ResumeThread, + _SetConsoleCtrlHandler, + _SetErrorMode, + _SetEvent, + _SetProcessPriorityBoost, + _SetThreadPriority, + _SetUnhandledExceptionFilter, + _SetWaitableTimer, + _SuspendThread, + _SwitchToThread, + _VirtualAlloc, + _VirtualFree, + _WSAGetOverlappedResult, + _WaitForSingleObject, + _WriteConsoleW, + _WriteFile stdFunction + + // Following syscalls are only available on some Windows PCs. + // We will load syscalls, if available, before using them. + _AddDllDirectory, + _AddVectoredContinueHandler, + _GetQueuedCompletionStatusEx, + _LoadLibraryExW, + _ stdFunction +) + +// Function to be called by windows CreateThread +// to start new os thread. +func tstart_stdcall(newm *m) uint32 + +func ctrlhandler(_type uint32) uint32 + +type mOS struct { + waitsema uintptr // semaphore for parking on locks +} + +//go:linkname os_sigpipe os.sigpipe +func os_sigpipe() { + throw("too many writes on closed pipe") +} + +// Stubs so tests can link correctly. These should never be called. +func open(name *byte, mode, perm int32) int32 { + throw("unimplemented") + return -1 +} +func closefd(fd int32) int32 { + throw("unimplemented") + return -1 +} +func read(fd int32, p unsafe.Pointer, n int32) int32 { + throw("unimplemented") + return -1 +} + +type sigset struct{} + +// Call a Windows function with stdcall conventions, +// and switch to os stack during the call. +func asmstdcall(fn unsafe.Pointer) + +var asmstdcallAddr unsafe.Pointer + +func windowsFindfunc(lib uintptr, name []byte) stdFunction { + if name[len(name)-1] != 0 { + throw("usage") + } + f := stdcall2(_GetProcAddress, lib, uintptr(unsafe.Pointer(&name[0]))) + return stdFunction(unsafe.Pointer(f)) +} + +func loadOptionalSyscalls() { + var kernel32dll = []byte("kernel32.dll\000") + k32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0]))) + if k32 == 0 { + throw("kernel32.dll not found") + } + _AddDllDirectory = windowsFindfunc(k32, []byte("AddDllDirectory\000")) + _AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000")) + _GetQueuedCompletionStatusEx = windowsFindfunc(k32, []byte("GetQueuedCompletionStatusEx\000")) + _LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000")) +} + +//go:nosplit +func getLoadLibrary() uintptr { + return uintptr(unsafe.Pointer(_LoadLibraryW)) +} + +//go:nosplit +func getLoadLibraryEx() uintptr { + return uintptr(unsafe.Pointer(_LoadLibraryExW)) +} + +//go:nosplit +func getGetProcAddress() uintptr { + return uintptr(unsafe.Pointer(_GetProcAddress)) +} + +func getproccount() int32 { + var mask, sysmask uintptr + ret := stdcall3(_GetProcessAffinityMask, currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) + if ret != 0 { + n := 0 + maskbits := int(unsafe.Sizeof(mask) * 8) + for i := 0; i < maskbits; i++ { + if mask&(1<= 0x80 { + isASCII = false + break + } + } + + if !isASCII { + var m uint32 + isConsole := stdcall2(_GetConsoleMode, handle, uintptr(unsafe.Pointer(&m))) != 0 + // If this is a console output, various non-unicode code pages can be in use. + // Use the dedicated WriteConsole call to ensure unicode is printed correctly. + if isConsole { + return int32(writeConsole(handle, buf, n)) + } + } + var written uint32 + stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0) + return int32(written) +} + +var ( + utf16ConsoleBack [1000]uint16 + utf16ConsoleBackLock mutex +) + +// writeConsole writes bufLen bytes from buf to the console File. +// It returns the number of bytes written. +func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int { + const surr2 = (surrogateMin + surrogateMax + 1) / 2 + + // Do not use defer for unlock. May cause issues when printing a panic. + lock(&utf16ConsoleBackLock) + + b := (*[1 << 30]byte)(buf)[:bufLen] + s := *(*string)(unsafe.Pointer(&b)) + + utf16tmp := utf16ConsoleBack[:] + + total := len(s) + w := 0 + for len(s) > 0 { + if w >= len(utf16tmp)-2 { + writeConsoleUTF16(handle, utf16tmp[:w]) + w = 0 + } + r, n := charntorune(s) + s = s[n:] + if r < 0x10000 { + utf16tmp[w] = uint16(r) + w++ + } else { + r -= 0x10000 + utf16tmp[w] = surrogateMin + uint16(r>>10)&0x3ff + utf16tmp[w+1] = surr2 + uint16(r)&0x3ff + w += 2 + } + } + writeConsoleUTF16(handle, utf16tmp[:w]) + unlock(&utf16ConsoleBackLock) + return total +} + +// writeConsoleUTF16 is the dedicated windows calls that correctly prints +// to the console regardless of the current code page. Input is utf-16 code points. +// The handle must be a console handle. +func writeConsoleUTF16(handle uintptr, b []uint16) { + l := uint32(len(b)) + if l == 0 { + return + } + var written uint32 + stdcall5(_WriteConsoleW, + handle, + uintptr(unsafe.Pointer(&b[0])), + uintptr(l), + uintptr(unsafe.Pointer(&written)), + 0, + ) + return +} + +//go:nosplit +func semasleep(ns int64) int32 { + // store ms in ns to save stack space + if ns < 0 { + ns = _INFINITE + } else { + ns = int64(timediv(ns, 1000000, nil)) + if ns == 0 { + ns = 1 + } + } + if stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) != 0 { + return -1 // timeout + } + return 0 +} + +//go:nosplit +func semawakeup(mp *m) { + stdcall1(_SetEvent, mp.waitsema) +} + +//go:nosplit +func semacreate(mp *m) { + if mp.waitsema != 0 { + return + } + mp.waitsema = stdcall4(_CreateEventA, 0, 0, 0, 0) +} + +// May run with m.p==nil, so write barriers are not allowed. This +// function is called by newosproc0, so it is also required to +// operate without stack guards. +//go:nowritebarrierc +//go:nosplit +func newosproc(mp *m, stk unsafe.Pointer) { + const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000 + thandle := stdcall6(_CreateThread, 0, 0x20000, + funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)), + _STACK_SIZE_PARAM_IS_A_RESERVATION, 0) + if thandle == 0 { + print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")\n") + throw("runtime.newosproc") + } +} + +// Used by the C library build mode. On Linux this function would allocate a +// stack, but that's not necessary for Windows. No stack guards are present +// and the GC has not been initialized, so write barriers will fail. +//go:nowritebarrierc +//go:nosplit +func newosproc0(mp *m, stk unsafe.Pointer) { + newosproc(mp, stk) +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { +} + +//go:nosplit +func msigsave(mp *m) { +} + +//go:nosplit +func msigrestore(sigmask sigset) { +} + +//go:nosplit +func sigblock() { +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + var thandle uintptr + stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS) + atomic.Storeuintptr(&getg().m.thread, thandle) +} + +// Called from dropm to undo the effect of an minit. +//go:nosplit +func unminit() { + tp := &getg().m.thread + stdcall1(_CloseHandle, *tp) + *tp = 0 +} + +// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/ +type _KSYSTEM_TIME struct { + LowPart uint32 + High1Time int32 + High2Time int32 +} + +const ( + _INTERRUPT_TIME = 0x7ffe0008 + _SYSTEM_TIME = 0x7ffe0014 +) + +//go:nosplit +func systime(addr uintptr) int64 { + timeaddr := (*_KSYSTEM_TIME)(unsafe.Pointer(addr)) + + var t _KSYSTEM_TIME + for i := 1; i < 10000; i++ { + // these fields must be read in that order (see URL above) + t.High1Time = timeaddr.High1Time + t.LowPart = timeaddr.LowPart + t.High2Time = timeaddr.High2Time + if t.High1Time == t.High2Time { + return int64(t.High1Time)<<32 | int64(t.LowPart) + } + if (i % 100) == 0 { + osyield() + } + } + systemstack(func() { + throw("interrupt/system time is changing too fast") + }) + return 0 +} + +//go:nosplit +func unixnano() int64 { + return (systime(_SYSTEM_TIME) - 116444736000000000) * 100 +} + +//go:nosplit +func nanotime() int64 { + return systime(_INTERRUPT_TIME) * 100 +} + +// Calling stdcall on os stack. +// May run during STW, so write barriers are not allowed. +//go:nowritebarrier +//go:nosplit +func stdcall(fn stdFunction) uintptr { + gp := getg() + mp := gp.m + mp.libcall.fn = uintptr(unsafe.Pointer(fn)) + + if mp.profilehz != 0 { + // leave pc/sp for cpu profiler + mp.libcallg.set(gp) + mp.libcallpc = getcallerpc(unsafe.Pointer(&fn)) + // sp must be the last, because once async cpu profiler finds + // all three values to be non-zero, it will use them + mp.libcallsp = getcallersp(unsafe.Pointer(&fn)) + } + asmcgocall(asmstdcallAddr, unsafe.Pointer(&mp.libcall)) + mp.libcallsp = 0 + return mp.libcall.r1 +} + +//go:nosplit +func stdcall0(fn stdFunction) uintptr { + mp := getg().m + mp.libcall.n = 0 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&fn))) // it's unused but must be non-nil, otherwise crashes + return stdcall(fn) +} + +//go:nosplit +func stdcall1(fn stdFunction, a0 uintptr) uintptr { + mp := getg().m + mp.libcall.n = 1 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) + return stdcall(fn) +} + +//go:nosplit +func stdcall2(fn stdFunction, a0, a1 uintptr) uintptr { + mp := getg().m + mp.libcall.n = 2 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) + return stdcall(fn) +} + +//go:nosplit +func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr { + mp := getg().m + mp.libcall.n = 3 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) + return stdcall(fn) +} + +//go:nosplit +func stdcall4(fn stdFunction, a0, a1, a2, a3 uintptr) uintptr { + mp := getg().m + mp.libcall.n = 4 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) + return stdcall(fn) +} + +//go:nosplit +func stdcall5(fn stdFunction, a0, a1, a2, a3, a4 uintptr) uintptr { + mp := getg().m + mp.libcall.n = 5 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) + return stdcall(fn) +} + +//go:nosplit +func stdcall6(fn stdFunction, a0, a1, a2, a3, a4, a5 uintptr) uintptr { + mp := getg().m + mp.libcall.n = 6 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) + return stdcall(fn) +} + +//go:nosplit +func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr { + mp := getg().m + mp.libcall.n = 7 + mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0))) + return stdcall(fn) +} + +// in sys_windows_386.s and sys_windows_amd64.s +func onosstack(fn unsafe.Pointer, arg uint32) +func usleep2(usec uint32) +func switchtothread() + +var usleep2Addr unsafe.Pointer +var switchtothreadAddr unsafe.Pointer + +//go:nosplit +func osyield() { + onosstack(switchtothreadAddr, 0) +} + +//go:nosplit +func usleep(us uint32) { + // Have 1us units; want 100ns units. + onosstack(usleep2Addr, 10*us) +} + +func ctrlhandler1(_type uint32) uint32 { + var s uint32 + + switch _type { + case _CTRL_C_EVENT, _CTRL_BREAK_EVENT: + s = _SIGINT + default: + return 0 + } + + if sigsend(s) { + return 1 + } + exit(2) // SIGINT, SIGTERM, etc + return 0 +} + +// in sys_windows_386.s and sys_windows_amd64.s +func profileloop() + +var profiletimer uintptr + +func profilem(mp *m) { + var r *context + rbuf := make([]byte, unsafe.Sizeof(*r)+15) + + tls := &mp.tls[0] + gp := *((**g)(unsafe.Pointer(tls))) + + // align Context to 16 bytes + r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15)) + r.contextflags = _CONTEXT_CONTROL + stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r))) + sigprof(r.ip(), r.sp(), 0, gp, mp) +} + +func profileloop1(param uintptr) uint32 { + stdcall2(_SetThreadPriority, currentThread, _THREAD_PRIORITY_HIGHEST) + + for { + stdcall2(_WaitForSingleObject, profiletimer, _INFINITE) + first := (*m)(atomic.Loadp(unsafe.Pointer(&allm))) + for mp := first; mp != nil; mp = mp.alllink { + thread := atomic.Loaduintptr(&mp.thread) + // Do not profile threads blocked on Notes, + // this includes idle worker threads, + // idle timer thread, idle heap scavenger, etc. + if thread == 0 || mp.profilehz == 0 || mp.blocked { + continue + } + stdcall1(_SuspendThread, thread) + if mp.profilehz != 0 && !mp.blocked { + profilem(mp) + } + stdcall1(_ResumeThread, thread) + } + } +} + +var cpuprofilerlock mutex + +func resetcpuprofiler(hz int32) { + lock(&cpuprofilerlock) + if profiletimer == 0 { + timer := stdcall3(_CreateWaitableTimerA, 0, 0, 0) + atomic.Storeuintptr(&profiletimer, timer) + thread := stdcall6(_CreateThread, 0, 0, funcPC(profileloop), 0, 0, 0) + stdcall2(_SetThreadPriority, thread, _THREAD_PRIORITY_HIGHEST) + stdcall1(_CloseHandle, thread) + } + unlock(&cpuprofilerlock) + + ms := int32(0) + due := ^int64(^uint64(1 << 63)) + if hz > 0 { + ms = 1000 / hz + if ms == 0 { + ms = 1 + } + due = int64(ms) * -10000 + } + stdcall6(_SetWaitableTimer, profiletimer, uintptr(unsafe.Pointer(&due)), uintptr(ms), 0, 0, 0) + atomic.Store((*uint32)(unsafe.Pointer(&getg().m.profilehz)), uint32(hz)) +} + +func memlimit() uintptr { + return 0 +} -- cgit v1.3 From 8455f3a3d5f2879e8574882978e7646db1ebabb5 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 04:38:00 +0000 Subject: os: consolidate os{1,2}_*.go files Change-Id: I463ca59f486b2842f67f151a55f530ee10663830 Reviewed-on: https://go-review.googlesource.com/21568 Run-TryBot: Brad Fitzpatrick Reviewed-by: Dave Cheney Reviewed-by: Minux Ma TryBot-Result: Gobot Gobot --- src/runtime/os1_dragonfly.go | 270 ------------------------------------- src/runtime/os1_linux_generic.go | 28 ---- src/runtime/os1_linux_mips64x.go | 26 ---- src/runtime/os1_netbsd.go | 275 ------------------------------------- src/runtime/os1_netbsd_386.go | 16 --- src/runtime/os1_netbsd_amd64.go | 16 --- src/runtime/os2_dragonfly.go | 15 --- src/runtime/os2_linux_generic.go | 30 ----- src/runtime/os2_linux_mips64x.go | 25 ---- src/runtime/os2_netbsd.go | 18 --- src/runtime/os_dragonfly.go | 273 +++++++++++++++++++++++++++++++++++++ src/runtime/os_linux_generic.go | 48 +++++++ src/runtime/os_linux_mips64x.go | 37 ++++- src/runtime/os_netbsd.go | 283 ++++++++++++++++++++++++++++++++++++++- src/runtime/os_netbsd_386.go | 16 +++ src/runtime/os_netbsd_amd64.go | 16 +++ 16 files changed, 671 insertions(+), 721 deletions(-) delete mode 100644 src/runtime/os1_dragonfly.go delete mode 100644 src/runtime/os1_linux_generic.go delete mode 100644 src/runtime/os1_linux_mips64x.go delete mode 100644 src/runtime/os1_netbsd.go delete mode 100644 src/runtime/os1_netbsd_386.go delete mode 100644 src/runtime/os1_netbsd_amd64.go delete mode 100644 src/runtime/os2_dragonfly.go delete mode 100644 src/runtime/os2_linux_generic.go delete mode 100644 src/runtime/os2_linux_mips64x.go delete mode 100644 src/runtime/os2_netbsd.go create mode 100644 src/runtime/os_linux_generic.go create mode 100644 src/runtime/os_netbsd_386.go create mode 100644 src/runtime/os_netbsd_amd64.go (limited to 'src') diff --git a/src/runtime/os1_dragonfly.go b/src/runtime/os1_dragonfly.go deleted file mode 100644 index d7044ae4b0..0000000000 --- a/src/runtime/os1_dragonfly.go +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2011 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 runtime - -import "unsafe" - -// From DragonFly's -const ( - _CTL_HW = 6 - _HW_NCPU = 3 -) - -var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}} - -func getncpu() int32 { - mib := [2]uint32{_CTL_HW, _HW_NCPU} - out := uint32(0) - nout := unsafe.Sizeof(out) - ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) - if ret >= 0 { - return int32(out) - } - return 1 -} - -//go:nosplit -func futexsleep(addr *uint32, val uint32, ns int64) { - systemstack(func() { - futexsleep1(addr, val, ns) - }) -} - -func futexsleep1(addr *uint32, val uint32, ns int64) { - var timeout int32 - if ns >= 0 { - // The timeout is specified in microseconds - ensure that we - // do not end up dividing to zero, which would put us to sleep - // indefinitely... - timeout = timediv(ns, 1000, nil) - if timeout == 0 { - timeout = 1 - } - } - - // sys_umtx_sleep will return EWOULDBLOCK (EAGAIN) when the timeout - // expires or EBUSY if the mutex value does not match. - ret := sys_umtx_sleep(addr, int32(val), timeout) - if ret >= 0 || ret == -_EINTR || ret == -_EAGAIN || ret == -_EBUSY { - return - } - - print("umtx_sleep addr=", addr, " val=", val, " ret=", ret, "\n") - *(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005 -} - -//go:nosplit -func futexwakeup(addr *uint32, cnt uint32) { - ret := sys_umtx_wakeup(addr, int32(cnt)) - if ret >= 0 { - return - } - - systemstack(func() { - print("umtx_wake_addr=", addr, " ret=", ret, "\n") - *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006 - }) -} - -func lwp_start(uintptr) - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " lwp_start=", funcPC(lwp_start), " id=", mp.id, " ostk=", &mp, "\n") - } - - var oset sigset - sigprocmask(_SIG_SETMASK, &sigset_all, &oset) - - params := lwpparams{ - start_func: funcPC(lwp_start), - arg: unsafe.Pointer(mp), - stack: uintptr(stk), - tid1: unsafe.Pointer(&mp.procid), - tid2: nil, - } - - lwp_create(¶ms) - sigprocmask(_SIG_SETMASK, &oset, nil) -} - -func osinit() { - ncpu = getncpu() -} - -var urandom_dev = []byte("/dev/urandom\x00") - -//go:nosplit -func getRandomData(r []byte) { - fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) - n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) - closefd(fd) - extendRandom(r, int(n)) -} - -func goenvs() { - goenvs_unix() -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - mp.gsignal = malg(32 * 1024) - mp.gsignal.m = mp -} - -//go:nosplit -func msigsave(mp *m) { - sigprocmask(_SIG_SETMASK, nil, &mp.sigmask) -} - -//go:nosplit -func msigrestore(sigmask sigset) { - sigprocmask(_SIG_SETMASK, &sigmask, nil) -} - -//go:nosplit -func sigblock() { - sigprocmask(_SIG_SETMASK, &sigset_all, nil) -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - _g_ := getg() - - // m.procid is a uint64, but lwp_start writes an int32. Fix it up. - _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid))) - - // Initialize signal handling. - - // On DragonFly a thread created by pthread_create inherits - // the signal stack of the creating thread. We always create - // a new signal stack here, to avoid having two Go threads - // using the same signal stack. This breaks the case of a - // thread created in C that calls sigaltstack and then calls a - // Go function, because we will lose track of the C code's - // sigaltstack, but it's the best we can do. - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true - - // restore signal mask from m.sigmask and unblock essential signals - nmask := _g_.m.sigmask - for i := range sigtable { - if sigtable[i].flags&_SigUnblock != 0 { - nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) - } - } - sigprocmask(_SIG_SETMASK, &nmask, nil) -} - -// Called from dropm to undo the effect of an minit. -//go:nosplit -func unminit() { - if getg().m.newSigstack { - signalstack(nil) - } -} - -func memlimit() uintptr { - /* - TODO: Convert to Go when something actually uses the result. - - Rlimit rl; - extern byte runtime·text[], runtime·end[]; - uintptr used; - - if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) - return 0; - if(rl.rlim_cur >= 0x7fffffff) - return 0; - - // Estimate our VM footprint excluding the heap. - // Not an exact science: use size of binary plus - // some room for thread stacks. - used = runtime·end - runtime·text + (64<<20); - if(used >= rl.rlim_cur) - return 0; - - // If there's not at least 16 MB left, we're probably - // not going to be able to do much. Treat as no limit. - rl.rlim_cur -= used; - if(rl.rlim_cur < (16<<20)) - return 0; - - return rl.rlim_cur - used; - */ - return 0 -} - -func sigtramp() - -type sigactiont struct { - sa_sigaction uintptr - sa_flags int32 - sa_mask sigset -} - -//go:nosplit -//go:nowritebarrierrec -func setsig(i int32, fn uintptr, restart bool) { - var sa sigactiont - sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK - if restart { - sa.sa_flags |= _SA_RESTART - } - sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) - } - sa.sa_sigaction = fn - sigaction(i, &sa, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func setsigstack(i int32) { - throw("setsigstack") -} - -//go:nosplit -//go:nowritebarrierrec -func getsig(i int32) uintptr { - var sa sigactiont - sigaction(i, nil, &sa) - if sa.sa_sigaction == funcPC(sigtramp) { - return funcPC(sighandler) - } - return sa.sa_sigaction -} - -//go:nosplit -func signalstack(s *stack) { - var st sigaltstackt - if s == nil { - st.ss_flags = _SS_DISABLE - } else { - st.ss_sp = s.lo - st.ss_size = s.hi - s.lo - st.ss_flags = 0 - } - sigaltstack(&st, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func updatesigmask(m sigmask) { - var mask sigset - copy(mask.__bits[:], m[:]) - sigprocmask(_SIG_SETMASK, &mask, nil) -} - -func unblocksig(sig int32) { - var mask sigset - mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31) - sigprocmask(_SIG_UNBLOCK, &mask, nil) -} diff --git a/src/runtime/os1_linux_generic.go b/src/runtime/os1_linux_generic.go deleted file mode 100644 index 50d6d6afb4..0000000000 --- a/src/runtime/os1_linux_generic.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2009 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 !mips64 -// +build !mips64le -// +build !s390x -// +build linux - -package runtime - -var sigset_all = sigset{^uint32(0), ^uint32(0)} - -func sigaddset(mask *sigset, i int) { - (*mask)[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31) -} - -func sigdelset(mask *sigset, i int) { - (*mask)[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) -} - -func sigfillset(mask *uint64) { - *mask = ^uint64(0) -} - -func sigcopyset(mask *sigset, m sigmask) { - copy((*mask)[:], m[:]) -} diff --git a/src/runtime/os1_linux_mips64x.go b/src/runtime/os1_linux_mips64x.go deleted file mode 100644 index 701e979102..0000000000 --- a/src/runtime/os1_linux_mips64x.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 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 mips64 mips64le -// +build linux - -package runtime - -var sigset_all = sigset{^uint64(0), ^uint64(0)} - -func sigaddset(mask *sigset, i int) { - (*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63) -} - -func sigdelset(mask *sigset, i int) { - (*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63) -} - -func sigfillset(mask *[2]uint64) { - (*mask)[0], (*mask)[1] = ^uint64(0), ^uint64(0) -} - -func sigcopyset(mask *sigset, m sigmask) { - (*mask)[0] = uint64(m[0]) | uint64(m[1])<<32 -} diff --git a/src/runtime/os1_netbsd.go b/src/runtime/os1_netbsd.go deleted file mode 100644 index 3c3b64186d..0000000000 --- a/src/runtime/os1_netbsd.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2011 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 runtime - -import ( - "runtime/internal/atomic" - "unsafe" -) - -const ( - _ESRCH = 3 - _ETIMEDOUT = 60 - - // From NetBSD's - _CLOCK_REALTIME = 0 - _CLOCK_VIRTUAL = 1 - _CLOCK_PROF = 2 - _CLOCK_MONOTONIC = 3 -) - -var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}} - -// From NetBSD's -const ( - _CTL_HW = 6 - _HW_NCPU = 3 -) - -func getncpu() int32 { - mib := [2]uint32{_CTL_HW, _HW_NCPU} - out := uint32(0) - nout := unsafe.Sizeof(out) - ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) - if ret >= 0 { - return int32(out) - } - return 1 -} - -//go:nosplit -func semacreate(mp *m) { -} - -//go:nosplit -func semasleep(ns int64) int32 { - _g_ := getg() - - // Compute sleep deadline. - var tsp *timespec - if ns >= 0 { - var ts timespec - var nsec int32 - ns += nanotime() - ts.set_sec(timediv(ns, 1000000000, &nsec)) - ts.set_nsec(nsec) - tsp = &ts - } - - for { - v := atomic.Load(&_g_.m.waitsemacount) - if v > 0 { - if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { - return 0 // semaphore acquired - } - continue - } - - // Sleep until unparked by semawakeup or timeout. - ret := lwp_park(tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil) - if ret == _ETIMEDOUT { - return -1 - } - } -} - -//go:nosplit -func semawakeup(mp *m) { - atomic.Xadd(&mp.waitsemacount, 1) - // From NetBSD's _lwp_unpark(2) manual: - // "If the target LWP is not currently waiting, it will return - // immediately upon the next call to _lwp_park()." - ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount)) - if ret != 0 && ret != _ESRCH { - // semawakeup can be called on signal stack. - systemstack(func() { - print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n") - }) - } -} - -// May run with m.p==nil, so write barriers are not allowed. -//go:nowritebarrier -func newosproc(mp *m, stk unsafe.Pointer) { - if false { - print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n") - } - - var uc ucontextt - getcontext(unsafe.Pointer(&uc)) - - uc.uc_flags = _UC_SIGMASK | _UC_CPU - uc.uc_link = nil - uc.uc_sigmask = sigset_all - - lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp.g0, funcPC(netbsdMstart)) - - ret := lwp_create(unsafe.Pointer(&uc), 0, unsafe.Pointer(&mp.procid)) - if ret < 0 { - print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n") - throw("runtime.newosproc") - } -} - -// netbsdMStart is the function call that starts executing a newly -// created thread. On NetBSD, a new thread inherits the signal stack -// of the creating thread. That confuses minit, so we remove that -// signal stack here before calling the regular mstart. It's a bit -// baroque to remove a signal stack here only to add one in minit, but -// it's a simple change that keeps NetBSD working like other OS's. -// At this point all signals are blocked, so there is no race. -//go:nosplit -func netbsdMstart() { - signalstack(nil) - mstart() -} - -func osinit() { - ncpu = getncpu() -} - -var urandom_dev = []byte("/dev/urandom\x00") - -//go:nosplit -func getRandomData(r []byte) { - fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) - n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) - closefd(fd) - extendRandom(r, int(n)) -} - -func goenvs() { - goenvs_unix() -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the parent thread (main thread in case of bootstrap), can allocate memory. -func mpreinit(mp *m) { - mp.gsignal = malg(32 * 1024) - mp.gsignal.m = mp -} - -//go:nosplit -func msigsave(mp *m) { - sigprocmask(_SIG_SETMASK, nil, &mp.sigmask) -} - -//go:nosplit -func msigrestore(sigmask sigset) { - sigprocmask(_SIG_SETMASK, &sigmask, nil) -} - -//go:nosplit -func sigblock() { - sigprocmask(_SIG_SETMASK, &sigset_all, nil) -} - -// Called to initialize a new m (including the bootstrap m). -// Called on the new thread, cannot allocate memory. -func minit() { - _g_ := getg() - _g_.m.procid = uint64(lwp_self()) - - // Initialize signal handling. - - // On NetBSD a thread created by pthread_create inherits the - // signal stack of the creating thread. We always create a - // new signal stack here, to avoid having two Go threads using - // the same signal stack. This breaks the case of a thread - // created in C that calls sigaltstack and then calls a Go - // function, because we will lose track of the C code's - // sigaltstack, but it's the best we can do. - signalstack(&_g_.m.gsignal.stack) - _g_.m.newSigstack = true - - // restore signal mask from m.sigmask and unblock essential signals - nmask := _g_.m.sigmask - for i := range sigtable { - if sigtable[i].flags&_SigUnblock != 0 { - nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) - } - } - sigprocmask(_SIG_SETMASK, &nmask, nil) -} - -// Called from dropm to undo the effect of an minit. -//go:nosplit -func unminit() { - if getg().m.newSigstack { - signalstack(nil) - } -} - -func memlimit() uintptr { - return 0 -} - -func sigtramp() - -type sigactiont struct { - sa_sigaction uintptr - sa_mask sigset - sa_flags int32 -} - -//go:nosplit -//go:nowritebarrierrec -func setsig(i int32, fn uintptr, restart bool) { - var sa sigactiont - sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK - if restart { - sa.sa_flags |= _SA_RESTART - } - sa.sa_mask = sigset_all - if fn == funcPC(sighandler) { - fn = funcPC(sigtramp) - } - sa.sa_sigaction = fn - sigaction(i, &sa, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func setsigstack(i int32) { - throw("setsigstack") -} - -//go:nosplit -//go:nowritebarrierrec -func getsig(i int32) uintptr { - var sa sigactiont - sigaction(i, nil, &sa) - if sa.sa_sigaction == funcPC(sigtramp) { - return funcPC(sighandler) - } - return sa.sa_sigaction -} - -//go:nosplit -func signalstack(s *stack) { - var st sigaltstackt - if s == nil { - st.ss_flags = _SS_DISABLE - } else { - st.ss_sp = s.lo - st.ss_size = s.hi - s.lo - st.ss_flags = 0 - } - sigaltstack(&st, nil) -} - -//go:nosplit -//go:nowritebarrierrec -func updatesigmask(m sigmask) { - var mask sigset - copy(mask.__bits[:], m[:]) - sigprocmask(_SIG_SETMASK, &mask, nil) -} - -func unblocksig(sig int32) { - var mask sigset - mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31) - sigprocmask(_SIG_UNBLOCK, &mask, nil) -} diff --git a/src/runtime/os1_netbsd_386.go b/src/runtime/os1_netbsd_386.go deleted file mode 100644 index 037f7e36dc..0000000000 --- a/src/runtime/os1_netbsd_386.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2009 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 runtime - -import "unsafe" - -func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { - // Machine dependent mcontext initialisation for LWP. - mc.__gregs[_REG_EIP] = uint32(funcPC(lwp_tramp)) - mc.__gregs[_REG_UESP] = uint32(uintptr(stk)) - mc.__gregs[_REG_EBX] = uint32(uintptr(unsafe.Pointer(mp))) - mc.__gregs[_REG_EDX] = uint32(uintptr(unsafe.Pointer(gp))) - mc.__gregs[_REG_ESI] = uint32(fn) -} diff --git a/src/runtime/os1_netbsd_amd64.go b/src/runtime/os1_netbsd_amd64.go deleted file mode 100644 index 5118b0c4ff..0000000000 --- a/src/runtime/os1_netbsd_amd64.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2009 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 runtime - -import "unsafe" - -func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { - // Machine dependent mcontext initialisation for LWP. - mc.__gregs[_REG_RIP] = uint64(funcPC(lwp_tramp)) - mc.__gregs[_REG_RSP] = uint64(uintptr(stk)) - mc.__gregs[_REG_R8] = uint64(uintptr(unsafe.Pointer(mp))) - mc.__gregs[_REG_R9] = uint64(uintptr(unsafe.Pointer(gp))) - mc.__gregs[_REG_R12] = uint64(fn) -} diff --git a/src/runtime/os2_dragonfly.go b/src/runtime/os2_dragonfly.go deleted file mode 100644 index 6ea2da0393..0000000000 --- a/src/runtime/os2_dragonfly.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2011 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 runtime - -const ( - _NSIG = 33 - _SI_USER = 0 - _SS_DISABLE = 4 - _RLIMIT_AS = 10 - _SIG_BLOCK = 1 - _SIG_UNBLOCK = 2 - _SIG_SETMASK = 3 -) diff --git a/src/runtime/os2_linux_generic.go b/src/runtime/os2_linux_generic.go deleted file mode 100644 index f1a2dd5130..0000000000 --- a/src/runtime/os2_linux_generic.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2009 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 !mips64 -// +build !mips64le -// +build !s390x -// +build linux - -package runtime - -const ( - _SS_DISABLE = 2 - _NSIG = 65 - _SI_USER = 0 - _SIG_BLOCK = 0 - _SIG_UNBLOCK = 1 - _SIG_SETMASK = 2 - _RLIMIT_AS = 9 -) - -// It's hard to tease out exactly how big a Sigset is, but -// rt_sigprocmask crashes if we get it wrong, so if binaries -// are running, this is right. -type sigset [2]uint32 - -type rlimit struct { - rlim_cur uintptr - rlim_max uintptr -} diff --git a/src/runtime/os2_linux_mips64x.go b/src/runtime/os2_linux_mips64x.go deleted file mode 100644 index 9a6a92a87d..0000000000 --- a/src/runtime/os2_linux_mips64x.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015 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 linux -// +build mips64 mips64le - -package runtime - -const ( - _SS_DISABLE = 2 - _NSIG = 65 - _SI_USER = 0 - _SIG_BLOCK = 1 - _SIG_UNBLOCK = 2 - _SIG_SETMASK = 3 - _RLIMIT_AS = 6 -) - -type sigset [2]uint64 - -type rlimit struct { - rlim_cur uintptr - rlim_max uintptr -} diff --git a/src/runtime/os2_netbsd.go b/src/runtime/os2_netbsd.go deleted file mode 100644 index 405dd5e727..0000000000 --- a/src/runtime/os2_netbsd.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2010 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 runtime - -const ( - _SS_DISABLE = 4 - _SIG_BLOCK = 1 - _SIG_UNBLOCK = 2 - _SIG_SETMASK = 3 - _NSIG = 33 - _SI_USER = 0 - - // From NetBSD's - _UC_SIGMASK = 0x01 - _UC_CPU = 0x04 -) diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go index c3833a397a..78a150eee5 100644 --- a/src/runtime/os_dragonfly.go +++ b/src/runtime/os_dragonfly.go @@ -6,6 +6,16 @@ package runtime import "unsafe" +const ( + _NSIG = 33 + _SI_USER = 0 + _SS_DISABLE = 4 + _RLIMIT_AS = 10 + _SIG_BLOCK = 1 + _SIG_UNBLOCK = 2 + _SIG_SETMASK = 3 +) + type mOS struct{} //go:noescape @@ -41,3 +51,266 @@ func sys_umtx_wakeup(addr *uint32, val int32) int32 func osyield() const stackSystem = 0 + +// From DragonFly's +const ( + _CTL_HW = 6 + _HW_NCPU = 3 +) + +var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}} + +func getncpu() int32 { + mib := [2]uint32{_CTL_HW, _HW_NCPU} + out := uint32(0) + nout := unsafe.Sizeof(out) + ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) + if ret >= 0 { + return int32(out) + } + return 1 +} + +//go:nosplit +func futexsleep(addr *uint32, val uint32, ns int64) { + systemstack(func() { + futexsleep1(addr, val, ns) + }) +} + +func futexsleep1(addr *uint32, val uint32, ns int64) { + var timeout int32 + if ns >= 0 { + // The timeout is specified in microseconds - ensure that we + // do not end up dividing to zero, which would put us to sleep + // indefinitely... + timeout = timediv(ns, 1000, nil) + if timeout == 0 { + timeout = 1 + } + } + + // sys_umtx_sleep will return EWOULDBLOCK (EAGAIN) when the timeout + // expires or EBUSY if the mutex value does not match. + ret := sys_umtx_sleep(addr, int32(val), timeout) + if ret >= 0 || ret == -_EINTR || ret == -_EAGAIN || ret == -_EBUSY { + return + } + + print("umtx_sleep addr=", addr, " val=", val, " ret=", ret, "\n") + *(*int32)(unsafe.Pointer(uintptr(0x1005))) = 0x1005 +} + +//go:nosplit +func futexwakeup(addr *uint32, cnt uint32) { + ret := sys_umtx_wakeup(addr, int32(cnt)) + if ret >= 0 { + return + } + + systemstack(func() { + print("umtx_wake_addr=", addr, " ret=", ret, "\n") + *(*int32)(unsafe.Pointer(uintptr(0x1006))) = 0x1006 + }) +} + +func lwp_start(uintptr) + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + if false { + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " lwp_start=", funcPC(lwp_start), " id=", mp.id, " ostk=", &mp, "\n") + } + + var oset sigset + sigprocmask(_SIG_SETMASK, &sigset_all, &oset) + + params := lwpparams{ + start_func: funcPC(lwp_start), + arg: unsafe.Pointer(mp), + stack: uintptr(stk), + tid1: unsafe.Pointer(&mp.procid), + tid2: nil, + } + + lwp_create(¶ms) + sigprocmask(_SIG_SETMASK, &oset, nil) +} + +func osinit() { + ncpu = getncpu() +} + +var urandom_dev = []byte("/dev/urandom\x00") + +//go:nosplit +func getRandomData(r []byte) { + fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) + n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) + closefd(fd) + extendRandom(r, int(n)) +} + +func goenvs() { + goenvs_unix() +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) + mp.gsignal.m = mp +} + +//go:nosplit +func msigsave(mp *m) { + sigprocmask(_SIG_SETMASK, nil, &mp.sigmask) +} + +//go:nosplit +func msigrestore(sigmask sigset) { + sigprocmask(_SIG_SETMASK, &sigmask, nil) +} + +//go:nosplit +func sigblock() { + sigprocmask(_SIG_SETMASK, &sigset_all, nil) +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + _g_ := getg() + + // m.procid is a uint64, but lwp_start writes an int32. Fix it up. + _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid))) + + // Initialize signal handling. + + // On DragonFly a thread created by pthread_create inherits + // the signal stack of the creating thread. We always create + // a new signal stack here, to avoid having two Go threads + // using the same signal stack. This breaks the case of a + // thread created in C that calls sigaltstack and then calls a + // Go function, because we will lose track of the C code's + // sigaltstack, but it's the best we can do. + signalstack(&_g_.m.gsignal.stack) + _g_.m.newSigstack = true + + // restore signal mask from m.sigmask and unblock essential signals + nmask := _g_.m.sigmask + for i := range sigtable { + if sigtable[i].flags&_SigUnblock != 0 { + nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) + } + } + sigprocmask(_SIG_SETMASK, &nmask, nil) +} + +// Called from dropm to undo the effect of an minit. +//go:nosplit +func unminit() { + if getg().m.newSigstack { + signalstack(nil) + } +} + +func memlimit() uintptr { + /* + TODO: Convert to Go when something actually uses the result. + + Rlimit rl; + extern byte runtime·text[], runtime·end[]; + uintptr used; + + if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) + return 0; + if(rl.rlim_cur >= 0x7fffffff) + return 0; + + // Estimate our VM footprint excluding the heap. + // Not an exact science: use size of binary plus + // some room for thread stacks. + used = runtime·end - runtime·text + (64<<20); + if(used >= rl.rlim_cur) + return 0; + + // If there's not at least 16 MB left, we're probably + // not going to be able to do much. Treat as no limit. + rl.rlim_cur -= used; + if(rl.rlim_cur < (16<<20)) + return 0; + + return rl.rlim_cur - used; + */ + return 0 +} + +func sigtramp() + +type sigactiont struct { + sa_sigaction uintptr + sa_flags int32 + sa_mask sigset +} + +//go:nosplit +//go:nowritebarrierrec +func setsig(i int32, fn uintptr, restart bool) { + var sa sigactiont + sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK + if restart { + sa.sa_flags |= _SA_RESTART + } + sa.sa_mask = sigset_all + if fn == funcPC(sighandler) { + fn = funcPC(sigtramp) + } + sa.sa_sigaction = fn + sigaction(i, &sa, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func setsigstack(i int32) { + throw("setsigstack") +} + +//go:nosplit +//go:nowritebarrierrec +func getsig(i int32) uintptr { + var sa sigactiont + sigaction(i, nil, &sa) + if sa.sa_sigaction == funcPC(sigtramp) { + return funcPC(sighandler) + } + return sa.sa_sigaction +} + +//go:nosplit +func signalstack(s *stack) { + var st sigaltstackt + if s == nil { + st.ss_flags = _SS_DISABLE + } else { + st.ss_sp = s.lo + st.ss_size = s.hi - s.lo + st.ss_flags = 0 + } + sigaltstack(&st, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func updatesigmask(m sigmask) { + var mask sigset + copy(mask.__bits[:], m[:]) + sigprocmask(_SIG_SETMASK, &mask, nil) +} + +func unblocksig(sig int32) { + var mask sigset + mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31) + sigprocmask(_SIG_UNBLOCK, &mask, nil) +} diff --git a/src/runtime/os_linux_generic.go b/src/runtime/os_linux_generic.go new file mode 100644 index 0000000000..a16d140776 --- /dev/null +++ b/src/runtime/os_linux_generic.go @@ -0,0 +1,48 @@ +// Copyright 2009 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 !mips64 +// +build !mips64le +// +build !s390x +// +build linux + +package runtime + +const ( + _SS_DISABLE = 2 + _NSIG = 65 + _SI_USER = 0 + _SIG_BLOCK = 0 + _SIG_UNBLOCK = 1 + _SIG_SETMASK = 2 + _RLIMIT_AS = 9 +) + +// It's hard to tease out exactly how big a Sigset is, but +// rt_sigprocmask crashes if we get it wrong, so if binaries +// are running, this is right. +type sigset [2]uint32 + +type rlimit struct { + rlim_cur uintptr + rlim_max uintptr +} + +var sigset_all = sigset{^uint32(0), ^uint32(0)} + +func sigaddset(mask *sigset, i int) { + (*mask)[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31) +} + +func sigdelset(mask *sigset, i int) { + (*mask)[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) +} + +func sigfillset(mask *uint64) { + *mask = ^uint64(0) +} + +func sigcopyset(mask *sigset, m sigmask) { + copy((*mask)[:], m[:]) +} diff --git a/src/runtime/os_linux_mips64x.go b/src/runtime/os_linux_mips64x.go index 4d2e9e8a20..92b5c82af7 100644 --- a/src/runtime/os_linux_mips64x.go +++ b/src/runtime/os_linux_mips64x.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build mips64 mips64le // +build linux +// +build mips64 mips64le package runtime @@ -16,3 +16,38 @@ func cputicks() int64 { // randomNumber provides better seeding of fastrand1. return nanotime() + int64(randomNumber) } + +const ( + _SS_DISABLE = 2 + _NSIG = 65 + _SI_USER = 0 + _SIG_BLOCK = 1 + _SIG_UNBLOCK = 2 + _SIG_SETMASK = 3 + _RLIMIT_AS = 6 +) + +type sigset [2]uint64 + +type rlimit struct { + rlim_cur uintptr + rlim_max uintptr +} + +var sigset_all = sigset{^uint64(0), ^uint64(0)} + +func sigaddset(mask *sigset, i int) { + (*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63) +} + +func sigdelset(mask *sigset, i int) { + (*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63) +} + +func sigfillset(mask *[2]uint64) { + (*mask)[0], (*mask)[1] = ^uint64(0), ^uint64(0) +} + +func sigcopyset(mask *sigset, m sigmask) { + (*mask)[0] = uint64(m[0]) | uint64(m[1])<<32 +} diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go index 0fba16d4f4..41f34f7132 100644 --- a/src/runtime/os_netbsd.go +++ b/src/runtime/os_netbsd.go @@ -4,7 +4,23 @@ package runtime -import "unsafe" +import ( + "runtime/internal/atomic" + "unsafe" +) + +const ( + _SS_DISABLE = 4 + _SIG_BLOCK = 1 + _SIG_UNBLOCK = 2 + _SIG_SETMASK = 3 + _NSIG = 33 + _SI_USER = 0 + + // From NetBSD's + _UC_SIGMASK = 0x01 + _UC_CPU = 0x04 +) type mOS struct { waitsemacount uint32 @@ -45,3 +61,268 @@ func lwp_unpark(lwp int32, hint unsafe.Pointer) int32 func lwp_self() int32 func osyield() + +const ( + _ESRCH = 3 + _ETIMEDOUT = 60 + + // From NetBSD's + _CLOCK_REALTIME = 0 + _CLOCK_VIRTUAL = 1 + _CLOCK_PROF = 2 + _CLOCK_MONOTONIC = 3 +) + +var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}} + +// From NetBSD's +const ( + _CTL_HW = 6 + _HW_NCPU = 3 +) + +func getncpu() int32 { + mib := [2]uint32{_CTL_HW, _HW_NCPU} + out := uint32(0) + nout := unsafe.Sizeof(out) + ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0) + if ret >= 0 { + return int32(out) + } + return 1 +} + +//go:nosplit +func semacreate(mp *m) { +} + +//go:nosplit +func semasleep(ns int64) int32 { + _g_ := getg() + + // Compute sleep deadline. + var tsp *timespec + if ns >= 0 { + var ts timespec + var nsec int32 + ns += nanotime() + ts.set_sec(timediv(ns, 1000000000, &nsec)) + ts.set_nsec(nsec) + tsp = &ts + } + + for { + v := atomic.Load(&_g_.m.waitsemacount) + if v > 0 { + if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { + return 0 // semaphore acquired + } + continue + } + + // Sleep until unparked by semawakeup or timeout. + ret := lwp_park(tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil) + if ret == _ETIMEDOUT { + return -1 + } + } +} + +//go:nosplit +func semawakeup(mp *m) { + atomic.Xadd(&mp.waitsemacount, 1) + // From NetBSD's _lwp_unpark(2) manual: + // "If the target LWP is not currently waiting, it will return + // immediately upon the next call to _lwp_park()." + ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount)) + if ret != 0 && ret != _ESRCH { + // semawakeup can be called on signal stack. + systemstack(func() { + print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n") + }) + } +} + +// May run with m.p==nil, so write barriers are not allowed. +//go:nowritebarrier +func newosproc(mp *m, stk unsafe.Pointer) { + if false { + print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n") + } + + var uc ucontextt + getcontext(unsafe.Pointer(&uc)) + + uc.uc_flags = _UC_SIGMASK | _UC_CPU + uc.uc_link = nil + uc.uc_sigmask = sigset_all + + lwp_mcontext_init(&uc.uc_mcontext, stk, mp, mp.g0, funcPC(netbsdMstart)) + + ret := lwp_create(unsafe.Pointer(&uc), 0, unsafe.Pointer(&mp.procid)) + if ret < 0 { + print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n") + throw("runtime.newosproc") + } +} + +// netbsdMStart is the function call that starts executing a newly +// created thread. On NetBSD, a new thread inherits the signal stack +// of the creating thread. That confuses minit, so we remove that +// signal stack here before calling the regular mstart. It's a bit +// baroque to remove a signal stack here only to add one in minit, but +// it's a simple change that keeps NetBSD working like other OS's. +// At this point all signals are blocked, so there is no race. +//go:nosplit +func netbsdMstart() { + signalstack(nil) + mstart() +} + +func osinit() { + ncpu = getncpu() +} + +var urandom_dev = []byte("/dev/urandom\x00") + +//go:nosplit +func getRandomData(r []byte) { + fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0) + n := read(fd, unsafe.Pointer(&r[0]), int32(len(r))) + closefd(fd) + extendRandom(r, int(n)) +} + +func goenvs() { + goenvs_unix() +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the parent thread (main thread in case of bootstrap), can allocate memory. +func mpreinit(mp *m) { + mp.gsignal = malg(32 * 1024) + mp.gsignal.m = mp +} + +//go:nosplit +func msigsave(mp *m) { + sigprocmask(_SIG_SETMASK, nil, &mp.sigmask) +} + +//go:nosplit +func msigrestore(sigmask sigset) { + sigprocmask(_SIG_SETMASK, &sigmask, nil) +} + +//go:nosplit +func sigblock() { + sigprocmask(_SIG_SETMASK, &sigset_all, nil) +} + +// Called to initialize a new m (including the bootstrap m). +// Called on the new thread, cannot allocate memory. +func minit() { + _g_ := getg() + _g_.m.procid = uint64(lwp_self()) + + // Initialize signal handling. + + // On NetBSD a thread created by pthread_create inherits the + // signal stack of the creating thread. We always create a + // new signal stack here, to avoid having two Go threads using + // the same signal stack. This breaks the case of a thread + // created in C that calls sigaltstack and then calls a Go + // function, because we will lose track of the C code's + // sigaltstack, but it's the best we can do. + signalstack(&_g_.m.gsignal.stack) + _g_.m.newSigstack = true + + // restore signal mask from m.sigmask and unblock essential signals + nmask := _g_.m.sigmask + for i := range sigtable { + if sigtable[i].flags&_SigUnblock != 0 { + nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) + } + } + sigprocmask(_SIG_SETMASK, &nmask, nil) +} + +// Called from dropm to undo the effect of an minit. +//go:nosplit +func unminit() { + if getg().m.newSigstack { + signalstack(nil) + } +} + +func memlimit() uintptr { + return 0 +} + +func sigtramp() + +type sigactiont struct { + sa_sigaction uintptr + sa_mask sigset + sa_flags int32 +} + +//go:nosplit +//go:nowritebarrierrec +func setsig(i int32, fn uintptr, restart bool) { + var sa sigactiont + sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK + if restart { + sa.sa_flags |= _SA_RESTART + } + sa.sa_mask = sigset_all + if fn == funcPC(sighandler) { + fn = funcPC(sigtramp) + } + sa.sa_sigaction = fn + sigaction(i, &sa, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func setsigstack(i int32) { + throw("setsigstack") +} + +//go:nosplit +//go:nowritebarrierrec +func getsig(i int32) uintptr { + var sa sigactiont + sigaction(i, nil, &sa) + if sa.sa_sigaction == funcPC(sigtramp) { + return funcPC(sighandler) + } + return sa.sa_sigaction +} + +//go:nosplit +func signalstack(s *stack) { + var st sigaltstackt + if s == nil { + st.ss_flags = _SS_DISABLE + } else { + st.ss_sp = s.lo + st.ss_size = s.hi - s.lo + st.ss_flags = 0 + } + sigaltstack(&st, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func updatesigmask(m sigmask) { + var mask sigset + copy(mask.__bits[:], m[:]) + sigprocmask(_SIG_SETMASK, &mask, nil) +} + +func unblocksig(sig int32) { + var mask sigset + mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31) + sigprocmask(_SIG_UNBLOCK, &mask, nil) +} diff --git a/src/runtime/os_netbsd_386.go b/src/runtime/os_netbsd_386.go new file mode 100644 index 0000000000..037f7e36dc --- /dev/null +++ b/src/runtime/os_netbsd_386.go @@ -0,0 +1,16 @@ +// Copyright 2009 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 runtime + +import "unsafe" + +func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { + // Machine dependent mcontext initialisation for LWP. + mc.__gregs[_REG_EIP] = uint32(funcPC(lwp_tramp)) + mc.__gregs[_REG_UESP] = uint32(uintptr(stk)) + mc.__gregs[_REG_EBX] = uint32(uintptr(unsafe.Pointer(mp))) + mc.__gregs[_REG_EDX] = uint32(uintptr(unsafe.Pointer(gp))) + mc.__gregs[_REG_ESI] = uint32(fn) +} diff --git a/src/runtime/os_netbsd_amd64.go b/src/runtime/os_netbsd_amd64.go new file mode 100644 index 0000000000..5118b0c4ff --- /dev/null +++ b/src/runtime/os_netbsd_amd64.go @@ -0,0 +1,16 @@ +// Copyright 2009 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 runtime + +import "unsafe" + +func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintptr) { + // Machine dependent mcontext initialisation for LWP. + mc.__gregs[_REG_RIP] = uint64(funcPC(lwp_tramp)) + mc.__gregs[_REG_RSP] = uint64(uintptr(stk)) + mc.__gregs[_REG_R8] = uint64(uintptr(unsafe.Pointer(mp))) + mc.__gregs[_REG_R9] = uint64(uintptr(unsafe.Pointer(gp))) + mc.__gregs[_REG_R12] = uint64(fn) +} -- cgit v1.3 From 5c7ae10f66eae34b8a786fc2fdf753bf48a3d116 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Wed, 6 Apr 2016 15:19:12 +1000 Subject: runtime: merge 64bit lfstack impls Merge all the 64bit lfstack impls into one file, adjust build tags to match. Merge all the comments on the various lfstack implementations for posterity. lfstack_amd64.go can probably be merged, but it is slightly different so that will happen in a followup. Change-Id: I5362d5e127daa81c9cb9d4fa8a0cc5c5e5c2707c Reviewed-on: https://go-review.googlesource.com/21591 Run-TryBot: Dave Cheney Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Minux Ma --- src/runtime/lfstack_64bit.go | 35 +++++++++++++++++++++++++++++++++++ src/runtime/lfstack_darwin_arm64.go | 25 ------------------------- src/runtime/lfstack_linux_arm64.go | 25 ------------------------- src/runtime/lfstack_linux_mips64x.go | 32 -------------------------------- src/runtime/lfstack_linux_ppc64x.go | 32 -------------------------------- 5 files changed, 35 insertions(+), 114 deletions(-) create mode 100644 src/runtime/lfstack_64bit.go delete mode 100644 src/runtime/lfstack_darwin_arm64.go delete mode 100644 src/runtime/lfstack_linux_arm64.go delete mode 100644 src/runtime/lfstack_linux_mips64x.go delete mode 100644 src/runtime/lfstack_linux_ppc64x.go (limited to 'src') diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go new file mode 100644 index 0000000000..27a058c763 --- /dev/null +++ b/src/runtime/lfstack_64bit.go @@ -0,0 +1,35 @@ +// Copyright 2014 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 arm64 mips64 mips64le ppc64 ppc64le + +package runtime + +import "unsafe" + +// On ppc64, Linux limits the user address space to 46 bits (see +// TASK_SIZE_USER64 in the Linux kernel). This has grown over time, +// so here we allow 48 bit addresses. +// +// On mips64, Linux limits the user address space to 40 bits (see +// TASK_SIZE64 in the Linux kernel). This has grown over time, +// so here we allow 48 bit addresses. +// +// In addition to the 16 bits taken from the top, we can take 3 from the +// bottom, because node must be pointer-aligned, giving a total of 19 bits +// of count. +const ( + addrBits = 48 + cntBits = 64 - addrBits + 3 +) + +func lfstackPack(node *lfnode, cnt uintptr) uint64 { + return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<> cntBits << 3))) + cnt = uintptr(val & (1<> cntBits << 3))) - cnt = uintptr(val & (1<> cntBits << 3))) - cnt = uintptr(val & (1<> cntBits << 3))) - cnt = uintptr(val & (1<> cntBits << 3))) - cnt = uintptr(val & (1< Date: Fri, 1 Apr 2016 11:05:30 -0700 Subject: cmd/compile: fix x=x assignments No point in doing anything for x=x assignments. In addition, skipping these assignments prevents generating: VARDEF x COPY x -> x which is bad because x is incorrectly considered dead before the vardef. Fixes #14904 Change-Id: I6817055ec20bcc34a9648617e0439505ee355f82 Reviewed-on: https://go-review.googlesource.com/21470 Reviewed-by: Brad Fitzpatrick Reviewed-by: Dave Cheney --- src/cmd/compile/internal/gc/ssa.go | 11 +++ src/cmd/compile/internal/gc/ssa_test.go | 2 + .../compile/internal/gc/testdata/namedReturn.go | 101 +++++++++++++++++++++ test/live_ssa.go | 13 ++- 4 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 src/cmd/compile/internal/gc/testdata/namedReturn.go (limited to 'src') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 359f4b22a2..1c2e528384 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -661,6 +661,17 @@ func (s *state) stmt(n *Node) { return } + if n.Left == n.Right && n.Left.Op == ONAME { + // An x=x assignment. No point in doing anything + // here. In addition, skipping this assignment + // prevents generating: + // VARDEF x + // COPY x -> x + // which is bad because x is incorrectly considered + // dead before the vardef. See issue #14904. + return + } + var t *Type if n.Right != nil { t = n.Right.Type diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/gc/ssa_test.go index 59a240237b..0fb0f17778 100644 --- a/src/cmd/compile/internal/gc/ssa_test.go +++ b/src/cmd/compile/internal/gc/ssa_test.go @@ -99,3 +99,5 @@ func TestUnsafe(t *testing.T) { runTest(t, "unsafe_ssa.go") } func TestPhi(t *testing.T) { runTest(t, "phi_ssa.go") } func TestSlice(t *testing.T) { runTest(t, "slice.go") } + +func TestNamedReturn(t *testing.T) { runTest(t, "namedReturn.go") } diff --git a/src/cmd/compile/internal/gc/testdata/namedReturn.go b/src/cmd/compile/internal/gc/testdata/namedReturn.go new file mode 100644 index 0000000000..dafb5d719f --- /dev/null +++ b/src/cmd/compile/internal/gc/testdata/namedReturn.go @@ -0,0 +1,101 @@ +// run + +// This test makes sure that naming named +// return variables in a return statement works. +// See issue #14904. + +package main + +import ( + "fmt" + "runtime" +) + +// Our heap-allocated object that will be GC'd incorrectly. +// Note that we always check the second word because that's +// where 0xdeaddeaddeaddead is written. +type B [4]int + +// small (SSAable) array +type T1 [3]*B + +//go:noinline +func f1() (t T1) { + t[0] = &B{91, 92, 93, 94} + runtime.GC() + return t +} + +// large (non-SSAable) array +type T2 [8]*B + +//go:noinline +func f2() (t T2) { + t[0] = &B{91, 92, 93, 94} + runtime.GC() + return t +} + +// small (SSAable) struct +type T3 struct { + a, b, c *B +} + +//go:noinline +func f3() (t T3) { + t.a = &B{91, 92, 93, 94} + runtime.GC() + return t +} + +// large (non-SSAable) struct +type T4 struct { + a, b, c, d, e, f *B +} + +//go:noinline +func f4() (t T4) { + t.a = &B{91, 92, 93, 94} + runtime.GC() + return t +} + +var sink *B + +func f5() int { + b := &B{91, 92, 93, 94} + t := T4{b, nil, nil, nil, nil, nil} + sink = b // make sure b is heap allocated ... + sink = nil // ... but not live + runtime.GC() + t = t + return t.a[1] +} + +func main() { + failed := false + + if v := f1()[0][1]; v != 92 { + fmt.Printf("f1()[0][1]=%d, want 92\n", v) + failed = true + } + if v := f2()[0][1]; v != 92 { + fmt.Printf("f2()[0][1]=%d, want 92\n", v) + failed = true + } + if v := f3().a[1]; v != 92 { + fmt.Printf("f3().a[1]=%d, want 92\n", v) + failed = true + } + if v := f4().a[1]; v != 92 { + fmt.Printf("f4().a[1]=%d, want 92\n", v) + failed = true + } + if v := f5(); v != 92 { + fmt.Printf("f5()=%d, want 92\n", v) + failed = true + } + if failed { + panic("bad") + } +} diff --git a/test/live_ssa.go b/test/live_ssa.go index fe2541395f..fae0a2b82a 100644 --- a/test/live_ssa.go +++ b/test/live_ssa.go @@ -606,13 +606,12 @@ func f39a() (x []int) { return } -// TODO: Reenable after #14904 is fixed. -//func f39b() (x [10]*int) { -// x = [10]*int{} -// x[0] = new(int) // E.R.R.O.R. "live at call to newobject: x$" -// printnl() // E.R.R.O.R. "live at call to printnl: x$" -// return x -//} +func f39b() (x [10]*int) { + x = [10]*int{} + x[0] = new(int) // ERROR "live at call to newobject: x$" + printnl() // ERROR "live at call to printnl: x$" + return x +} func f39c() (x [10]*int) { x = [10]*int{} -- cgit v1.3 From b2fc9f1c23453e16ab08d411ed0e439212d6e5e6 Mon Sep 17 00:00:00 2001 From: Dan Peterson Date: Wed, 6 Apr 2016 11:18:55 -0300 Subject: net/http/pprof: note calling runtime.SetBlockProfileRate is required for block profile Fixes #15076 Change-Id: I5ce8f6253245d8cc1f862a1bf618775f557f955d Reviewed-on: https://go-review.googlesource.com/21610 Reviewed-by: Brad Fitzpatrick --- src/net/http/pprof/pprof.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/net/http/pprof/pprof.go b/src/net/http/pprof/pprof.go index 2357d8ed1e..44afa2d8d8 100644 --- a/src/net/http/pprof/pprof.go +++ b/src/net/http/pprof/pprof.go @@ -30,7 +30,8 @@ // // go tool pprof http://localhost:6060/debug/pprof/profile // -// Or to look at the goroutine blocking profile: +// Or to look at the goroutine blocking profile, after calling +// runtime.SetBlockProfileRate in your program: // // go tool pprof http://localhost:6060/debug/pprof/block // -- cgit v1.3 From 530e2164940d6650c43ecc8c652eed557e0bff8a Mon Sep 17 00:00:00 2001 From: Ross Light Date: Thu, 31 Mar 2016 14:57:49 -0700 Subject: os/user: wrap getgrnam_r to fix type issues Even with -D_POSIX_PTHREAD_SEMANTICS, Solaris seems to not define getgrnam_r in a POSIX compatible way. Fixes #14967 Change-Id: I78cb7e5b30b2d8b860e336060a0a06f4720c0475 Reviewed-on: https://go-review.googlesource.com/21385 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/os/user/lookup_unix.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/os/user/lookup_unix.go b/src/os/user/lookup_unix.go index 97b649cdad..58ecf32405 100644 --- a/src/os/user/lookup_unix.go +++ b/src/os/user/lookup_unix.go @@ -37,6 +37,11 @@ static int mygetgrgid_r(int gid, struct group *grp, char *buf, size_t buflen, struct group **result) { return getgrgid_r(gid, grp, buf, buflen, result); } + +static int mygetgrnam_r(const char *name, struct group *grp, + char *buf, size_t buflen, struct group **result) { + return getgrnam_r(name, grp, buf, buflen, result); +} */ import "C" @@ -139,7 +144,7 @@ func lookupGroup(groupname string) (*Group, error) { defer C.free(unsafe.Pointer(cname)) err := retryWithBuffer(buf, func() syscall.Errno { - return syscall.Errno(C.getgrnam_r(cname, + return syscall.Errno(C.mygetgrnam_r(cname, &grp, (*C.char)(buf.ptr), C.size_t(buf.size), -- cgit v1.3 From 0d375381963d1236b1f70546386ffe92dddb59fc Mon Sep 17 00:00:00 2001 From: Konstantin Shaposhnikov Date: Tue, 5 Apr 2016 23:54:50 +0800 Subject: cmd/vet: do not treat declaration as asignment in atomic check Fixes #15118 Change-Id: Iad56ed412535c8ac0a01c4bd7769fd3d37688ac9 Reviewed-on: https://go-review.googlesource.com/21526 Run-TryBot: Rob Pike Reviewed-by: Rob Pike --- src/cmd/vet/atomic.go | 3 +++ src/cmd/vet/testdata/atomic.go | 9 +++++++++ 2 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/cmd/vet/atomic.go b/src/cmd/vet/atomic.go index c084f13ab3..b2ca2d80f3 100644 --- a/src/cmd/vet/atomic.go +++ b/src/cmd/vet/atomic.go @@ -23,6 +23,9 @@ func checkAtomicAssignment(f *File, node ast.Node) { if len(n.Lhs) != len(n.Rhs) { return } + if len(n.Lhs) == 1 && n.Tok == token.DEFINE { + return + } for i, right := range n.Rhs { call, ok := right.(*ast.CallExpr) diff --git a/src/cmd/vet/testdata/atomic.go b/src/cmd/vet/testdata/atomic.go index 1ba261d941..d5a8e61184 100644 --- a/src/cmd/vet/testdata/atomic.go +++ b/src/cmd/vet/testdata/atomic.go @@ -40,4 +40,13 @@ func AtomicTests() { *ap[1] = atomic.AddUint64(ap[0], 1) x = atomic.AddUint64() // Used to make vet crash; now silently ignored. + + { + // A variable declaration creates a new variable in the current scope. + x := atomic.AddUint64(&x, 1) // ERROR "declaration of .x. shadows declaration at testdata/atomic.go:16" + + // Re-declaration assigns a new value. + x, w := atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value" + _ = w + } } -- cgit v1.3 From d70cb46f0fde56ec0147c5cc679714fd63cb10bb Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Wed, 6 Apr 2016 16:11:22 +1000 Subject: runtime: use windows.NewLazySystemDLL in mksyscall_windows.go Change-Id: Ie4c4ff4167ee45ae93a8b764fb6197f402e7994d Reviewed-on: https://go-review.googlesource.com/21593 Reviewed-by: Brad Fitzpatrick --- src/syscall/mksyscall_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/syscall/mksyscall_windows.go b/src/syscall/mksyscall_windows.go index 7786d1349e..a6cef6fca7 100644 --- a/src/syscall/mksyscall_windows.go +++ b/src/syscall/mksyscall_windows.go @@ -707,9 +707,9 @@ func (src *Source) Generate(w io.Writer) error { } if *sysRepo { if packageName == "windows" { - return "&LazyDLL{Name: " + arg + ", System: true}" + return "NewLazySystemDLL(" + arg + ")" } else { - return "&windows.LazyDLL{Name: " + arg + ", System: true}" + return "windows.NewLazySystemDLL(" + arg + ")" } } else { return syscalldot() + "NewLazyDLL(" + arg + ")" -- cgit v1.3 From 63cea5ac2b8ed0cf257c7bfe7ed13bdd42373a0c Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Wed, 6 Apr 2016 09:59:32 +0200 Subject: testing: fixed bug introduced by CL 21504 This broke T.Run Change-Id: I12c8fe3612f3fa2caa83049c1c7003056daf2b0c Reviewed-on: https://go-review.googlesource.com/21600 Run-TryBot: Marcel van Lohuizen TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/testing/sub_test.go | 24 ++++++++++++++++++++++++ src/testing/testing.go | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index e053a3c348..7fe0fffd8f 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -178,6 +178,22 @@ func TestTRun(t *T) { === RUN skipping without message, chatty --- SKIP: skipping without message, chatty (0.00s)`, f: func(t *T) { t.SkipNow() }, + }, { + desc: "chatty with recursion", + ok: true, + chatty: true, + output: ` +=== RUN chatty with recursion +=== RUN chatty with recursion/#00 +=== RUN chatty with recursion/#00/#00 +--- PASS: chatty with recursion (0.00s) + --- PASS: chatty with recursion/#00 (0.00s) + --- PASS: chatty with recursion/#00/#00 (0.00s)`, + f: func(t *T) { + t.Run("", func(t *T) { + t.Run("", func(t *T) {}) + }) + }, }, { desc: "skipping without message, not chatty", ok: true, @@ -435,6 +451,14 @@ func TestBRun(t *T) { --- SKIP: root sub_test.go:: skipping`, f: func(b *B) { b.Skip("skipping") }, + }, { + desc: "chatty with recursion", + chatty: true, + f: func(b *B) { + b.Run("", func(b *B) { + b.Run("", func(b *B) {}) + }) + }, }, { desc: "skipping without message, not chatty", f: func(b *B) { b.SkipNow() }, diff --git a/src/testing/testing.go b/src/testing/testing.go index 8e16db321d..3a7a135a3c 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -571,7 +571,7 @@ func (t *T) Run(name string, f func(t *T)) bool { if t.chatty { // Print directly to root's io.Writer so there is no delay. root := t.parent - for ; root.parent != nil; root = t.parent { + for ; root.parent != nil; root = root.parent { } fmt.Fprintf(root.w, "=== RUN %s\n", t.name) } -- cgit v1.3 From ec3c5b9d178ee373a41e46f6074c8729cfd11084 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 5 Apr 2016 23:01:10 -0700 Subject: cmd/link: eliminate a bunch of open coded elf64/rela switches We already have variables to track whether the target platform is 64-bit vs 32-bit or RELA vs REL, so no point in repeating the list of obscure architecture characters everywhere. Passes toolstash/buildall. Change-Id: I6a07f74188ac592ef229a7c65848a9ba93013cdb Reviewed-on: https://go-review.googlesource.com/21569 Run-TryBot: Matthew Dempsky Reviewed-by: Michael Hudson-Doyle Reviewed-by: Brad Fitzpatrick --- src/cmd/link/internal/ld/dwarf.go | 21 ++++++--------------- src/cmd/link/internal/ld/elf.go | 10 ++++------ src/cmd/link/internal/ld/symtab.go | 6 ++---- 3 files changed, 12 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index fd177cfef0..4465a727a5 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -2169,18 +2169,10 @@ func dwarfaddshstrings(shstrtab *LSym) { elfstrdbg[ElfStrDebugStr] = Addstring(shstrtab, ".debug_str") elfstrdbg[ElfStrGDBScripts] = Addstring(shstrtab, ".debug_gdb_scripts") if Linkmode == LinkExternal { - switch Thearch.Thechar { - case '0', '6', '7', '9', 'z': - elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rela.debug_info") - elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rela.debug_aranges") - elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rela.debug_line") - elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rela.debug_frame") - default: - elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rel.debug_info") - elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rel.debug_aranges") - elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rel.debug_line") - elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rel.debug_frame") - } + elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, elfRelType+".debug_info") + elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, elfRelType+".debug_aranges") + elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, elfRelType+".debug_line") + elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, elfRelType+".debug_frame") infosym = Linklookup(Ctxt, ".debug_info", 0) infosym.Attr |= AttrHidden @@ -2222,10 +2214,9 @@ func dwarfaddelfsectionsyms() { func dwarfaddelfrelocheader(elfstr int, shdata *ElfShdr, off int64, size int64) { sh := newElfShdr(elfstrdbg[elfstr]) - switch Thearch.Thechar { - case '0', '6', '7', '9', 'z': + if elfRelType == ".rela" { sh.type_ = SHT_RELA - default: + } else { sh.type_ = SHT_REL } diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index ffb7c4bdde..3b40c66592 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1885,10 +1885,9 @@ func doelf() { s.Type = obj.SELFROSECT s.Attr |= AttrReachable - switch Thearch.Thechar { - case '0', '6', '7', '9', 'z': + if elf64 { s.Size += ELF64SYMSIZE - default: + } else { s.Size += ELF32SYMSIZE } @@ -1967,10 +1966,9 @@ func doelf() { elfwritedynentsym(s, DT_HASH, Linklookup(Ctxt, ".hash", 0)) elfwritedynentsym(s, DT_SYMTAB, Linklookup(Ctxt, ".dynsym", 0)) - switch Thearch.Thechar { - case '0', '6', '7', '9', 'z': + if elf64 { Elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE) - default: + } else { Elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE) } elfwritedynentsym(s, DT_STRTAB, Linklookup(Ctxt, ".dynstr", 0)) diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 3258bc1ff9..0fe0a68c65 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -66,8 +66,7 @@ func putelfstr(s string) int { } func putelfsyment(off int, addr int64, size int64, info int, shndx int, other int) { - switch Thearch.Thechar { - case '0', '6', '7', '9', 'z': + if elf64 { Thearch.Lput(uint32(off)) Cput(uint8(info)) Cput(uint8(other)) @@ -75,8 +74,7 @@ func putelfsyment(off int, addr int64, size int64, info int, shndx int, other in Thearch.Vput(uint64(addr)) Thearch.Vput(uint64(size)) Symsize += ELF64SYMSIZE - - default: + } else { Thearch.Lput(uint32(off)) Thearch.Lput(uint32(addr)) Thearch.Lput(uint32(size)) -- cgit v1.3 From d7ddee78ddca805e6609149ff4320b1547698259 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Tue, 5 Apr 2016 18:42:07 +0300 Subject: icmd/vet: improved checking for variadic Println-like functions - Automatically determine the first argument to check. - Skip checking matching non-variadic functions. - Skip checking matching functions accepting non-interface{} variadic arguments. - Removed fragile 'magic' code for special cases such as math.Log and error interface. Fixes #15067 Fixes #15099 Change-Id: Ib313557f18b12b36daa493f4b02c598b9503b55b Reviewed-on: https://go-review.googlesource.com/21513 Run-TryBot: Rob Pike Reviewed-by: Rob Pike --- src/cmd/vet/doc.go | 9 +--- src/cmd/vet/print.go | 104 ++++++++++++++++++++---------------------- src/cmd/vet/testdata/print.go | 38 +++++++++++++-- src/cmd/vet/types.go | 66 --------------------------- 4 files changed, 86 insertions(+), 131 deletions(-) (limited to 'src') diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go index d295fb4345..2b5e8fcb59 100644 --- a/src/cmd/vet/doc.go +++ b/src/cmd/vet/doc.go @@ -188,13 +188,8 @@ These flags configure the behavior of vet: -v Verbose mode -printfuncs - A comma-separated list of print-like functions to supplement the - standard list. Each entry is in the form Name:N where N is the - zero-based argument position of the first argument involved in the - print: either the format or the first print argument for non-formatted - prints. For example, if you have Warn and Warnf functions that - take an io.Writer as their first argument, like Fprintf, - -printfuncs=Warn:1,Warnf:1 + A comma-separated list of print-like function names + to supplement the standard list. For more information, see the discussion of the -printf flag. -shadowstrict Whether to be strict about shadowing; can be noisy. diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go index 4e3252f2fb..07499e6ae6 100644 --- a/src/cmd/vet/print.go +++ b/src/cmd/vet/print.go @@ -35,20 +35,18 @@ func initPrintFlags() { if len(name) == 0 { flag.Usage() } - skip := 0 + + // Backwards compatibility: skip optional first argument + // index after the colon. if colon := strings.LastIndex(name, ":"); colon > 0 { - var err error - skip, err = strconv.Atoi(name[colon+1:]) - if err != nil { - errorf(`illegal format for "Func:N" argument %q; %s`, name, err) - } name = name[:colon] } + name = strings.ToLower(name) if name[len(name)-1] == 'f' { isFormattedPrint[name] = true } else { - printList[name] = skip + isPrint[name] = true } } } @@ -65,17 +63,20 @@ var isFormattedPrint = map[string]bool{ "sprintf": true, } -// printList records the unformatted-print functions. The value is the location -// of the first parameter to be printed. Names are lower-cased so the lookup is -// case insensitive. -var printList = map[string]int{ - "error": 0, - "fatal": 0, - "fprint": 1, "fprintln": 1, - "log": 0, - "panic": 0, "panicln": 0, - "print": 0, "println": 0, - "sprint": 0, "sprintln": 0, +// isPrint records the unformatted-print functions. Names are lower-cased +// so the lookup is case insensitive. +var isPrint = map[string]bool{ + "error": true, + "fatal": true, + "fprint": true, + "fprintln": true, + "log": true, + "panic": true, + "panicln": true, + "print": true, + "println": true, + "sprint": true, + "sprintln": true, } // formatString returns the format string argument and its index within @@ -171,8 +172,8 @@ func checkFmtPrintfCall(f *File, node ast.Node) { f.checkPrintf(call, Name) return } - if skip, ok := printList[name]; ok { - f.checkPrint(call, Name, skip) + if _, ok := isPrint[name]; ok { + f.checkPrint(call, Name) return } } @@ -583,25 +584,36 @@ func (f *File) argCanBeChecked(call *ast.CallExpr, formatArg int, isStar bool, s } // checkPrint checks a call to an unformatted print routine such as Println. -// call.Args[firstArg] is the first argument to be printed. -func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { - isLn := strings.HasSuffix(name, "ln") - isF := strings.HasPrefix(name, "F") - args := call.Args - if name == "Log" && len(args) > 0 { - // Special case: Don't complain about math.Log or cmplx.Log. - // Not strictly necessary because the only complaint likely is for Log("%d") - // but it feels wrong to check that math.Log is a good print function. - if sel, ok := args[0].(*ast.SelectorExpr); ok { - if x, ok := sel.X.(*ast.Ident); ok { - if x.Name == "math" || x.Name == "cmplx" { - return - } +func (f *File) checkPrint(call *ast.CallExpr, name string) { + firstArg := 0 + typ := f.pkg.types[call.Fun].Type + if typ != nil { + if sig, ok := typ.(*types.Signature); ok { + if !sig.Variadic() { + // Skip checking non-variadic functions. + return + } + params := sig.Params() + firstArg = params.Len() - 1 + + typ := params.At(firstArg).Type() + typ = typ.(*types.Slice).Elem() + it, ok := typ.(*types.Interface) + if !ok || !it.Empty() { + // Skip variadic functions accepting non-interface{} args. + return } } } + args := call.Args + if len(args) <= firstArg { + // Skip calls without variadic args. + return + } + args = args[firstArg:] + // check for Println(os.Stderr, ...) - if firstArg == 0 && !isF && len(args) > 0 { + if firstArg == 0 { if sel, ok := args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { @@ -610,31 +622,15 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { } } } - if len(args) <= firstArg { - // If we have a call to a method called Error that satisfies the Error interface, - // then it's ok. Otherwise it's something like (*T).Error from the testing package - // and we need to check it. - if name == "Error" && f.isErrorMethodCall(call) { - return - } - // If it's an Error call now, it's probably for printing errors. - if !isLn { - // Check the signature to be sure: there are niladic functions called "error". - if firstArg != 0 || f.numArgsInSignature(call) != firstArg { - f.Badf(call.Pos(), "no args in %s call", name) - } - } - return - } - arg := args[firstArg] + arg := args[0] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.Contains(lit.Value, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) } } - if isLn { + if strings.HasSuffix(name, "ln") { // The last item, if a string, should not have a newline. - arg = args[len(call.Args)-1] + arg = args[len(args)-1] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.HasSuffix(lit.Value, `\n"`) { f.Badf(call.Pos(), "%s call ends with newline", name) diff --git a/src/cmd/vet/testdata/print.go b/src/cmd/vet/testdata/print.go index 5c7ff35c90..261ee788c7 100644 --- a/src/cmd/vet/testdata/print.go +++ b/src/cmd/vet/testdata/print.go @@ -185,11 +185,11 @@ func PrintfTests() { // Something that looks like an error interface but isn't, such as the (*T).Error method // in the testing package. var et1 errorTest1 - fmt.Println(et1.Error()) // ERROR "no args in Error call" + fmt.Println(et1.Error()) // ok fmt.Println(et1.Error("hi")) // ok fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call" var et2 errorTest2 - et2.Error() // ERROR "no args in Error call" + et2.Error() // ok et2.Error("hi") // ok, not an error method. et2.Error("%d", 3) // ERROR "possible formatting directive in Error call" var et3 errorTest3 @@ -231,11 +231,41 @@ func PrintfTests() { externalprintf.Logf(level, "%d", 42) // OK externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK externalprintf.Logf(level, "%d") // ERROR "format reads arg 1, have only 0 args" + + // user-defined Println-like functions + ss := &someStruct{} + ss.Log(someFunction, "foo") // OK + ss.Error(someFunction, someFunction) // OK + ss.Println() // OK + ss.Println(1.234, "foo") // OK + ss.Println(1, someFunction) // ERROR "arg someFunction in Println call is a function value, not a function call" + ss.log(someFunction) // OK + ss.log(someFunction, "bar", 1.33) // OK + ss.log(someFunction, someFunction) // ERROR "arg someFunction in log call is a function value, not a function call" } +type someStruct struct{} + +// Log is non-variadic user-define Println-like function. +// Calls to this func must be skipped when checking +// for Println-like arguments. +func (ss *someStruct) Log(f func(), s string) {} + +// Error is variadic user-define Println-like function. +// Calls to this func mustn't be checked for Println-like arguments, +// since variadic arguments type isn't interface{}. +func (ss *someStruct) Error(args ...func()) {} + +// Println is variadic user-defined Println-like function. +// Calls to this func must be checked for Println-like arguments. +func (ss *someStruct) Println(args ...interface{}) {} + +// log is variadic user-defined Println-like function. +// Calls to this func must be checked for Println-like arguments. +func (ss *someStruct) log(f func(), args ...interface{}) {} + // A function we use as a function value; it has no other purpose. -func someFunction() { -} +func someFunction() {} // Printf is used by the test so we must declare it. func Printf(format string, args ...interface{}) { diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go index 692bae6192..4358955d93 100644 --- a/src/cmd/vet/types.go +++ b/src/cmd/vet/types.go @@ -292,72 +292,6 @@ func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Ex return true } -// numArgsInSignature tells how many formal arguments the function type -// being called has. -func (f *File) numArgsInSignature(call *ast.CallExpr) int { - // Check the type of the function or method declaration - typ := f.pkg.types[call.Fun].Type - if typ == nil { - return 0 - } - // The type must be a signature, but be sure for safety. - sig, ok := typ.(*types.Signature) - if !ok { - return 0 - } - return sig.Params().Len() -} - -// isErrorMethodCall reports whether the call is of a method with signature -// func Error() string -// where "string" is the universe's string type. We know the method is called "Error". -func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { - typ := f.pkg.types[call].Type - if typ != nil { - // We know it's called "Error", so just check the function signature - // (stringerType has exactly one method, String). - if stringerType != nil && stringerType.NumMethods() == 1 { - return types.Identical(f.pkg.types[call.Fun].Type, stringerType.Method(0).Type()) - } - } - // Without types, we can still check by hand. - // Is it a selector expression? Otherwise it's a function call, not a method call. - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return false - } - // The package is type-checked, so if there are no arguments, we're done. - if len(call.Args) > 0 { - return false - } - // Check the type of the method declaration - typ = f.pkg.types[sel].Type - if typ == nil { - return false - } - // The type must be a signature, but be sure for safety. - sig, ok := typ.(*types.Signature) - if !ok { - return false - } - // There must be a receiver for it to be a method call. Otherwise it is - // a function, not something that satisfies the error interface. - if sig.Recv() == nil { - return false - } - // There must be no arguments. Already verified by type checking, but be thorough. - if sig.Params().Len() > 0 { - return false - } - // Finally the real questions. - // There must be one result. - if sig.Results().Len() != 1 { - return false - } - // It must have return type "string" from the universe. - return sig.Results().At(0).Type() == types.Typ[types.String] -} - // hasMethod reports whether the type contains a method with the given name. // It is part of the workaround for Formatters and should be deleted when // that workaround is no longer necessary. -- cgit v1.3 From ff7ba773f43bf04c34ffb6ed67da464072e476f7 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 6 Apr 2016 10:49:12 -0700 Subject: cmd/gofmt: make gofmt -s simplify slices in presence of dot-imports A dot-import cannot possibly introduce a `len` function since that function would not be exported (it's lowercase). Furthermore, the existing code already (incorrectly) assumed that there was no other `len` function in another file of the package. Since this has been an ok assumption for years, let's leave it, but remove the dot-import restriction. Fixes #15153. Change-Id: I18fbb27acc5a5668833b4b4aead0cca540862b52 Reviewed-on: https://go-review.googlesource.com/21613 Reviewed-by: Alan Donovan Run-TryBot: Robert Griesemer TryBot-Result: Gobot Gobot --- src/cmd/gofmt/simplify.go | 28 ++++++---------- src/cmd/gofmt/testdata/slices2.golden | 63 ----------------------------------- src/cmd/gofmt/testdata/slices2.input | 63 ----------------------------------- 3 files changed, 10 insertions(+), 144 deletions(-) delete mode 100644 src/cmd/gofmt/testdata/slices2.golden delete mode 100644 src/cmd/gofmt/testdata/slices2.input (limited to 'src') diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go index 69f7bf23c0..2ebf4cde0b 100644 --- a/src/cmd/gofmt/simplify.go +++ b/src/cmd/gofmt/simplify.go @@ -10,11 +10,9 @@ import ( "reflect" ) -type simplifier struct { - hasDotImport bool // package file contains: import . "some/import/path" -} +type simplifier struct{} -func (s *simplifier) Visit(node ast.Node) ast.Visitor { +func (s simplifier) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.CompositeLit: // array, slice, and map composite literals may be simplified @@ -68,10 +66,13 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor { // a slice expression of the form: s[a:len(s)] // can be simplified to: s[a:] // if s is "simple enough" (for now we only accept identifiers) - if n.Max != nil || s.hasDotImport { + // + // Note: This may not be correct because len may have been redeclared in another + // file belonging to the same package. However, this is extremely unlikely + // and so far (April 2016, after years of supporting this rewrite feature) + // has never come up, so let's keep it working as is (see also #15153). + if n.Max != nil { // - 3-index slices always require the 2nd and 3rd index - // - if dot imports are present, we cannot be certain that an - // unresolved "len" identifier refers to the predefined len() break } if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil { @@ -118,20 +119,11 @@ func isBlank(x ast.Expr) bool { } func simplify(f *ast.File) { - var s simplifier - - // determine if f contains dot imports - for _, imp := range f.Imports { - if imp.Name != nil && imp.Name.Name == "." { - s.hasDotImport = true - break - } - } - // remove empty declarations such as "const ()", etc removeEmptyDeclGroups(f) - ast.Walk(&s, f) + var s simplifier + ast.Walk(s, f) } func removeEmptyDeclGroups(f *ast.File) { diff --git a/src/cmd/gofmt/testdata/slices2.golden b/src/cmd/gofmt/testdata/slices2.golden deleted file mode 100644 index ab657004e6..0000000000 --- a/src/cmd/gofmt/testdata/slices2.golden +++ /dev/null @@ -1,63 +0,0 @@ -//gofmt -s - -// Test cases for slice expression simplification. -// Because of a dot import, these slices must remain untouched. -package p - -import . "math" - -var ( - a [10]byte - b [20]float32 - s []int - t struct { - s []byte - } - - _ = a[0:] - _ = a[1:10] - _ = a[2:len(a)] - _ = a[3:(len(a))] - _ = a[len(a) : len(a)-1] - _ = a[0:len(b)] - - _ = a[:] - _ = a[:10] - _ = a[:len(a)] - _ = a[:(len(a))] - _ = a[:len(a)-1] - _ = a[:len(b)] - - _ = s[0:] - _ = s[1:10] - _ = s[2:len(s)] - _ = s[3:(len(s))] - _ = s[len(a) : len(s)-1] - _ = s[0:len(b)] - - _ = s[:] - _ = s[:10] - _ = s[:len(s)] - _ = s[:(len(s))] - _ = s[:len(s)-1] - _ = s[:len(b)] - - _ = t.s[0:] - _ = t.s[1:10] - _ = t.s[2:len(t.s)] - _ = t.s[3:(len(t.s))] - _ = t.s[len(a) : len(t.s)-1] - _ = t.s[0:len(b)] - - _ = t.s[:] - _ = t.s[:10] - _ = t.s[:len(t.s)] - _ = t.s[:(len(t.s))] - _ = t.s[:len(t.s)-1] - _ = t.s[:len(b)] -) - -func _() { - s := s[0:len(s)] - _ = s -} diff --git a/src/cmd/gofmt/testdata/slices2.input b/src/cmd/gofmt/testdata/slices2.input deleted file mode 100644 index ab657004e6..0000000000 --- a/src/cmd/gofmt/testdata/slices2.input +++ /dev/null @@ -1,63 +0,0 @@ -//gofmt -s - -// Test cases for slice expression simplification. -// Because of a dot import, these slices must remain untouched. -package p - -import . "math" - -var ( - a [10]byte - b [20]float32 - s []int - t struct { - s []byte - } - - _ = a[0:] - _ = a[1:10] - _ = a[2:len(a)] - _ = a[3:(len(a))] - _ = a[len(a) : len(a)-1] - _ = a[0:len(b)] - - _ = a[:] - _ = a[:10] - _ = a[:len(a)] - _ = a[:(len(a))] - _ = a[:len(a)-1] - _ = a[:len(b)] - - _ = s[0:] - _ = s[1:10] - _ = s[2:len(s)] - _ = s[3:(len(s))] - _ = s[len(a) : len(s)-1] - _ = s[0:len(b)] - - _ = s[:] - _ = s[:10] - _ = s[:len(s)] - _ = s[:(len(s))] - _ = s[:len(s)-1] - _ = s[:len(b)] - - _ = t.s[0:] - _ = t.s[1:10] - _ = t.s[2:len(t.s)] - _ = t.s[3:(len(t.s))] - _ = t.s[len(a) : len(t.s)-1] - _ = t.s[0:len(b)] - - _ = t.s[:] - _ = t.s[:10] - _ = t.s[:len(t.s)] - _ = t.s[:(len(t.s))] - _ = t.s[:len(t.s)-1] - _ = t.s[:len(b)] -) - -func _() { - s := s[0:len(s)] - _ = s -} -- cgit v1.3 From 5176a4b39b4595e5d9025e7aaf19146c29b7e349 Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Wed, 6 Apr 2016 15:01:50 +0200 Subject: testing: fix flakey test on plan9 allow for more than 0.00s. Fixes #15149 Change-Id: I1d428a9b3c9bb3d1db8682c53b86e44cecc1dde1 Reviewed-on: https://go-review.googlesource.com/21602 Reviewed-by: Brad Fitzpatrick Run-TryBot: Marcel van Lohuizen TryBot-Result: Gobot Gobot --- src/testing/sub_test.go | 59 ++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go index 7fe0fffd8f..4f26a53ab6 100644 --- a/src/testing/sub_test.go +++ b/src/testing/sub_test.go @@ -122,8 +122,8 @@ func TestTRun(t *T) { ok: false, maxPar: 1, output: ` ---- FAIL: failnow skips future sequential and parallel tests at same level (0.00s) - --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (0.00s) +--- FAIL: failnow skips future sequential and parallel tests at same level (N.NNs) + --- FAIL: failnow skips future sequential and parallel tests at same level/#00 (N.NNs) `, f: func(t *T) { ranSeq := false @@ -157,9 +157,9 @@ func TestTRun(t *T) { ok: false, maxPar: 1, output: ` ---- FAIL: failure in parallel test propagates upwards (0.00s) - --- FAIL: failure in parallel test propagates upwards/#00 (0.00s) - --- FAIL: failure in parallel test propagates upwards/#00/par (0.00s) +--- FAIL: failure in parallel test propagates upwards (N.NNs) + --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs) + --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs) `, f: func(t *T) { t.Run("", func(t *T) { @@ -176,7 +176,7 @@ func TestTRun(t *T) { chatty: true, output: ` === RUN skipping without message, chatty ---- SKIP: skipping without message, chatty (0.00s)`, +--- SKIP: skipping without message, chatty (N.NNs)`, f: func(t *T) { t.SkipNow() }, }, { desc: "chatty with recursion", @@ -186,9 +186,9 @@ func TestTRun(t *T) { === RUN chatty with recursion === RUN chatty with recursion/#00 === RUN chatty with recursion/#00/#00 ---- PASS: chatty with recursion (0.00s) - --- PASS: chatty with recursion/#00 (0.00s) - --- PASS: chatty with recursion/#00/#00 (0.00s)`, +--- PASS: chatty with recursion (N.NNs) + --- PASS: chatty with recursion/#00 (N.NNs) + --- PASS: chatty with recursion/#00/#00 (N.NNs)`, f: func(t *T) { t.Run("", func(t *T) { t.Run("", func(t *T) {}) @@ -201,9 +201,9 @@ func TestTRun(t *T) { }, { desc: "skipping after error", output: ` ---- FAIL: skipping after error (0.00s) - sub_test.go:nnn: an error - sub_test.go:nnn: skipped`, +--- FAIL: skipping after error (N.NNs) + sub_test.go:NNN: an error + sub_test.go:NNN: skipped`, f: func(t *T) { t.Error("an error") t.Skip("skipped") @@ -383,9 +383,10 @@ func TestTRun(t *T) { if ctx.running != 0 || ctx.numWaiting != 0 { t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting) } - got := sanitizeLog(buf.String()) - want := sanitizeLog(tc.output) - if got != want { + got := strings.TrimSpace(buf.String()) + want := strings.TrimSpace(tc.output) + re := makeRegexp(want) + if ok, err := regexp.MatchString(re, got); !ok || err != nil { t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) } } @@ -449,7 +450,7 @@ func TestBRun(t *T) { chatty: true, output: ` --- SKIP: root - sub_test.go:: skipping`, + sub_test.go:NNN: skipping`, f: func(b *B) { b.Skip("skipping") }, }, { desc: "chatty with recursion", @@ -467,8 +468,8 @@ func TestBRun(t *T) { failed: true, output: ` --- FAIL: root - sub_test.go:nnn: an error - sub_test.go:nnn: skipped`, + sub_test.go:NNN: an error + sub_test.go:NNN: skipped`, f: func(b *B) { b.Error("an error") b.Skip("skipped") @@ -523,25 +524,19 @@ func TestBRun(t *T) { if root.result.N != 1 { t.Errorf("%s: N for parent benchmark was %d; want 1", tc.desc, root.result.N) } - got := sanitizeLog(buf.String()) - want := sanitizeLog(tc.output) - if got != want { + got := strings.TrimSpace(buf.String()) + want := strings.TrimSpace(tc.output) + re := makeRegexp(want) + if ok, err := regexp.MatchString(re, got); !ok || err != nil { t.Errorf("%s:ouput:\ngot:\n%s\nwant:\n%s", tc.desc, got, want) } } } -// sanitizeLog removes line numbers from log entries. -func sanitizeLog(s string) string { - s = strings.TrimSpace(s) - lines := strings.Split(s, "\n") - for i, line := range lines { - p := strings.IndexByte(line, ':') - if p > 0 && line[p+4] == ':' { // assuming 3-digit file positions - lines[i] = line[:p+1] + line[p+4:] - } - } - return strings.Join(lines, "\n") +func makeRegexp(s string) string { + s = strings.Replace(s, ":NNN:", `:\d\d\d:`, -1) + s = strings.Replace(s, "(N.NNs)", `\(\d*\.\d*s\)`, -1) + return s } func TestBenchmarkOutput(t *T) { -- cgit v1.3 From f38f43d029de16f21f9102226d5c24684fb0ea25 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 1 Apr 2016 20:11:30 -0700 Subject: cmd/compile: shrink gc.Type in half MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/align.go | 12 +- src/cmd/compile/internal/gc/bexport.go | 4 +- src/cmd/compile/internal/gc/bimport.go | 28 +- src/cmd/compile/internal/gc/dcl.go | 10 +- src/cmd/compile/internal/gc/export.go | 2 +- src/cmd/compile/internal/gc/fmt.go | 17 +- src/cmd/compile/internal/gc/pgen.go | 2 +- src/cmd/compile/internal/gc/pgen_test.go | 20 +- src/cmd/compile/internal/gc/reflect.go | 90 +++--- src/cmd/compile/internal/gc/sizeof_test.go | 16 +- src/cmd/compile/internal/gc/ssa.go | 2 +- src/cmd/compile/internal/gc/type.go | 487 ++++++++++++++++++++++------- src/cmd/compile/internal/gc/typecheck.go | 17 +- src/cmd/compile/internal/gc/universe.go | 6 +- src/cmd/compile/internal/gc/walk.go | 2 +- 15 files changed, 490 insertions(+), 225 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index 9d5c3a550c..e43ed7b225 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -198,11 +198,11 @@ func dowidth(t *Type) { // make fake type to check later to // trigger channel argument check. - t1 := typWrapper(TCHANARGS, t) + t1 := typChanArgs(t) checkwidth(t1) case TCHANARGS: - t1 := t.Wrapped() + t1 := t.ChanArgs() dowidth(t1) // just in case if t1.Elem().Width >= 1<<16 { Yyerror("channel element type too large (>64kB)") @@ -271,18 +271,18 @@ func dowidth(t *Type) { // make fake type to check later to // trigger function argument computation. case TFUNC: - t1 := typWrapper(TFUNCARGS, t) + t1 := typFuncArgs(t) checkwidth(t1) w = int64(Widthptr) // width of func type is pointer // function is 3 cated structures; // compute their widths as side-effect. case TFUNCARGS: - t1 := t.Wrapped() + t1 := t.FuncArgs() w = widstruct(t1, t1.Recvs(), 0, 0) w = widstruct(t1, t1.Params(), w, Widthreg) w = widstruct(t1, t1.Results(), w, Widthreg) - t1.Argwid = w + t1.Extra.(*FuncType).Argwid = w if w%int64(Widthreg) != 0 { Warn("bad type %v %d\n", t1, w) } @@ -386,7 +386,7 @@ func Argsize(t *Type) int { } } - w = (w + int64(Widthptr) - 1) &^ (int64(Widthptr) - 1) + w = Rnd(w, int64(Widthptr)) if int64(int(w)) != w { Fatalf("argsize too big") } diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index f88afd2488..8dcf97b31d 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -602,7 +602,7 @@ func (p *exporter) typ(t *Type) { case TDDDFIELD: // see p.param use of TDDDFIELD p.tag(dddTag) - p.typ(t.Wrapped()) + p.typ(t.DDDField()) case TSTRUCT: p.tag(structTag) @@ -768,7 +768,7 @@ func (p *exporter) param(q *Field, n int, numbered bool) { t := q.Type if q.Isddd { // create a fake type to encode ... just for the p.typ call - t = typWrapper(TDDDFIELD, t.Elem()) + t = typDDDField(t.Elem()) } p.typ(t) if n > 0 { diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 8c53372b80..7ad4d9dbb0 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -359,16 +359,20 @@ func (p *importer) typ() *Type { case arrayTag, sliceTag: t = p.newtyp(TARRAY) + var bound int64 if i == arrayTag { - t.SetNumElem(p.int64()) + bound = p.int64() + } + elem := p.typ() + if i == arrayTag { + t.Extra = &ArrayType{Elem: elem, Bound: bound} } else { - t.SetNumElem(sliceBound) + t.Extra = SliceType{Elem: elem} } - t.Type = p.typ() case dddTag: t = p.newtyp(TDDDFIELD) - t.Type = p.typ() + t.Extra = DDDFieldType{T: p.typ()} case structTag: t = p.newtyp(TSTRUCT) @@ -376,7 +380,7 @@ func (p *importer) typ() *Type { case pointerTag: t = p.newtyp(Tptr) - t.Type = p.typ() + t.Extra = PtrType{Elem: p.typ()} case signatureTag: t = p.newtyp(TFUNC) @@ -393,13 +397,15 @@ func (p *importer) typ() *Type { case mapTag: t = p.newtyp(TMAP) - t.Down = p.typ() // key - t.Type = p.typ() // val + mt := t.MapType() + mt.Key = p.typ() + mt.Val = p.typ() case chanTag: t = p.newtyp(TCHAN) - t.Chan = ChanDir(p.int()) - t.Type = p.typ() + ct := t.ChanType() + ct.Dir = ChanDir(p.int()) + ct.Elem = p.typ() default: Fatalf("importer: unexpected type (tag = %d)", i) @@ -444,7 +450,7 @@ func (p *importer) field() *Node { // anonymous field - typ must be T or *T and T must be a type name s := typ.Sym if s == nil && typ.IsPtr() { - s = typ.Type.Sym // deref + s = typ.Elem().Sym // deref } pkg := importpkg if sym != nil { @@ -531,7 +537,7 @@ func (p *importer) param(named bool) *Node { isddd := false if typ.Etype == TDDDFIELD { // TDDDFIELD indicates wrapped ... slice type - typ = typSlice(typ.Wrapped()) + typ = typSlice(typ.DDDField()) isddd = true } diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index fb81545a46..c652c65962 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -743,8 +743,8 @@ func checkembeddedtype(t *Type) { if t.IsPtr() { Yyerror("embedded type cannot be a pointer") - } else if t.Etype == TFORW && t.Embedlineno == 0 { - t.Embedlineno = lineno + } else if t.Etype == TFORW && t.ForwardType().Embedlineno == 0 { + t.ForwardType().Embedlineno = lineno } } @@ -855,7 +855,7 @@ func tostruct0(t *Type, l []*Node) { func tofunargs(l []*Node) *Type { t := typ(TSTRUCT) - t.Funarg = true + t.StructType().Funarg = true fields := make([]*Field, len(l)) for i, n := range l { @@ -1061,11 +1061,11 @@ func functype0(t *Type, this *Node, in, out []*Node) { t.Broke = true } - t.Outnamed = false + t.FuncType().Outnamed = false if len(out) > 0 && out[0].Left != nil && out[0].Left.Orig != nil { s := out[0].Left.Orig.Sym if s != nil && (s.Name[0] != '~' || s.Name[1] != 'r') { // ~r%d is the name invented for an unnamed result - t.Outnamed = true + t.FuncType().Outnamed = true } } } diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 6de7da0667..17311cf6af 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -592,7 +592,7 @@ func dumpasmhdr() { case OTYPE: t := n.Type - if !t.IsStruct() || t.Map != nil || t.IsFuncArgStruct() { + if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() { break } fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width)) diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 27ccdfbdcf..5c5503619f 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -671,19 +671,20 @@ func typefmt(t *Type, flag FmtFlag) string { return buf.String() case TSTRUCT: - if t.Map != nil { + if m := t.StructType().Map; m != nil { + mt := m.MapType() // Format the bucket struct for map[x]y as map.bucket[x]y. // This avoids a recursive print that generates very long names. - if t.Map.Bucket == t { - return "map.bucket[" + t.Map.Key().String() + "]" + t.Map.Val().String() + if mt.Bucket == t { + return "map.bucket[" + m.Key().String() + "]" + m.Val().String() } - if t.Map.Hmap == t { - return "map.hdr[" + t.Map.Key().String() + "]" + t.Map.Val().String() + if mt.Hmap == t { + return "map.hdr[" + m.Key().String() + "]" + m.Val().String() } - if t.Map.Hiter == t { - return "map.iter[" + t.Map.Key().String() + "]" + t.Map.Val().String() + if mt.Hiter == t { + return "map.iter[" + m.Key().String() + "]" + m.Val().String() } Yyerror("unknown internal map type") @@ -735,7 +736,7 @@ func typefmt(t *Type, flag FmtFlag) string { if fmtmode == FExp { Fatalf("cannot use TDDDFIELD with old exporter") } - return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.Wrapped()) + return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.DDDField()) } if fmtmode == FExp { diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index efe10a419c..63f7bf825e 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -375,7 +375,7 @@ func compile(fn *Node) { // set up domain for labels clearlabels() - if Curfn.Type.Outnamed { + if Curfn.Type.FuncType().Outnamed { // add clearing of the output parameters for _, t := range Curfn.Type.Results().Fields().Slice() { if t.Nname != nil { diff --git a/src/cmd/compile/internal/gc/pgen_test.go b/src/cmd/compile/internal/gc/pgen_test.go index fcb8bfa0c2..44dc1db12e 100644 --- a/src/cmd/compile/internal/gc/pgen_test.go +++ b/src/cmd/compile/internal/gc/pgen_test.go @@ -10,6 +10,14 @@ import ( "testing" ) +func typeWithoutPointers() *Type { + return &Type{Etype: TSTRUCT, Extra: &StructType{Haspointers: 1}} // haspointers -> false +} + +func typeWithPointers() *Type { + return &Type{Etype: TSTRUCT, Extra: &StructType{Haspointers: 2}} // haspointers -> true +} + // Test all code paths for cmpstackvarlt. func TestCmpstackvar(t *testing.T) { testdata := []struct { @@ -62,13 +70,13 @@ func TestCmpstackvar(t *testing.T) { false, }, { - Node{Class: PAUTO, Type: &Type{Haspointers: 1}}, // haspointers -> false - Node{Class: PAUTO, Type: &Type{Haspointers: 2}}, // haspointers -> true + Node{Class: PAUTO, Type: typeWithoutPointers()}, + Node{Class: PAUTO, Type: typeWithPointers()}, false, }, { - Node{Class: PAUTO, Type: &Type{Haspointers: 2}}, // haspointers -> true - Node{Class: PAUTO, Type: &Type{Haspointers: 1}}, // haspointers -> false + Node{Class: PAUTO, Type: typeWithPointers()}, + Node{Class: PAUTO, Type: typeWithoutPointers()}, true, }, { @@ -127,7 +135,7 @@ func TestStackvarSort(t *testing.T) { {Class: PFUNC, Xoffset: 10, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PFUNC, Xoffset: 20, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Used: true, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, - {Class: PAUTO, Type: &Type{Haspointers: 1}, Name: &Name{}, Sym: &Sym{}}, // haspointers -> false + {Class: PAUTO, Type: typeWithoutPointers(), Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{}, Name: &Name{Needzero: true}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{Width: 1}, Name: &Name{}, Sym: &Sym{}}, @@ -148,7 +156,7 @@ func TestStackvarSort(t *testing.T) { {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{Name: "abc"}}, {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{Name: "xyz"}}, - {Class: PAUTO, Type: &Type{Haspointers: 1}, Name: &Name{}, Sym: &Sym{}}, // haspointers -> false + {Class: PAUTO, Type: typeWithoutPointers(), Name: &Name{}, Sym: &Sym{}}, } // haspointers updates Type.Haspointers as a side effect, so // exercise this function on all inputs so that reflect.DeepEqual diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index c069b35787..df9ef27b7a 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -86,8 +86,8 @@ func makefield(name string, t *Type) *Field { } func mapbucket(t *Type) *Type { - if t.Bucket != nil { - return t.Bucket + if t.MapType().Bucket != nil { + return t.MapType().Bucket } bucket := typ(TSTRUCT) @@ -157,17 +157,17 @@ func mapbucket(t *Type) *Type { Yyerror("bad math in mapbucket for %v", t) } - t.Bucket = bucket + t.MapType().Bucket = bucket - bucket.Map = t + bucket.StructType().Map = t return bucket } // Builds a type representing a Hmap structure for the given map type. // Make sure this stays in sync with ../../../../runtime/hashmap.go! func hmap(t *Type) *Type { - if t.Hmap != nil { - return t.Hmap + if t.MapType().Hmap != nil { + return t.MapType().Hmap } bucket := mapbucket(t) @@ -186,14 +186,14 @@ func hmap(t *Type) *Type { h.Local = t.Local h.SetFields(field[:]) dowidth(h) - t.Hmap = h - h.Map = t + t.MapType().Hmap = h + h.StructType().Map = t return h } func hiter(t *Type) *Type { - if t.Hiter != nil { - return t.Hiter + if t.MapType().Hiter != nil { + return t.MapType().Hiter } // build a struct: @@ -234,8 +234,8 @@ func hiter(t *Type) *Type { if i.Width != int64(12*Widthptr) { Yyerror("hash_iter size not correct %d %d", i.Width, 12*Widthptr) } - t.Hiter = i - i.Map = t + t.MapType().Hiter = i + i.StructType().Map = t return i } @@ -664,67 +664,47 @@ var kinds = []int{ } func haspointers(t *Type) bool { - if t.Haspointers != 0 { - return t.Haspointers-1 != 0 - } - - var ret bool switch t.Etype { - case TINT, - TUINT, - TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TUINTPTR, - TFLOAT32, - TFLOAT64, - TCOMPLEX64, - TCOMPLEX128, - TBOOL: - ret = false + case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, + TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL: + return false case TARRAY: if t.IsSlice() { - ret = true - break + return true } - if t.NumElem() == 0 { // empty array - ret = false - break + at := t.Extra.(*ArrayType) + if at.Haspointers != 0 { + return at.Haspointers-1 != 0 } - ret = haspointers(t.Elem()) + ret := false + if t.NumElem() != 0 { // non-empty array + ret = haspointers(t.Elem()) + } + + at.Haspointers = 1 + uint8(obj.Bool2int(ret)) + return ret case TSTRUCT: - ret = false + st := t.StructType() + if st.Haspointers != 0 { + return st.Haspointers-1 != 0 + } + + ret := false for _, t1 := range t.Fields().Slice() { if haspointers(t1.Type) { ret = true break } } - - case TSTRING, - TPTR32, - TPTR64, - TUNSAFEPTR, - TINTER, - TCHAN, - TMAP, - TFUNC: - fallthrough - default: - ret = true + st.Haspointers = 1 + uint8(obj.Bool2int(ret)) + return ret } - t.Haspointers = 1 + uint8(obj.Bool2int(ret)) - return ret + return true } // typeptrdata returns the length in bytes of the prefix of t diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go index 11c0f419da..8b0dfe538e 100644 --- a/src/cmd/compile/internal/gc/sizeof_test.go +++ b/src/cmd/compile/internal/gc/sizeof_test.go @@ -27,7 +27,21 @@ func TestSizeof(t *testing.T) { {Name{}, 52, 80}, {Node{}, 92, 144}, {Sym{}, 60, 112}, - {Type{}, 116, 184}, + {Type{}, 56, 88}, + {MapType{}, 20, 40}, + {ForwardType{}, 16, 32}, + {FuncType{}, 28, 48}, + {StructType{}, 12, 24}, + {InterType{}, 4, 8}, + {ChanType{}, 8, 16}, + {ArrayType{}, 16, 24}, + {InterMethType{}, 4, 8}, + {DDDFieldType{}, 4, 8}, + {FuncArgsType{}, 4, 8}, + {ChanArgsType{}, 4, 8}, + {PtrType{}, 4, 8}, + {SliceType{}, 4, 8}, + {DDDArrayType{}, 4, 8}, } for _, tt := range tests { diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 1c2e528384..127a7c4698 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4218,7 +4218,7 @@ func (e *ssaExport) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.Local func (e *ssaExport) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) { n := name.N.(*Node) - ptrType := Ptrto(n.Type.Type) + ptrType := Ptrto(n.Type.Elem()) lenType := Types[TINT] if n.Class == PAUTO && !n.Addrtaken { // Split this slice up into three separate variables. diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index e04cfcda63..3d2f01ef7d 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -122,55 +122,174 @@ var ( // A Type represents a Go type. type Type struct { - Etype EType - Noalg bool - Chan ChanDir - Trecur uint8 // to detect loops - Printed bool - Funarg bool // TSTRUCT only: whether this struct represents function parameters - Local bool // created in this file - Deferwidth bool - Broke bool // broken type definition. - Align uint8 - Haspointers uint8 // 0 unknown, 1 no, 2 yes - Outnamed bool // on TFUNC - - Nod *Node // canonical OTYPE node - Orig *Type // original type (type literal or predefined type) + // Extra contains extra etype-specific fields. + // As an optimization, those etype-specific structs which contain exactly + // one pointer-shaped field are stored as values rather than pointers when possible. + // + // TMAP: *MapType + // TFORW: *ForwardType + // TFUNC: *FuncType + // TINTERMETHOD: InterMethType + // TSTRUCT: *StructType + // TINTER: *InterType + // TDDDFIELD: DDDFieldType + // TFUNCARGS: FuncArgsType + // TCHANARGS: ChanArgsType + // TCHAN: *ChanType + // TPTR32, TPTR64: PtrType + // TARRAY: *ArrayType, SliceType, or DDDArrayType + Extra interface{} + + // Width is the width of this Type in bytes. + Width int64 methods Fields allMethods Fields - Sym *Sym + Nod *Node // canonical OTYPE node + Orig *Type // original type (type literal or predefined type) + + Sym *Sym // symbol containing name, for named types Vargen int32 // unique name for OTYPE/ONAME - Lineno int32 + Lineno int32 // line at which this type was declared, implicitly or explicitly + + Maplineno int32 // first use of this type as a map key + + Etype EType // kind of type + Noalg bool // suppress hash and eq algorithm generation + Trecur uint8 // to detect loops + Printed bool // prevent duplicate export printing + Local bool // created in this file + Deferwidth bool + Broke bool // broken type definition. + Align uint8 // the required alignment of this type, in bytes +} + +// MapType contains Type fields specific to maps. +type MapType struct { + Key *Type // Key type + Val *Type // Val (elem) type + + Bucket *Type // internal struct type representing a hash bucket + Hmap *Type // internal struct type representing the Hmap (map header object) + Hiter *Type // internal struct type representing hash iterator state +} + +// MapType returns t's extra map-specific fields. +func (t *Type) MapType() *MapType { + t.wantEtype(TMAP) + return t.Extra.(*MapType) +} + +// ForwardType contains Type fields specific to forward types. +type ForwardType struct { + Copyto []*Node // where to copy the eventual value to + Embedlineno int32 // first use of this type as an embedded type +} + +// ForwardType returns t's extra forward-type-specific fields. +func (t *Type) ForwardType() *ForwardType { + t.wantEtype(TFORW) + return t.Extra.(*ForwardType) +} + +// FuncType contains Type fields specific to func types. +type FuncType struct { + Receiver *Type // function receiver + Results *Type // function results + Params *Type // function params + + Nname *Node - nname *Node + // Argwid is the total width of the function receiver, params, and results. + // It gets calculated via a temporary TFUNCARGS type. + // Note that TFUNC's Width is Widthptr. Argwid int64 - // most nodes - Type *Type // element type for TARRAY, TCHAN, TMAP, TPTRxx - Width int64 + Outnamed bool +} + +// FuncType returns t's extra func-specific fields. +func (t *Type) FuncType() *FuncType { + t.wantEtype(TFUNC) + return t.Extra.(*FuncType) +} + +// InterMethType contains Type fields specific to interface method psuedo-types. +type InterMethType struct { + Nname *Node +} - // TSTRUCT +// StructType contains Type fields specific to struct types. +type StructType struct { fields Fields - Down *Type // key type in TMAP; next struct in Funarg TSTRUCT + // Maps have three associated internal structs (see struct MapType). + // Map links such structs back to their map type. + Map *Type - // TARRAY - Bound int64 // negative is slice + Funarg bool // whether this struct represents function parameters + Haspointers uint8 // 0 unknown, 1 no, 2 yes +} + +// StructType returns t's extra struct-specific fields. +func (t *Type) StructType() *StructType { + t.wantEtype(TSTRUCT) + return t.Extra.(*StructType) +} + +// InterType contains Type fields specific to interface types. +type InterType struct { + fields Fields +} + +// PtrType contains Type fields specific to pointer types. +type PtrType struct { + Elem *Type // element type +} + +// DDDFieldType contains Type fields specific to TDDDFIELD types. +type DDDFieldType struct { + T *Type // reference to a slice type for ... args +} + +// ChanArgsType contains Type fields specific to TCHANARGS types. +type ChanArgsType struct { + T *Type // reference to a chan type whose elements need a width check +} - // TMAP - Bucket *Type // internal type representing a hash bucket - Hmap *Type // internal type representing a Hmap (map header object) - Hiter *Type // internal type representing hash iterator state - Map *Type // link from the above 3 internal types back to the map type. +// // FuncArgsType contains Type fields specific to TFUNCARGS types. +type FuncArgsType struct { + T *Type // reference to a func type whose elements need a width check +} - Maplineno int32 // first use of TFORW as map key - Embedlineno int32 // first use of TFORW as embedded type +// ChanType contains Type fields specific to channel types. +type ChanType struct { + Elem *Type // element type + Dir ChanDir // channel direction +} - // for TFORW, where to copy the eventual value to - Copyto []*Node +// ChanType returns t's extra channel-specific fields. +func (t *Type) ChanType() *ChanType { + t.wantEtype(TCHAN) + return t.Extra.(*ChanType) +} + +// ArrayType contains Type fields specific to array types with known lengths. +type ArrayType struct { + Elem *Type // element type + Bound int64 // number of elements; always >= 0; do not use with sliceBound or dddBound + Haspointers uint8 // 0 unknown, 1 no, 2 yes +} + +// SliceType contains Type fields specific to slice types. +type SliceType struct { + Elem *Type // element type +} + +// DDDArrayType contains Type fields specific to ddd array types. +type DDDArrayType struct { + Elem *Type // element type } // A Field represents a field in a struct or a method in an interface or @@ -252,38 +371,61 @@ func typ(et EType) *Type { Lineno: lineno, } t.Orig = t + // TODO(josharian): lazily initialize some of these? + switch t.Etype { + case TMAP: + t.Extra = new(MapType) + case TFORW: + t.Extra = new(ForwardType) + case TFUNC: + t.Extra = new(FuncType) + case TINTERMETH: + t.Extra = InterMethType{} + case TSTRUCT: + t.Extra = new(StructType) + case TINTER: + t.Extra = new(InterType) + case TPTR32, TPTR64: + t.Extra = PtrType{} + case TCHANARGS: + t.Extra = ChanArgsType{} + case TFUNCARGS: + t.Extra = FuncArgsType{} + case TDDDFIELD: + t.Extra = DDDFieldType{} + case TCHAN: + t.Extra = new(ChanType) + } return t } // typArray returns a new fixed-length array Type. func typArray(elem *Type, bound int64) *Type { t := typ(TARRAY) - t.Type = elem - t.Bound = bound + t.Extra = &ArrayType{Elem: elem, Bound: bound} return t } // typSlice returns a new slice Type. func typSlice(elem *Type) *Type { t := typ(TARRAY) - t.Type = elem - t.Bound = sliceBound + t.Extra = SliceType{Elem: elem} return t } // typDDDArray returns a new [...]T array Type. func typDDDArray(elem *Type) *Type { t := typ(TARRAY) - t.Type = elem - t.Bound = dddBound + t.Extra = DDDArrayType{Elem: elem} return t } // typChan returns a new chan Type with direction dir. func typChan(elem *Type, dir ChanDir) *Type { t := typ(TCHAN) - t.Type = elem - t.Chan = dir + ct := t.ChanType() + ct.Elem = elem + ct.Dir = dir return t } @@ -294,29 +436,39 @@ func typMap(k, v *Type) *Type { } t := typ(TMAP) - t.Down = k - t.Type = v + mt := t.MapType() + mt.Key = k + mt.Val = v return t } // typPtr returns a new pointer type pointing to t. func typPtr(elem *Type) *Type { t := typ(Tptr) - t.Type = elem + t.Extra = PtrType{Elem: elem} t.Width = int64(Widthptr) t.Align = uint8(Widthptr) return t } -// typWrapper returns a new wrapper psuedo-type. -func typWrapper(et EType, wrapped *Type) *Type { - switch et { - case TCHANARGS, TFUNCARGS, TDDDFIELD: - default: - Fatalf("typWrapper bad etype %s", et) - } - t := typ(et) - t.Type = wrapped +// typDDDField returns a new TDDDFIELD type for slice type s. +func typDDDField(s *Type) *Type { + t := typ(TDDDFIELD) + t.Extra = DDDFieldType{T: s} + return t +} + +// typChanArgs returns a new TCHANARGS type for channel type c. +func typChanArgs(c *Type) *Type { + t := typ(TCHANARGS) + t.Extra = ChanArgsType{T: c} + return t +} + +// typFuncArgs returns a new TFUNCARGS type for func type f. +func typFuncArgs(f *Type) *Type { + t := typ(TFUNCARGS) + t.Extra = FuncArgsType{T: f} return t } @@ -362,20 +514,43 @@ func substAny(t *Type, types *[]*Type) *Type { t = (*types)[0] *types = (*types)[1:] - case TPTR32, TPTR64, TCHAN, TARRAY: - elem := substAny(t.Type, types) - if elem != t.Type { + case TPTR32, TPTR64: + elem := substAny(t.Elem(), types) + if elem != t.Elem() { + t = t.Copy() + t.Extra = PtrType{Elem: elem} + } + + case TARRAY: + elem := substAny(t.Elem(), types) + if elem != t.Elem() { + t = t.Copy() + switch x := t.Extra.(type) { + case *ArrayType: + x.Elem = elem + case SliceType: + t.Extra = SliceType{Elem: elem} + case DDDArrayType: + t.Extra = DDDArrayType{Elem: elem} + default: + Fatalf("substAny bad array elem type %T %v", x, t) + } + } + + case TCHAN: + elem := substAny(t.Elem(), types) + if elem != t.Elem() { t = t.Copy() - t.Type = elem + t.Extra.(*ChanType).Elem = elem } case TMAP: - key := substAny(t.Down, types) - val := substAny(t.Type, types) - if key != t.Down || val != t.Type { + key := substAny(t.Key(), types) + val := substAny(t.Val(), types) + if key != t.Key() || val != t.Val() { t = t.Copy() - t.Down = key - t.Type = val + t.Extra.(*MapType).Key = key + t.Extra.(*MapType).Val = val } case TFUNC: @@ -426,6 +601,32 @@ func (t *Type) Copy() *Type { return nil } nt := *t + // copy any *T Extra fields, to avoid aliasing + switch t.Etype { + case TMAP: + x := *t.Extra.(*MapType) + nt.Extra = &x + case TFORW: + x := *t.Extra.(*ForwardType) + nt.Extra = &x + case TFUNC: + x := *t.Extra.(*FuncType) + nt.Extra = &x + case TSTRUCT: + x := *t.Extra.(*StructType) + nt.Extra = &x + case TINTER: + x := *t.Extra.(*InterType) + nt.Extra = &x + case TCHAN: + x := *t.Extra.(*ChanType) + nt.Extra = &x + case TARRAY: + if arr, ok := t.Extra.(*ArrayType); ok { + x := *arr + nt.Extra = &x + } + } // TODO(mdempsky): Find out why this is necessary and explain. if t.Orig == t { nt.Orig = &nt @@ -483,17 +684,17 @@ func (t *Type) wantEtype2(et1, et2 EType) { func (t *Type) RecvsP() **Type { t.wantEtype(TFUNC) - return &t.Type + return &t.Extra.(*FuncType).Receiver } func (t *Type) ParamsP() **Type { t.wantEtype(TFUNC) - return &t.Type.Down.Down + return &t.Extra.(*FuncType).Params } func (t *Type) ResultsP() **Type { t.wantEtype(TFUNC) - return &t.Type.Down + return &t.Extra.(*FuncType).Results } func (t *Type) Recvs() *Type { return *t.RecvsP() } @@ -524,51 +725,82 @@ var paramsResults = [2]func(*Type) *Type{ // Key returns the key type of map type t. func (t *Type) Key() *Type { t.wantEtype(TMAP) - return t.Down + return t.Extra.(*MapType).Key } // Val returns the value type of map type t. func (t *Type) Val() *Type { t.wantEtype(TMAP) - return t.Type + return t.Extra.(*MapType).Val } // Elem returns the type of elements of t. // Usable with pointers, channels, arrays, and slices. func (t *Type) Elem() *Type { switch t.Etype { - case TPTR32, TPTR64, TCHAN, TARRAY: - default: - Fatalf("Type.Elem %s", t.Etype) + case TPTR32, TPTR64: + return t.Extra.(PtrType).Elem + case TARRAY: + switch t := t.Extra.(type) { + case *ArrayType: + return t.Elem + case SliceType: + return t.Elem + case DDDArrayType: + return t.Elem + } + case TCHAN: + return t.Extra.(*ChanType).Elem } - return t.Type + Fatalf("Type.Elem %s", t.Etype) + return nil } -// Wrapped returns the type that pseudo-type t wraps. -func (t *Type) Wrapped() *Type { - switch t.Etype { - case TCHANARGS, TFUNCARGS, TDDDFIELD: - default: - Fatalf("Type.Wrapped %s", t.Etype) - } - return t.Type +// DDDField returns the slice ... type for TDDDFIELD type t. +func (t *Type) DDDField() *Type { + t.wantEtype(TDDDFIELD) + return t.Extra.(DDDFieldType).T +} + +// ChanArgs returns the channel type for TCHANARGS type t. +func (t *Type) ChanArgs() *Type { + t.wantEtype(TCHANARGS) + return t.Extra.(ChanArgsType).T +} + +// FuncArgs returns the channel type for TFUNCARGS type t. +func (t *Type) FuncArgs() *Type { + t.wantEtype(TFUNCARGS) + return t.Extra.(FuncArgsType).T } // Nname returns the associated function's nname. func (t *Type) Nname() *Node { - t.wantEtype2(TFUNC, TINTERMETH) - return t.nname + switch t.Etype { + case TFUNC: + return t.Extra.(*FuncType).Nname + case TINTERMETH: + return t.Extra.(InterMethType).Nname + } + Fatalf("Type.Nname %v %v", t.Etype, t) + return nil } // Nname sets the associated function's nname. func (t *Type) SetNname(n *Node) { - t.wantEtype2(TFUNC, TINTERMETH) - t.nname = n + switch t.Etype { + case TFUNC: + t.Extra.(*FuncType).Nname = n + case TINTERMETH: + t.Extra = InterMethType{Nname: n} + default: + Fatalf("Type.SetNname %v %v", t.Etype, t) + } } // IsFuncArgStruct reports whether t is a struct representing function parameters. func (t *Type) IsFuncArgStruct() bool { - return t.Etype == TSTRUCT && t.Funarg + return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg } func (t *Type) Methods() *Fields { @@ -582,10 +814,14 @@ func (t *Type) AllMethods() *Fields { } func (t *Type) Fields() *Fields { - if t.Etype != TSTRUCT && t.Etype != TINTER { - Fatalf("Fields: type %v does not have fields", t) + switch t.Etype { + case TSTRUCT: + return &t.Extra.(*StructType).fields + case TINTER: + return &t.Extra.(*InterType).fields } - return &t.fields + Fatalf("Fields: type %v does not have fields", t) + return nil } // Field returns the i'th field/method of struct/interface type t. @@ -608,15 +844,15 @@ func (t *Type) isDDDArray() bool { if t.Etype != TARRAY { return false } - t.checkBound() - return t.Bound == dddBound + _, ok := t.Extra.(DDDArrayType) + return ok } // ArgWidth returns the total aligned argument size for a function. // It includes the receiver, parameters, and results. func (t *Type) ArgWidth() int64 { t.wantEtype(TFUNC) - return t.Argwid + return t.Extra.(*FuncType).Argwid } func (t *Type) Size() int64 { @@ -764,20 +1000,20 @@ func (t *Type) cmp(x *Type) ssa.Cmp { // by the general code after the switch. case TSTRUCT: - if t.Map == nil { - if x.Map != nil { + if t.StructType().Map == nil { + if x.StructType().Map != nil { return ssa.CMPlt // nil < non-nil } // to the fallthrough - } else if x.Map == nil { + } else if x.StructType().Map == nil { return ssa.CMPgt // nil > non-nil - } else if t.Map.Bucket == t { + } else if t.StructType().Map.MapType().Bucket == t { // Both have non-nil Map // Special case for Maps which include a recursive type where the recursion is not broken with a named type - if x.Map.Bucket != x { + if x.StructType().Map.MapType().Bucket != x { return ssa.CMPlt // bucket maps are least } - return t.Map.cmp(x.Map) + return t.StructType().Map.cmp(x.StructType().Map) } // If t != t.Map.Bucket, fall through to general case fallthrough @@ -910,21 +1146,22 @@ func (t *Type) IsChan() bool { return t.Etype == TCHAN } -// checkBound enforces that Bound has an acceptable value. -func (t *Type) checkBound() { - if t.Bound != sliceBound && t.Bound < 0 && t.Bound != dddBound { - Fatalf("bad TARRAY bounds %d %s", t.Bound, t) - } -} - +// TODO: Remove noinline when issue 15084 is resolved. +//go:noinline func (t *Type) IsSlice() bool { - t.checkBound() - return t.Etype == TARRAY && t.Bound == sliceBound + if t.Etype != TARRAY { + return false + } + _, ok := t.Extra.(SliceType) + return ok } func (t *Type) IsArray() bool { - t.checkBound() - return t.Etype == TARRAY && t.Bound >= 0 + if t.Etype != TARRAY { + return false + } + _, ok := t.Extra.(*ArrayType) + return ok } func (t *Type) IsStruct() bool { @@ -961,24 +1198,48 @@ func (t *Type) FieldOff(i int) int64 { func (t *Type) NumElem() int64 { t.wantEtype(TARRAY) - t.checkBound() - return t.Bound + switch t := t.Extra.(type) { + case *ArrayType: + return t.Bound + case SliceType: + return sliceBound + case DDDArrayType: + return dddBound + } + Fatalf("NumElem on non-array %T %v", t.Extra, t) + return 0 } // SetNumElem sets the number of elements in an array type. // It should not be used if at all possible. // Create a new array/slice/dddArray with typX instead. -// TODO(josharian): figure out how to get rid of this. +// The only allowed uses are: +// * array -> slice as a hack to suppress extra error output +// * ddd array -> array +// TODO(josharian): figure out how to get rid of this entirely. func (t *Type) SetNumElem(n int64) { t.wantEtype(TARRAY) - t.Bound = n + switch { + case n >= 0: + if !t.isDDDArray() { + Fatalf("SetNumElem non-ddd -> array %v", t) + } + t.Extra = &ArrayType{Elem: t.Elem(), Bound: n} + case n == sliceBound: + if !t.IsArray() { + Fatalf("SetNumElem non-array -> slice %v", t) + } + t.Extra = SliceType{Elem: t.Elem()} + default: + Fatalf("SetNumElem %d %v", n, t) + } } // ChanDir returns the direction of a channel type t. // The direction will be one of Crecv, Csend, or Cboth. func (t *Type) ChanDir() ChanDir { t.wantEtype(TCHAN) - return t.Chan + return t.Extra.(*ChanType).Dir } func (t *Type) IsMemory() bool { return false } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index db74a0d246..ab7d257aac 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2103,7 +2103,7 @@ OpSwitch: return n } - if Curfn.Type.Outnamed && n.List.Len() == 0 { + if Curfn.Type.FuncType().Outnamed && n.List.Len() == 0 { break OpSwitch } typecheckaste(ORETURN, nil, false, Curfn.Type.Results(), n.List, func() string { return "return argument" }) @@ -2161,12 +2161,8 @@ OpSwitch: t := n.Type if t != nil && !t.IsFuncArgStruct() && n.Op != OTYPE { switch t.Etype { - case TFUNC, // might have TANY; wait until its called - TANY, - TFORW, - TIDEAL, - TNIL, - TBLANK: + case TFUNC, // might have TANY; wait until it's called + TANY, TFORW, TIDEAL, TNIL, TBLANK: break default: @@ -3522,13 +3518,13 @@ var mapqueue []*Node func copytype(n *Node, t *Type) { if t.Etype == TFORW { // This type isn't computed yet; when it is, update n. - t.Copyto = append(t.Copyto, n) + t.ForwardType().Copyto = append(t.ForwardType().Copyto, n) return } maplineno := n.Type.Maplineno - embedlineno := n.Type.Embedlineno - l := n.Type.Copyto + embedlineno := n.Type.ForwardType().Embedlineno + l := n.Type.ForwardType().Copyto // TODO(mdempsky): Fix Type rekinding. *n.Type = *t @@ -3544,7 +3540,6 @@ func copytype(n *Node, t *Type) { t.Nod = nil t.Printed = false t.Deferwidth = false - t.Copyto = nil // Update nodes waiting on this type. for _, n := range l { diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go index c2ba9c9a93..3330fbbab2 100644 --- a/src/cmd/compile/internal/gc/universe.go +++ b/src/cmd/compile/internal/gc/universe.go @@ -359,16 +359,16 @@ func lexinit1() { // t = interface { Error() string } rcvr := typ(TSTRUCT) - rcvr.Funarg = true + rcvr.StructType().Funarg = true field := newField() field.Type = Ptrto(typ(TSTRUCT)) rcvr.SetFields([]*Field{field}) in := typ(TSTRUCT) - in.Funarg = true + in.StructType().Funarg = true out := typ(TSTRUCT) - out.Funarg = true + out.StructType().Funarg = true field = newField() field.Type = Types[TSTRING] out.SetFields([]*Field{field}) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 392dae0fa9..ff8ddea7f6 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -287,7 +287,7 @@ func walkstmt(n *Node) *Node { if n.List.Len() == 0 { break } - if (Curfn.Type.Outnamed && n.List.Len() > 1) || paramoutheap(Curfn) { + if (Curfn.Type.FuncType().Outnamed && n.List.Len() > 1) || paramoutheap(Curfn) { // assign to the function out parameters, // so that reorder3 can fix up conflicts var rl []*Node -- cgit v1.3 From 2cefd12a1bf7ee1d1aad03e17c4680d4b611d6da Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 19:02:27 +0000 Subject: net, runtime: skip flaky tests on OpenBSD Flaky tests are a distraction and cover up real problems. File bugs instead and mark them as flaky. This moves the net/http flaky test flagging mechanism to internal/testenv. Updates #15156 Updates #15157 Updates #15158 Change-Id: I0e561cd2a09c0dec369cd4ed93bc5a2b40233dfe Reviewed-on: https://go-review.googlesource.com/21614 Reviewed-by: Matthew Dempsky Run-TryBot: Brad Fitzpatrick --- src/context/context_test.go | 4 ++++ src/go/build/deps_test.go | 2 +- src/internal/testenv/testenv.go | 9 +++++++++ src/net/dial_test.go | 4 ++++ src/net/http/main_test.go | 9 --------- src/net/http/transport_test.go | 3 ++- src/net/timeout_test.go | 4 ++++ src/net/unixsock_test.go | 4 ++++ src/runtime/pprof/pprof_test.go | 3 +++ 9 files changed, 31 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/context/context_test.go b/src/context/context_test.go index 05345fc5e5..60020303c7 100644 --- a/src/context/context_test.go +++ b/src/context/context_test.go @@ -6,6 +6,7 @@ package context import ( "fmt" + "internal/testenv" "math/rand" "runtime" "strings" @@ -258,6 +259,9 @@ func TestDeadline(t *testing.T) { } func TestTimeout(t *testing.T) { + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 15158) + } c, _ := WithTimeout(Background(), 100*time.Millisecond) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index c066048630..8e2fd6e584 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -168,7 +168,7 @@ var pkgDeps = map[string][]string{ "testing": {"L2", "flag", "fmt", "os", "runtime/debug", "runtime/pprof", "runtime/trace", "time"}, "testing/iotest": {"L2", "log"}, "testing/quick": {"L2", "flag", "fmt", "reflect"}, - "internal/testenv": {"L2", "OS", "testing"}, + "internal/testenv": {"L2", "OS", "flag", "testing"}, // L4 is defined as L3+fmt+log+time, because in general once // you're using L3 packages, use of fmt, log, or time is not a big deal. diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go index e751e0cf11..9e684e3034 100644 --- a/src/internal/testenv/testenv.go +++ b/src/internal/testenv/testenv.go @@ -11,6 +11,7 @@ package testenv import ( + "flag" "os" "os/exec" "path/filepath" @@ -124,3 +125,11 @@ func MustHaveExternalNetwork(t *testing.T) { t.Skipf("skipping test: no external network in -short mode") } } + +var flaky = flag.Bool("flaky", false, "run known-flaky tests too") + +func SkipFlaky(t *testing.T, issue int) { + if !*flaky { + t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) + } +} diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 2fc75c6356..f8e90abb48 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -59,6 +59,8 @@ func TestDialTimeoutFDLeak(t *testing.T) { switch runtime.GOOS { case "plan9": t.Skipf("%s does not have full support of socktest", runtime.GOOS) + case "openbsd": + testenv.SkipFlaky(t, 15157) } const T = 100 * time.Millisecond @@ -126,6 +128,8 @@ func TestDialerDualStackFDLeak(t *testing.T) { t.Skipf("%s does not have full support of socktest", runtime.GOOS) case "windows": t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS) + case "openbsd": + testenv.SkipFlaky(t, 15157) } if !supportsIPv4 || !supportsIPv6 { t.Skip("both IPv4 and IPv6 are required") diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go index 299cd7b2d2..1163874ac2 100644 --- a/src/net/http/main_test.go +++ b/src/net/http/main_test.go @@ -5,7 +5,6 @@ package http_test import ( - "flag" "fmt" "net/http" "os" @@ -16,8 +15,6 @@ import ( "time" ) -var flaky = flag.Bool("flaky", false, "run known-flaky tests too") - func TestMain(m *testing.M) { v := m.Run() if v == 0 && goroutineLeaked() { @@ -91,12 +88,6 @@ func setParallel(t *testing.T) { } } -func setFlaky(t *testing.T, issue int) { - if !*flaky { - t.Skipf("skipping known flaky test; see golang.org/issue/%d", issue) - } -} - func afterTest(t testing.TB) { http.DefaultTransport.(*http.Transport).CloseIdleConnections() if testing.Short() { diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 7a01dca394..1aa26610b0 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -18,6 +18,7 @@ import ( "crypto/tls" "errors" "fmt" + "internal/testenv" "io" "io/ioutil" "log" @@ -2229,7 +2230,7 @@ func TestTransportTLSHandshakeTimeout(t *testing.T) { // Trying to repro golang.org/issue/3514 func TestTLSServerClosesConnection(t *testing.T) { defer afterTest(t) - setFlaky(t, 7634) + testenv.SkipFlaky(t, 7634) closedc := make(chan bool, 1) ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index d80e478c77..3ea0ec1ebd 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -6,6 +6,7 @@ package net import ( "fmt" + "internal/testenv" "io" "io/ioutil" "net/internal/socktest" @@ -112,6 +113,9 @@ var dialTimeoutMaxDurationTests = []struct { func TestDialTimeoutMaxDuration(t *testing.T) { t.Parallel() + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 15157) + } ln, err := newLocalListener("tcp") if err != nil { diff --git a/src/net/unixsock_test.go b/src/net/unixsock_test.go index d70c0d1953..f0f88ed37b 100644 --- a/src/net/unixsock_test.go +++ b/src/net/unixsock_test.go @@ -8,6 +8,7 @@ package net import ( "bytes" + "internal/testenv" "os" "reflect" "runtime" @@ -20,6 +21,9 @@ func TestReadUnixgramWithUnnamedSocket(t *testing.T) { if !testableNetwork("unixgram") { t.Skip("unixgram test") } + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 15157) + } addr := testUnixAddr() la, err := ResolveUnixAddr("unixgram", addr) diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index fa0af59b37..23bc72c1e4 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -585,6 +585,9 @@ func func3(c chan int) { <-c } func func4(c chan int) { <-c } func TestGoroutineCounts(t *testing.T) { + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 15156) + } c := make(chan int) for i := 0; i < 100; i++ { if i%10 == 0 { -- cgit v1.3 From cdc0ebbebe64d8fa601914945112db306c85c426 Mon Sep 17 00:00:00 2001 From: Håvard Haugen Date: Wed, 3 Feb 2016 23:41:55 +0100 Subject: encoding/json: respect json.Marshaler when encoding byte kind slices Fixes #13783. Change-Id: I0122c1f0cf4075acabf5f58241bded1835699dc1 Reviewed-on: https://go-review.googlesource.com/19725 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/encoding/json/encode.go | 4 ++- src/encoding/json/encode_test.go | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index bcae6838cc..927f47b179 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -679,7 +679,9 @@ func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, _ bool) { func newSliceEncoder(t reflect.Type) encoderFunc { // Byte slices get special treatment; arrays don't. - if t.Elem().Kind() == reflect.Uint8 { + if t.Elem().Kind() == reflect.Uint8 && + !t.Elem().Implements(marshalerType) && + !t.Elem().Implements(textMarshalerType) { return encodeByteSlice } enc := &sliceEncoder{newArrayEncoder(t)} diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go index eed40a4272..eee59ccb49 100644 --- a/src/encoding/json/encode_test.go +++ b/src/encoding/json/encode_test.go @@ -6,6 +6,7 @@ package json import ( "bytes" + "fmt" "math" "reflect" "testing" @@ -537,6 +538,60 @@ func TestEncodeString(t *testing.T) { } } +type jsonbyte byte + +func (b jsonbyte) MarshalJSON() ([]byte, error) { return tenc(`{"JB":%d}`, b) } + +type textbyte byte + +func (b textbyte) MarshalText() ([]byte, error) { return tenc(`TB:%d`, b) } + +type jsonint int + +func (i jsonint) MarshalJSON() ([]byte, error) { return tenc(`{"JI":%d}`, i) } + +type textint int + +func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) } + +func tenc(format string, a ...interface{}) ([]byte, error) { + var buf bytes.Buffer + fmt.Fprintf(&buf, format, a...) + return buf.Bytes(), nil +} + +// Issue 13783 +func TestEncodeBytekind(t *testing.T) { + testdata := []struct { + data interface{} + want string + }{ + {byte(7), "7"}, + {jsonbyte(7), `{"JB":7}`}, + {textbyte(4), `"TB:4"`}, + {jsonint(5), `{"JI":5}`}, + {textint(1), `"TI:1"`}, + {[]byte{0, 1}, `"AAE="`}, + {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, + {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, + {[]textbyte{2, 3}, `["TB:2","TB:3"]`}, + {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, + {[]textint{9, 3}, `["TI:9","TI:3"]`}, + {[]int{9, 3}, `[9,3]`}, + } + for _, d := range testdata { + js, err := Marshal(d.data) + if err != nil { + t.Error(err) + continue + } + got, want := string(js), d.want + if got != want { + t.Errorf("got %s, want %s", got, want) + } + } +} + func TestTextMarshalerMapKeysAreSorted(t *testing.T) { b, err := Marshal(map[unmarshalerText]int{ {"x", "y"}: 1, -- cgit v1.3 From 04945edd40fff4d66321a4f98c1bb070b6356008 Mon Sep 17 00:00:00 2001 From: Alexandru Moșoi Date: Mon, 4 Apr 2016 19:23:41 +0200 Subject: cmd/compile: replaces ANDQ with MOV?ZX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Where possible replace ANDQ with MOV?ZX. Takes care that we don't regress wrt bounds checking, for example [1000]int{}[i&255]. According to "Intel 64 and IA-32 Architectures Optimization Reference Manual" Section: "3.5.1.13 Zero-Latency MOV Instructions" MOV?ZX instructions have zero latency on newer processors. Updates #15105 Change-Id: I63539fdbc5812d5563aa1ebc49eca035bd307997 Reviewed-on: https://go-review.googlesource.com/21508 Reviewed-by: Айнар Гарипов Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/gen/AMD64.rules | 8 +++ src/cmd/compile/internal/ssa/rewriteAMD64.go | 81 ++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 4ad0f883b0..b37720eb39 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -587,6 +587,11 @@ (CMPB x (MOVBconst [c])) -> (CMPBconst x [c]) (CMPB (MOVBconst [c]) x) -> (InvertFlags (CMPBconst x [c])) +// Using MOVBQZX instead of ANDQ is cheaper. +(ANDQconst [0xFF] x) -> (MOVBQZX x) +(ANDQconst [0xFFFF] x) -> (MOVWQZX x) +(ANDQconst [0xFFFFFFFF] x) -> (MOVLQZX x) + // strength reduction // Assumes that the following costs from https://gmplib.org/~tege/x86-timing.pdf: // 1 - addq, shlq, leaq, negq @@ -1093,6 +1098,9 @@ (CMPBconst (MOVBconst [x]) [y]) && int8(x)>int8(y) && uint8(x)>uint8(y) -> (FlagGT_UGT) // Other known comparisons. +(CMPQconst (MOVBQZX _) [c]) && 0xFF < c -> (FlagLT_ULT) +(CMPQconst (MOVWQZX _) [c]) && 0xFFFF < c -> (FlagLT_ULT) +(CMPQconst (MOVLQZX _) [c]) && 0xFFFFFFFF < c -> (FlagLT_ULT) (CMPQconst (ANDQconst _ [m]) [n]) && 0 <= m && m < n -> (FlagLT_ULT) (CMPLconst (ANDLconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT_ULT) (CMPWconst (ANDWconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < int16(n) -> (FlagLT_ULT) diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 11c2de391c..a1d1e4edd9 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -1838,6 +1838,42 @@ func rewriteValueAMD64_OpAMD64ANDQconst(v *Value, config *Config) bool { v.AddArg(x) return true } + // match: (ANDQconst [0xFF] x) + // cond: + // result: (MOVBQZX x) + for { + if v.AuxInt != 0xFF { + break + } + x := v.Args[0] + v.reset(OpAMD64MOVBQZX) + v.AddArg(x) + return true + } + // match: (ANDQconst [0xFFFF] x) + // cond: + // result: (MOVWQZX x) + for { + if v.AuxInt != 0xFFFF { + break + } + x := v.Args[0] + v.reset(OpAMD64MOVWQZX) + v.AddArg(x) + return true + } + // match: (ANDQconst [0xFFFFFFFF] x) + // cond: + // result: (MOVLQZX x) + for { + if v.AuxInt != 0xFFFFFFFF { + break + } + x := v.Args[0] + v.reset(OpAMD64MOVLQZX) + v.AddArg(x) + return true + } // match: (ANDQconst [0] _) // cond: // result: (MOVQconst [0]) @@ -3026,6 +3062,51 @@ func rewriteValueAMD64_OpAMD64CMPQconst(v *Value, config *Config) bool { v.reset(OpAMD64FlagGT_UGT) return true } + // match: (CMPQconst (MOVBQZX _) [c]) + // cond: 0xFF < c + // result: (FlagLT_ULT) + for { + v_0 := v.Args[0] + if v_0.Op != OpAMD64MOVBQZX { + break + } + c := v.AuxInt + if !(0xFF < c) { + break + } + v.reset(OpAMD64FlagLT_ULT) + return true + } + // match: (CMPQconst (MOVWQZX _) [c]) + // cond: 0xFFFF < c + // result: (FlagLT_ULT) + for { + v_0 := v.Args[0] + if v_0.Op != OpAMD64MOVWQZX { + break + } + c := v.AuxInt + if !(0xFFFF < c) { + break + } + v.reset(OpAMD64FlagLT_ULT) + return true + } + // match: (CMPQconst (MOVLQZX _) [c]) + // cond: 0xFFFFFFFF < c + // result: (FlagLT_ULT) + for { + v_0 := v.Args[0] + if v_0.Op != OpAMD64MOVLQZX { + break + } + c := v.AuxInt + if !(0xFFFFFFFF < c) { + break + } + v.reset(OpAMD64FlagLT_ULT) + return true + } // match: (CMPQconst (ANDQconst _ [m]) [n]) // cond: 0 <= m && m < n // result: (FlagLT_ULT) -- cgit v1.3 From 0c81248bf46f611b56e3ab38b4d83e449b3c8636 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Wed, 6 Apr 2016 18:43:23 +1000 Subject: runtime: remove unused return value from lfstackUnpack None of the two places that call lfstackUnpack use the second argument. This simplifies a followup CL that merges the lfstack{Pack,Unpack} implementations. Change-Id: I3c93f6259da99e113d94f8c8027584da79c1ac2c Reviewed-on: https://go-review.googlesource.com/21595 Run-TryBot: Dave Cheney Reviewed-by: Brad Fitzpatrick --- src/runtime/lfstack.go | 4 ++-- src/runtime/lfstack_32bit.go | 6 ++---- src/runtime/lfstack_64bit.go | 6 ++---- src/runtime/lfstack_amd64.go | 6 ++---- 4 files changed, 8 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/runtime/lfstack.go b/src/runtime/lfstack.go index ea640eb12f..1261f54d97 100644 --- a/src/runtime/lfstack.go +++ b/src/runtime/lfstack.go @@ -15,7 +15,7 @@ import ( func lfstackpush(head *uint64, node *lfnode) { node.pushcnt++ new := lfstackPack(node, node.pushcnt) - if node1, _ := lfstackUnpack(new); node1 != node { + if node1 := lfstackUnpack(new); node1 != node { print("runtime: lfstackpush invalid packing: node=", node, " cnt=", hex(node.pushcnt), " packed=", hex(new), " -> node=", node1, "\n") throw("lfstackpush") } @@ -34,7 +34,7 @@ func lfstackpop(head *uint64) unsafe.Pointer { if old == 0 { return nil } - node, _ := lfstackUnpack(old) + node := lfstackUnpack(old) next := atomic.Load64(&node.next) if atomic.Cas64(head, old, next) { return unsafe.Pointer(node) diff --git a/src/runtime/lfstack_32bit.go b/src/runtime/lfstack_32bit.go index 36811c1e47..2f59e0212e 100644 --- a/src/runtime/lfstack_32bit.go +++ b/src/runtime/lfstack_32bit.go @@ -14,8 +14,6 @@ func lfstackPack(node *lfnode, cnt uintptr) uint64 { return uint64(uintptr(unsafe.Pointer(node)))<<32 | uint64(cnt) } -func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) { - node = (*lfnode)(unsafe.Pointer(uintptr(val >> 32))) - cnt = uintptr(val) - return +func lfstackUnpack(val uint64) *lfnode { + return (*lfnode)(unsafe.Pointer(uintptr(val >> 32))) } diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go index 27a058c763..07c2a141f0 100644 --- a/src/runtime/lfstack_64bit.go +++ b/src/runtime/lfstack_64bit.go @@ -28,8 +28,6 @@ func lfstackPack(node *lfnode, cnt uintptr) uint64 { return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<> cntBits << 3))) - cnt = uintptr(val & (1<> cntBits << 3))) } diff --git a/src/runtime/lfstack_amd64.go b/src/runtime/lfstack_amd64.go index 0a71455c6b..6397e1d47f 100644 --- a/src/runtime/lfstack_amd64.go +++ b/src/runtime/lfstack_amd64.go @@ -17,8 +17,6 @@ func lfstackPack(node *lfnode, cnt uintptr) uint64 { return uint64(uintptr(unsafe.Pointer(node)))<<16 | uint64(cnt&(1<<19-1)) } -func lfstackUnpack(val uint64) (node *lfnode, cnt uintptr) { - node = (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> 19 << 3))) - cnt = uintptr(val & (1<<19 - 1)) - return +func lfstackUnpack(val uint64) *lfnode { + return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> 19 << 3))) } -- cgit v1.3 From d636d7907c46b728b07b58669ec1fa1158105579 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Tue, 5 Apr 2016 15:43:07 -0700 Subject: bytes: add ContainsAny This function is present in the strings package but missing from bytes, and we would like to keep the two packages consistent. Add it to bytes, and copy the test over as well. Fixes #15140 Change-Id: I5dbd28da83a9fe741885794ed15f2af2f826cb3c Reviewed-on: https://go-review.googlesource.com/21562 Reviewed-by: Brad Fitzpatrick --- src/bytes/bytes.go | 5 +++++ src/bytes/bytes_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'src') diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go index 8a4409cb6b..698d881c9d 100644 --- a/src/bytes/bytes.go +++ b/src/bytes/bytes.go @@ -83,6 +83,11 @@ func Contains(b, subslice []byte) bool { return Index(b, subslice) != -1 } +// ContainsAny reports whether any of the UTF-8-encoded Unicode code points in chars are within b. +func ContainsAny(b []byte, chars string) bool { + return IndexAny(b, chars) >= 0 +} + // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. func Index(s, sep []byte) int { n := len(sep) diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go index 1be29d6cc6..40e8d09b59 100644 --- a/src/bytes/bytes_test.go +++ b/src/bytes/bytes_test.go @@ -1218,6 +1218,33 @@ func TestContains(t *testing.T) { } } +var ContainsAnyTests = []struct { + b []byte + substr string + expected bool +}{ + {[]byte(""), "", false}, + {[]byte(""), "a", false}, + {[]byte(""), "abc", false}, + {[]byte("a"), "", false}, + {[]byte("a"), "a", true}, + {[]byte("aaa"), "a", true}, + {[]byte("abc"), "xyz", false}, + {[]byte("abc"), "xcz", true}, + {[]byte("a☺b☻c☹d"), "uvw☻xyz", true}, + {[]byte("aRegExp*"), ".(|)*+?^$[]", true}, + {[]byte(dots + dots + dots), " ", false}, +} + +func TestContainsAny(t *testing.T) { + for _, ct := range ContainsAnyTests { + if ContainsAny(ct.b, ct.substr) != ct.expected { + t.Errorf("ContainsAny(%s, %s) = %v, want %v", + ct.b, ct.substr, !ct.expected, ct.expected) + } + } +} + var makeFieldsInput = func() []byte { x := make([]byte, 1<<20) // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. -- cgit v1.3 From 007b12977aa8f3373b358361fe21802d5a8408b4 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 6 Apr 2016 14:12:48 -0700 Subject: cmd/compile: move Type.Maplineno to separate data structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relatively few types are ever used as map keys, so tracking this separately is a net win. Passes toolstash -cmp. name old alloc/op new alloc/op delta Template 55.9MB ± 0% 55.5MB ± 0% -0.71% (p=0.000 n=10+10) Unicode 37.8MB ± 0% 37.7MB ± 0% -0.27% (p=0.000 n=10+10) GoTypes 180MB ± 0% 179MB ± 0% -0.52% (p=0.000 n=7+10) Compiler 806MB ± 0% 803MB ± 0% -0.41% (p=0.000 n=10+10) CPU and number of allocs are unchanged. Change-Id: I6d60d74a4866995a231dfed3dd5792d75d904292 Reviewed-on: https://go-review.googlesource.com/21622 Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/sizeof_test.go | 2 +- src/cmd/compile/internal/gc/subr.go | 4 ++-- src/cmd/compile/internal/gc/type.go | 2 -- src/cmd/compile/internal/gc/typecheck.go | 14 +++++++++----- 4 files changed, 12 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go index 8b0dfe538e..f2b1461bc8 100644 --- a/src/cmd/compile/internal/gc/sizeof_test.go +++ b/src/cmd/compile/internal/gc/sizeof_test.go @@ -27,7 +27,7 @@ func TestSizeof(t *testing.T) { {Name{}, 52, 80}, {Node{}, 92, 144}, {Sym{}, 60, 112}, - {Type{}, 56, 88}, + {Type{}, 52, 80}, {MapType{}, 20, 40}, {ForwardType{}, 16, 32}, {FuncType{}, 28, 48}, diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index a61b8bcd27..035bd815c2 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -390,8 +390,8 @@ func checkMapKeyType(key *Type) { // before key is fully defined, the error // will only be printed for the first one. // good enough. - if key.Maplineno == 0 { - key.Maplineno = lineno + if maplineno[key] == 0 { + maplineno[key] = lineno } } } diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index 3d2f01ef7d..eee8e0384a 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -153,8 +153,6 @@ type Type struct { Vargen int32 // unique name for OTYPE/ONAME Lineno int32 // line at which this type was declared, implicitly or explicitly - Maplineno int32 // first use of this type as a map key - Etype EType // kind of type Noalg bool // suppress hash and eq algorithm generation Trecur uint8 // to detect loops diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index ab7d257aac..a20f87d940 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -3513,7 +3513,11 @@ func domethod(n *Node) { checkwidth(n.Type) } -var mapqueue []*Node +var ( + mapqueue []*Node + // maplineno tracks the line numbers at which types are first used as map keys + maplineno = map[*Type]int32{} +) func copytype(n *Node, t *Type) { if t.Etype == TFORW { @@ -3522,7 +3526,7 @@ func copytype(n *Node, t *Type) { return } - maplineno := n.Type.Maplineno + mapline := maplineno[n.Type] embedlineno := n.Type.ForwardType().Embedlineno l := n.Type.ForwardType().Copyto @@ -3559,8 +3563,8 @@ func copytype(n *Node, t *Type) { lineno = lno // Queue check for map until all the types are done settling. - if maplineno != 0 { - t.Maplineno = maplineno + if mapline != 0 { + maplineno[t] = mapline mapqueue = append(mapqueue, n) } } @@ -3609,7 +3613,7 @@ ret: } for _, n := range mapqueue { - lineno = n.Type.Maplineno + lineno = maplineno[n.Type] checkMapKeyType(n.Type) } -- cgit v1.3 From 81aacb80d55eddcb95cbe2c87392cc922e026e45 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 6 Apr 2016 15:27:30 -0700 Subject: cmd/compile, go/importer: minor cleanups Change-Id: I4ffb79d8cb08b0b44f59757fb7f0ec3ed1e4479f Reviewed-on: https://go-review.googlesource.com/21624 Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/bexport.go | 8 +++----- src/go/internal/gcimporter/bimport.go | 5 ++--- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 8dcf97b31d..092cdac2f6 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -109,8 +109,6 @@ import ( // // NOTE: This flag is the first flag to enable if importing dies because of // (suspected) format errors, and whenever a change is made to the format. -// Having debugFormat enabled increases the export data size massively (by -// several factors) - avoid running with the flag enabled in general. const debugFormat = false // default: false // TODO(gri) remove eventually @@ -515,19 +513,19 @@ func (p *exporter) typ(t *Type) { p.typIndex[t] = len(p.typIndex) // pick off named types - if sym := t.Sym; sym != nil { + if tsym := t.Sym; tsym != nil { // Predeclared types should have been found in the type map. if t.Orig == t { Fatalf("exporter: predeclared type missing from type map?") } // TODO(gri) The assertion below seems incorrect (crashes during all.bash). // we expect the respective definition to point to us - // if sym.Def.Type != t { + // if tsym.Def.Type != t { // Fatalf("exporter: type definition doesn't point to us?") // } p.tag(namedTag) - p.qualifiedName(sym) + p.qualifiedName(tsym) // write underlying type p.typ(t.Orig) diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index 12efb2aaf3..aa9569de52 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -232,8 +232,7 @@ func (p *importer) typ(parent *types.Package) types.Type { switch i { case namedTag: // read type object - name := p.string() - parent = p.pkg() + parent, name := p.qualifiedName() scope := parent.Scope() obj := scope.Lookup(name) @@ -258,7 +257,7 @@ func (p *importer) typ(parent *types.Package) types.Type { t0.SetUnderlying(p.typ(parent)) // interfaces don't have associated methods - if _, ok := t0.Underlying().(*types.Interface); ok { + if types.IsInterface(t0) { return t } -- cgit v1.3 From 0382a30dd6cd78efd9fb27bfed50dd1d6d7f722b Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Sun, 20 Mar 2016 21:44:31 -0400 Subject: math: add functions and stubs for s390x Includes assembly implementations of Sqrt and Dim. Change-Id: I57472e8d31e2ee74bcebf9f8e818f765eb9b8abf Reviewed-on: https://go-review.googlesource.com/20936 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/math/dim_s390x.s | 132 +++++++++++++++++++++++++++++++++++++++++++++++++ src/math/sqrt_s390x.s | 12 +++++ src/math/stubs_s390x.s | 77 +++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 src/math/dim_s390x.s create mode 100644 src/math/sqrt_s390x.s create mode 100644 src/math/stubs_s390x.s (limited to 'src') diff --git a/src/math/dim_s390x.s b/src/math/dim_s390x.s new file mode 100644 index 0000000000..503d2611f8 --- /dev/null +++ b/src/math/dim_s390x.s @@ -0,0 +1,132 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on dim_amd64.s + +#include "textflag.h" + +#define PosInf 0x7FF0000000000000 +#define NaN 0x7FF8000000000001 +#define NegInf 0xFFF0000000000000 + +// func Dim(x, y float64) float64 +TEXT ·Dim(SB),NOSPLIT,$0 + // (+Inf, +Inf) special case + MOVD x+0(FP), R2 + MOVD y+8(FP), R3 + MOVD $PosInf, R4 + CMPUBNE R4, R2, dim2 + CMPUBEQ R4, R3, bothInf +dim2: // (-Inf, -Inf) special case + MOVD $NegInf, R4 + CMPUBNE R4, R2, dim3 + CMPUBEQ R4, R3, bothInf +dim3: // (NaN, x) or (x, NaN) + MOVD $~(1<<63), R5 + MOVD $PosInf, R4 + AND R5, R2 // x = |x| + CMPUBLT R4, R2, isDimNaN + AND R5, R3 // y = |y| + CMPUBLT R4, R3, isDimNaN + + FMOVD x+0(FP), F1 + FMOVD y+8(FP), F2 + FSUB F2, F1 + FMOVD $(0.0), F2 + FCMPU F2, F1 + BGE +3(PC) + FMOVD F1, ret+16(FP) + RET + FMOVD F2, ret+16(FP) + RET +bothInf: // Dim(-Inf, -Inf) or Dim(+Inf, +Inf) +isDimNaN: + MOVD $NaN, R4 + MOVD R4, ret+16(FP) + RET + +// func ·Max(x, y float64) float64 +TEXT ·Max(SB),NOSPLIT,$0 + // +Inf special cases + MOVD $PosInf, R4 + MOVD x+0(FP), R8 + CMPUBEQ R4, R8, isPosInf + MOVD y+8(FP), R9 + CMPUBEQ R4, R9, isPosInf + // NaN special cases + MOVD $~(1<<63), R5 // bit mask + MOVD $PosInf, R4 + MOVD R8, R2 + AND R5, R2 // x = |x| + CMPUBLT R4, R2, isMaxNaN + MOVD R9, R3 + AND R5, R3 // y = |y| + CMPUBLT R4, R3, isMaxNaN + // ±0 special cases + OR R3, R2 + BEQ isMaxZero + + FMOVD x+0(FP), F1 + FMOVD y+8(FP), F2 + FCMPU F2, F1 + BGT +3(PC) + FMOVD F1, ret+16(FP) + RET + FMOVD F2, ret+16(FP) + RET +isMaxNaN: // return NaN + MOVD $NaN, R4 +isPosInf: // return +Inf + MOVD R4, ret+16(FP) + RET +isMaxZero: + MOVD $(1<<63), R4 // -0.0 + CMPUBEQ R4, R8, +3(PC) + MOVD R8, ret+16(FP) // return 0 + RET + MOVD R9, ret+16(FP) // return other 0 + RET + +// func Min(x, y float64) float64 +TEXT ·Min(SB),NOSPLIT,$0 + // -Inf special cases + MOVD $NegInf, R4 + MOVD x+0(FP), R8 + CMPUBEQ R4, R8, isNegInf + MOVD y+8(FP), R9 + CMPUBEQ R4, R9, isNegInf + // NaN special cases + MOVD $~(1<<63), R5 + MOVD $PosInf, R4 + MOVD R8, R2 + AND R5, R2 // x = |x| + CMPUBLT R4, R2, isMinNaN + MOVD R9, R3 + AND R5, R3 // y = |y| + CMPUBLT R4, R3, isMinNaN + // ±0 special cases + OR R3, R2 + BEQ isMinZero + + FMOVD x+0(FP), F1 + FMOVD y+8(FP), F2 + FCMPU F2, F1 + BLT +3(PC) + FMOVD F1, ret+16(FP) + RET + FMOVD F2, ret+16(FP) + RET +isMinNaN: // return NaN + MOVD $NaN, R4 +isNegInf: // return -Inf + MOVD R4, ret+16(FP) + RET +isMinZero: + MOVD $(1<<63), R4 // -0.0 + CMPUBEQ R4, R8, +3(PC) + MOVD R9, ret+16(FP) // return other 0 + RET + MOVD R8, ret+16(FP) // return -0 + RET + diff --git a/src/math/sqrt_s390x.s b/src/math/sqrt_s390x.s new file mode 100644 index 0000000000..37ca0bec91 --- /dev/null +++ b/src/math/sqrt_s390x.s @@ -0,0 +1,12 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Sqrt(x float64) float64 +TEXT ·Sqrt(SB),NOSPLIT,$0 + FMOVD x+0(FP), F1 + FSQRT F1, F1 + FMOVD F1, ret+8(FP) + RET diff --git a/src/math/stubs_s390x.s b/src/math/stubs_s390x.s new file mode 100644 index 0000000000..76868447cd --- /dev/null +++ b/src/math/stubs_s390x.s @@ -0,0 +1,77 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "../runtime/textflag.h" + +TEXT ·Asin(SB),NOSPLIT,$0 + BR ·asin(SB) + +TEXT ·Acos(SB),NOSPLIT,$0 + BR ·acos(SB) + +TEXT ·Atan2(SB),NOSPLIT,$0 + BR ·atan2(SB) + +TEXT ·Atan(SB),NOSPLIT,$0 + BR ·atan(SB) + +TEXT ·Exp2(SB),NOSPLIT,$0 + BR ·exp2(SB) + +TEXT ·Expm1(SB),NOSPLIT,$0 + BR ·expm1(SB) + +TEXT ·Exp(SB),NOSPLIT,$0 + BR ·exp(SB) + +TEXT ·Floor(SB),NOSPLIT,$0 + BR ·floor(SB) + +TEXT ·Ceil(SB),NOSPLIT,$0 + BR ·ceil(SB) + +TEXT ·Trunc(SB),NOSPLIT,$0 + BR ·trunc(SB) + +TEXT ·Frexp(SB),NOSPLIT,$0 + BR ·frexp(SB) + +TEXT ·Hypot(SB),NOSPLIT,$0 + BR ·hypot(SB) + +TEXT ·Ldexp(SB),NOSPLIT,$0 + BR ·ldexp(SB) + +TEXT ·Log10(SB),NOSPLIT,$0 + BR ·log10(SB) + +TEXT ·Log2(SB),NOSPLIT,$0 + BR ·log2(SB) + +TEXT ·Log1p(SB),NOSPLIT,$0 + BR ·log1p(SB) + +TEXT ·Log(SB),NOSPLIT,$0 + BR ·log(SB) + +TEXT ·Modf(SB),NOSPLIT,$0 + BR ·modf(SB) + +TEXT ·Mod(SB),NOSPLIT,$0 + BR ·mod(SB) + +TEXT ·Remainder(SB),NOSPLIT,$0 + BR ·remainder(SB) + +TEXT ·Sincos(SB),NOSPLIT,$0 + BR ·sincos(SB) + +TEXT ·Sin(SB),NOSPLIT,$0 + BR ·sin(SB) + +TEXT ·Cos(SB),NOSPLIT,$0 + BR ·cos(SB) + +TEXT ·Tan(SB),NOSPLIT,$0 + BR ·tan(SB) -- cgit v1.3 From a037c73ccfc7c35a389e95ffa7996c8dd38e0cde Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Mon, 14 Mar 2016 09:23:04 -0700 Subject: cmd/link: generate DWARF info using symbols This updates dwarf.go to generate debug information as symbols instead of directly writing to the output file. This should make it easier to move generation of some of the debug info into the compiler. Change-Id: Id2358988bfb689865ab4d68f82716f0676336df4 Reviewed-on: https://go-review.googlesource.com/20679 Reviewed-by: David Crawshaw Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot --- src/cmd/internal/obj/link.go | 4 + src/cmd/link/internal/amd64/asm.go | 22 +- src/cmd/link/internal/arm/asm.go | 21 +- src/cmd/link/internal/arm64/asm.go | 21 +- src/cmd/link/internal/ld/data.go | 114 +++ src/cmd/link/internal/ld/dwarf.go | 1493 +++++++++++++---------------------- src/cmd/link/internal/ld/elf.go | 23 +- src/cmd/link/internal/ld/macho.go | 29 +- src/cmd/link/internal/ld/symtab.go | 12 - src/cmd/link/internal/mips64/asm.go | 10 +- src/cmd/link/internal/ppc64/asm.go | 10 +- src/cmd/link/internal/s390x/asm.go | 6 +- src/cmd/link/internal/x86/asm.go | 24 +- 13 files changed, 727 insertions(+), 1062 deletions(-) (limited to 'src') diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 24f028f737..9c06e8dec6 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -423,6 +423,8 @@ const ( SCONST SDYNIMPORT SHOSTOBJ + SDWARFSECT + SDWARFINFO SSUB = 1 << 8 SMASK = SSUB - 1 SHIDDEN = 1 << 9 @@ -495,6 +497,8 @@ const ( // of a JMP instruction, by encoding the address into the instruction. // The stack nosplit check ignores this since it is not a function call. R_JMPMIPS + // R_DWARFREF resolves to the offset of the symbol from its section. + R_DWARFREF // Platform dependent relocations. Architectures with fixed width instructions // have the inherent issue that a 32-bit (or 64-bit!) displacement cannot be diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index d809f6e8ed..25232e6e01 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -650,19 +650,11 @@ func asmb() { ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.Cseek(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + machlink := int64(0) if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - - dwarfoff := ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) - ld.Cseek(dwarfoff) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - machlink = ld.Domacholink() } @@ -715,11 +707,11 @@ func asmb() { obj.Hdragonfly, obj.Hsolaris, obj.Hnacl: - symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = ld.Rnd(symo, int64(ld.INITRND)) case obj.Hwindows: - symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = ld.Rnd(symo, ld.PEFILEALIGN) } @@ -736,8 +728,6 @@ func asmb() { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } - ld.Dwarfemitdebugsections() - if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } @@ -762,8 +752,6 @@ func asmb() { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } - ld.Dwarfemitdebugsections() - case obj.Hdarwin: if ld.Linkmode == ld.LinkExternal { ld.Machoemitreloc() diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index bb90cf77b6..e2718bfac8 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -597,19 +597,11 @@ func asmb() { ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.Cseek(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + machlink := uint32(0) if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - - dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) - ld.Cseek(int64(dwarfoff)) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - machlink = uint32(ld.Domacholink()) } @@ -627,7 +619,7 @@ func asmb() { switch ld.HEADTYPE { default: if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) } @@ -649,11 +641,6 @@ func asmb() { ld.Cflush() ld.Cwrite(ld.Elfstrdat) - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 250f0afb16..8227dc0d28 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -435,19 +435,11 @@ func asmb() { ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.Cseek(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + machlink := uint32(0) if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - - dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) - ld.Cseek(int64(dwarfoff)) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - machlink = uint32(ld.Domacholink()) } @@ -465,7 +457,7 @@ func asmb() { switch ld.HEADTYPE { default: if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) } @@ -487,11 +479,6 @@ func asmb() { ld.Cflush() ld.Cwrite(ld.Elfstrdat) - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index fe74cc9208..6bbd6c7d5c 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -84,6 +84,20 @@ func setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 { return off + wid } +func Addbytes(ctxt *Link, s *LSym, bytes []byte) int64 { + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Attr |= AttrReachable + s.Size += int64(len(bytes)) + if int64(int(s.Size)) != s.Size { + log.Fatalf("Addbytes size %d too long", s.Size) + } + s.P = append(s.P, bytes...) + + return s.Size +} + func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 { off := s.Size setuintxx(ctxt, s, off, v, int64(wid)) @@ -489,6 +503,25 @@ func relocsym(s *LSym) { errorexit() } + case obj.R_DWARFREF: + if r.Sym.Sect == nil { + Diag("missing DWARF section: %s from %s", r.Sym.Name, s.Name) + } + if Linkmode == LinkExternal { + r.Done = 0 + r.Type = obj.R_ADDR + + r.Xsym = Linkrlookup(Ctxt, r.Sym.Sect.Name, 0) + r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + o = r.Xadd + rs = r.Xsym + if Iself && Thearch.Thechar == '6' { + o = 0 + } + break + } + o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr) + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL: if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != obj.SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == obj.R_GOTPCREL) { @@ -614,6 +647,9 @@ func reloc() { for s := datap; s != nil; s = s.Next { relocsym(s) } + for s := dwarfp; s != nil; s = s.Next { + relocsym(s) + } } func dynrelocsym(s *LSym) { @@ -893,6 +929,14 @@ func Datblk(addr int64, size int64) { fmt.Fprintf(&Bso, "\t%.8x|\n", uint(eaddr)) } +func Dwarfblk(addr int64, size int64) { + if Debug['a'] != 0 { + fmt.Fprintf(&Bso, "dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + } + + blk(dwarfp, addr, size) +} + var zeros [512]byte // strnput writes the first n bytes of s. @@ -1691,6 +1735,40 @@ func dodata() { Diag("read-only data segment too large") } + dwarfgeneratedebugsyms() + + for s = dwarfp; s != nil && s.Type == obj.SDWARFSECT; s = s.Next { + sect = addsection(&Segdwarf, s.Name, 04) + sect.Align = 1 + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + sect.Length = uint64(datsize) - sect.Vaddr + } + + if s != nil { + sect = addsection(&Segdwarf, ".debug_info", 04) + sect.Align = 1 + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for ; s != nil && s.Type == obj.SDWARFINFO; s = s.Next { + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + s.Attr |= AttrLocal + growdatsize(&datsize, s) + } + sect.Length = uint64(datsize) - sect.Vaddr + } + + // The compiler uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if datsize != int64(uint32(datsize)) { + Diag("dwarf segment too large") + } + /* number the sections */ n := int32(1) @@ -1706,6 +1784,10 @@ func dodata() { sect.Extnum = int16(n) n++ } + for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } } // Add buildid to beginning of text segment, on non-ELF systems. @@ -1857,6 +1939,29 @@ func address() { Segdata.Filelen = bss.Vaddr - Segdata.Vaddr + va = uint64(Rnd(int64(va), int64(INITRND))) + Segdwarf.Rwx = 06 + Segdwarf.Vaddr = va + Segdwarf.Fileoff = Segdata.Fileoff + uint64(Rnd(int64(Segdata.Filelen), int64(INITRND))) + Segdwarf.Filelen = 0 + if HEADTYPE == obj.Hwindows { + Segdwarf.Fileoff = Segdata.Fileoff + uint64(Rnd(int64(Segdata.Filelen), int64(PEFILEALIGN))) + } + for s := Segdwarf.Sect; s != nil; s = s.Next { + vlen = int64(s.Length) + if s.Next != nil { + vlen = int64(s.Next.Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + if HEADTYPE == obj.Hwindows { + va = uint64(Rnd(int64(va), PEFILEALIGN)) + } + Segdwarf.Length = va - Segdwarf.Vaddr + } + + Segdwarf.Filelen = va - Segdwarf.Vaddr + text := Segtext.Sect var rodata *Section if Segrodata.Sect != nil { @@ -1884,6 +1989,15 @@ func address() { sub.Value += sym.Value } } + for sym := dwarfp; sym != nil; sym = sym.Next { + Ctxt.Cursym = sym + if sym.Sect != nil { + sym.Value += int64(sym.Sect.Vaddr) + } + for sub = sym.Sub; sub != nil; sub = sub.Sub { + sub.Value += sym.Value + } + } if Buildmode == BuildmodeShared { s := Linklookup(Ctxt, "go.link.abihashbytes", 0) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 4465a727a5..eaa0bdbb41 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -17,97 +17,34 @@ package ld import ( "cmd/internal/obj" "fmt" + "log" "os" "strings" ) +const infoprefix = "go.dwarf.info." + /* * Offsets and sizes of the debug_* sections in the cout file. */ -var abbrevo int64 - -var abbrevsize int64 - var abbrevsym *LSym - -var abbrevsympos int64 - -var lineo int64 - -var linesize int64 - -var linesym *LSym - -var linesympos int64 - -var infoo int64 // also the base for DWDie->offs and reference attributes. - -var infosize int64 - -var infosym *LSym - -var infosympos int64 - -var frameo int64 - -var framesize int64 - -var framesym *LSym - -var framesympos int64 - -var pubnameso int64 - -var pubnamessize int64 - -var pubtypeso int64 - -var pubtypessize int64 - -var arangeso int64 - -var arangessize int64 - -var gdbscripto int64 - -var gdbscriptsize int64 - -var infosec *LSym - -var inforeloco int64 - -var inforelocsize int64 - var arangessec *LSym - -var arangesreloco int64 - -var arangesrelocsize int64 - -var linesec *LSym - -var linereloco int64 - -var linerelocsize int64 - var framesec *LSym - -var framereloco int64 - -var framerelocsize int64 +var infosec *LSym +var linesec *LSym var gdbscript string /* * Basic I/O */ -func addrput(addr int64) { +func addrput(s *LSym, addr int64) { switch Thearch.Ptrsize { case 4: - Thearch.Lput(uint32(addr)) + Adduint32(Ctxt, s, uint32(addr)) case 8: - Thearch.Vput(uint64(addr)) + Adduint64(Ctxt, s, uint64(addr)) } } @@ -144,14 +81,16 @@ func appendSleb128(b []byte, v int64) []byte { var encbuf [10]byte -func uleb128put(v int64) { +func uleb128put(s *LSym, v int64) { b := appendUleb128(encbuf[:0], uint64(v)) - Cwrite(b) + Addbytes(Ctxt, s, b) } -func sleb128put(v int64) { +func sleb128put(s *LSym, v int64) { b := appendSleb128(encbuf[:0], v) - Cwrite(b) + for _, x := range b { + Adduint8(Ctxt, s, x) + } } /* @@ -462,24 +401,29 @@ var abbrevs = [DW_NABRV]DWAbbrev{ }, } -func writeabbrev() { - abbrevo = Cpos() +var dwarfp *LSym + +func writeabbrev() *LSym { + s := Linklookup(Ctxt, ".debug_abbrev", 0) + s.Type = obj.SDWARFSECT + abbrevsym = s + for i := 1; i < DW_NABRV; i++ { // See section 7.5.3 - uleb128put(int64(i)) + uleb128put(s, int64(i)) - uleb128put(int64(abbrevs[i].tag)) - Cput(abbrevs[i].children) + uleb128put(s, int64(abbrevs[i].tag)) + Adduint8(Ctxt, s, abbrevs[i].children) for _, f := range abbrevs[i].attr { - uleb128put(int64(f.attr)) - uleb128put(int64(f.form)) + uleb128put(s, int64(f.attr)) + uleb128put(s, int64(f.form)) } - uleb128put(0) - uleb128put(0) + uleb128put(s, 0) + uleb128put(s, 0) } - Cput(0) - abbrevsize = Cpos() - abbrevo + Adduint8(Ctxt, s, 0) + return s } /* @@ -504,10 +448,7 @@ type DWDie struct { link *DWDie child *DWDie attr *DWAttr - // offset into .debug_info section, i.e relative to - // infoo. only valid after call to putdie() - offs int64 - hash map[string]*DWDie // optional index of DWAttr by name, enabled by mkindex() + sym *LSym } /* @@ -556,9 +497,8 @@ func getattr(die *DWDie, attr uint16) *DWAttr { } // Every DIE has at least a DW_AT_name attribute (but it will only be -// written out if it is listed in the abbrev). If its parent is -// keeping an index, the new DIE will be inserted there. -func newdie(parent *DWDie, abbrev int, name string) *DWDie { +// written out if it is listed in the abbrev). +func newdie(parent *DWDie, abbrev int, name string, version int) *DWDie { die := new(DWDie) die.abbrev = abbrev die.link = parent.child @@ -566,17 +506,16 @@ func newdie(parent *DWDie, abbrev int, name string) *DWDie { newattr(die, DW_AT_name, DW_CLS_STRING, int64(len(name)), name) - if parent.hash != nil { - parent.hash[name] = die + if name != "" && (abbrev <= DW_ABRV_VARIABLE || abbrev >= DW_ABRV_NULLTYPE) { + if abbrev != DW_ABRV_VARIABLE || version == 0 { + die.sym = Linklookup(Ctxt, infoprefix+name, version) + die.sym.Type = obj.SDWARFINFO + } } return die } -func mkindex(die *DWDie) { - die.hash = make(map[string]*DWDie) -} - func walktypedef(die *DWDie) *DWDie { // Resolve typedef if present. if die.abbrev == DW_ABRV_TYPEDECL { @@ -590,159 +529,150 @@ func walktypedef(die *DWDie) *DWDie { return die } +func walksymtypedef(s *LSym) *LSym { + if t := Linkrlookup(Ctxt, s.Name+".def", int(s.Version)); t != nil { + return t + } + return s +} + // Find child by AT_name using hashtable if available or linear scan // if not. -func find(die *DWDie, name string) *DWDie { +func findchild(die *DWDie, name string) *DWDie { var prev *DWDie for ; die != prev; prev, die = die, walktypedef(die) { - if die.hash == nil { - for a := die.child; a != nil; a = a.link { - if name == getattr(a, DW_AT_name).data { - return a - } + for a := die.child; a != nil; a = a.link { + if name == getattr(a, DW_AT_name).data { + return a } - continue - } - if a := die.hash[name]; a != nil { - return a } + continue } return nil } -func mustFind(die *DWDie, name string) *DWDie { - r := find(die, name) +func find(name string) *LSym { + return Linkrlookup(Ctxt, infoprefix+name, 0) +} + +func mustFind(name string) *LSym { + r := find(name) if r == nil { - Exitf("dwarf find: %s %p has no %s", getattr(die, DW_AT_name).data, die, name) + Exitf("dwarf find: cannot find %s", name) } return r } -func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64) { - r := Addrel(sec) - r.Sym = sym - r.Xsym = sym - r.Off = int32(Cpos() - offsetbase) - r.Siz = uint8(siz) - r.Type = obj.R_ADDR - r.Add = addend - r.Xadd = addend - if Iself && Thearch.Thechar == '6' { - addend = 0 - } - if HEADTYPE == obj.Hdarwin { - addend += sym.Value - } - switch siz { - case 4: - Thearch.Lput(uint32(addend)) - - case 8: - Thearch.Vput(uint64(addend)) - +func adddwarfref(ctxt *Link, s *LSym, t *LSym, size int) int64 { + var result int64 + switch size { default: - Diag("bad size in adddwarfrel") + Diag("invalid size %d in adddwarfref\n", size) + fallthrough + case Thearch.Ptrsize: + result = Addaddr(ctxt, s, t) + case 4: + result = addaddrplus4(ctxt, s, t, 0) } + r := &s.R[len(s.R)-1] + r.Type = obj.R_DWARFREF + return result } -func newrefattr(die *DWDie, attr uint16, ref *DWDie) *DWAttr { +func newrefattr(die *DWDie, attr uint16, ref *LSym) *DWAttr { if ref == nil { return nil } return newattr(die, attr, DW_CLS_REFERENCE, 0, ref) } -var fwdcount int - -func putattr(abbrev int, form int, cls int, value int64, data interface{}) { +func putattr(s *LSym, abbrev int, form int, cls int, value int64, data interface{}) { switch form { case DW_FORM_addr: // address if Linkmode == LinkExternal { value -= (data.(*LSym)).Value - adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) + Addaddrplus(Ctxt, s, data.(*LSym), value) break } - addrput(value) + addrput(s, value) case DW_FORM_block1: // block if cls == DW_CLS_ADDRESS { - Cput(uint8(1 + Thearch.Ptrsize)) - Cput(DW_OP_addr) - if Linkmode == LinkExternal { - value -= (data.(*LSym)).Value - adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) - break - } - - addrput(value) + Adduint8(Ctxt, s, uint8(1+Thearch.Ptrsize)) + Adduint8(Ctxt, s, DW_OP_addr) + Addaddr(Ctxt, s, data.(*LSym)) break } value &= 0xff - Cput(uint8(value)) + Adduint8(Ctxt, s, uint8(value)) p := data.([]byte) for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) + Adduint8(Ctxt, s, uint8(p[i])) } case DW_FORM_block2: // block value &= 0xffff - Thearch.Wput(uint16(value)) + Adduint16(Ctxt, s, uint16(value)) p := data.([]byte) for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) + Adduint8(Ctxt, s, uint8(p[i])) } case DW_FORM_block4: // block value &= 0xffffffff - Thearch.Lput(uint32(value)) + Adduint32(Ctxt, s, uint32(value)) p := data.([]byte) for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) + Adduint8(Ctxt, s, uint8(p[i])) } case DW_FORM_block: // block - uleb128put(value) + uleb128put(s, value) p := data.([]byte) for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) + Adduint8(Ctxt, s, uint8(p[i])) } case DW_FORM_data1: // constant - Cput(uint8(value)) + Adduint8(Ctxt, s, uint8(value)) case DW_FORM_data2: // constant - Thearch.Wput(uint16(value)) + Adduint16(Ctxt, s, uint16(value)) case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr if Linkmode == LinkExternal && cls == DW_CLS_PTR { - adddwarfrel(infosec, linesym, infoo, 4, value) + adddwarfref(Ctxt, s, linesec, 4) break } - Thearch.Lput(uint32(value)) + Adduint32(Ctxt, s, uint32(value)) case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr - Thearch.Vput(uint64(value)) + Adduint64(Ctxt, s, uint64(value)) case DW_FORM_sdata: // constant - sleb128put(value) + sleb128put(s, value) case DW_FORM_udata: // constant - uleb128put(value) + uleb128put(s, value) case DW_FORM_string: // string - strnput(data.(string), int(value+1)) + str := data.(string) + Addstring(s, str) + for i := int64(len(str)); i < value; i++ { + Adduint8(Ctxt, s, 0) + } case DW_FORM_flag: // flag if value != 0 { - Cput(1) + Adduint8(Ctxt, s, 1) } else { - Cput(0) + Adduint8(Ctxt, s, 0) } // In DWARF 2 (which is what we claim to generate), @@ -753,21 +683,13 @@ func putattr(abbrev int, form int, cls int, value int64, data interface{}) { if data == nil { Diag("dwarf: null reference in %d", abbrev) if Thearch.Ptrsize == 8 { - Thearch.Vput(0) // invalid dwarf, gdb will complain. + Adduint64(Ctxt, s, 0) // invalid dwarf, gdb will complain. } else { - Thearch.Lput(0) // invalid dwarf, gdb will complain. + Adduint32(Ctxt, s, 0) // invalid dwarf, gdb will complain. } } else { - off := (data.(*DWDie)).offs - if off == 0 { - fwdcount++ - } - if Linkmode == LinkExternal { - adddwarfrel(infosec, infosym, infoo, Thearch.Ptrsize, off) - break - } - - addrput(off) + dsym := data.(*LSym) + adddwarfref(Ctxt, s, dsym, Thearch.Ptrsize) } case DW_FORM_ref1, // reference within the compilation unit @@ -786,34 +708,45 @@ func putattr(abbrev int, form int, cls int, value int64, data interface{}) { // Note that we can (and do) add arbitrary attributes to a DIE, but // only the ones actually listed in the Abbrev will be written out. -func putattrs(abbrev int, attr *DWAttr) { +func putattrs(s *LSym, abbrev int, attr *DWAttr) { Outer: for _, f := range abbrevs[abbrev].attr { for ap := attr; ap != nil; ap = ap.link { if ap.atr == f.attr { - putattr(abbrev, int(f.form), int(ap.cls), ap.value, ap.data) + putattr(s, abbrev, int(f.form), int(ap.cls), ap.value, ap.data) continue Outer } } - putattr(abbrev, int(f.form), 0, 0, nil) + putattr(s, abbrev, int(f.form), 0, 0, nil) } } -func putdies(die *DWDie) { +func putdies(prev *LSym, die *DWDie) *LSym { for ; die != nil; die = die.link { - putdie(die) + prev = putdie(prev, die) } + Adduint8(Ctxt, prev, 0) + return prev } -func putdie(die *DWDie) { - die.offs = Cpos() - infoo - uleb128put(int64(die.abbrev)) - putattrs(die.abbrev, die.attr) +func putdie(prev *LSym, die *DWDie) *LSym { + s := die.sym + if s == nil { + s = prev + } else { + if s.Attr.OnList() { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Attr |= AttrOnList + prev.Next = s + } + uleb128put(s, int64(die.abbrev)) + putattrs(s, die.abbrev, die.attr) if abbrevs[die.abbrev].children != 0 { - putdies(die.child) - Cput(0) + return putdies(s, die.child) } + return s } func reverselist(list **DWDie) { @@ -880,44 +813,49 @@ func dotypedef(parent *DWDie, name string, def *DWDie) { Diag("dwarf: bad def in dotypedef") } + def.sym = Linklookup(Ctxt, def.sym.Name+".def", 0) + def.sym.Type = obj.SDWARFINFO + // The typedef entry must be created after the def, // so that future lookups will find the typedef instead // of the real definition. This hooks the typedef into any // circular definition loops, so that gdb can understand them. - die := newdie(parent, DW_ABRV_TYPEDECL, name) + die := newdie(parent, DW_ABRV_TYPEDECL, name, 0) - newrefattr(die, DW_AT_type, def) + newrefattr(die, DW_AT_type, def.sym) } // Define gotype, for composite ones recurse into constituents. -func defgotype(gotype *LSym) *DWDie { +func defgotype(gotype *LSym) *LSym { if gotype == nil { - return mustFind(&dwtypes, "") + return mustFind("") } if !strings.HasPrefix(gotype.Name, "type.") { Diag("dwarf: type name doesn't start with \"type.\": %s", gotype.Name) - return mustFind(&dwtypes, "") + return mustFind("") } name := gotype.Name[5:] // could also decode from Type.string - die := find(&dwtypes, name) + sdie := find(name) - if die != nil { - return die + if sdie != nil { + return sdie } - if false && Debug['v'] > 2 { - fmt.Printf("new type: %v\n", gotype) - } + return newtype(gotype).sym +} +func newtype(gotype *LSym) *DWDie { + name := gotype.Name[5:] // could also decode from Type.string kind := decodetype_kind(gotype) bytesize := decodetype_size(gotype) + var die *DWDie switch kind { case obj.KindBool: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0) newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) @@ -926,7 +864,7 @@ func defgotype(gotype *LSym) *DWDie { obj.KindInt16, obj.KindInt32, obj.KindInt64: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0) newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) @@ -936,66 +874,69 @@ func defgotype(gotype *LSym) *DWDie { obj.KindUint32, obj.KindUint64, obj.KindUintptr: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0) newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) case obj.KindFloat32, obj.KindFloat64: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0) newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) case obj.KindComplex64, obj.KindComplex128: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0) newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) case obj.KindArray: - die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name) + die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name, 0) dotypedef(&dwtypes, name, die) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) s := decodetype_arrayelem(gotype) newrefattr(die, DW_AT_type, defgotype(s)) - fld := newdie(die, DW_ABRV_ARRAYRANGE, "range") + fld := newdie(die, DW_ABRV_ARRAYRANGE, "range", 0) // use actual length not upper bound; correct for 0-length arrays. newattr(fld, DW_AT_count, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0) - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) + newrefattr(fld, DW_AT_type, mustFind("uintptr")) case obj.KindChan: - die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name) + die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) s := decodetype_chanelem(gotype) newrefattr(die, DW_AT_go_elem, defgotype(s)) + // Save elem type for synthesizechantypes. We could synthesize here + // but that would change the order of DIEs we output. + newrefattr(die, DW_AT_type, s) case obj.KindFunc: - die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name) + die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name, 0) dotypedef(&dwtypes, name, die) - newrefattr(die, DW_AT_type, mustFind(&dwtypes, "void")) + newrefattr(die, DW_AT_type, mustFind("void")) nfields := decodetype_funcincount(gotype) var fld *DWDie var s *LSym for i := 0; i < nfields; i++ { s = decodetype_funcintype(gotype, i) - fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) newrefattr(fld, DW_AT_type, defgotype(s)) } if decodetype_funcdotdotdot(gotype) { - newdie(die, DW_ABRV_DOTDOTDOT, "...") + newdie(die, DW_ABRV_DOTDOTDOT, "...", 0) } nfields = decodetype_funcoutcount(gotype) for i := 0; i < nfields; i++ { s = decodetype_funcouttype(gotype, i) - fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0) newrefattr(fld, DW_AT_type, defptrto(defgotype(s))) } case obj.KindInterface: - die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name) + die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name, 0) dotypedef(&dwtypes, name, die) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) nfields := int(decodetype_ifacemethodcount(gotype)) @@ -1008,31 +949,35 @@ func defgotype(gotype *LSym) *DWDie { newrefattr(die, DW_AT_type, defgotype(s)) case obj.KindMap: - die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name) + die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name, 0) s := decodetype_mapkey(gotype) newrefattr(die, DW_AT_go_key, defgotype(s)) s = decodetype_mapvalue(gotype) newrefattr(die, DW_AT_go_elem, defgotype(s)) + // Save gotype for use in synthesizemaptypes. We could synthesize here, + // but that would change the order of the DIEs. + newrefattr(die, DW_AT_type, gotype) case obj.KindPtr: - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name) + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name, 0) dotypedef(&dwtypes, name, die) s := decodetype_ptrelem(gotype) newrefattr(die, DW_AT_type, defgotype(s)) case obj.KindSlice: - die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name) + die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name, 0) dotypedef(&dwtypes, name, die) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) s := decodetype_arrayelem(gotype) - newrefattr(die, DW_AT_go_elem, defgotype(s)) + elem := defgotype(s) + newrefattr(die, DW_AT_go_elem, elem) case obj.KindString: - die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name) + die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) case obj.KindStruct: - die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name) + die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name, 0) dotypedef(&dwtypes, name, die) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) nfields := decodetype_structfieldcount(gotype) @@ -1045,32 +990,41 @@ func defgotype(gotype *LSym) *DWDie { if f == "" { f = s.Name[5:] // skip "type." } - fld = newdie(die, DW_ABRV_STRUCTFIELD, f) + fld = newdie(die, DW_ABRV_STRUCTFIELD, f, 0) newrefattr(fld, DW_AT_type, defgotype(s)) newmemberoffsetattr(fld, int32(decodetype_structfieldoffs(gotype, i))) } case obj.KindUnsafePointer: - die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name) + die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name, 0) default: Diag("dwarf: definition of unknown kind %d: %s", kind, gotype.Name) - die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name) - newrefattr(die, DW_AT_type, mustFind(&dwtypes, "")) + die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name, 0) + newrefattr(die, DW_AT_type, mustFind("")) } newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, int64(kind), 0) + if _, ok := prototypedies[gotype.Name]; ok { + prototypedies[gotype.Name] = die + } + return die } +func nameFromDIESym(dwtype *LSym) string { + return strings.TrimSuffix(dwtype.Name[len(infoprefix):], ".def") +} + // Find or construct *T given T. -func defptrto(dwtype *DWDie) *DWDie { - ptrname := fmt.Sprintf("*%s", getattr(dwtype, DW_AT_name).data) - die := find(&dwtypes, ptrname) +func defptrto(dwtype *LSym) *LSym { + ptrname := "*" + nameFromDIESym(dwtype) + die := find(ptrname) if die == nil { - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname) - newrefattr(die, DW_AT_type, dwtype) + pdie := newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname, 0) + newrefattr(pdie, DW_AT_type, dwtype) + return pdie.sym } return die @@ -1084,7 +1038,7 @@ func copychildrenexcept(dst *DWDie, src *DWDie, except *DWDie) { if src == except { continue } - c := newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string)) + c := newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string), 0) for a := src.attr; a != nil; a = a.link { newattr(c, a.atr, int(a.cls), a.value, a.data) } @@ -1100,9 +1054,11 @@ func copychildren(dst *DWDie, src *DWDie) { // Search children (assumed to have DW_TAG_member) for the one named // field and set its DW_AT_type to dwtype -func substitutetype(structdie *DWDie, field string, dwtype *DWDie) { - child := mustFind(structdie, field) +func substitutetype(structdie *DWDie, field string, dwtype *LSym) { + child := findchild(structdie, field) if child == nil { + Exitf("dwarf substitutetype: %s does not have member %s", + getattr(structdie, DW_AT_name).data, field) return } @@ -1114,8 +1070,17 @@ func substitutetype(structdie *DWDie, field string, dwtype *DWDie) { } } +func findprotodie(name string) *DWDie { + die, ok := prototypedies[name] + if ok && die == nil { + defgotype(lookup_or_diag(name)) + die = prototypedies[name] + } + return die +} + func synthesizestringtypes(die *DWDie) { - prototype := walktypedef(defgotype(lookup_or_diag("type.runtime.stringStructDWARF"))) + prototype := walktypedef(findprotodie("type.runtime.stringStructDWARF")) if prototype == nil { return } @@ -1129,7 +1094,7 @@ func synthesizestringtypes(die *DWDie) { } func synthesizeslicetypes(die *DWDie) { - prototype := walktypedef(defgotype(lookup_or_diag("type.runtime.slice"))) + prototype := walktypedef(findprotodie("type.runtime.slice")) if prototype == nil { return } @@ -1139,7 +1104,7 @@ func synthesizeslicetypes(die *DWDie) { continue } copychildren(die, prototype) - elem := getattr(die, DW_AT_go_elem).data.(*DWDie) + elem := getattr(die, DW_AT_go_elem).data.(*LSym) substitutetype(die, "array", defptrto(elem)) } } @@ -1163,9 +1128,21 @@ const ( BucketSize = 8 ) +func mkinternaltype(abbrev int, typename, keyname, valname string, f func(*DWDie)) *LSym { + name := mkinternaltypename(typename, keyname, valname) + symname := infoprefix + name + s := Linkrlookup(Ctxt, symname, 0) + if s != nil { + return s + } + die := newdie(&dwtypes, abbrev, name, 0) + f(die) + return die.sym +} + func synthesizemaptypes(die *DWDie) { - hash := walktypedef(defgotype(lookup_or_diag("type.runtime.hmap"))) - bucket := walktypedef(defgotype(lookup_or_diag("type.runtime.bmap"))) + hash := walktypedef(findprotodie("type.runtime.hmap")) + bucket := walktypedef(findprotodie("type.runtime.bmap")) if hash == nil { return @@ -1175,97 +1152,92 @@ func synthesizemaptypes(die *DWDie) { if die.abbrev != DW_ABRV_MAPTYPE { continue } - - keytype := walktypedef(getattr(die, DW_AT_go_key).data.(*DWDie)) - valtype := walktypedef(getattr(die, DW_AT_go_elem).data.(*DWDie)) + gotype := getattr(die, DW_AT_type).data.(*LSym) + keytype := decodetype_mapkey(gotype) + valtype := decodetype_mapvalue(gotype) + keysize, valsize := decodetype_size(keytype), decodetype_size(valtype) + keytype, valtype = walksymtypedef(defgotype(keytype)), walksymtypedef(defgotype(valtype)) // compute size info like hashmap.c does. - keysize, valsize := Thearch.Ptrsize, Thearch.Ptrsize - a := getattr(keytype, DW_AT_byte_size) - if a != nil { - keysize = int(a.value) - } - a = getattr(valtype, DW_AT_byte_size) - if a != nil { - valsize = int(a.value) - } indirect_key, indirect_val := false, false if keysize > MaxKeySize { - keysize = Thearch.Ptrsize + keysize = int64(Thearch.Ptrsize) indirect_key = true } if valsize > MaxValSize { - valsize = Thearch.Ptrsize + valsize = int64(Thearch.Ptrsize) indirect_val = true } // Construct type to represent an array of BucketSize keys - dwhk := newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]key", getattr(keytype, DW_AT_name).data.(string), "")) - - newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(keysize), 0) - t := keytype - if indirect_key { - t = defptrto(keytype) - } - newrefattr(dwhk, DW_AT_type, t) - fld := newdie(dwhk, DW_ABRV_ARRAYRANGE, "size") - newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) + keyname := nameFromDIESym(keytype) + dwhks := mkinternaltype(DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *DWDie) { + newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(keysize), 0) + t := keytype + if indirect_key { + t = defptrto(keytype) + } + newrefattr(dwhk, DW_AT_type, t) + fld := newdie(dwhk, DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, DW_AT_type, mustFind("uintptr")) + }) // Construct type to represent an array of BucketSize values - dwhv := newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]val", getattr(valtype, DW_AT_name).data.(string), "")) - - newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(valsize), 0) - t = valtype - if indirect_val { - t = defptrto(valtype) - } - newrefattr(dwhv, DW_AT_type, t) - fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size") - newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) + valname := nameFromDIESym(valtype) + dwhvs := mkinternaltype(DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *DWDie) { + newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(valsize), 0) + t := valtype + if indirect_val { + t = defptrto(valtype) + } + newrefattr(dwhv, DW_AT_type, t) + fld := newdie(dwhv, DW_ABRV_ARRAYRANGE, "size", 0) + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, DW_AT_type, mustFind("uintptr")) + }) // Construct bucket - dwhb := newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("bucket", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) - - // Copy over all fields except the field "data" from the generic bucket. - // "data" will be replaced with keys/values below. - copychildrenexcept(dwhb, bucket, find(bucket, "data")) - - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys") - newrefattr(fld, DW_AT_type, dwhk) - newmemberoffsetattr(fld, BucketSize) - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values") - newrefattr(fld, DW_AT_type, dwhv) - newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow") - newrefattr(fld, DW_AT_type, defptrto(dwhb)) - newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) - if Thearch.Regsize > Thearch.Ptrsize { - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad") - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) - newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(Thearch.Ptrsize)) - } + dwhbs := mkinternaltype(DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *DWDie) { + // Copy over all fields except the field "data" from the generic + // bucket. "data" will be replaced with keys/values below. + copychildrenexcept(dwhb, bucket, findchild(bucket, "data")) + + fld := newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys", 0) + newrefattr(fld, DW_AT_type, dwhks) + newmemberoffsetattr(fld, BucketSize) + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values", 0) + newrefattr(fld, DW_AT_type, dwhvs) + newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow", 0) + newrefattr(fld, DW_AT_type, defptrto(dwhb.sym)) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) + if Thearch.Regsize > Thearch.Ptrsize { + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad", 0) + newrefattr(fld, DW_AT_type, mustFind("uintptr")) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(Thearch.Ptrsize)) + } - newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0) + newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0) + }) // Construct hash - dwh := newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hash", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) - - copychildren(dwh, hash) - substitutetype(dwh, "buckets", defptrto(dwhb)) - substitutetype(dwh, "oldbuckets", defptrto(dwhb)) - newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil) + dwhs := mkinternaltype(DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *DWDie) { + copychildren(dwh, hash) + substitutetype(dwh, "buckets", defptrto(dwhbs)) + substitutetype(dwh, "oldbuckets", defptrto(dwhbs)) + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil) + }) // make map type a pointer to hash - newrefattr(die, DW_AT_type, defptrto(dwh)) + newrefattr(die, DW_AT_type, defptrto(dwhs)) } } func synthesizechantypes(die *DWDie) { - sudog := walktypedef(defgotype(lookup_or_diag("type.runtime.sudog"))) - waitq := walktypedef(defgotype(lookup_or_diag("type.runtime.waitq"))) - hchan := walktypedef(defgotype(lookup_or_diag("type.runtime.hchan"))) + sudog := walktypedef(findprotodie("type.runtime.sudog")) + waitq := walktypedef(findprotodie("type.runtime.waitq")) + hchan := walktypedef(findprotodie("type.runtime.hchan")) if sudog == nil || waitq == nil || hchan == nil { return } @@ -1276,42 +1248,41 @@ func synthesizechantypes(die *DWDie) { if die.abbrev != DW_ABRV_CHANTYPE { continue } - elemsize := Thearch.Ptrsize - elemtype := getattr(die, DW_AT_go_elem).data.(*DWDie) - a := getattr(elemtype, DW_AT_byte_size) - if a != nil { - elemsize = int(a.value) - } + elemgotype := getattr(die, DW_AT_type).data.(*LSym) + elemsize := decodetype_size(elemgotype) + elemname := elemgotype.Name[5:] + elemtype := walksymtypedef(defgotype(elemgotype)) // sudog - dws := newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("sudog", getattr(elemtype, DW_AT_name).data.(string), "")) - - copychildren(dws, sudog) - substitutetype(dws, "elem", elemtype) - if elemsize > 8 { - elemsize -= 8 - } else { - elemsize = 0 - } - newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, int64(sudogsize)+int64(elemsize), nil) + dwss := mkinternaltype(DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *DWDie) { + copychildren(dws, sudog) + substitutetype(dws, "elem", elemtype) + if elemsize > 8 { + elemsize -= 8 + } else { + elemsize = 0 + } + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, int64(sudogsize)+int64(elemsize), nil) + }) // waitq - dww := newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("waitq", getattr(elemtype, DW_AT_name).data.(string), "")) + dwws := mkinternaltype(DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *DWDie) { - copychildren(dww, waitq) - substitutetype(dww, "first", defptrto(dws)) - substitutetype(dww, "last", defptrto(dws)) - newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(waitq, DW_AT_byte_size).value, nil) + copychildren(dww, waitq) + substitutetype(dww, "first", defptrto(dwss)) + substitutetype(dww, "last", defptrto(dwss)) + newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(waitq, DW_AT_byte_size).value, nil) + }) // hchan - dwh := newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hchan", getattr(elemtype, DW_AT_name).data.(string), "")) + dwhs := mkinternaltype(DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *DWDie) { + copychildren(dwh, hchan) + substitutetype(dwh, "recvq", dwws) + substitutetype(dwh, "sendq", dwws) + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hchan, DW_AT_byte_size).value, nil) + }) - copychildren(dwh, hchan) - substitutetype(dwh, "recvq", dww) - substitutetype(dwh, "sendq", dww) - newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hchan, DW_AT_byte_size).value, nil) - - newrefattr(die, DW_AT_type, defptrto(dwh)) + newrefattr(die, DW_AT_type, defptrto(dwhs)) } } @@ -1331,13 +1302,13 @@ func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype var dv *DWDie - var dt *DWDie + var dt *LSym switch t { default: return case 'd', 'b', 'D', 'B': - dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s) + dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s, ver) newabslocexprattr(dv, v, sym) if ver == 0 { newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0) @@ -1386,23 +1357,23 @@ const ( OPCODE_BASE = 10 ) -func putpclcdelta(delta_pc int64, delta_lc int64) { +func putpclcdelta(s *LSym, delta_pc int64, delta_lc int64) { if LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE { var opcode int64 = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc) if OPCODE_BASE <= opcode && opcode < 256 { - Cput(uint8(opcode)) + Adduint8(Ctxt, s, uint8(opcode)) return } } if delta_pc != 0 { - Cput(DW_LNS_advance_pc) - sleb128put(delta_pc) + Adduint8(Ctxt, s, DW_LNS_advance_pc) + sleb128put(s, delta_pc) } - Cput(DW_LNS_advance_line) - sleb128put(delta_lc) - Cput(DW_LNS_copy) + Adduint8(Ctxt, s, DW_LNS_advance_line) + sleb128put(s, delta_lc) + Adduint8(Ctxt, s, DW_LNS_copy) } func newcfaoffsetattr(die *DWDie, offs int32) { @@ -1428,26 +1399,6 @@ func mkvarname(name string, da int) string { * Walk prog table, emit line program and build DIE tree. */ -// flush previous compilation unit. -func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_length int32) { - if dwinfo != nil && pc != 0 { - newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, pcsym) - } - - if unitstart >= 0 { - Cput(0) // start extended opcode - uleb128put(1) - Cput(DW_LNE_end_sequence) - - here := Cpos() - Cseek(unitstart) - Thearch.Lput(uint32(here - unitstart - 4)) // unit_length - Thearch.Wput(2) // dwarf version - Thearch.Lput(uint32(header_length)) // header length starting here - Cseek(here) - } -} - func getCompilationDir() string { if dir, err := os.Getwd(); err == nil { return dir @@ -1455,28 +1406,30 @@ func getCompilationDir() string { return "/" } -func writelines() { +func writelines(prev *LSym) *LSym { if linesec == nil { - linesec = Linklookup(Ctxt, ".dwarfline", 0) + linesec = Linklookup(Ctxt, ".debug_line", 0) } + linesec.Type = obj.SDWARFSECT linesec.R = linesec.R[:0] + ls := linesec + prev.Next = ls + unitstart := int64(-1) + headerstart := int64(-1) headerend := int64(-1) epc := int64(0) var epcs *LSym - lineo = Cpos() var dwinfo *DWDie - flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) - unitstart = Cpos() lang := DW_LANG_Go s := Ctxt.Textp - dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go") + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go", 0) newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0) - newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart-lineo, 0) + newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, 0, 0) newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. compDir := getCompilationDir() @@ -1484,26 +1437,30 @@ func writelines() { // Write .debug_line Line Number Program Header (sec 6.2.4) // Fields marked with (*) must be changed for 64-bit dwarf - Thearch.Lput(0) // unit_length (*), will be filled in by flushunit. - Thearch.Wput(2) // dwarf version (appendix F) - Thearch.Lput(0) // header_length (*), filled in by flushunit. + unit_length_offset := ls.Size + Adduint32(Ctxt, ls, 0) // unit_length (*), filled in at end. + unitstart = ls.Size + Adduint16(Ctxt, ls, 2) // dwarf version (appendix F) + header_length_offset := ls.Size + Adduint32(Ctxt, ls, 0) // header_length (*), filled in at end. + headerstart = ls.Size // cpos == unitstart + 4 + 2 + 4 - Cput(1) // minimum_instruction_length - Cput(1) // default_is_stmt - Cput(LINE_BASE & 0xFF) // line_base - Cput(LINE_RANGE) // line_range - Cput(OPCODE_BASE) // opcode_base - Cput(0) // standard_opcode_lengths[1] - Cput(1) // standard_opcode_lengths[2] - Cput(1) // standard_opcode_lengths[3] - Cput(1) // standard_opcode_lengths[4] - Cput(1) // standard_opcode_lengths[5] - Cput(0) // standard_opcode_lengths[6] - Cput(0) // standard_opcode_lengths[7] - Cput(0) // standard_opcode_lengths[8] - Cput(1) // standard_opcode_lengths[9] - Cput(0) // include_directories (empty) + Adduint8(Ctxt, ls, 1) // minimum_instruction_length + Adduint8(Ctxt, ls, 1) // default_is_stmt + Adduint8(Ctxt, ls, LINE_BASE&0xFF) // line_base + Adduint8(Ctxt, ls, LINE_RANGE) // line_range + Adduint8(Ctxt, ls, OPCODE_BASE) // opcode_base + Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[1] + Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[2] + Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[3] + Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[4] + Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[5] + Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[6] + Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[7] + Adduint8(Ctxt, ls, 0) // standard_opcode_lengths[8] + Adduint8(Ctxt, ls, 1) // standard_opcode_lengths[9] + Adduint8(Ctxt, ls, 0) // include_directories (empty) files := make([]*LSym, Ctxt.Nhistfile) @@ -1512,25 +1469,28 @@ func writelines() { } for i := 0; int32(i) < Ctxt.Nhistfile; i++ { - strnput(files[i].Name, len(files[i].Name)+4) + Addstring(ls, files[i].Name) + Adduint8(Ctxt, ls, 0) + Adduint8(Ctxt, ls, 0) + Adduint8(Ctxt, ls, 0) } // 4 zeros: the string termination + 3 fields. - Cput(0) + Adduint8(Ctxt, ls, 0) // terminate file_names. - headerend = Cpos() + headerend = ls.Size - Cput(0) // start extended opcode - uleb128put(1 + int64(Thearch.Ptrsize)) - Cput(DW_LNE_set_address) + Adduint8(Ctxt, ls, 0) // start extended opcode + uleb128put(ls, 1+int64(Thearch.Ptrsize)) + Adduint8(Ctxt, ls, DW_LNE_set_address) pc := s.Value line := 1 file := 1 if Linkmode == LinkExternal { - adddwarfrel(linesec, s, lineo, Thearch.Ptrsize, 0) + Addaddr(Ctxt, ls, s) } else { - addrput(pc) + addrput(ls, pc) } var pcfile Pciter @@ -1538,7 +1498,7 @@ func writelines() { for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { s = Ctxt.Cursym - dwfunc := newdie(dwinfo, DW_ABRV_FUNCTION, s.Name) + dwfunc := newdie(dwinfo, DW_ABRV_FUNCTION, s.Name, int(s.Version)) newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) epc = s.Value + s.Size epcs = s @@ -1568,12 +1528,12 @@ func writelines() { } if int32(file) != pcfile.value { - Cput(DW_LNS_set_file) - uleb128put(int64(pcfile.value)) + Adduint8(Ctxt, ls, DW_LNS_set_file) + uleb128put(ls, int64(pcfile.value)) file = int(pcfile.value) } - putpclcdelta(s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line)) + putpclcdelta(ls, s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line)) pc = s.Value + int64(pcline.pc) line = int(pcline.value) @@ -1610,7 +1570,7 @@ func writelines() { continue } var n string - if find(dwfunc, a.Asym.Name) != nil { + if findchild(dwfunc, a.Asym.Name) != nil { n = mkvarname(a.Asym.Name, da) } else { n = a.Asym.Name @@ -1621,7 +1581,7 @@ func writelines() { n = n[i+1:] } - dwvar := newdie(dwfunc, dt, n) + dwvar := newdie(dwfunc, dt, n, 0) newcfaoffsetattr(dwvar, int32(offs)) newrefattr(dwvar, DW_AT_type, defgotype(a.Gotype)) @@ -1642,8 +1602,16 @@ func writelines() { } } - flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) - linesize = Cpos() - lineo + Adduint8(Ctxt, ls, 0) // start extended opcode + uleb128put(ls, 1) + Adduint8(Ctxt, ls, DW_LNE_end_sequence) + + newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, epc+1, epcs) + + setuint32(Ctxt, ls, unit_length_offset, uint32(ls.Size-unitstart)) + setuint32(Ctxt, ls, header_length_offset, uint32(headerend-headerstart)) + + return ls } /* @@ -1675,47 +1643,49 @@ func appendPCDeltaCFA(b []byte, deltapc, cfa int64) []byte { return b } -func writeframes() { +func writeframes(prev *LSym) *LSym { if framesec == nil { - framesec = Linklookup(Ctxt, ".dwarfframe", 0) + framesec = Linklookup(Ctxt, ".debug_frame", 0) } + framesec.Type = obj.SDWARFSECT framesec.R = framesec.R[:0] - frameo = Cpos() + fs := framesec + prev.Next = fs // Emit the CIE, Section 6.4.1 - Thearch.Lput(CIERESERVE) // initial length, must be multiple of thearch.ptrsize - Thearch.Lput(0xffffffff) // cid. - Cput(3) // dwarf version (appendix F) - Cput(0) // augmentation "" - uleb128put(1) // code_alignment_factor - sleb128put(DATAALIGNMENTFACTOR) // guess - uleb128put(int64(Thearch.Dwarfreglr)) // return_address_register + Adduint32(Ctxt, fs, CIERESERVE) // initial length, must be multiple of thearch.ptrsize + Adduint32(Ctxt, fs, 0xffffffff) // cid. + Adduint8(Ctxt, fs, 3) // dwarf version (appendix F) + Adduint8(Ctxt, fs, 0) // augmentation "" + uleb128put(fs, 1) // code_alignment_factor + sleb128put(fs, DATAALIGNMENTFACTOR) // guess + uleb128put(fs, int64(Thearch.Dwarfreglr)) // return_address_register - Cput(DW_CFA_def_cfa) + Adduint8(Ctxt, fs, DW_CFA_def_cfa) - uleb128put(int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h) + uleb128put(fs, int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h) if haslinkregister() { - uleb128put(int64(0)) // offset + uleb128put(fs, int64(0)) // offset } else { - uleb128put(int64(Thearch.Ptrsize)) // offset + uleb128put(fs, int64(Thearch.Ptrsize)) // offset } - Cput(DW_CFA_offset_extended) - uleb128put(int64(Thearch.Dwarfreglr)) // return address + Adduint8(Ctxt, fs, DW_CFA_offset_extended) + uleb128put(fs, int64(Thearch.Dwarfreglr)) // return address if haslinkregister() { - uleb128put(int64(0) / DATAALIGNMENTFACTOR) // at cfa - 0 + uleb128put(fs, int64(0)/DATAALIGNMENTFACTOR) // at cfa - 0 } else { - uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4 + uleb128put(fs, int64(-Thearch.Ptrsize)/DATAALIGNMENTFACTOR) // at cfa - x*4 } // 4 is to exclude the length field. - pad := CIERESERVE + frameo + 4 - Cpos() + pad := CIERESERVE + 4 - fs.Size if pad < 0 { Exitf("dwarf: CIERESERVE too small by %d bytes.", -pad) } - strnput("", int(pad)) + Addbytes(Ctxt, fs, zeros[:pad]) var deltaBuf []byte var pcsp Pciter @@ -1754,21 +1724,17 @@ func writeframes() { // 4 bytes: Pointer to the CIE above, at offset 0 // ptrsize: initial location // ptrsize: address range - Thearch.Lput(uint32(4 + 2*Thearch.Ptrsize + len(deltaBuf))) // length (excludes itself) + Adduint32(Ctxt, fs, uint32(4+2*Thearch.Ptrsize+len(deltaBuf))) // length (excludes itself) if Linkmode == LinkExternal { - adddwarfrel(framesec, framesym, frameo, 4, 0) // CIE offset - adddwarfrel(framesec, s, frameo, Thearch.Ptrsize, 0) // initial location + adddwarfref(Ctxt, fs, framesec, 4) } else { - Thearch.Lput(0) // CIE offset - addrput(s.Value) // initial location + Adduint32(Ctxt, fs, 0) // CIE offset } - addrput(s.Size) // address range - - Cwrite(deltaBuf) + Addaddr(Ctxt, fs, s) + addrput(fs, s.Size) // address range + Addbytes(Ctxt, fs, deltaBuf) } - - Cflush() - framesize = Cpos() - frameo + return fs } /* @@ -1778,12 +1744,14 @@ const ( COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 ) -func writeinfo() { - fwdcount = 0 +func writeinfo(prev *LSym) *LSym { if infosec == nil { - infosec = Linklookup(Ctxt, ".dwarfinfo", 0) + infosec = Linklookup(Ctxt, ".debug_info", 0) } infosec.R = infosec.R[:0] + infosec.Type = obj.SDWARFINFO + infosec.Attr |= AttrReachable + prev.Next, prev = infosec, infosec if arangessec == nil { arangessec = Linklookup(Ctxt, ".dwarfaranges", 0) @@ -1791,32 +1759,30 @@ func writeinfo() { arangessec.R = arangessec.R[:0] for compunit := dwroot.child; compunit != nil; compunit = compunit.link { - unitstart := Cpos() + s := compunit.sym + prev.Next, prev = s, s // Write .debug_info Compilation Unit Header (sec 7.5.1) // Fields marked with (*) must be changed for 64-bit dwarf // This must match COMPUNITHEADERSIZE above. - Thearch.Lput(0) // unit_length (*), will be filled in later. - Thearch.Wput(2) // dwarf version (appendix F) + Adduint32(Ctxt, s, 0) // unit_length (*), will be filled in later. + Adduint16(Ctxt, s, 2) // dwarf version (appendix F) // debug_abbrev_offset (*) - if Linkmode == LinkExternal { - adddwarfrel(infosec, abbrevsym, infoo, 4, 0) - } else { - Thearch.Lput(0) - } + adddwarfref(Ctxt, s, abbrevsym, 4) - Cput(uint8(Thearch.Ptrsize)) // address_size + Adduint8(Ctxt, s, uint8(Thearch.Ptrsize)) // address_size - putdie(compunit) + prev = putdie(prev, compunit) + cusize := s.Size - 4 // exclude the length field. + for child := s.Next; child != nil; child = child.Next { + cusize += child.Size + } - here := Cpos() - Cseek(unitstart) - Thearch.Lput(uint32(here - unitstart - 4)) // exclude the length field. - Cseek(here) + setuint32(Ctxt, s, 0, uint32(cusize)) + newattr(compunit, DW_AT_byte_size, DW_CLS_CONSTANT, int64(cusize), 0) } - - Cflush() + return prev } /* @@ -1837,48 +1803,49 @@ func ispubtype(die *DWDie) bool { return die.abbrev >= DW_ABRV_NULLTYPE } -func writepub(ispub func(*DWDie) bool) int64 { - sectionstart := Cpos() +func writepub(sname string, ispub func(*DWDie) bool, prev *LSym) *LSym { + s := Linklookup(Ctxt, sname, 0) + s.Type = obj.SDWARFSECT + prev.Next = s for compunit := dwroot.child; compunit != nil; compunit = compunit.link { - unitend := infoo + infosize - unitstart := compunit.offs - COMPUNITHEADERSIZE - if compunit.link != nil { - unitend = compunit.link.offs - COMPUNITHEADERSIZE - } + sectionstart := s.Size + culength := uint32(getattr(compunit, DW_AT_byte_size).value) + 4 // Write .debug_pubnames/types Header (sec 6.1.1) - Thearch.Lput(0) // unit_length (*), will be filled in later. - Thearch.Wput(2) // dwarf version (appendix F) - Thearch.Lput(uint32(unitstart)) // debug_info_offset (of the Comp unit Header) - Thearch.Lput(uint32(unitend - unitstart)) // debug_info_length + Adduint32(Ctxt, s, 0) // unit_length (*), will be filled in later. + Adduint16(Ctxt, s, 2) // dwarf version (appendix F) + adddwarfref(Ctxt, s, compunit.sym, 4) // debug_info_offset (of the Comp unit Header) + Adduint32(Ctxt, s, culength) // debug_info_length for die := compunit.child; die != nil; die = die.link { if !ispub(die) { continue } - Thearch.Lput(uint32(die.offs - unitstart)) dwa := getattr(die, DW_AT_name) - strnput(dwa.data.(string), int(dwa.value+1)) + name := dwa.data.(string) + if die.sym == nil { + fmt.Println("Missing sym for ", name) + } + adddwarfref(Ctxt, s, die.sym, 4) + Addstring(s, name) } - Thearch.Lput(0) + Adduint32(Ctxt, s, 0) - here := Cpos() - Cseek(sectionstart) - Thearch.Lput(uint32(here - sectionstart - 4)) // exclude the length field. - Cseek(here) + setuint32(Ctxt, s, sectionstart, uint32(s.Size-sectionstart)-4) // exclude the length field. } - return sectionstart + return s } /* * emit .debug_aranges. _info must have been written before, * because we need die->offs of dw_globals. */ -func writearanges() int64 { - sectionstart := Cpos() +func writearanges(prev *LSym) *LSym { + s := Linklookup(Ctxt, ".debug_aranges", 0) + s.Type = obj.SDWARFSECT // The first tuple is aligned to a multiple of the size of a single tuple // (twice the size of an address) headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize*2))) // don't count unit_length field itself @@ -1894,78 +1861,46 @@ func writearanges() int64 { } // Write .debug_aranges Header + entry (sec 6.1.2) - Thearch.Lput(uint32(headersize) + 4*uint32(Thearch.Ptrsize) - 4) // unit_length (*) - Thearch.Wput(2) // dwarf version (appendix F) - - value := compunit.offs - COMPUNITHEADERSIZE // debug_info_offset - if Linkmode == LinkExternal { - adddwarfrel(arangessec, infosym, sectionstart, 4, value) - } else { - Thearch.Lput(uint32(value)) - } + unitlength := uint32(headersize) + 4*uint32(Thearch.Ptrsize) - 4 + Adduint32(Ctxt, s, unitlength) // unit_length (*) + Adduint16(Ctxt, s, 2) // dwarf version (appendix F) - Cput(uint8(Thearch.Ptrsize)) // address_size - Cput(0) // segment_size - strnput("", headersize-(4+2+4+1+1)) // align to thearch.ptrsize + adddwarfref(Ctxt, s, compunit.sym, 4) - if Linkmode == LinkExternal { - adddwarfrel(arangessec, b.data.(*LSym), sectionstart, Thearch.Ptrsize, b.value-(b.data.(*LSym)).Value) - } else { - addrput(b.value) + Adduint8(Ctxt, s, uint8(Thearch.Ptrsize)) // address_size + Adduint8(Ctxt, s, 0) // segment_size + padding := headersize - (4 + 2 + 4 + 1 + 1) + for i := 0; i < padding; i++ { + Adduint8(Ctxt, s, 0) } - addrput(e.value - b.value) - addrput(0) - addrput(0) + Addaddrplus(Ctxt, s, b.data.(*LSym), b.value-(b.data.(*LSym)).Value) + addrput(s, e.value-b.value) + addrput(s, 0) + addrput(s, 0) } - - Cflush() - return sectionstart -} - -func writegdbscript() int64 { - sectionstart := Cpos() - - if gdbscript != "" { - Cput(1) // magic 1 byte? - strnput(gdbscript, len(gdbscript)+1) - Cflush() + if s.Size > 0 { + prev.Next = s + prev = s } - - return sectionstart + return prev } -func align(size int64) { - if HEADTYPE == obj.Hwindows { // Only Windows PE need section align. - strnput("", int(Rnd(size, PEFILEALIGN)-size)) - } -} +func writegdbscript(prev *LSym) *LSym { -func writedwarfreloc(s *LSym) int64 { - start := Cpos() - for ri := 0; ri < len(s.R); ri++ { - r := &s.R[ri] - i := -1 - if Iself { - i = Thearch.Elfreloc1(r, int64(r.Off)) - } else if HEADTYPE == obj.Hdarwin { - i = Thearch.Machoreloc1(r, int64(r.Off)) - } - if i < 0 { - Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) - } + if gdbscript != "" { + s := Linklookup(Ctxt, ".debug_gdb_scripts", 0) + s.Type = obj.SDWARFSECT + prev.Next = s + prev = s + Adduint8(Ctxt, s, 1) // magic 1 byte? + Addstring(s, gdbscript) } - return start + return prev } -func addmachodwarfsect(prev *Section, name string) *Section { - sect := addsection(&Segdwarf, name, 04) - sect.Extnum = prev.Extnum + 1 - sym := Linklookup(Ctxt, name, 0) - sym.Sect = sect - return sect -} +var prototypedies map[string]*DWDie /* * This is the main entry point for generating dwarf. After emitting @@ -1976,58 +1911,49 @@ func addmachodwarfsect(prev *Section, name string) *Section { * passes. * */ -func Dwarfemitdebugsections() { +func dwarfgeneratedebugsyms() { if Debug['w'] != 0 { // disable dwarf return } + if HEADTYPE == obj.Hplan9 { + return + } if Linkmode == LinkExternal { if !Iself && HEADTYPE != obj.Hdarwin { return } - if HEADTYPE == obj.Hdarwin { - sect := Segdata.Sect - // find the last section. - for sect.Next != nil { - sect = sect.Next - } - sect = addmachodwarfsect(sect, ".debug_abbrev") - sect = addmachodwarfsect(sect, ".debug_line") - sect = addmachodwarfsect(sect, ".debug_frame") - sect = addmachodwarfsect(sect, ".debug_info") - - infosym = Linklookup(Ctxt, ".debug_info", 0) - infosym.Attr |= AttrHidden - - abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0) - abbrevsym.Attr |= AttrHidden - - linesym = Linklookup(Ctxt, ".debug_line", 0) - linesym.Attr |= AttrHidden + } - framesym = Linklookup(Ctxt, ".debug_frame", 0) - framesym.Attr |= AttrHidden - } + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f dwarf\n", obj.Cputime()) } // For diagnostic messages. newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") - mkindex(&dwroot) - mkindex(&dwtypes) - mkindex(&dwglobals) - // Some types that must exist to define other ones. - newdie(&dwtypes, DW_ABRV_NULLTYPE, "") + newdie(&dwtypes, DW_ABRV_NULLTYPE, "", 0) - newdie(&dwtypes, DW_ABRV_NULLTYPE, "void") - newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") + newdie(&dwtypes, DW_ABRV_NULLTYPE, "void", 0) + newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0) - die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr") // needed for array size + die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, int64(Thearch.Ptrsize), 0) newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0) + // Prototypes needed for type synthesis. + prototypedies = map[string]*DWDie{ + "type.runtime.stringStructDWARF": nil, + "type.runtime.slice": nil, + "type.runtime.hmap": nil, + "type.runtime.bmap": nil, + "type.runtime.sudog": nil, + "type.runtime.waitq": nil, + "type.runtime.hchan": nil, + } + // Needed by the prettyprinter code for interface inspection. defgotype(lookup_or_diag("type.runtime._type")) @@ -2036,12 +1962,10 @@ func Dwarfemitdebugsections() { genasmsym(defdwsymb) - writeabbrev() - align(abbrevsize) - writelines() - align(linesize) - writeframes() - align(framesize) + dwarfp = writeabbrev() + last := dwarfp + last = writelines(last) + last = writeframes(last) synthesizestringtypes(dwtypes.child) synthesizeslicetypes(dwtypes.child) @@ -2055,403 +1979,61 @@ func Dwarfemitdebugsections() { movetomodule(&dwtypes) movetomodule(&dwglobals) - infoo = Cpos() - writeinfo() - infoe := Cpos() - pubnameso = infoe - pubtypeso = infoe - arangeso = infoe - gdbscripto = infoe - - if fwdcount > 0 { - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f dwarf pass 2.\n", obj.Cputime()) - } - Cseek(infoo) - writeinfo() - if fwdcount > 0 { - Exitf("dwarf: unresolved references after first dwarf info pass") - } - - if infoe != Cpos() { - Exitf("dwarf: inconsistent second dwarf info pass") - } - } - - infosize = infoe - infoo - align(infosize) - - pubnameso = writepub(ispubname) - pubnamessize = Cpos() - pubnameso - align(pubnamessize) - - pubtypeso = writepub(ispubtype) - pubtypessize = Cpos() - pubtypeso - align(pubtypessize) + // Need to reorder symbols so SDWARFINFO is after all SDWARFSECT + // (but we need to generate dies before writepub) + writeinfo(last) + infosyms := last.Next - arangeso = writearanges() - arangessize = Cpos() - arangeso - align(arangessize) - - gdbscripto = writegdbscript() - gdbscriptsize = Cpos() - gdbscripto - align(gdbscriptsize) - - for Cpos()&7 != 0 { - Cput(0) - } - if HEADTYPE != obj.Hdarwin { - dwarfemitreloc() - } -} - -func dwarfemitreloc() { - if Debug['w'] != 0 { // disable dwarf - return - } - inforeloco = writedwarfreloc(infosec) - inforelocsize = Cpos() - inforeloco - align(inforelocsize) - - arangesreloco = writedwarfreloc(arangessec) - arangesrelocsize = Cpos() - arangesreloco - align(arangesrelocsize) - - linereloco = writedwarfreloc(linesec) - linerelocsize = Cpos() - linereloco - align(linerelocsize) - - framereloco = writedwarfreloc(framesec) - framerelocsize = Cpos() - framereloco - align(framerelocsize) + last = writepub(".debug_pubnames", ispubname, last) + last = writepub(".debug_pubtypes", ispubtype, last) + last = writearanges(last) + last = writegdbscript(last) + last.Next = infosyms } /* * Elf. */ -const ( - ElfStrDebugAbbrev = iota - ElfStrDebugAranges - ElfStrDebugFrame - ElfStrDebugInfo - ElfStrDebugLine - ElfStrDebugLoc - ElfStrDebugMacinfo - ElfStrDebugPubNames - ElfStrDebugPubTypes - ElfStrDebugRanges - ElfStrDebugStr - ElfStrGDBScripts - ElfStrRelDebugInfo - ElfStrRelDebugAranges - ElfStrRelDebugLine - ElfStrRelDebugFrame - NElfStrDbg -) - -var elfstrdbg [NElfStrDbg]int64 - func dwarfaddshstrings(shstrtab *LSym) { if Debug['w'] != 0 { // disable dwarf return } - elfstrdbg[ElfStrDebugAbbrev] = Addstring(shstrtab, ".debug_abbrev") - elfstrdbg[ElfStrDebugAranges] = Addstring(shstrtab, ".debug_aranges") - elfstrdbg[ElfStrDebugFrame] = Addstring(shstrtab, ".debug_frame") - elfstrdbg[ElfStrDebugInfo] = Addstring(shstrtab, ".debug_info") - elfstrdbg[ElfStrDebugLine] = Addstring(shstrtab, ".debug_line") - elfstrdbg[ElfStrDebugLoc] = Addstring(shstrtab, ".debug_loc") - elfstrdbg[ElfStrDebugMacinfo] = Addstring(shstrtab, ".debug_macinfo") - elfstrdbg[ElfStrDebugPubNames] = Addstring(shstrtab, ".debug_pubnames") - elfstrdbg[ElfStrDebugPubTypes] = Addstring(shstrtab, ".debug_pubtypes") - elfstrdbg[ElfStrDebugRanges] = Addstring(shstrtab, ".debug_ranges") - elfstrdbg[ElfStrDebugStr] = Addstring(shstrtab, ".debug_str") - elfstrdbg[ElfStrGDBScripts] = Addstring(shstrtab, ".debug_gdb_scripts") + Addstring(shstrtab, ".debug_abbrev") + Addstring(shstrtab, ".debug_aranges") + Addstring(shstrtab, ".debug_frame") + Addstring(shstrtab, ".debug_info") + Addstring(shstrtab, ".debug_line") + Addstring(shstrtab, ".debug_pubnames") + Addstring(shstrtab, ".debug_pubtypes") + Addstring(shstrtab, ".debug_gdb_scripts") if Linkmode == LinkExternal { - elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, elfRelType+".debug_info") - elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, elfRelType+".debug_aranges") - elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, elfRelType+".debug_line") - elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, elfRelType+".debug_frame") - - infosym = Linklookup(Ctxt, ".debug_info", 0) - infosym.Attr |= AttrHidden - - abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0) - abbrevsym.Attr |= AttrHidden - - linesym = Linklookup(Ctxt, ".debug_line", 0) - linesym.Attr |= AttrHidden - - framesym = Linklookup(Ctxt, ".debug_frame", 0) - framesym.Attr |= AttrHidden + Addstring(shstrtab, elfRelType+".debug_info") + Addstring(shstrtab, elfRelType+".debug_aranges") + Addstring(shstrtab, elfRelType+".debug_line") + Addstring(shstrtab, elfRelType+".debug_frame") + Addstring(shstrtab, elfRelType+".debug_pubnames") + Addstring(shstrtab, elfRelType+".debug_pubtypes") } } -// Add section symbols for DWARF debug info. This is called before +// Add section symbols for DWARF debug info. This is called before // dwarfaddelfheaders. func dwarfaddelfsectionsyms() { - if infosym != nil { - infosympos = Cpos() - putelfsectionsym(infosym, 0) - } - - if abbrevsym != nil { - abbrevsympos = Cpos() - putelfsectionsym(abbrevsym, 0) - } - - if linesym != nil { - linesympos = Cpos() - putelfsectionsym(linesym, 0) - } - - if framesym != nil { - framesympos = Cpos() - putelfsectionsym(framesym, 0) - } -} - -func dwarfaddelfrelocheader(elfstr int, shdata *ElfShdr, off int64, size int64) { - sh := newElfShdr(elfstrdbg[elfstr]) - if elfRelType == ".rela" { - sh.type_ = SHT_RELA - } else { - sh.type_ = SHT_REL - } - - sh.entsize = uint64(Thearch.Ptrsize) * 2 - if sh.type_ == SHT_RELA { - sh.entsize += uint64(Thearch.Ptrsize) - } - sh.link = uint32(elfshname(".symtab").shnum) - sh.info = uint32(shdata.shnum) - sh.off = uint64(off) - sh.size = uint64(size) - sh.addralign = uint64(Thearch.Ptrsize) -} - -func dwarfaddelfheaders() { if Debug['w'] != 0 { // disable dwarf return } - - sh := newElfShdr(elfstrdbg[ElfStrDebugAbbrev]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(abbrevo) - sh.size = uint64(abbrevsize) - sh.addralign = 1 - if abbrevsympos > 0 { - putelfsymshndx(abbrevsympos, sh.shnum) - } - - sh = newElfShdr(elfstrdbg[ElfStrDebugLine]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(lineo) - sh.size = uint64(linesize) - sh.addralign = 1 - if linesympos > 0 { - putelfsymshndx(linesympos, sh.shnum) - } - shline := sh - - sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(frameo) - sh.size = uint64(framesize) - sh.addralign = 1 - if framesympos > 0 { - putelfsymshndx(framesympos, sh.shnum) - } - shframe := sh - - sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(infoo) - sh.size = uint64(infosize) - sh.addralign = 1 - if infosympos > 0 { - putelfsymshndx(infosympos, sh.shnum) - } - shinfo := sh - - if pubnamessize > 0 { - sh := newElfShdr(elfstrdbg[ElfStrDebugPubNames]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(pubnameso) - sh.size = uint64(pubnamessize) - sh.addralign = 1 - } - - if pubtypessize > 0 { - sh := newElfShdr(elfstrdbg[ElfStrDebugPubTypes]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(pubtypeso) - sh.size = uint64(pubtypessize) - sh.addralign = 1 - } - - var sharanges *ElfShdr - if arangessize != 0 { - sh := newElfShdr(elfstrdbg[ElfStrDebugAranges]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(arangeso) - sh.size = uint64(arangessize) - sh.addralign = 1 - sharanges = sh - } - - if gdbscriptsize != 0 { - sh := newElfShdr(elfstrdbg[ElfStrGDBScripts]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(gdbscripto) - sh.size = uint64(gdbscriptsize) - sh.addralign = 1 - } - - if inforelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugInfo, shinfo, inforeloco, inforelocsize) - } - - if arangesrelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugAranges, sharanges, arangesreloco, arangesrelocsize) - } - - if linerelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize) - } - - if framerelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize) - } -} - -/* - * Macho - */ -func dwarfaddmachoheaders(ms *MachoSeg) { - if Debug['w'] != 0 { // disable dwarf - return - } - - // Zero vsize segments won't be loaded in memory, even so they - // have to be page aligned in the file. - fakestart := Rnd(int64(Segdwarf.Fileoff), 0x1000) - addr := Segdata.Vaddr + Segdata.Length - - nsect := 4 - if pubnamessize > 0 { - nsect++ - } - if pubtypessize > 0 { - nsect++ - } - if arangessize > 0 { - nsect++ - } - if gdbscriptsize > 0 { - nsect++ - } - if Linkmode != LinkExternal { - ms = newMachoSeg("__DWARF", nsect) - ms.fileoffset = uint64(fakestart) - ms.filesize = Segdwarf.Filelen - ms.vaddr = addr - } - - msect := newMachoSect(ms, "__debug_abbrev", "__DWARF") - msect.off = uint32(abbrevo) - msect.size = uint64(abbrevsize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - if abbrevsym != nil { - abbrevsym.Value = int64(msect.addr) - } - - msect = newMachoSect(ms, "__debug_line", "__DWARF") - msect.off = uint32(lineo) - msect.size = uint64(linesize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - if linesym != nil { - linesym.Value = int64(msect.addr) - } - if linerelocsize > 0 { - msect.nreloc = uint32(len(linesec.R)) - msect.reloc = uint32(linereloco) - } - - msect = newMachoSect(ms, "__debug_frame", "__DWARF") - msect.off = uint32(frameo) - msect.size = uint64(framesize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - if framesym != nil { - framesym.Value = int64(msect.addr) - } - if framerelocsize > 0 { - msect.nreloc = uint32(len(framesec.R)) - msect.reloc = uint32(framereloco) - } - - msect = newMachoSect(ms, "__debug_info", "__DWARF") - msect.off = uint32(infoo) - msect.size = uint64(infosize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - if infosym != nil { - infosym.Value = int64(msect.addr) - } - if inforelocsize > 0 { - msect.nreloc = uint32(len(infosec.R)) - msect.reloc = uint32(inforeloco) - } - - if pubnamessize > 0 { - msect := newMachoSect(ms, "__debug_pubnames", "__DWARF") - msect.off = uint32(pubnameso) - msect.size = uint64(pubnamessize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - } - - if pubtypessize > 0 { - msect := newMachoSect(ms, "__debug_pubtypes", "__DWARF") - msect.off = uint32(pubtypeso) - msect.size = uint64(pubtypessize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - } - - if arangessize > 0 { - msect := newMachoSect(ms, "__debug_aranges", "__DWARF") - msect.off = uint32(arangeso) - msect.size = uint64(arangessize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - if arangesrelocsize > 0 { - msect.nreloc = uint32(len(arangessec.R)) - msect.reloc = uint32(arangesreloco) - } - } - - // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) - if gdbscriptsize > 0 { - msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF") - msect.off = uint32(gdbscripto) - msect.size = uint64(gdbscriptsize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 + return } + sym := Linklookup(Ctxt, ".debug_info", 0) + putelfsectionsym(sym, sym.Sect.Elfsect.shnum) + sym = Linklookup(Ctxt, ".debug_abbrev", 0) + putelfsectionsym(sym, sym.Sect.Elfsect.shnum) + sym = Linklookup(Ctxt, ".debug_line", 0) + putelfsectionsym(sym, sym.Sect.Elfsect.shnum) + sym = Linklookup(Ctxt, ".debug_frame", 0) + putelfsectionsym(sym, sym.Sect.Elfsect.shnum) } /* @@ -2461,13 +2043,12 @@ func dwarfaddpeheaders() { if Debug['w'] != 0 { // disable dwarf return } - - newPEDWARFSection(".debug_abbrev", abbrevsize) - newPEDWARFSection(".debug_line", linesize) - newPEDWARFSection(".debug_frame", framesize) - newPEDWARFSection(".debug_info", infosize) - newPEDWARFSection(".debug_pubnames", pubnamessize) - newPEDWARFSection(".debug_pubtypes", pubtypessize) - newPEDWARFSection(".debug_aranges", arangessize) - newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize) + for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { + h := newPEDWARFSection(sect.Name, int64(sect.Length)) + fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff + if uint64(h.PointerToRawData) != fileoff { + Diag("%s.PointerToRawData = %#x, want %#x", sect.Name, h.PointerToRawData, fileoff) + errorexit() + } + } } diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 3b40c66592..035826df7c 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1622,6 +1622,9 @@ func elfshbits(sect *Section) *ElfShdr { sh.flags |= SHF_TLS sh.type_ = SHT_NOBITS } + if strings.HasPrefix(sect.Name, ".debug") { + sh.flags = 0 + } if Linkmode != LinkExternal { sh.addr = sect.Vaddr @@ -1739,6 +1742,9 @@ func Elfemitreloc() { for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfrelocsect(sect, datap) } + for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { + elfrelocsect(sect, dwarfp) + } } func addgonote(sectionName string, tag uint32, desc []byte) { @@ -2067,6 +2073,9 @@ func Asmbelfsetup() { for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfshalloc(sect) } + for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { + elfshalloc(sect) + } } func Asmbelf(symo int64) { @@ -2432,6 +2441,9 @@ elfobj: for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfshbits(sect) } + for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { + elfshbits(sect) + } if Linkmode == LinkExternal { for sect := Segtext.Sect; sect != nil; sect = sect.Next { @@ -2443,7 +2455,14 @@ elfobj: for sect := Segdata.Sect; sect != nil; sect = sect.Next { elfshreloc(sect) } - + for s := dwarfp; s != nil; s = s.Next { + if len(s.R) > 0 || s.Type == obj.SDWARFINFO { + elfshreloc(s.Sect) + } + if s.Type == obj.SDWARFINFO { + break + } + } // add a .note.GNU-stack section to mark the stack as non-executable sh := elfshname(".note.GNU-stack") @@ -2467,8 +2486,6 @@ elfobj: sh.off = uint64(symo) + uint64(Symsize) sh.size = uint64(len(Elfstrdat)) sh.addralign = 1 - - dwarfaddelfheaders() } /* Main header */ diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index d60203fb91..cafc6b0382 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -356,7 +356,7 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) { buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) var msect *MachoSect - if sect.Rwx&1 == 0 && (Thearch.Thechar == '7' || // arm64 + if sect.Rwx&1 == 0 && segname != "__DWARF" && (Thearch.Thechar == '7' || // arm64 (Thearch.Thechar == '6' && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive))) { // amd64 // Darwin external linker on arm64 and on amd64 in c-shared/c-archive buildmode // complains about absolute relocs in __TEXT, so if the section is not @@ -411,6 +411,10 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) { msect.name = "__mod_init_func" msect.flag = 9 // S_MOD_INIT_FUNC_POINTERS } + + if segname == "__DWARF" { + msect.flag |= 0x02000000 + } } func Asmbmacho() { @@ -492,6 +496,20 @@ func Asmbmacho() { machoshbits(ms, sect, "__DATA") } + /* dwarf */ + if Debug['w'] == 0 { + if Linkmode != LinkExternal { + ms = newMachoSeg("__DWARF", 20) + ms.vaddr = Segdwarf.Vaddr + ms.vsize = 0 + ms.fileoffset = Segdwarf.Fileoff + ms.filesize = Segdwarf.Filelen + } + for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { + machoshbits(ms, sect, "__DWARF") + } + } + if Linkmode != LinkExternal { switch Thearch.Thechar { default: @@ -586,11 +604,6 @@ func Asmbmacho() { ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0 } - // TODO: dwarf headers go in ms too - if Debug['s'] == 0 { - dwarfaddmachoheaders(ms) - } - a := machowrite() if int32(a) > HEADR { Exitf("HEADR too small: %d > %d", a, HEADR) @@ -876,5 +889,7 @@ func Machoemitreloc() { for sect := Segdata.Sect; sect != nil; sect = sect.Next { machorelocsect(sect, datap) } - dwarfemitreloc() + for sect := Segdwarf.Sect; sect != nil; sect = sect.Next { + machorelocsect(sect, dwarfp) + } } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 0fe0a68c65..167176cc2d 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -195,18 +195,6 @@ func putelfsectionsym(s *LSym, shndx int) { numelfsym++ } -func putelfsymshndx(sympos int64, shndx int) { - here := Cpos() - if elf64 { - Cseek(sympos + 6) - } else { - Cseek(sympos + 14) - } - - Thearch.Wput(uint16(shndx)) - Cseek(here) -} - func Asmelfsym() { // the first symbol entry is reserved putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_NOTYPE, 0, 0) diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 8249c54e45..d0977e9b00 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -147,6 +147,9 @@ func asmb() { ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.Cseek(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + /* output symbol table */ ld.Symsize = 0 @@ -161,7 +164,7 @@ func asmb() { switch ld.HEADTYPE { default: if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) } @@ -180,11 +183,6 @@ func asmb() { ld.Cflush() ld.Cwrite(ld.Elfstrdat) - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index ae69799abf..4c2131dfc6 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -868,6 +868,9 @@ func asmb() { ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.Cseek(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + /* output symbol table */ ld.Symsize = 0 @@ -882,7 +885,7 @@ func asmb() { switch ld.HEADTYPE { default: if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) } @@ -901,11 +904,6 @@ func asmb() { ld.Cflush() ld.Cwrite(ld.Elfstrdat) - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 75a8206174..c5e2d187b8 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -539,6 +539,9 @@ func asmb() { ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.Cseek(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + /* output symbol table */ ld.Symsize = 0 @@ -552,7 +555,7 @@ func asmb() { fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) ld.Cseek(int64(symo)) @@ -566,7 +569,6 @@ func asmb() { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } - ld.Dwarfemitdebugsections() if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 97fccf3ee6..7da5dd02be 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -643,19 +643,11 @@ func asmb() { ld.Cseek(int64(ld.Segdata.Fileoff)) ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + ld.Cseek(int64(ld.Segdwarf.Fileoff)) + ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen)) + machlink := uint32(0) if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - - dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) - ld.Cseek(int64(dwarfoff)) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - machlink = uint32(ld.Domacholink()) } @@ -672,7 +664,7 @@ func asmb() { switch ld.HEADTYPE { default: if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) } @@ -683,7 +675,7 @@ func asmb() { symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink)) case obj.Hwindows: - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) } @@ -698,11 +690,6 @@ func asmb() { ld.Cflush() ld.Cwrite(ld.Elfstrdat) - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - if ld.Linkmode == ld.LinkExternal { ld.Elfemitreloc() } @@ -726,7 +713,6 @@ func asmb() { if ld.Debug['v'] != 0 { fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } - ld.Dwarfemitdebugsections() case obj.Hdarwin: if ld.Linkmode == ld.LinkExternal { -- cgit v1.3 From 31cf1c17792d4da9dae2504c703633a0db8072c7 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 7 Apr 2016 11:47:32 +1200 Subject: runtime: clamp OS-reported number of processors to _MaxGomaxprocs So that all Go processes do not die on startup on a system with >256 CPUs. I tested this by hacking osinit to set ncpu to 1000. Updates #15131 Change-Id: I52e061a0de97be41d684dd8b748fa9087d6f1aef Reviewed-on: https://go-review.googlesource.com/21599 Reviewed-by: Brad Fitzpatrick --- src/runtime/proc.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 5145c84aea..1f55b0fa21 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -449,6 +449,9 @@ func schedinit() { sched.lastpoll = uint64(nanotime()) procs := int(ncpu) + if procs > _MaxGomaxprocs { + procs = _MaxGomaxprocs + } if n := atoi(gogetenv("GOMAXPROCS")); n > 0 { if n > _MaxGomaxprocs { n = _MaxGomaxprocs -- cgit v1.3 From c6e11fe03765e3fe1fc68bd794625ca0ecd833be Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 6 Apr 2016 12:01:40 -0700 Subject: cmd: add new common architecture representation Information about CPU architectures (e.g., name, family, byte ordering, pointer and register size) is currently redundantly scattered around the source tree. Instead consolidate the basic information into a single new package cmd/internal/sys. Also, introduce new sys.I386, sys.AMD64, etc. names for the constants '8', '6', etc. and replace most uses of the latter. The notable exceptions are a couple of error messages that still refer to the old char-based toolchain names and function reltype in cmd/link. Passes toolstash/buildall. Change-Id: I8a6f0cbd49577ec1672a98addebc45f767e36461 Reviewed-on: https://go-review.googlesource.com/21623 Reviewed-by: Michael Hudson-Doyle Reviewed-by: Brad Fitzpatrick Run-TryBot: Matthew Dempsky TryBot-Result: Gobot Gobot --- src/cmd/asm/internal/asm/asm.go | 49 +++++----- src/cmd/asm/internal/asm/parse.go | 16 ++-- src/cmd/compile/internal/amd64/galign.go | 12 +-- src/cmd/compile/internal/arm/galign.go | 7 +- src/cmd/compile/internal/arm64/galign.go | 7 +- src/cmd/compile/internal/gc/cgen.go | 88 +++++++++--------- src/cmd/compile/internal/gc/gen.go | 3 +- src/cmd/compile/internal/gc/go.go | 5 +- src/cmd/compile/internal/gc/gsubr.go | 19 ++-- src/cmd/compile/internal/gc/main.go | 21 +++-- src/cmd/compile/internal/gc/pgen.go | 5 +- src/cmd/compile/internal/gc/plive.go | 3 +- src/cmd/compile/internal/gc/reg.go | 7 +- src/cmd/compile/internal/gc/ssa.go | 7 +- src/cmd/compile/internal/gc/walk.go | 10 +-- src/cmd/compile/internal/mips64/galign.go | 10 +-- src/cmd/compile/internal/ppc64/galign.go | 11 +-- src/cmd/compile/internal/x86/galign.go | 7 +- src/cmd/dist/buildtool.go | 1 + src/cmd/internal/obj/arm/obj5.go | 15 ++-- src/cmd/internal/obj/arm64/obj7.go | 15 ++-- src/cmd/internal/obj/data.go | 2 +- src/cmd/internal/obj/link.go | 19 ++-- src/cmd/internal/obj/mips/obj0.go | 22 ++--- src/cmd/internal/obj/objfile.go | 3 +- src/cmd/internal/obj/pcln.go | 4 +- src/cmd/internal/obj/ppc64/obj9.go | 22 ++--- src/cmd/internal/obj/s390x/objz.go | 15 ++-- src/cmd/internal/obj/sym.go | 3 +- src/cmd/internal/obj/x86/asm6.go | 2 +- src/cmd/internal/obj/x86/obj6.go | 55 +++++------- src/cmd/internal/sys/arch.go | 145 ++++++++++++++++++++++++++++++ src/cmd/link/internal/amd64/asm.go | 2 +- src/cmd/link/internal/amd64/l.go | 5 -- src/cmd/link/internal/amd64/obj.go | 11 +-- src/cmd/link/internal/arm/l.go | 2 - src/cmd/link/internal/arm/obj.go | 9 +- src/cmd/link/internal/arm64/asm.go | 2 +- src/cmd/link/internal/arm64/l.go | 2 - src/cmd/link/internal/arm64/obj.go | 9 +- src/cmd/link/internal/ld/arch.go | 97 -------------------- src/cmd/link/internal/ld/data.go | 57 ++++++------ src/cmd/link/internal/ld/deadcode.go | 3 +- src/cmd/link/internal/ld/decodesym.go | 77 ++++++++-------- src/cmd/link/internal/ld/dwarf.go | 44 ++++----- src/cmd/link/internal/ld/elf.go | 92 ++++++++++--------- src/cmd/link/internal/ld/ldelf.go | 26 +++--- src/cmd/link/internal/ld/ldmacho.go | 18 ++-- src/cmd/link/internal/ld/ldpe.go | 3 +- src/cmd/link/internal/ld/lib.go | 53 +++++------ src/cmd/link/internal/ld/link.go | 25 ++---- src/cmd/link/internal/ld/macho.go | 46 ++++------ src/cmd/link/internal/ld/pcln.go | 38 ++++---- src/cmd/link/internal/ld/pe.go | 29 +++--- src/cmd/link/internal/ld/pobj.go | 9 +- src/cmd/link/internal/ld/sym.go | 33 +++---- src/cmd/link/internal/ld/symtab.go | 5 +- src/cmd/link/internal/mips64/asm.go | 9 +- src/cmd/link/internal/mips64/l.go | 2 - src/cmd/link/internal/mips64/obj.go | 15 ++-- src/cmd/link/internal/ppc64/l.go | 2 - src/cmd/link/internal/ppc64/obj.go | 17 ++-- src/cmd/link/internal/s390x/l.go | 5 -- src/cmd/link/internal/s390x/obj.go | 9 +- src/cmd/link/internal/x86/asm.go | 4 +- src/cmd/link/internal/x86/l.go | 3 - src/cmd/link/internal/x86/obj.go | 9 +- 67 files changed, 639 insertions(+), 743 deletions(-) create mode 100644 src/cmd/internal/sys/arch.go delete mode 100644 src/cmd/link/internal/ld/arch.go (limited to 'src') diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index 950fd735c9..d674914c67 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -13,6 +13,7 @@ import ( "cmd/asm/internal/flags" "cmd/asm/internal/lex" "cmd/internal/obj" + "cmd/internal/sys" ) // TODO: configure the architecture @@ -23,14 +24,14 @@ var testOut *bytes.Buffer // Gathers output when testing. // If doLabel is set, it also defines the labels collect for this Prog. func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) { if cond != "" { - switch p.arch.Thechar { - case '5': + switch p.arch.Family { + case sys.ARM: if !arch.ARMConditionCodes(prog, cond) { p.errorf("unrecognized condition code .%q", cond) return } - case '7': + case sys.ARM64: if !arch.ARM64Suffix(prog, cond) { p.errorf("unrecognized suffix .%q", cond) return @@ -361,7 +362,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { target = &a[1] prog.From = a[0] case 3: - if p.arch.Thechar == '9' { + if p.arch.Family == sys.PPC64 { // Special 3-operand jumps. // First two must be constants; a[1] is a register number. target = &a[2] @@ -378,7 +379,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { prog.Reg = reg break } - if p.arch.Thechar == '0' { + if p.arch.Family == sys.MIPS64 { // 3-operand jumps. // First two must be registers target = &a[2] @@ -386,7 +387,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { prog.Reg = p.getRegister(prog, op, &a[1]) break } - if p.arch.Thechar == 'z' { + if p.arch.Family == sys.S390X { // 3-operand jumps. target = &a[2] prog.From = a[0] @@ -438,7 +439,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { // JMP 4(R0) prog.To = *target // On the ppc64, 9a encodes BR (CTR) as BR CTR. We do the same. - if p.arch.Thechar == '9' && target.Offset == 0 { + if p.arch.Family == sys.PPC64 && target.Offset == 0 { prog.To.Type = obj.TYPE_REG } case target.Type == obj.TYPE_CONST: @@ -492,14 +493,14 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.From = a[0] // prog.To is no address. } - if p.arch.Thechar == '9' && arch.IsPPC64NEG(op) { + if p.arch.Family == sys.PPC64 && arch.IsPPC64NEG(op) { // NEG: From and To are both a[0]. prog.To = a[0] prog.From = a[0] break } case 2: - if p.arch.Thechar == '5' { + if p.arch.Family == sys.ARM { if arch.IsARMCMP(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) @@ -532,11 +533,11 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.Reg = p.getRegister(prog, op, &a[1]) break } - } else if p.arch.Thechar == '7' && arch.IsARM64CMP(op) { + } else if p.arch.Family == sys.ARM64 && arch.IsARM64CMP(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break - } else if p.arch.Thechar == '0' { + } else if p.arch.Family == sys.MIPS64 { if arch.IsMIPS64CMP(op) || arch.IsMIPS64MUL(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) @@ -546,12 +547,12 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.From = a[0] prog.To = a[1] case 3: - switch p.arch.Thechar { - case '0': + switch p.arch.Family { + case sys.MIPS64: prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] - case '5': + case sys.ARM: // Special cases. if arch.IsARMSTREX(op) { /* @@ -567,7 +568,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] - case '7': + case sys.ARM64: // ARM64 instructions with one input and two outputs. if arch.IsARM64STLXR(op) { prog.From = a[0] @@ -582,11 +583,11 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] - case '6', '8': + case sys.AMD64, sys.I386: prog.From = a[0] prog.From3 = newAddr(a[1]) prog.To = a[2] - case '9': + case sys.PPC64: if arch.IsPPC64CMP(op) { // CMPW etc.; third argument is a CR register that goes into prog.Reg. prog.From = a[0] @@ -612,7 +613,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { p.errorf("invalid addressing modes for %s instruction", obj.Aconv(op)) return } - case 'z': + case sys.S390X: if arch.IsS390xWithLength(op) || arch.IsS390xWithIndex(op) { prog.From = a[1] prog.From3 = newAddr(a[0]) @@ -626,7 +627,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { return } case 4: - if p.arch.Thechar == '5' && arch.IsARMMULA(op) { + if p.arch.Family == sys.ARM && arch.IsARMMULA(op) { // All must be registers. p.getRegister(prog, op, &a[0]) r1 := p.getRegister(prog, op, &a[1]) @@ -639,14 +640,14 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.Reg = r1 break } - if p.arch.Thechar == '7' { + if p.arch.Family == sys.ARM64 { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.From3 = newAddr(a[2]) prog.To = a[3] break } - if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) { + if p.arch.Family == sys.PPC64 && arch.IsPPC64RLD(op) { // 2nd operand must always be a register. // TODO: Do we need to guard this with the instruction type? // That is, are there 4-operand instructions without this property? @@ -656,7 +657,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.To = a[3] break } - if p.arch.Thechar == 'z' { + if p.arch.Family == sys.S390X { prog.From = a[1] prog.Reg = p.getRegister(prog, op, &a[2]) prog.From3 = newAddr(a[0]) @@ -666,7 +667,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { p.errorf("can't handle %s instruction with 4 operands", obj.Aconv(op)) return case 5: - if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) { + if p.arch.Family == sys.PPC64 && arch.IsPPC64RLD(op) { // Always reg, reg, con, con, reg. (con, con is a 'mask'). prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) @@ -688,7 +689,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { p.errorf("can't handle %s instruction with 5 operands", obj.Aconv(op)) return case 6: - if p.arch.Thechar == '5' && arch.IsARMMRC(op) { + if p.arch.Family == sys.ARM && arch.IsARMMRC(op) { // Strange special case: MCR, MRC. prog.To.Type = obj.TYPE_CONST x0 := p.getConstant(prog, op, &a[0]) diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go index ee37439962..40206e6dc1 100644 --- a/src/cmd/asm/internal/asm/parse.go +++ b/src/cmd/asm/internal/asm/parse.go @@ -19,6 +19,7 @@ import ( "cmd/asm/internal/flags" "cmd/asm/internal/lex" "cmd/internal/obj" + "cmd/internal/sys" ) type Parser struct { @@ -130,7 +131,7 @@ func (p *Parser) line() bool { for { tok = p.lex.Next() if len(operands) == 0 && len(items) == 0 { - if (p.arch.Thechar == '5' || p.arch.Thechar == '7') && tok == '.' { + if p.arch.InFamily(sys.ARM, sys.ARM64) && tok == '.' { // ARM conditionals. tok = p.lex.Next() str := p.lex.Text() @@ -420,7 +421,7 @@ func (p *Parser) atStartOfRegister(name string) bool { // We have consumed the register or R prefix. func (p *Parser) atRegisterShift() bool { // ARM only. - if p.arch.Thechar != '5' { + if p.arch.Family != sys.ARM { return false } // R1<<... @@ -476,15 +477,14 @@ func (p *Parser) register(name string, prefix rune) (r1, r2 int16, scale int8, o if c == ':' || c == ',' || c == '+' { // 2nd register; syntax (R1+R2) etc. No two architectures agree. // Check the architectures match the syntax. - char := p.arch.Thechar switch p.next().ScanToken { case ',': - if char != '5' && char != '7' { + if !p.arch.InFamily(sys.ARM, sys.ARM64) { p.errorf("(register,register) not supported on this architecture") return } case '+': - if char != '9' { + if p.arch.Family != sys.PPC64 { p.errorf("(register+register) not supported on this architecture") return } @@ -649,7 +649,7 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) { a.Reg = r1 if r2 != 0 { // TODO: Consistency in the encoding would be nice here. - if p.arch.Thechar == '5' || p.arch.Thechar == '7' { + if p.arch.InFamily(sys.ARM, sys.ARM64) { // Special form // ARM: destination register pair (R1, R2). // ARM64: register pair (R1, R2) for LDP/STP. @@ -662,7 +662,7 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) { // Nothing may follow return } - if p.arch.Thechar == '9' { + if p.arch.Family == sys.PPC64 { // Special form for PPC64: (R1+R2); alias for (R1)(R2*1). if prefix != 0 || scale != 0 { p.errorf("illegal address mode for register+register") @@ -752,7 +752,7 @@ ListLoop: // register number is ARM-specific. It returns the number of the specified register. func (p *Parser) registerNumber(name string) uint16 { - if p.arch.Thechar == '5' && name == "g" { + if p.arch.Family == sys.ARM && name == "g" { return 10 } if name[0] != 'R' { diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go index 14721ea35b..461ef2ada1 100644 --- a/src/cmd/compile/internal/amd64/galign.go +++ b/src/cmd/compile/internal/amd64/galign.go @@ -18,12 +18,7 @@ var ( ) func betypeinit() { - gc.Widthptr = 8 - gc.Widthint = 8 - gc.Widthreg = 8 if obj.Getgoarch() == "amd64p32" { - gc.Widthptr = 4 - gc.Widthint = 4 addptr = x86.AADDL movptr = x86.AMOVL leaptr = x86.ALEAL @@ -42,12 +37,9 @@ func Main() { resvd = append(resvd, x86.REG_BP) } - gc.Thearch.Thechar = '6' - gc.Thearch.Thestring = "amd64" - gc.Thearch.Thelinkarch = &x86.Linkamd64 + gc.Thearch.LinkArch = &x86.Linkamd64 if obj.Getgoarch() == "amd64p32" { - gc.Thearch.Thestring = "amd64p32" - gc.Thearch.Thelinkarch = &x86.Linkamd64p32 + gc.Thearch.LinkArch = &x86.Linkamd64p32 } gc.Thearch.REGSP = x86.REGSP gc.Thearch.REGCTXT = x86.REGCTXT diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go index e05f4d06bb..afd86e44c8 100644 --- a/src/cmd/compile/internal/arm/galign.go +++ b/src/cmd/compile/internal/arm/galign.go @@ -11,15 +11,10 @@ import ( ) func betypeinit() { - gc.Widthptr = 4 - gc.Widthint = 4 - gc.Widthreg = 4 } func Main() { - gc.Thearch.Thechar = '5' - gc.Thearch.Thestring = "arm" - gc.Thearch.Thelinkarch = &arm.Linkarm + gc.Thearch.LinkArch = &arm.Linkarm gc.Thearch.REGSP = arm.REGSP gc.Thearch.REGCTXT = arm.REGCTXT gc.Thearch.REGCALLX = arm.REG_R1 diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go index 7e1226fee1..17c851cb14 100644 --- a/src/cmd/compile/internal/arm64/galign.go +++ b/src/cmd/compile/internal/arm64/galign.go @@ -10,15 +10,10 @@ import ( ) func betypeinit() { - gc.Widthptr = 8 - gc.Widthint = 8 - gc.Widthreg = 8 } func Main() { - gc.Thearch.Thechar = '7' - gc.Thearch.Thestring = "arm64" - gc.Thearch.Thelinkarch = &arm64.Linkarm64 + gc.Thearch.LinkArch = &arm64.Linkarm64 gc.Thearch.REGSP = arm64.REGSP gc.Thearch.REGCTXT = arm64.REGCTXT gc.Thearch.REGCALLX = arm64.REGRT1 diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go index c594ad4c11..a9cedf7cfc 100644 --- a/src/cmd/compile/internal/gc/cgen.go +++ b/src/cmd/compile/internal/gc/cgen.go @@ -7,6 +7,7 @@ package gc import ( "cmd/internal/obj" "cmd/internal/obj/ppc64" + "cmd/internal/sys" "fmt" ) @@ -88,7 +89,7 @@ func cgen_wb(n, res *Node, wb bool) { if !res.Addable { if n.Ullman > res.Ullman { - if Ctxt.Arch.Regsize == 4 && Is64(n.Type) { + if Ctxt.Arch.RegSize == 4 && Is64(n.Type) { var n1 Node Tempname(&n1, n.Type) Cgen(n, &n1) @@ -127,7 +128,7 @@ func cgen_wb(n, res *Node, wb bool) { f = false } - if !n.Type.IsComplex() && Ctxt.Arch.Regsize == 8 && !wb { + if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 && !wb { a := Thearch.Optoas(OAS, res.Type) var addr obj.Addr if Thearch.Sudoaddable(a, res, &addr) { @@ -151,7 +152,7 @@ func cgen_wb(n, res *Node, wb bool) { } } - if Ctxt.Arch.Thechar == '8' { + if Ctxt.Arch.Family == sys.I386 { // no registers to speak of var n1, n2 Node Tempname(&n1, n.Type) @@ -203,7 +204,7 @@ func cgen_wb(n, res *Node, wb bool) { // Write barrier now handled. Code below this line can ignore wb. - if Ctxt.Arch.Thechar == '5' { // TODO(rsc): Maybe more often? + if Ctxt.Arch.Family == sys.ARM { // TODO(rsc): Maybe more often? // if both are addressable, move if n.Addable && res.Addable { if Is64(n.Type) || Is64(res.Type) || n.Op == OREGISTER || res.Op == OREGISTER || n.Type.IsComplex() || res.Type.IsComplex() { @@ -246,12 +247,12 @@ func cgen_wb(n, res *Node, wb bool) { return } - if (Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8') && n.Addable { + if Ctxt.Arch.InFamily(sys.AMD64, sys.I386) && n.Addable { Thearch.Gmove(n, res) return } - if Ctxt.Arch.Thechar == '0' || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' { + if Ctxt.Arch.InFamily(sys.ARM64, sys.MIPS64, sys.PPC64) { // if both are addressable, move if n.Addable { if n.Op == OREGISTER || res.Op == OREGISTER { @@ -268,7 +269,7 @@ func cgen_wb(n, res *Node, wb bool) { } // if n is sudoaddable generate addr and move - if Ctxt.Arch.Thechar == '5' && !Is64(n.Type) && !Is64(res.Type) && !n.Type.IsComplex() && !res.Type.IsComplex() { + if Ctxt.Arch.Family == sys.ARM && !Is64(n.Type) && !Is64(res.Type) && !n.Type.IsComplex() && !res.Type.IsComplex() { a := Thearch.Optoas(OAS, n.Type) var addr obj.Addr if Thearch.Sudoaddable(a, n, &addr) { @@ -310,7 +311,7 @@ func cgen_wb(n, res *Node, wb bool) { } // 64-bit ops are hard on 32-bit machine. - if Ctxt.Arch.Regsize == 4 && (Is64(n.Type) || Is64(res.Type) || n.Left != nil && Is64(n.Left.Type)) { + if Ctxt.Arch.RegSize == 4 && (Is64(n.Type) || Is64(res.Type) || n.Left != nil && Is64(n.Left.Type)) { switch n.Op { // math goes to cgen64. case OMINUS, @@ -334,7 +335,7 @@ func cgen_wb(n, res *Node, wb bool) { return } - if !n.Type.IsComplex() && Ctxt.Arch.Regsize == 8 { + if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 { a := Thearch.Optoas(OAS, n.Type) var addr obj.Addr if Thearch.Sudoaddable(a, n, &addr) { @@ -401,11 +402,11 @@ func cgen_wb(n, res *Node, wb bool) { Regalloc(&n1, nl.Type, res) Cgen(nl, &n1) - if Ctxt.Arch.Thechar == '5' { + if Ctxt.Arch.Family == sys.ARM { var n2 Node Nodconst(&n2, nl.Type, 0) Thearch.Gins(a, &n2, &n1) - } else if Ctxt.Arch.Thechar == '7' { + } else if Ctxt.Arch.Family == sys.ARM64 { Thearch.Gins(a, &n1, &n1) } else { Thearch.Gins(a, nil, &n1) @@ -452,7 +453,7 @@ func cgen_wb(n, res *Node, wb bool) { return } - if Ctxt.Arch.Thechar == '8' { + if Ctxt.Arch.Family == sys.I386 { var n1 Node var n2 Node Tempname(&n2, n.Type) @@ -465,7 +466,7 @@ func cgen_wb(n, res *Node, wb bool) { var n1 Node var n2 Node - if Ctxt.Arch.Thechar == '5' { + if Ctxt.Arch.Family == sys.ARM { if nl.Addable && !Is64(nl.Type) { Regalloc(&n1, nl.Type, res) Thearch.Gmove(nl, &n1) @@ -707,7 +708,7 @@ sbop: // symmetric binary abop: // asymmetric binary var n1 Node var n2 Node - if Ctxt.Arch.Thechar == '8' { + if Ctxt.Arch.Family == sys.I386 { // no registers, sigh if Smallintconst(nr) { var n1 Node @@ -751,14 +752,14 @@ abop: // asymmetric binary Regalloc(&n1, nl.Type, res) Cgen(nl, &n1) - if Smallintconst(nr) && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm + if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm n2 = *nr } else { Regalloc(&n2, nr.Type, nil) Cgen(nr, &n2) } } else { - if Smallintconst(nr) && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm + if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm n2 = *nr } else { Regalloc(&n2, nr.Type, res) @@ -876,8 +877,8 @@ func cgen_wbfat(n, res *Node) { // cgen_norm moves n1 to res, truncating to expected type if necessary. // n1 is a register, and cgen_norm frees it. func cgen_norm(n, n1, res *Node) { - switch Ctxt.Arch.Thechar { - case '6', '8': + switch Ctxt.Arch.Family { + case sys.AMD64, sys.I386: // We use sized math, so the result is already truncated. default: switch n.Op { @@ -980,7 +981,7 @@ func Agenr(n *Node, a *Node, res *Node) { Cgen_checknil(a) case OINDEX: - if Ctxt.Arch.Thechar == '5' { + if Ctxt.Arch.Family == sys.ARM { var p2 *obj.Prog // to be patched to panicindex. w := uint32(n.Type.Width) bounded := Debug['B'] != 0 || n.Bounded @@ -1127,7 +1128,7 @@ func Agenr(n *Node, a *Node, res *Node) { Regfree(&n2) break } - if Ctxt.Arch.Thechar == '8' { + if Ctxt.Arch.Family == sys.I386 { var p2 *obj.Prog // to be patched to panicindex. w := uint32(n.Type.Width) bounded := Debug['B'] != 0 || n.Bounded @@ -1604,7 +1605,7 @@ func Agen(n *Node, res *Node) { } func addOffset(res *Node, offset int64) { - if Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8' { + if Ctxt.Arch.InFamily(sys.AMD64, sys.I386) { Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), Nodintconst(offset), res) return } @@ -1825,13 +1826,14 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { return case ONAME: + // Some architectures might need a temporary or other help here, + // but they don't support direct generation of a bool value yet. + // We can fix that as we go. + mayNeedTemp := Ctxt.Arch.InFamily(sys.ARM, sys.ARM64, sys.MIPS64, sys.PPC64) + if genval { - // 5g, 7g, and 9g might need a temporary or other help here, - // but they don't support direct generation of a bool value yet. - // We can fix that as we go. - switch Ctxt.Arch.Thechar { - case '0', '5', '7', '9': - Fatalf("genval 0g, 5g, 7g, 9g ONAMES not fully implemented") + if mayNeedTemp { + Fatalf("genval ONAMES not fully implemented") } Cgen(n, res) if !wantTrue { @@ -1840,7 +1842,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { return } - if n.Addable && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { + if n.Addable && !mayNeedTemp { // no need for a temporary bgenNonZero(n, nil, wantTrue, likely, to) return @@ -1977,7 +1979,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { return } - if Ctxt.Arch.Regsize == 4 && Is64(nr.Type) { + if Ctxt.Arch.RegSize == 4 && Is64(nr.Type) { if genval { // TODO: Teach Cmp64 to generate boolean values and remove this. bvgenjump(n, res, wantTrue, false) @@ -2015,7 +2017,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { Regfree(&n2) } else { var n1 Node - if !nl.Addable && Ctxt.Arch.Thechar == '8' { + if !nl.Addable && Ctxt.Arch.Family == sys.I386 { Tempname(&n1, nl.Type) } else { Regalloc(&n1, nl.Type, nil) @@ -2024,13 +2026,13 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { Cgen(nl, &n1) nl = &n1 - if Smallintconst(nr) && Ctxt.Arch.Thechar != '0' && Ctxt.Arch.Thechar != '9' { + if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.PPC64 { Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), nl, nr) bins(nr.Type, res, op, likely, to) return } - if !nr.Addable && Ctxt.Arch.Thechar == '8' { + if !nr.Addable && Ctxt.Arch.Family == sys.I386 { nr = CgenTemp(nr) } @@ -2044,13 +2046,13 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { l, r := nl, nr // On x86, only < and <= work right with NaN; reverse if needed - if Ctxt.Arch.Thechar == '6' && nl.Type.IsFloat() && (op == OGT || op == OGE) { + if Ctxt.Arch.Family == sys.AMD64 && nl.Type.IsFloat() && (op == OGT || op == OGE) { l, r = r, l op = Brrev(op) } // MIPS does not have CMP instruction - if Ctxt.Arch.Thechar == '0' { + if Ctxt.Arch.Family == sys.MIPS64 { p := Thearch.Ginscmp(op, nr.Type, l, r, likely) Patch(p, to) return @@ -2062,8 +2064,8 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { // Handle floating point special cases. // Note that 8g has Bgen_float and is handled above. if nl.Type.IsFloat() { - switch Ctxt.Arch.Thechar { - case '5': + switch Ctxt.Arch.Family { + case sys.ARM: if genval { Fatalf("genval 5g Isfloat special cases not implemented") } @@ -2077,7 +2079,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { Patch(p, Pc) } return - case '6': + case sys.AMD64: switch n.Op { case OEQ: // neither NE nor P @@ -2111,7 +2113,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { } return } - case '7', '9': + case sys.ARM64, sys.PPC64: if genval { Fatalf("genval 7g, 9g Isfloat special cases not implemented") } @@ -2143,7 +2145,7 @@ func bgenNonZero(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { } // MIPS does not have CMP instruction - if Thearch.Thechar == '0' { + if Thearch.LinkArch.Family == sys.MIPS64 { p := Gbranch(Thearch.Optoas(op, n.Type), n.Type, likely) Naddr(&p.From, n) Patch(p, to) @@ -2352,7 +2354,7 @@ func Ginscall(f *Node, proc int) { // into the instruction stream. Thearch.Ginsnop() - if Thearch.Thechar == '9' { + if Thearch.LinkArch.Family == sys.PPC64 { // On ppc64, when compiling Go into position // independent code on ppc64le we insert an // instruction to reload the TOC pointer from the @@ -2630,7 +2632,7 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) { // in peep and optoas in order to enable this. // TODO(rsc): ppc64 needs to support the relevant instructions // in peep and optoas in order to enable this. - if nr.Op != OLITERAL || Ctxt.Arch.Thechar == '0' || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' { + if nr.Op != OLITERAL || Ctxt.Arch.Family == sys.MIPS64 || Ctxt.Arch.Family == sys.ARM64 || Ctxt.Arch.Family == sys.PPC64 { goto longdiv } w = int(nl.Type.Width * 8) @@ -2995,7 +2997,7 @@ func cgen_slice(n, res *Node, wb bool) { regalloc := Regalloc ginscon := Thearch.Ginscon gins := Thearch.Gins - if Thearch.Thechar == '8' { + if Thearch.LinkArch.Family == sys.I386 { regalloc = func(n *Node, t *Type, reuse *Node) { Tempname(n, t) } @@ -3238,7 +3240,7 @@ func cgen_slice(n, res *Node, wb bool) { compare := func(n1, n2 *Node) { // n1 might be a 64-bit constant, even on 32-bit architectures, // but it will be represented in 32 bits. - if Ctxt.Arch.Regsize == 4 && Is64(n1.Type) { + if Ctxt.Arch.RegSize == 4 && Is64(n1.Type) { if n1.Val().U.(*Mpint).CmpInt64(1<<31) >= 0 { Fatalf("missed slice out of bounds check") } diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go index 4a98f41bcb..7527452c93 100644 --- a/src/cmd/compile/internal/gc/gen.go +++ b/src/cmd/compile/internal/gc/gen.go @@ -8,6 +8,7 @@ package gc import ( "cmd/internal/obj" + "cmd/internal/sys" "fmt" ) @@ -1174,7 +1175,7 @@ func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset i } // NOTE: Assuming little endian (signed top half at offset 4). // We don't have any 32-bit big-endian systems. - if Thearch.Thechar != '5' && Thearch.Thechar != '8' { + if !Thearch.LinkArch.InFamily(sys.ARM, sys.I386) { Fatalf("unknown 32-bit architecture") } return f(Types[TUINT32], startOffset) && diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 4cb985b1be..ef8b516ea5 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -360,9 +360,8 @@ const ( ) type Arch struct { - Thechar int - Thestring string - Thelinkarch *obj.LinkArch + LinkArch *obj.LinkArch + REGSP int REGCTXT int REGCALLX int // BX diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index a2fa5f8b31..63a8e969c3 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -32,6 +32,7 @@ package gc import ( "cmd/internal/obj" + "cmd/internal/sys" "fmt" "runtime" "strings" @@ -57,7 +58,7 @@ func Ismem(n *Node) bool { return true case OADDR: - return Thearch.Thechar == '6' || Thearch.Thechar == '9' // because 6g uses PC-relative addressing; TODO(rsc): not sure why 9g too + return Thearch.LinkArch.InFamily(sys.AMD64, sys.PPC64) // because 6g uses PC-relative addressing; TODO(rsc): not sure why 9g too } return false @@ -83,7 +84,7 @@ func Gbranch(as obj.As, t *Type, likely int) *obj.Prog { p := Prog(as) p.To.Type = obj.TYPE_BRANCH p.To.Val = nil - if as != obj.AJMP && likely != 0 && Thearch.Thechar != '9' && Thearch.Thechar != '7' && Thearch.Thechar != '0' { + if as != obj.AJMP && likely != 0 && Thearch.LinkArch.Family != sys.PPC64 && Thearch.LinkArch.Family != sys.ARM64 && Thearch.LinkArch.Family != sys.MIPS64 { p.From.Type = obj.TYPE_CONST if likely > 0 { p.From.Offset = 1 @@ -330,7 +331,7 @@ func Naddr(a *obj.Addr, n *Node) { a.Type = obj.TYPE_REG a.Reg = n.Reg a.Sym = nil - if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. + if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width. a.Width = 0 } @@ -342,7 +343,7 @@ func Naddr(a *obj.Addr, n *Node) { if a.Offset != int64(int32(a.Offset)) { Yyerror("offset %d too large for OINDREG", a.Offset) } - if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. + if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width. a.Width = 0 } @@ -424,7 +425,7 @@ func Naddr(a *obj.Addr, n *Node) { Naddr(a, n.Left) case OLITERAL: - if Thearch.Thechar == '8' { + if Thearch.LinkArch.Family == sys.I386 { a.Width = 0 } switch n.Val().Ctype() { @@ -457,7 +458,7 @@ func Naddr(a *obj.Addr, n *Node) { case OADDR: Naddr(a, n.Left) a.Etype = uint8(Tptr) - if Thearch.Thechar != '0' && Thearch.Thechar != '5' && Thearch.Thechar != '7' && Thearch.Thechar != '9' { // TODO(rsc): Do this even for arm, ppc64. + if !Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) { // TODO(rsc): Do this even for arm, ppc64. a.Width = int64(Widthptr) } if a.Type != obj.TYPE_MEM { @@ -496,7 +497,7 @@ func Naddr(a *obj.Addr, n *Node) { } a.Etype = uint8(Simtype[TUINT]) a.Offset += int64(Array_nel) - if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. + if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm. a.Width = int64(Widthint) } @@ -509,7 +510,7 @@ func Naddr(a *obj.Addr, n *Node) { } a.Etype = uint8(Simtype[TUINT]) a.Offset += int64(Array_cap) - if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. + if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm. a.Width = int64(Widthint) } } @@ -695,7 +696,7 @@ func Regalloc(n *Node, t *Type, o *Node) { Fatalf("regalloc: t nil") } et := Simtype[t.Etype] - if Ctxt.Arch.Regsize == 4 && (et == TINT64 || et == TUINT64) { + if Ctxt.Arch.RegSize == 4 && (et == TINT64 || et == TUINT64) { Fatalf("regalloc 64bit") } diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 73ecb09fa5..72e6478afe 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -10,6 +10,7 @@ import ( "bufio" "cmd/compile/internal/ssa" "cmd/internal/obj" + "cmd/internal/sys" "flag" "fmt" "io" @@ -96,12 +97,12 @@ func Main() { // but not other values. p := obj.Getgoarch() - if !strings.HasPrefix(p, Thearch.Thestring) { - log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.Thechar, p) + if !strings.HasPrefix(p, Thearch.LinkArch.Name) { + log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.LinkArch.Family, p) } goarch = p - Ctxt = obj.Linknew(Thearch.Thelinkarch) + Ctxt = obj.Linknew(Thearch.LinkArch) Ctxt.DiagFunc = Yyerror Ctxt.Bso = &bstdout bstdout = *obj.Binitw(os.Stdout) @@ -200,15 +201,13 @@ func Main() { obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y']) var flag_shared int var flag_dynlink bool - switch Thearch.Thechar { - case '5', '6', '7', '8', '9': + if Thearch.LinkArch.InFamily(sys.ARM, sys.AMD64, sys.ARM64, sys.I386, sys.PPC64) { obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared) } - if Thearch.Thechar == '6' { + if Thearch.LinkArch.Family == sys.AMD64 { obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) } - switch Thearch.Thechar { - case '5', '6', '7', '8', '9': + if Thearch.LinkArch.InFamily(sys.ARM, sys.AMD64, sys.ARM64, sys.I386, sys.PPC64) { flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") } obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) @@ -301,9 +300,9 @@ func Main() { } Thearch.Betypeinit() - if Widthptr == 0 { - Fatalf("betypeinit failed") - } + Widthint = Thearch.LinkArch.IntSize + Widthptr = Thearch.LinkArch.PtrSize + Widthreg = Thearch.LinkArch.RegSize initUniverse() diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 63f7bf825e..bfb65ade38 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -7,6 +7,7 @@ package gc import ( "cmd/compile/internal/ssa" "cmd/internal/obj" + "cmd/internal/sys" "crypto/md5" "fmt" "sort" @@ -286,7 +287,7 @@ func allocauto(ptxt *obj.Prog) { if haspointers(n.Type) { stkptrsize = Stksize } - if Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { + if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) { Stksize = Rnd(Stksize, int64(Widthptr)) } if Stksize >= 1<<31 { @@ -323,7 +324,7 @@ func Cgen_checknil(n *Node) { Fatalf("bad checknil") } - if ((Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { + if (Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { var reg Node Regalloc(®, Types[Tptr], n) Cgen(n, ®) diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go index 43f594e2ea..6e43d3133f 100644 --- a/src/cmd/compile/internal/gc/plive.go +++ b/src/cmd/compile/internal/gc/plive.go @@ -17,6 +17,7 @@ package gc import ( "cmd/internal/obj" + "cmd/internal/sys" "fmt" "sort" "strings" @@ -1396,7 +1397,7 @@ func livenessepilogue(lv *Liveness) { // The instruction before a call to deferreturn is always a // no-op, to keep PC-specific data unambiguous. prev := p.Opt.(*obj.Prog) - if Ctxt.Arch.Thechar == '9' { + if Ctxt.Arch.Family == sys.PPC64 { // On ppc64 there is an additional instruction // (another no-op or reload of toc pointer) before // the call. diff --git a/src/cmd/compile/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go index 26746a5bcf..8705d6dfa4 100644 --- a/src/cmd/compile/internal/gc/reg.go +++ b/src/cmd/compile/internal/gc/reg.go @@ -33,6 +33,7 @@ package gc import ( "bytes" "cmd/internal/obj" + "cmd/internal/sys" "fmt" "sort" "strings" @@ -249,7 +250,7 @@ func addmove(r *Flow, bn int, rn int, f int) { p1.As = Thearch.Optoas(OAS, Types[uint8(v.etype)]) // TODO(rsc): Remove special case here. - if (Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && v.etype == TBOOL { + if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) && v.etype == TBOOL { p1.As = Thearch.Optoas(OAS, Types[TUINT8]) } p1.From.Type = obj.TYPE_REG @@ -302,7 +303,7 @@ func mkvar(f *Flow, a *obj.Addr) Bits { // TODO(rsc): Remove special case here. case obj.TYPE_ADDR: var bit Bits - if Thearch.Thechar == '0' || Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { + if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) { goto memcase } a.Type = obj.TYPE_MEM @@ -368,7 +369,7 @@ func mkvar(f *Flow, a *obj.Addr) Bits { if v.etype == et { if int64(v.width) == w { // TODO(rsc): Remove special case for arm here. - if flag == 0 || Thearch.Thechar != '5' { + if flag == 0 || Thearch.LinkArch.Family != sys.ARM { return blsh(uint(i)) } } diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 127a7c4698..90c4d4e95e 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -13,6 +13,7 @@ import ( "cmd/compile/internal/ssa" "cmd/internal/obj" + "cmd/internal/sys" ) var ssaEnabled = true @@ -24,13 +25,13 @@ func initssa() *ssa.Config { ssaExp.unimplemented = false ssaExp.mustImplement = true if ssaConfig == nil { - ssaConfig = ssa.NewConfig(Thearch.Thestring, &ssaExp, Ctxt, Debug['N'] == 0) + ssaConfig = ssa.NewConfig(Thearch.LinkArch.Name, &ssaExp, Ctxt, Debug['N'] == 0) } return ssaConfig } func shouldssa(fn *Node) bool { - switch Thearch.Thestring { + switch Thearch.LinkArch.Name { default: // Only available for testing. if os.Getenv("SSATEST") == "" { @@ -2409,7 +2410,7 @@ func isSSAIntrinsic1(s *Sym) bool { // so far has only been noticed for Bswap32 and the 16-bit count // leading/trailing instructions, but heuristics might change // in the future or on different architectures). - if !ssaEnabled || ssa.IntrinsicsDisable || Thearch.Thechar != '6' { + if !ssaEnabled || ssa.IntrinsicsDisable || Thearch.LinkArch.Family != sys.AMD64 { return false } if s != nil && s.Pkg != nil && s.Pkg.Path == "runtime/internal/sys" { diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index ff8ddea7f6..586a8e9c4f 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -6,6 +6,7 @@ package gc import ( "cmd/internal/obj" + "cmd/internal/sys" "fmt" "strings" ) @@ -672,8 +673,7 @@ opswitch: walkexprlist(n.List.Slice(), init) if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" && n.Left.Sym.Pkg.Path == "math" { - switch Thearch.Thechar { - case '5', '6', '7', '9': + if Thearch.LinkArch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.PPC64) { n.Op = OSQRT n.Left = n.List.First() n.List.Set(nil) @@ -1056,7 +1056,7 @@ opswitch: n = walkexpr(n, init) case OCONV, OCONVNOP: - if Thearch.Thechar == '5' { + if Thearch.LinkArch.Family == sys.ARM { if n.Left.Type.IsFloat() { if n.Type.Etype == TINT64 { n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64])) @@ -3274,7 +3274,7 @@ func samecheap(a *Node, b *Node) bool { // The result of walkrotate MUST be assigned back to n, e.g. // n.Left = walkrotate(n.Left) func walkrotate(n *Node) *Node { - if Thearch.Thechar == '0' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { + if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM64, sys.PPC64) { return n } @@ -3401,7 +3401,7 @@ func walkdiv(n *Node, init *Nodes) *Node { // if >= 0, nr is 1<= 32678 { ld.Diag("TLS offset out of range %d", v) } diff --git a/src/cmd/link/internal/arm64/l.go b/src/cmd/link/internal/arm64/l.go index b9b7ea50e3..67ad5c977f 100644 --- a/src/cmd/link/internal/arm64/l.go +++ b/src/cmd/link/internal/arm64/l.go @@ -62,11 +62,9 @@ package arm64 // THE SOFTWARE. const ( - thechar = '7' MaxAlign = 32 // max data alignment MinAlign = 1 // min data alignment FuncAlign = 8 - MINLC = 4 ) /* Used by ../internal/ld/dwarf.go */ diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go index 693e106ff1..1169e79a58 100644 --- a/src/cmd/link/internal/arm64/obj.go +++ b/src/cmd/link/internal/arm64/obj.go @@ -32,6 +32,7 @@ package arm64 import ( "cmd/internal/obj" + "cmd/internal/sys" "cmd/link/internal/ld" "fmt" "log" @@ -45,17 +46,11 @@ func Main() { } func linkarchinit() { - ld.Thestring = obj.Getgoarch() - ld.Thelinkarch = &ld.Linkarm64 + ld.SysArch = sys.ArchARM64 - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize ld.Thearch.Funcalign = FuncAlign ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minalign = MinAlign - ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP ld.Thearch.Dwarfreglr = DWARFREGLR diff --git a/src/cmd/link/internal/ld/arch.go b/src/cmd/link/internal/ld/arch.go deleted file mode 100644 index d28f37fa02..0000000000 --- a/src/cmd/link/internal/ld/arch.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2015 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 ld - -import "encoding/binary" - -var Linkarm = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "arm", - Thechar: '5', - Minlc: 4, - Ptrsize: 4, - Regsize: 4, -} - -var Linkarm64 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "arm64", - Thechar: '7', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} - -var Linkamd64 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "amd64", - Thechar: '6', - Minlc: 1, - Ptrsize: 8, - Regsize: 8, -} - -var Linkamd64p32 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "amd64p32", - Thechar: '6', - Minlc: 1, - Ptrsize: 4, - Regsize: 8, -} - -var Link386 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "386", - Thechar: '8', - Minlc: 1, - Ptrsize: 4, - Regsize: 4, -} - -var Linkppc64 = LinkArch{ - ByteOrder: binary.BigEndian, - Name: "ppc64", - Thechar: '9', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} - -var Linkppc64le = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "ppc64le", - Thechar: '9', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} - -var Linkmips64 = LinkArch{ - ByteOrder: binary.BigEndian, - Name: "mips64", - Thechar: '0', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} - -var Linkmips64le = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "mips64le", - Thechar: '0', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} - -var Links390x = LinkArch{ - ByteOrder: binary.BigEndian, - Name: "s390x", - Thechar: 'z', - Minlc: 2, - Ptrsize: 8, - Regsize: 8, -} diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 6bbd6c7d5c..ae430b4e45 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -34,6 +34,7 @@ package ld import ( "cmd/internal/gcprog" "cmd/internal/obj" + "cmd/internal/sys" "fmt" "log" "os" @@ -121,7 +122,7 @@ func Adduint64(ctxt *Link, s *LSym, v uint64) int64 { } func adduint(ctxt *Link, s *LSym, v uint64) int64 { - return adduintxx(ctxt, s, v, Thearch.Intsize) + return adduintxx(ctxt, s, v, SysArch.IntSize) } func setuint8(ctxt *Link, s *LSym, r int64, v uint8) int64 { @@ -138,12 +139,12 @@ func Addaddrplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { } s.Attr |= AttrReachable i := s.Size - s.Size += int64(ctxt.Arch.Ptrsize) + s.Size += int64(ctxt.Arch.PtrSize) Symgrow(ctxt, s, s.Size) r := Addrel(s) r.Sym = t r.Off = int32(i) - r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Siz = uint8(ctxt.Arch.PtrSize) r.Type = obj.R_ADDR r.Add = add return i + int64(r.Siz) @@ -163,7 +164,7 @@ func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { r.Add = add r.Type = obj.R_PCREL r.Siz = 4 - if Thearch.Thechar == 'z' { + if SysArch.Family == sys.S390X { r.Variant = RV_390_DBL } return i + int64(r.Siz) @@ -178,15 +179,15 @@ func setaddrplus(ctxt *Link, s *LSym, off int64, t *LSym, add int64) int64 { s.Type = obj.SDATA } s.Attr |= AttrReachable - if off+int64(ctxt.Arch.Ptrsize) > s.Size { - s.Size = off + int64(ctxt.Arch.Ptrsize) + if off+int64(ctxt.Arch.PtrSize) > s.Size { + s.Size = off + int64(ctxt.Arch.PtrSize) Symgrow(ctxt, s, s.Size) } r := Addrel(s) r.Sym = t r.Off = int32(off) - r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Siz = uint8(ctxt.Arch.PtrSize) r.Type = obj.R_ADDR r.Add = add return off + int64(r.Siz) @@ -202,12 +203,12 @@ func addsize(ctxt *Link, s *LSym, t *LSym) int64 { } s.Attr |= AttrReachable i := s.Size - s.Size += int64(ctxt.Arch.Ptrsize) + s.Size += int64(ctxt.Arch.PtrSize) Symgrow(ctxt, s, s.Size) r := Addrel(s) r.Sym = t r.Off = int32(i) - r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Siz = uint8(ctxt.Arch.PtrSize) r.Type = obj.R_SIZE return i + int64(r.Siz) } @@ -356,7 +357,7 @@ func relocsym(s *LSym) { // We need to be able to reference dynimport symbols when linking against // shared libraries, and Solaris needs it always if HEADTYPE != obj.Hsolaris && r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT && !DynlinkingGo() { - if !(Thearch.Thechar == '9' && Linkmode == LinkExternal && r.Sym.Name == ".TOC.") { + if !(SysArch.Family == sys.PPC64 && Linkmode == LinkExternal && r.Sym.Name == ".TOC.") { Diag("unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type) } } @@ -365,7 +366,7 @@ func relocsym(s *LSym) { } // TODO(mundaym): remove this special case - see issue 14218. - if Thearch.Thechar == 'z' { + if SysArch.Family == sys.S390X { switch r.Type { case obj.R_PCRELDBL: r.Type = obj.R_PCREL @@ -394,7 +395,7 @@ func relocsym(s *LSym) { } case obj.R_TLS_LE: - isAndroidX86 := goos == "android" && (Thearch.Thechar == '6' || Thearch.Thechar == '8') + isAndroidX86 := goos == "android" && (SysArch.InFamily(sys.AMD64, sys.I386)) if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd && !isAndroidX86 { r.Done = 0 @@ -404,13 +405,13 @@ func relocsym(s *LSym) { r.Xsym = r.Sym r.Xadd = r.Add o = 0 - if Thearch.Thechar != '6' { + if SysArch.Family != sys.AMD64 { o = r.Add } break } - if Iself && Thearch.Thechar == '5' { + if Iself && SysArch.Family == sys.ARM { // On ELF ARM, the thread pointer is 8 bytes before // the start of the thread-local data block, so add 8 // to the actual TLS offset (r->sym->value). @@ -428,7 +429,7 @@ func relocsym(s *LSym) { } case obj.R_TLS_IE: - isAndroidX86 := goos == "android" && (Thearch.Thechar == '6' || Thearch.Thechar == '8') + isAndroidX86 := goos == "android" && (SysArch.InFamily(sys.AMD64, sys.I386)) if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd && !isAndroidX86 { r.Done = 0 @@ -438,7 +439,7 @@ func relocsym(s *LSym) { r.Xsym = r.Sym r.Xadd = r.Add o = 0 - if Thearch.Thechar != '6' { + if SysArch.Family != sys.AMD64 { o = r.Add } break @@ -465,7 +466,7 @@ func relocsym(s *LSym) { o = r.Xadd if Iself { - if Thearch.Thechar == '6' { + if SysArch.Family == sys.AMD64 { o = 0 } } else if HEADTYPE == obj.Hdarwin { @@ -475,10 +476,10 @@ func relocsym(s *LSym) { // The workaround is that on arm64 don't ever add symaddr to o and always use // extern relocation by requiring rs->dynid >= 0. if rs.Type != obj.SHOSTOBJ { - if Thearch.Thechar == '7' && rs.Dynid < 0 { + if SysArch.Family == sys.ARM64 && rs.Dynid < 0 { Diag("R_ADDR reloc to %s+%d is not supported on darwin/arm64", rs.Name, o) } - if Thearch.Thechar != '7' { + if SysArch.Family != sys.ARM64 { o += Symaddr(rs) } } @@ -498,7 +499,7 @@ func relocsym(s *LSym) { // fail at runtime. See https://golang.org/issue/7980. // Instead of special casing only amd64, we treat this as an error on all // 64-bit architectures so as to be future-proof. - if int32(o) < 0 && Thearch.Ptrsize > 4 && siz == 4 { + if int32(o) < 0 && SysArch.PtrSize > 4 && siz == 4 { Diag("non-pc-relative relocation address is too big: %#x (%#x + %#x)", uint64(o), Symaddr(r.Sym), r.Add) errorexit() } @@ -515,7 +516,7 @@ func relocsym(s *LSym) { r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) o = r.Xadd rs = r.Xsym - if Iself && Thearch.Thechar == '6' { + if Iself && SysArch.Family == sys.AMD64 { o = 0 } break @@ -544,7 +545,7 @@ func relocsym(s *LSym) { o = r.Xadd if Iself { - if Thearch.Thechar == '6' { + if SysArch.Family == sys.AMD64 { o = 0 } } else if HEADTYPE == obj.Hdarwin { @@ -556,7 +557,7 @@ func relocsym(s *LSym) { } else { o += int64(r.Siz) } - } else if HEADTYPE == obj.Hwindows && Thearch.Thechar == '6' { // only amd64 needs PCREL + } else if HEADTYPE == obj.Hwindows && SysArch.Family == sys.AMD64 { // only amd64 needs PCREL // PE/COFF's PC32 relocation uses the address after the relocated // bytes as the base. Compensate by skewing the addend. o += int64(r.Siz) @@ -675,7 +676,7 @@ func dynrelocsym(s *LSym) { r.Add = int64(targ.Plt) // jmp *addr - if Thearch.Thechar == '8' { + if SysArch.Family == sys.I386 { Adduint8(Ctxt, rel, 0xff) Adduint8(Ctxt, rel, 0x25) Addaddr(Ctxt, rel, targ) @@ -982,7 +983,7 @@ func addstrdata(name string, value string) { s.Attr |= AttrDuplicateOK reachable := s.Attr.Reachable() Addaddr(Ctxt, s, sp) - adduintxx(Ctxt, s, uint64(len(value)), Thearch.Ptrsize) + adduintxx(Ctxt, s, uint64(len(value)), SysArch.PtrSize) // addstring, addaddr, etc., mark the symbols as reachable. // In this case that is not necessarily true, so stick to what @@ -1128,7 +1129,7 @@ func (p *GCProg) writeByte(x byte) { } func (p *GCProg) End(size int64) { - p.w.ZeroUntil(size / int64(Thearch.Ptrsize)) + p.w.ZeroUntil(size / int64(SysArch.PtrSize)) p.w.End() if debugGCProg { fmt.Fprintf(os.Stderr, "ld: end GCProg\n") @@ -1144,7 +1145,7 @@ func (p *GCProg) AddSym(s *LSym) { return } - ptrsize := int64(Thearch.Ptrsize) + ptrsize := int64(SysArch.PtrSize) nptr := decodetype_ptrdata(typ) / ptrsize if debugGCProg { @@ -1532,7 +1533,7 @@ func dodata() { if s != nil && s.Type == obj.STLSBSS { if Iself && (Linkmode == LinkExternal || Debug['d'] == 0) && HEADTYPE != obj.Hopenbsd { sect = addsection(&Segdata, ".tbss", 06) - sect.Align = int32(Thearch.Ptrsize) + sect.Align = int32(SysArch.PtrSize) sect.Vaddr = 0 } else { sect = nil diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 56c4370bcc..b17b96001e 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -6,6 +6,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "fmt" "strings" "unicode" @@ -227,7 +228,7 @@ func (d *deadcodepass) markMethod(m methodref) { func (d *deadcodepass) init() { var names []string - if Thearch.Thechar == '5' { + if SysArch.Family == sys.ARM { // mark some functions that are only referenced after linker code editing if d.ctxt.Goarm == 5 { names = append(names, "_sfloat") diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 0a6bf094aa..bc29938590 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -7,6 +7,7 @@ package ld import ( "bytes" "cmd/internal/obj" + "cmd/internal/sys" "debug/elf" "fmt" ) @@ -46,39 +47,39 @@ func decode_inuxi(p []byte, sz int) uint64 { } } -func commonsize() int { return 6*Thearch.Ptrsize + 8 } // runtime._type -func structfieldSize() int { return 3 * Thearch.Ptrsize } // runtime.structfield -func uncommonSize() int { return 2*Thearch.Ptrsize + 2*Thearch.Intsize } // runtime.uncommontype +func commonsize() int { return 6*SysArch.PtrSize + 8 } // runtime._type +func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield +func uncommonSize() int { return 2*SysArch.PtrSize + 2*SysArch.IntSize } // runtime.uncommontype // Type.commonType.kind func decodetype_kind(s *LSym) uint8 { - return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindMask) // 0x13 / 0x1f + return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindMask) // 0x13 / 0x1f } // Type.commonType.kind func decodetype_noptr(s *LSym) uint8 { - return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindNoPointers) // 0x13 / 0x1f + return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindNoPointers) // 0x13 / 0x1f } // Type.commonType.kind func decodetype_usegcprog(s *LSym) uint8 { - return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindGCProg) // 0x13 / 0x1f + return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindGCProg) // 0x13 / 0x1f } // Type.commonType.size func decodetype_size(s *LSym) int64 { - return int64(decode_inuxi(s.P, Thearch.Ptrsize)) // 0x8 / 0x10 + return int64(decode_inuxi(s.P, SysArch.PtrSize)) // 0x8 / 0x10 } // Type.commonType.ptrdata func decodetype_ptrdata(s *LSym) int64 { - return int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Ptrsize)) // 0x8 / 0x10 + return int64(decode_inuxi(s.P[SysArch.PtrSize:], SysArch.PtrSize)) // 0x8 / 0x10 } // Type.commonType.tflag func decodetype_hasUncommon(s *LSym) bool { const tflagUncommon = 1 // see ../../../../reflect/type.go:/^type.tflag - return s.P[2*Thearch.Ptrsize+4]&tflagUncommon != 0 + return s.P[2*SysArch.PtrSize+4]&tflagUncommon != 0 } // Find the elf.Section of a given shared library that contains a given address. @@ -112,11 +113,11 @@ func decodetype_gcprog(s *LSym) []byte { Exitf("cannot find gcprog for %s", s.Name) return nil } - return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)).P + return decode_reloc_sym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize)).P } func decodetype_gcprog_shlib(s *LSym) uint64 { - if Thearch.Thechar == '7' { + if SysArch.Family == sys.ARM64 { for _, shlib := range Ctxt.Shlibs { if shlib.Path == s.File { return shlib.gcdata_addresses[s] @@ -124,7 +125,7 @@ func decodetype_gcprog_shlib(s *LSym) uint64 { } return 0 } - return decode_inuxi(s.P[2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize):], Thearch.Ptrsize) + return decode_inuxi(s.P[2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize):], SysArch.PtrSize) } func decodetype_gcmask(s *LSym) []byte { @@ -133,14 +134,14 @@ func decodetype_gcmask(s *LSym) []byte { ptrdata := decodetype_ptrdata(s) sect := findShlibSection(s.File, addr) if sect != nil { - r := make([]byte, ptrdata/int64(Thearch.Ptrsize)) + r := make([]byte, ptrdata/int64(SysArch.PtrSize)) sect.ReadAt(r, int64(addr-sect.Addr)) return r } Exitf("cannot find gcmask for %s", s.Name) return nil } - mask := decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) + mask := decode_reloc_sym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize)) return mask.P } @@ -150,7 +151,7 @@ func decodetype_arrayelem(s *LSym) *LSym { } func decodetype_arraylen(s *LSym) int64 { - return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Ptrsize)) + return int64(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.PtrSize)) } // Type.PtrType.elem @@ -164,7 +165,7 @@ func decodetype_mapkey(s *LSym) *LSym { } func decodetype_mapvalue(s *LSym) *LSym { - return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)) // 0x20 / 0x38 + return decode_reloc_sym(s, int32(commonsize())+int32(SysArch.PtrSize)) // 0x20 / 0x38 } // Type.ChanType.elem @@ -188,13 +189,13 @@ func decodetype_funcoutcount(s *LSym) int { func decodetype_funcintype(s *LSym, i int) *LSym { uadd := commonsize() + 4 - if Thearch.Ptrsize == 8 { + if SysArch.PtrSize == 8 { uadd += 4 } if decodetype_hasUncommon(s) { uadd += uncommonSize() } - return decode_reloc_sym(s, int32(uadd+i*Thearch.Ptrsize)) + return decode_reloc_sym(s, int32(uadd+i*SysArch.PtrSize)) } func decodetype_funcouttype(s *LSym, i int) *LSym { @@ -203,11 +204,11 @@ func decodetype_funcouttype(s *LSym, i int) *LSym { // Type.StructType.fields.Slice::length func decodetype_structfieldcount(s *LSym) int { - return int(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize)) + return int(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize)) } func decodetype_structfieldarrayoff(s *LSym, i int) int { - off := commonsize() + 2*Thearch.Ptrsize + 2*Thearch.Intsize + off := commonsize() + 2*SysArch.PtrSize + 2*SysArch.IntSize if decodetype_hasUncommon(s) { off += uncommonSize() } @@ -224,7 +225,7 @@ func decodetype_stringptr(s *LSym, off int) string { if r == nil { // shouldn't happen. return "" } - strlen := int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Intsize)) + strlen := int64(decode_inuxi(s.P[SysArch.PtrSize:], SysArch.IntSize)) return string(r.Sym.P[r.Add : r.Add+strlen]) } @@ -248,17 +249,17 @@ func decodetype_structfieldname(s *LSym, i int) string { func decodetype_structfieldtype(s *LSym, i int) *LSym { off := decodetype_structfieldarrayoff(s, i) - return decode_reloc_sym(s, int32(off+Thearch.Ptrsize)) + return decode_reloc_sym(s, int32(off+SysArch.PtrSize)) } func decodetype_structfieldoffs(s *LSym, i int) int64 { off := decodetype_structfieldarrayoff(s, i) - return int64(decode_inuxi(s.P[off+2*Thearch.Ptrsize:], Thearch.Intsize)) + return int64(decode_inuxi(s.P[off+2*SysArch.PtrSize:], SysArch.IntSize)) } // InterfaceType.methods.length func decodetype_ifacemethodcount(s *LSym) int64 { - return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize)) + return int64(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize)) } // methodsig is a fully qualified typed method signature, like @@ -288,7 +289,7 @@ func decode_methodsig(s *LSym, off, size, count int) []methodsig { var methods []methodsig for i := 0; i < count; i++ { buf.WriteString(decodetype_name(s, off)) - mtypSym := decode_reloc_sym(s, int32(off+Thearch.Ptrsize)) + mtypSym := decode_reloc_sym(s, int32(off+SysArch.PtrSize)) buf.WriteRune('(') inCount := decodetype_funcincount(mtypSym) @@ -319,7 +320,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig { if decodetype_kind(s)&kindMask != kindInterface { panic(fmt.Sprintf("symbol %q is not an interface", s.Name)) } - r := decode_reloc(s, int32(commonsize()+Thearch.Ptrsize)) + r := decode_reloc(s, int32(commonsize()+SysArch.PtrSize)) if r == nil { return nil } @@ -328,7 +329,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig { } off := int(r.Add) // array of reflect.imethod values numMethods := int(decodetype_ifacemethodcount(s)) - sizeofIMethod := 2 * Thearch.Ptrsize + sizeofIMethod := 2 * SysArch.PtrSize return decode_methodsig(s, off, sizeofIMethod, numMethods) } @@ -339,31 +340,31 @@ func decodetype_methods(s *LSym) []methodsig { off := commonsize() // reflect.rtype switch decodetype_kind(s) & kindMask { case kindStruct: // reflect.structType - off += 2*Thearch.Ptrsize + 2*Thearch.Intsize + off += 2*SysArch.PtrSize + 2*SysArch.IntSize case kindPtr: // reflect.ptrType - off += Thearch.Ptrsize + off += SysArch.PtrSize case kindFunc: // reflect.funcType - off += Thearch.Ptrsize // 4 bytes, pointer aligned + off += SysArch.PtrSize // 4 bytes, pointer aligned case kindSlice: // reflect.sliceType - off += Thearch.Ptrsize + off += SysArch.PtrSize case kindArray: // reflect.arrayType - off += 3 * Thearch.Ptrsize + off += 3 * SysArch.PtrSize case kindChan: // reflect.chanType - off += 2 * Thearch.Ptrsize + off += 2 * SysArch.PtrSize case kindMap: // reflect.mapType - off += 4*Thearch.Ptrsize + 8 + off += 4*SysArch.PtrSize + 8 case kindInterface: // reflect.interfaceType - off += Thearch.Ptrsize + 2*Thearch.Intsize + off += SysArch.PtrSize + 2*SysArch.IntSize default: // just Sizeof(rtype) } - numMethods := int(decode_inuxi(s.P[off+2*Thearch.Ptrsize:], Thearch.Intsize)) - r := decode_reloc(s, int32(off+Thearch.Ptrsize)) + numMethods := int(decode_inuxi(s.P[off+2*SysArch.PtrSize:], SysArch.IntSize)) + r := decode_reloc(s, int32(off+SysArch.PtrSize)) if r.Sym != s { panic(fmt.Sprintf("method slice pointer in %s leads to a different symbol %s", s, r.Sym)) } off = int(r.Add) // array of reflect.method values - sizeofMethod := 4 * Thearch.Ptrsize // sizeof reflect.method in program + sizeofMethod := 4 * SysArch.PtrSize // sizeof reflect.method in program return decode_methodsig(s, off, sizeofMethod, numMethods) } diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index eaa0bdbb41..230d146877 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -39,7 +39,7 @@ var gdbscript string * Basic I/O */ func addrput(s *LSym, addr int64) { - switch Thearch.Ptrsize { + switch SysArch.PtrSize { case 4: Adduint32(Ctxt, s, uint32(addr)) @@ -569,7 +569,7 @@ func adddwarfref(ctxt *Link, s *LSym, t *LSym, size int) int64 { default: Diag("invalid size %d in adddwarfref\n", size) fallthrough - case Thearch.Ptrsize: + case SysArch.PtrSize: result = Addaddr(ctxt, s, t) case 4: result = addaddrplus4(ctxt, s, t, 0) @@ -599,7 +599,7 @@ func putattr(s *LSym, abbrev int, form int, cls int, value int64, data interface case DW_FORM_block1: // block if cls == DW_CLS_ADDRESS { - Adduint8(Ctxt, s, uint8(1+Thearch.Ptrsize)) + Adduint8(Ctxt, s, uint8(1+SysArch.PtrSize)) Adduint8(Ctxt, s, DW_OP_addr) Addaddr(Ctxt, s, data.(*LSym)) break @@ -682,14 +682,14 @@ func putattr(s *LSym, abbrev int, form int, cls int, value int64, data interface case DW_FORM_ref_addr: // reference to a DIE in the .info section if data == nil { Diag("dwarf: null reference in %d", abbrev) - if Thearch.Ptrsize == 8 { + if SysArch.PtrSize == 8 { Adduint64(Ctxt, s, 0) // invalid dwarf, gdb will complain. } else { Adduint32(Ctxt, s, 0) // invalid dwarf, gdb will complain. } } else { dsym := data.(*LSym) - adddwarfref(Ctxt, s, dsym, Thearch.Ptrsize) + adddwarfref(Ctxt, s, dsym, SysArch.PtrSize) } case DW_FORM_ref1, // reference within the compilation unit @@ -1161,11 +1161,11 @@ func synthesizemaptypes(die *DWDie) { // compute size info like hashmap.c does. indirect_key, indirect_val := false, false if keysize > MaxKeySize { - keysize = int64(Thearch.Ptrsize) + keysize = int64(SysArch.PtrSize) indirect_key = true } if valsize > MaxValSize { - valsize = int64(Thearch.Ptrsize) + valsize = int64(SysArch.PtrSize) indirect_val = true } @@ -1212,13 +1212,13 @@ func synthesizemaptypes(die *DWDie) { fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow", 0) newrefattr(fld, DW_AT_type, defptrto(dwhb.sym)) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) - if Thearch.Regsize > Thearch.Ptrsize { + if SysArch.RegSize > SysArch.PtrSize { fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad", 0) newrefattr(fld, DW_AT_type, mustFind("uintptr")) - newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(Thearch.Ptrsize)) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(SysArch.PtrSize)) } - newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0) + newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(SysArch.RegSize), 0) }) // Construct hash @@ -1481,7 +1481,7 @@ func writelines(prev *LSym) *LSym { headerend = ls.Size Adduint8(Ctxt, ls, 0) // start extended opcode - uleb128put(ls, 1+int64(Thearch.Ptrsize)) + uleb128put(ls, 1+int64(SysArch.PtrSize)) Adduint8(Ctxt, ls, DW_LNE_set_address) pc := s.Value @@ -1555,7 +1555,7 @@ func writelines(prev *LSym) *LSym { dt = DW_ABRV_AUTO offs = int64(a.Aoffset) if !haslinkregister() { - offs -= int64(Thearch.Ptrsize) + offs -= int64(SysArch.PtrSize) } case obj.A_PARAM: @@ -1667,7 +1667,7 @@ func writeframes(prev *LSym) *LSym { if haslinkregister() { uleb128put(fs, int64(0)) // offset } else { - uleb128put(fs, int64(Thearch.Ptrsize)) // offset + uleb128put(fs, int64(SysArch.PtrSize)) // offset } Adduint8(Ctxt, fs, DW_CFA_offset_extended) @@ -1675,7 +1675,7 @@ func writeframes(prev *LSym) *LSym { if haslinkregister() { uleb128put(fs, int64(0)/DATAALIGNMENTFACTOR) // at cfa - 0 } else { - uleb128put(fs, int64(-Thearch.Ptrsize)/DATAALIGNMENTFACTOR) // at cfa - x*4 + uleb128put(fs, int64(-SysArch.PtrSize)/DATAALIGNMENTFACTOR) // at cfa - x*4 } // 4 is to exclude the length field. @@ -1713,10 +1713,10 @@ func writeframes(prev *LSym) *LSym { if haslinkregister() { deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(pcsp.value)) } else { - deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value)) + deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(SysArch.PtrSize)+int64(pcsp.value)) } } - pad := int(Rnd(int64(len(deltaBuf)), int64(Thearch.Ptrsize))) - len(deltaBuf) + pad := int(Rnd(int64(len(deltaBuf)), int64(SysArch.PtrSize))) - len(deltaBuf) deltaBuf = append(deltaBuf, zeros[:pad]...) // Emit the FDE header, Section 6.4.1. @@ -1724,7 +1724,7 @@ func writeframes(prev *LSym) *LSym { // 4 bytes: Pointer to the CIE above, at offset 0 // ptrsize: initial location // ptrsize: address range - Adduint32(Ctxt, fs, uint32(4+2*Thearch.Ptrsize+len(deltaBuf))) // length (excludes itself) + Adduint32(Ctxt, fs, uint32(4+2*SysArch.PtrSize+len(deltaBuf))) // length (excludes itself) if Linkmode == LinkExternal { adddwarfref(Ctxt, fs, framesec, 4) } else { @@ -1771,7 +1771,7 @@ func writeinfo(prev *LSym) *LSym { // debug_abbrev_offset (*) adddwarfref(Ctxt, s, abbrevsym, 4) - Adduint8(Ctxt, s, uint8(Thearch.Ptrsize)) // address_size + Adduint8(Ctxt, s, uint8(SysArch.PtrSize)) // address_size prev = putdie(prev, compunit) cusize := s.Size - 4 // exclude the length field. @@ -1848,7 +1848,7 @@ func writearanges(prev *LSym) *LSym { s.Type = obj.SDWARFSECT // The first tuple is aligned to a multiple of the size of a single tuple // (twice the size of an address) - headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize*2))) // don't count unit_length field itself + headersize := int(Rnd(4+2+4+1+1, int64(SysArch.PtrSize*2))) // don't count unit_length field itself for compunit := dwroot.child; compunit != nil; compunit = compunit.link { b := getattr(compunit, DW_AT_low_pc) @@ -1861,13 +1861,13 @@ func writearanges(prev *LSym) *LSym { } // Write .debug_aranges Header + entry (sec 6.1.2) - unitlength := uint32(headersize) + 4*uint32(Thearch.Ptrsize) - 4 + unitlength := uint32(headersize) + 4*uint32(SysArch.PtrSize) - 4 Adduint32(Ctxt, s, unitlength) // unit_length (*) Adduint16(Ctxt, s, 2) // dwarf version (appendix F) adddwarfref(Ctxt, s, compunit.sym, 4) - Adduint8(Ctxt, s, uint8(Thearch.Ptrsize)) // address_size + Adduint8(Ctxt, s, uint8(SysArch.PtrSize)) // address_size Adduint8(Ctxt, s, 0) // segment_size padding := headersize - (4 + 2 + 4 + 1 + 1) for i := 0; i < padding; i++ { @@ -1940,7 +1940,7 @@ func dwarfgeneratedebugsyms() { die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, int64(Thearch.Ptrsize), 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, int64(SysArch.PtrSize), 0) newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0) // Prototypes needed for type synthesis. diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 035826df7c..7c760775b5 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -6,6 +6,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "crypto/sha1" "encoding/binary" "encoding/hex" @@ -866,25 +867,23 @@ var buildinfo []byte func Elfinit() { Iself = true - switch Thearch.Thechar { - case '0', '6', '7', '9', 'z': + if SysArch.InFamily(sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.S390X) { elfRelType = ".rela" - default: + } else { elfRelType = ".rel" } - switch Thearch.Thechar { + switch SysArch.Family { // 64-bit architectures - case '9', 'z': + case sys.PPC64, sys.S390X: if Ctxt.Arch.ByteOrder == binary.BigEndian { ehdr.flags = 1 /* Version 1 ABI */ } else { ehdr.flags = 2 /* Version 2 ABI */ } fallthrough - - case '0', '6', '7': - if Thearch.Thechar == '0' { + case sys.AMD64, sys.ARM64, sys.MIPS64: + if SysArch.Family == sys.MIPS64 { ehdr.flags = 0x20000000 /* MIPS 3 */ } elf64 = true @@ -897,7 +896,7 @@ func Elfinit() { // we use EABI on both linux/arm and freebsd/arm. // 32-bit architectures - case '5': + case sys.ARM: // we use EABI on both linux/arm and freebsd/arm. if HEADTYPE == obj.Hlinux || HEADTYPE == obj.Hfreebsd { // We set a value here that makes no indication of which @@ -911,7 +910,6 @@ func Elfinit() { ehdr.flags = 0x5000002 // has entry point, Version5 EABI } fallthrough - default: ehdr.phoff = ELF32HDRSIZE /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ @@ -1432,7 +1430,7 @@ func elfdynhash() { } // s390x (ELF64) hash table entries are 8 bytes - if Thearch.Thechar == 'z' { + if SysArch.Family == sys.S390X { Adduint64(Ctxt, s, uint64(nbucket)) Adduint64(Ctxt, s, uint64(nsym)) for i := 0; i < nbucket; i++ { @@ -1660,15 +1658,15 @@ func elfshreloc(sect *Section) *ElfShdr { sh := elfshname(elfRelType + sect.Name) sh.type_ = uint32(typ) - sh.entsize = uint64(Thearch.Regsize) * 2 + sh.entsize = uint64(SysArch.RegSize) * 2 if typ == SHT_RELA { - sh.entsize += uint64(Thearch.Regsize) + sh.entsize += uint64(SysArch.RegSize) } sh.link = uint32(elfshname(".symtab").shnum) sh.info = uint32(sect.Elfsect.shnum) sh.off = sect.Reloff sh.size = sect.Rellen - sh.addralign = uint64(Thearch.Regsize) + sh.addralign = uint64(SysArch.RegSize) return sh } @@ -1872,7 +1870,7 @@ func doelf() { Addstring(shstrtab, ".interp") Addstring(shstrtab, ".hash") Addstring(shstrtab, ".got") - if Thearch.Thechar == '9' { + if SysArch.Family == sys.PPC64 { Addstring(shstrtab, ".glink") } Addstring(shstrtab, ".got.plt") @@ -1919,7 +1917,7 @@ func doelf() { s.Type = obj.SELFGOT // writable /* ppc64 glink resolver */ - if Thearch.Thechar == '9' { + if SysArch.Family == sys.PPC64 { s := Linklookup(Ctxt, ".glink", 0) s.Attr |= AttrReachable s.Type = obj.SELFRXSECT @@ -1938,7 +1936,7 @@ func doelf() { s = Linklookup(Ctxt, ".plt", 0) s.Attr |= AttrReachable - if Thearch.Thechar == '9' { + if SysArch.Family == sys.PPC64 { // In the ppc64 ABI, .plt is a data section // written by the dynamic linker. s.Type = obj.SELFSECT @@ -1993,15 +1991,15 @@ func doelf() { Elfwritedynent(s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val))) } - if Thearch.Thechar == '9' { + if SysArch.Family == sys.PPC64 { elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".plt", 0)) - } else if Thearch.Thechar == 'z' { + } else if SysArch.Family == sys.S390X { elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got", 0)) } else { elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got.plt", 0)) } - if Thearch.Thechar == '9' { + if SysArch.Family == sys.PPC64 { Elfwritedynent(s, DT_PPC64_OPT, 0) } @@ -2080,22 +2078,22 @@ func Asmbelfsetup() { func Asmbelf(symo int64) { eh := getElfEhdr() - switch Thearch.Thechar { + switch SysArch.Family { default: - Exitf("unknown architecture in asmbelf: %v", Thearch.Thechar) - case '0': + Exitf("unknown architecture in asmbelf: %v", SysArch.Family) + case sys.MIPS64: eh.machine = EM_MIPS - case '5': + case sys.ARM: eh.machine = EM_ARM - case '6': + case sys.AMD64: eh.machine = EM_X86_64 - case '7': + case sys.ARM64: eh.machine = EM_AARCH64 - case '8': + case sys.I386: eh.machine = EM_386 - case '9': + case sys.PPC64: eh.machine = EM_PPC64 - case 'z': + case sys.S390X: eh.machine = EM_S390 } @@ -2251,7 +2249,7 @@ func Asmbelf(symo int64) { } else { sh.entsize = ELF32SYMSIZE } - sh.addralign = uint64(Thearch.Regsize) + sh.addralign = uint64(SysArch.RegSize) sh.link = uint32(elfshname(".dynstr").shnum) // sh->info = index of first non-local symbol (number of local symbols) @@ -2275,7 +2273,7 @@ func Asmbelf(symo int64) { sh = elfshname(".gnu.version_r") sh.type_ = SHT_GNU_VERNEED sh.flags = SHF_ALLOC - sh.addralign = uint64(Thearch.Regsize) + sh.addralign = uint64(SysArch.RegSize) sh.info = uint32(elfverneed) sh.link = uint32(elfshname(".dynstr").shnum) shsym(sh, Linklookup(Ctxt, ".gnu.version_r", 0)) @@ -2286,7 +2284,7 @@ func Asmbelf(symo int64) { sh.type_ = SHT_RELA sh.flags = SHF_ALLOC sh.entsize = ELF64RELASIZE - sh.addralign = uint64(Thearch.Regsize) + sh.addralign = uint64(SysArch.RegSize) sh.link = uint32(elfshname(".dynsym").shnum) sh.info = uint32(elfshname(".plt").shnum) shsym(sh, Linklookup(Ctxt, ".rela.plt", 0)) @@ -2350,15 +2348,15 @@ func Asmbelf(symo int64) { sh := elfshname(".got") sh.type_ = SHT_PROGBITS sh.flags = SHF_ALLOC + SHF_WRITE - sh.entsize = uint64(Thearch.Regsize) - sh.addralign = uint64(Thearch.Regsize) + sh.entsize = uint64(SysArch.RegSize) + sh.addralign = uint64(SysArch.RegSize) shsym(sh, Linklookup(Ctxt, ".got", 0)) sh = elfshname(".got.plt") sh.type_ = SHT_PROGBITS sh.flags = SHF_ALLOC + SHF_WRITE - sh.entsize = uint64(Thearch.Regsize) - sh.addralign = uint64(Thearch.Regsize) + sh.entsize = uint64(SysArch.RegSize) + sh.addralign = uint64(SysArch.RegSize) shsym(sh, Linklookup(Ctxt, ".got.plt", 0)) } @@ -2366,7 +2364,7 @@ func Asmbelf(symo int64) { sh.type_ = SHT_HASH sh.flags = SHF_ALLOC sh.entsize = 4 - sh.addralign = uint64(Thearch.Regsize) + sh.addralign = uint64(SysArch.RegSize) sh.link = uint32(elfshname(".dynsym").shnum) shsym(sh, Linklookup(Ctxt, ".hash", 0)) @@ -2375,8 +2373,8 @@ func Asmbelf(symo int64) { sh.type_ = SHT_DYNAMIC sh.flags = SHF_ALLOC + SHF_WRITE - sh.entsize = 2 * uint64(Thearch.Regsize) - sh.addralign = uint64(Thearch.Regsize) + sh.entsize = 2 * uint64(SysArch.RegSize) + sh.addralign = uint64(SysArch.RegSize) sh.link = uint32(elfshname(".dynstr").shnum) shsym(sh, Linklookup(Ctxt, ".dynamic", 0)) ph := newElfPhdr() @@ -2402,7 +2400,7 @@ func Asmbelf(symo int64) { ph.type_ = PT_TLS ph.flags = PF_R ph.memsz = tlssize - ph.align = uint64(Thearch.Regsize) + ph.align = uint64(SysArch.RegSize) } } } @@ -2411,12 +2409,12 @@ func Asmbelf(symo int64) { ph := newElfPhdr() ph.type_ = PT_GNU_STACK ph.flags = PF_W + PF_R - ph.align = uint64(Thearch.Regsize) + ph.align = uint64(SysArch.RegSize) ph = newElfPhdr() ph.type_ = PT_PAX_FLAGS ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled - ph.align = uint64(Thearch.Regsize) + ph.align = uint64(SysArch.RegSize) } elfobj: @@ -2476,8 +2474,8 @@ elfobj: sh.type_ = SHT_SYMTAB sh.off = uint64(symo) sh.size = uint64(Symsize) - sh.addralign = uint64(Thearch.Regsize) - sh.entsize = 8 + 2*uint64(Thearch.Regsize) + sh.addralign = uint64(SysArch.RegSize) + sh.entsize = 8 + 2*uint64(SysArch.RegSize) sh.link = uint32(elfshname(".strtab").shnum) sh.info = uint32(elfglobalsymndx) @@ -2600,7 +2598,7 @@ func Elfadddynsym(ctxt *Link, s *LSym) { /* size of object */ Adduint64(ctxt, d, uint64(s.Size)) - if Thearch.Thechar == '6' && !s.Attr.CgoExportDynamic() && s.Dynimplib != "" && !seenlib[s.Dynimplib] { + if SysArch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib != "" && !seenlib[s.Dynimplib] { Elfwritedynent(Linklookup(ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(Linklookup(ctxt, ".dynstr", 0), s.Dynimplib))) } } else { @@ -2628,9 +2626,9 @@ func Elfadddynsym(ctxt *Link, s *LSym) { t := STB_GLOBAL << 4 // TODO(mwhudson): presumably the behaviour should actually be the same on both arm and 386. - if Thearch.Thechar == '8' && s.Attr.CgoExport() && s.Type&obj.SMASK == obj.STEXT { + if SysArch.Family == sys.I386 && s.Attr.CgoExport() && s.Type&obj.SMASK == obj.STEXT { t |= STT_FUNC - } else if Thearch.Thechar == '5' && s.Attr.CgoExportDynamic() && s.Type&obj.SMASK == obj.STEXT { + } else if SysArch.Family == sys.ARM && s.Attr.CgoExportDynamic() && s.Type&obj.SMASK == obj.STEXT { t |= STT_FUNC } else { t |= STT_OBJECT diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index 0255331ac6..3aee2d5ece 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -3,6 +3,7 @@ package ld import ( "bytes" "cmd/internal/obj" + "cmd/internal/sys" "encoding/binary" "fmt" "io" @@ -546,47 +547,48 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { return } - switch Thearch.Thechar { + switch SysArch.Family { default: - Diag("%s: elf %s unimplemented", pn, Thestring) + Diag("%s: elf %s unimplemented", pn, SysArch.Name) return - case '0': + case sys.MIPS64: if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not mips64", pn) return } - case '5': + case sys.ARM: if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { Diag("%s: elf object but not arm", pn) return } - case '6': + case sys.AMD64: if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not amd64", pn) return } - case '7': + case sys.ARM64: if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not arm64", pn) return } - case '8': + case sys.I386: if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { Diag("%s: elf object but not 386", pn) return } - case '9': + case sys.PPC64: if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not ppc64", pn) return } - case 'z': + + case sys.S390X: if elfobj.machine != ElfMachS390 || hdr.Ident[4] != ElfClass64 { Diag("%s: elf object but not s390x", pn) return @@ -1056,7 +1058,7 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) { } case ElfSymBindLocal: - if Thearch.Thechar == '5' && (strings.HasPrefix(sym.name, "$a") || strings.HasPrefix(sym.name, "$d")) { + if SysArch.Family == sys.ARM && (strings.HasPrefix(sym.name, "$a") || strings.HasPrefix(sym.name, "$d")) { // binutils for arm generate these mapping // symbols, ignore these break @@ -1127,7 +1129,9 @@ func (x rbyoff) Less(i, j int) bool { } func reltype(pn string, elftype int, siz *uint8) int { - switch uint32(Thearch.Thechar) | uint32(elftype)<<24 { + // TODO(mdempsky): Remove dependency on ArchFamily char values. + + switch uint32(SysArch.Family) | uint32(elftype)<<24 { default: Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype) fallthrough diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go index c4c13f13b9..9fbb2123af 100644 --- a/src/cmd/link/internal/ld/ldmacho.go +++ b/src/cmd/link/internal/ld/ldmacho.go @@ -2,6 +2,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "encoding/binary" "fmt" "log" @@ -471,18 +472,18 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { m.length = length m.name = pn - switch Thearch.Thechar { + switch SysArch.Family { default: - Diag("%s: mach-o %s unimplemented", pn, Thestring) + Diag("%s: mach-o %s unimplemented", pn, SysArch.Name) return - case '6': + case sys.AMD64: if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { Diag("%s: mach-o object but not amd64", pn) return } - case '8': + case sys.I386: if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { Diag("%s: mach-o object but not 386", pn) return @@ -724,10 +725,9 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { rp = &r[rpi] rel = §.rel[j] if rel.scattered != 0 { - if Thearch.Thechar != '8' { + if SysArch.Family != sys.I386 { // mach-o only uses scattered relocation on 32-bit platforms Diag("unexpected scattered relocation") - continue } @@ -821,7 +821,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { rp.Off = int32(rel.addr) // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). - if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 { + if SysArch.Family == sys.AMD64 && rel.extrn == 0 && rel.type_ == 1 { // Calculate the addend as the offset into the section. // // The rip-relative offset stored in the object file is encoded @@ -847,7 +847,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { // For i386 Mach-O PC-relative, the addend is written such that // it *is* the PC being subtracted. Use that to make // it match our version of PC-relative. - if rel.pcrel != 0 && Thearch.Thechar == '8' { + if rel.pcrel != 0 && SysArch.Family == sys.I386 { rp.Add += int64(rp.Off) + int64(rp.Siz) } if rel.extrn == 0 { @@ -866,7 +866,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { // include that information in the addend. // We only care about the delta from the // section base. - if Thearch.Thechar == '8' { + if SysArch.Family == sys.I386 { rp.Add -= int64(c.seg.sect[rel.symnum-1].addr) } } else { diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go index 5c3e99c44f..ea0c482838 100644 --- a/src/cmd/link/internal/ld/ldpe.go +++ b/src/cmd/link/internal/ld/ldpe.go @@ -6,6 +6,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "encoding/binary" "fmt" "log" @@ -492,7 +493,7 @@ func readpesym(peobj *PeObj, i int, y **PeSym) (err error) { if strings.HasPrefix(name, "__imp_") { name = name[6:] // __imp_Name => Name } - if Thearch.Thechar == '8' && name[0] == '_' { + if SysArch.Family == sys.I386 && name[0] == '_' { name = name[1:] // _Name => Name } } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 5616700445..3e0bd8ebc4 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -34,6 +34,7 @@ import ( "bufio" "bytes" "cmd/internal/obj" + "cmd/internal/sys" "crypto/sha1" "debug/elf" "encoding/binary" @@ -82,14 +83,9 @@ import ( // THE SOFTWARE. type Arch struct { - Thechar int - Ptrsize int - Intsize int - Regsize int Funcalign int Maxalign int Minalign int - Minlc int Dwarfregsp int Dwarfreglr int Linuxdynld string @@ -191,8 +187,7 @@ func UseRelro() bool { } var ( - Thestring string - Thelinkarch *LinkArch + SysArch *sys.Arch outfile string dynexp []*LSym dynlib []string @@ -509,7 +504,7 @@ func loadlib() { } loadinternal("runtime") - if Thearch.Thechar == '5' { + if SysArch.Family == sys.ARM { loadinternal("math") } if flag_race != 0 { @@ -562,7 +557,7 @@ func loadlib() { // dependency problems when compiling natively (external linking requires // runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be // compiled using external linking.) - if (Thearch.Thechar == '5' || Thearch.Thechar == '7') && HEADTYPE == obj.Hdarwin && iscgo { + if SysArch.InFamily(sys.ARM, sys.ARM64) && HEADTYPE == obj.Hdarwin && iscgo { Linkmode = LinkExternal } @@ -621,7 +616,7 @@ func loadlib() { // a variable to hold g in assembly (currently only intel). if tlsg.Type == 0 { tlsg.Type = obj.STLSBSS - tlsg.Size = int64(Thearch.Ptrsize) + tlsg.Size = int64(SysArch.PtrSize) } else if tlsg.Type != obj.SDYNIMPORT { Diag("internal error: runtime declared tlsg variable %d", tlsg.Type) } @@ -639,7 +634,7 @@ func loadlib() { // In addition, on ARM, the runtime depends on the linker // recording the value of GOARM. - if Thearch.Thechar == '5' { + if SysArch.Family == sys.ARM { s := Linklookup(Ctxt, "runtime.goarm", 0) s.Type = obj.SRODATA @@ -1226,7 +1221,7 @@ func hostlink() { if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin { // Skip combining dwarf on arm. - if Thearch.Thechar != '5' && Thearch.Thechar != '7' { + if !SysArch.InFamily(sys.ARM, sys.ARM64) { dsym := filepath.Join(tmpdir, "go.dwarf") if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil { Ctxt.Cursym = nil @@ -1254,14 +1249,14 @@ func hostlink() { // hostlinkArchArgs returns arguments to pass to the external linker // based on the architecture. func hostlinkArchArgs() []string { - switch Thearch.Thechar { - case '8': + switch SysArch.Family { + case sys.I386: return []string{"-m32"} - case '6', '9', 'z': + case sys.AMD64, sys.PPC64, sys.S390X: return []string{"-m64"} - case '5': + case sys.ARM: return []string{"-marm"} - case '7': + case sys.ARM64: // nothing needed } return nil @@ -1306,10 +1301,10 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when if !strings.HasPrefix(line, "go object ") { if strings.HasSuffix(pn, ".go") { - Exitf("%cl: input %s is not .%c file (use %cg to compile .go files)", Thearch.Thechar, pn, Thearch.Thechar, Thearch.Thechar) + Exitf("%cl: input %s is not .%c file (use %cg to compile .go files)", SysArch.Family, pn, SysArch.Family, SysArch.Family) } - if line == Thestring { + if line == SysArch.Name { // old header format: just $GOOS Diag("%s: stale object file", pn) return nil @@ -1500,12 +1495,12 @@ func ldshlibsyms(shlib string) { // the type data. if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") { lsym.P = readelfsymboldata(f, &elfsym) - gcdata_locations[elfsym.Value+2*uint64(Thearch.Ptrsize)+8+1*uint64(Thearch.Ptrsize)] = lsym + gcdata_locations[elfsym.Value+2*uint64(SysArch.PtrSize)+8+1*uint64(SysArch.PtrSize)] = lsym } } } gcdata_addresses := make(map[*LSym]uint64) - if Thearch.Thechar == '7' { + if SysArch.Family == sys.ARM64 { for _, sect := range f.Sections { if sect.Type == elf.SHT_RELA { var rela elf.Rela64 @@ -1565,8 +1560,8 @@ func mywhatsys() { goos = obj.Getgoos() goarch = obj.Getgoarch() - if !strings.HasPrefix(goarch, Thestring) { - log.Fatalf("cannot use %cc with GOARCH=%s", Thearch.Thechar, goarch) + if !strings.HasPrefix(goarch, SysArch.Name) { + log.Fatalf("cannot use %cc with GOARCH=%s", SysArch.Family, goarch) } } @@ -1608,7 +1603,7 @@ func addsection(seg *Segment, name string, rwx int) *Section { sect.Rwx = uint8(rwx) sect.Name = name sect.Seg = seg - sect.Align = int32(Thearch.Ptrsize) // everything is at least pointer-aligned + sect.Align = int32(SysArch.PtrSize) // everything is at least pointer-aligned *l = sect return sect } @@ -1652,7 +1647,7 @@ func callsize() int { if haslinkregister() { return 0 } - return Thearch.Regsize + return SysArch.RegSize } func dostkcheck() { @@ -1986,7 +1981,7 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) // NOTE(ality): acid can't produce a stack trace without .frame symbols - put(nil, ".frame", 'm', int64(s.Locals)+int64(Thearch.Ptrsize), 0, 0, nil) + put(nil, ".frame", 'm', int64(s.Locals)+int64(SysArch.PtrSize), 0, 0, nil) for _, a := range s.Autom { // Emit a or p according to actual offset, even if label is wrong. @@ -1999,7 +1994,7 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { if a.Name == obj.A_PARAM { off = a.Aoffset } else { - off = a.Aoffset - int32(Thearch.Ptrsize) + off = a.Aoffset - int32(SysArch.PtrSize) } // FP @@ -2009,8 +2004,8 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { } // SP - if off <= int32(-Thearch.Ptrsize) { - put(nil, a.Asym.Name, 'a', -(int64(off) + int64(Thearch.Ptrsize)), 0, 0, a.Gotype) + if off <= int32(-SysArch.PtrSize) { + put(nil, a.Asym.Name, 'a', -(int64(off) + int64(SysArch.PtrSize)), 0, 0, a.Gotype) continue } } diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 67a855933e..f0811389d2 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -32,8 +32,8 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "debug/elf" - "encoding/binary" "fmt" ) @@ -161,11 +161,9 @@ type Shlib struct { } type Link struct { - Thechar int32 - Thestring string Goarm int32 Headtype int - Arch *LinkArch + Arch *sys.Arch Debugvlog int32 Bso *obj.Biobuf Windows int32 @@ -196,15 +194,15 @@ type Link struct { // on the stack in the function prologue and so always have a pointer between // the hardware stack pointer and the local variable area. func (ctxt *Link) FixedFrameSize() int64 { - switch ctxt.Arch.Thechar { - case '6', '8': + switch ctxt.Arch.Family { + case sys.AMD64, sys.I386: return 0 - case '9': + case sys.PPC64: // PIC code on ppc64le requires 32 bytes of stack, and it's easier to // just use that much stack always on ppc64x. - return int64(4 * ctxt.Arch.Ptrsize) + return int64(4 * ctxt.Arch.PtrSize) default: - return int64(ctxt.Arch.Ptrsize) + return int64(ctxt.Arch.PtrSize) } } @@ -213,15 +211,6 @@ func (l *Link) IncVersion() { l.Hash = append(l.Hash, make(map[string]*LSym)) } -type LinkArch struct { - ByteOrder binary.ByteOrder - Name string - Thechar int - Minlc int - Ptrsize int - Regsize int -} - type Library struct { Objref string Srcref string diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index cafc6b0382..25d48fbf22 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -6,6 +6,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "sort" "strings" ) @@ -131,15 +132,7 @@ var nsortsym int var load_budget int = INITIAL_MACHO_HEADR - 2*1024 func Machoinit() { - switch Thearch.Thechar { - // 64-bit architectures - case '6', '7', '9': - macho64 = true - - // 32-bit architectures - default: - break - } + macho64 = SysArch.RegSize == 8 } func getMachoHdr() *MachoHdr { @@ -356,8 +349,8 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) { buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) var msect *MachoSect - if sect.Rwx&1 == 0 && segname != "__DWARF" && (Thearch.Thechar == '7' || // arm64 - (Thearch.Thechar == '6' && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive))) { // amd64 + if sect.Rwx&1 == 0 && segname != "__DWARF" && (SysArch.Family == sys.ARM64 || + (SysArch.Family == sys.AMD64 && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive))) { // Darwin external linker on arm64 and on amd64 in c-shared/c-archive buildmode // complains about absolute relocs in __TEXT, so if the section is not // executable, put it in __DATA segment. @@ -422,23 +415,23 @@ func Asmbmacho() { va := INITTEXT - int64(HEADR) mh := getMachoHdr() - switch Thearch.Thechar { + switch SysArch.Family { default: - Exitf("unknown macho architecture: %v", Thearch.Thechar) + Exitf("unknown macho architecture: %v", SysArch.Family) - case '5': + case sys.ARM: mh.cpu = MACHO_CPU_ARM mh.subcpu = MACHO_SUBCPU_ARMV7 - case '6': + case sys.AMD64: mh.cpu = MACHO_CPU_AMD64 mh.subcpu = MACHO_SUBCPU_X86 - case '7': + case sys.ARM64: mh.cpu = MACHO_CPU_ARM64 mh.subcpu = MACHO_SUBCPU_ARM64_ALL - case '8': + case sys.I386: mh.cpu = MACHO_CPU_386 mh.subcpu = MACHO_SUBCPU_X86 } @@ -449,7 +442,7 @@ func Asmbmacho() { ms = newMachoSeg("", 40) ms.fileoffset = Segtext.Fileoff - if Thearch.Thechar == '5' || Buildmode == BuildmodeCArchive { + if SysArch.Family == sys.ARM || Buildmode == BuildmodeCArchive { ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff } else { ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff @@ -511,31 +504,31 @@ func Asmbmacho() { } if Linkmode != LinkExternal { - switch Thearch.Thechar { + switch SysArch.Family { default: - Exitf("unknown macho architecture: %v", Thearch.Thechar) + Exitf("unknown macho architecture: %v", SysArch.Family) - case '5': + case sys.ARM: ml := newMachoLoad(5, 17+2) /* unix thread */ ml.data[0] = 1 /* thread type */ ml.data[1] = 17 /* word count */ ml.data[2+15] = uint32(Entryvalue()) /* start pc */ - case '6': + case sys.AMD64: ml := newMachoLoad(5, 42+2) /* unix thread */ ml.data[0] = 4 /* thread type */ ml.data[1] = 42 /* word count */ ml.data[2+32] = uint32(Entryvalue()) /* start pc */ ml.data[2+32+1] = uint32(Entryvalue() >> 32) - case '7': + case sys.ARM64: ml := newMachoLoad(5, 68+2) /* unix thread */ ml.data[0] = 6 /* thread type */ ml.data[1] = 68 /* word count */ ml.data[2+64] = uint32(Entryvalue()) /* start pc */ ml.data[2+64+1] = uint32(Entryvalue() >> 32) - case '8': + case sys.I386: ml := newMachoLoad(5, 16+2) /* unix thread */ ml.data[0] = 1 /* thread type */ ml.data[1] = 16 /* word count */ @@ -546,7 +539,6 @@ func Asmbmacho() { if Debug['d'] == 0 { // must match domacholink below s1 := Linklookup(Ctxt, ".machosymtab", 0) - s2 := Linklookup(Ctxt, ".linkedit.plt", 0) s3 := Linklookup(Ctxt, ".linkedit.got", 0) s4 := Linklookup(Ctxt, ".machosymstr", 0) @@ -729,7 +721,7 @@ func machosymtab() { Adduint8(Ctxt, symtab, 0x01) // type N_EXT, external symbol Adduint8(Ctxt, symtab, 0) // no section Adduint16(Ctxt, symtab, 0) // desc - adduintxx(Ctxt, symtab, 0, Thearch.Ptrsize) // no value + adduintxx(Ctxt, symtab, 0, SysArch.PtrSize) // no value } else { if s.Attr.CgoExport() { Adduint8(Ctxt, symtab, 0x0f) @@ -747,7 +739,7 @@ func machosymtab() { Adduint8(Ctxt, symtab, uint8(o.Sect.Extnum)) } Adduint16(Ctxt, symtab, 0) // desc - adduintxx(Ctxt, symtab, uint64(Symaddr(s)), Thearch.Ptrsize) + adduintxx(Ctxt, symtab, uint64(Symaddr(s)), SysArch.PtrSize) } } } diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index ff29ce2d70..471dda712f 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -93,7 +93,7 @@ func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) { it.value = -1 it.start = 1 it.done = 0 - it.pcscale = uint32(ctxt.Arch.Minlc) + it.pcscale = uint32(ctxt.Arch.MinLC) pciternext(it) } @@ -242,12 +242,12 @@ func pclntab() { } pclntabNfunc = nfunc - Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) + Symgrow(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize)+4) setuint32(Ctxt, ftab, 0, 0xfffffffb) - setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) - setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) - setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) - pclntabPclntabOffset = int32(8 + Thearch.Ptrsize) + setuint8(Ctxt, ftab, 6, uint8(SysArch.MinLC)) + setuint8(Ctxt, ftab, 7, uint8(SysArch.PtrSize)) + setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize)) + pclntabPclntabOffset = int32(8 + SysArch.PtrSize) nfunc = 0 var last *LSym @@ -272,16 +272,16 @@ func pclntab() { } funcstart = int32(len(ftab.P)) - funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) + funcstart += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1) - setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) - setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) + setaddr(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), Ctxt.Cursym) + setuintxx(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint64(funcstart), int64(SysArch.PtrSize)) // fixed size of struct, checked below off = funcstart - end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(Thearch.Ptrsize) - if len(pcln.Funcdata) > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { + end = funcstart + int32(SysArch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(SysArch.PtrSize) + if len(pcln.Funcdata) > 0 && (end&int32(SysArch.PtrSize-1) != 0) { end += 4 } Symgrow(Ctxt, ftab, int64(end)) @@ -330,25 +330,25 @@ func pclntab() { // funcdata, must be pointer-aligned and we're only int32-aligned. // Missing funcdata will be 0 (nil pointer). if len(pcln.Funcdata) > 0 { - if off&int32(Thearch.Ptrsize-1) != 0 { + if off&int32(SysArch.PtrSize-1) != 0 { off += 4 } for i = 0; i < int32(len(pcln.Funcdata)); i++ { if pcln.Funcdata[i] == nil { - setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) + setuintxx(Ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(SysArch.PtrSize)) } else { // TODO: Dedup. funcdata_bytes += pcln.Funcdata[i].Size - setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) + setaddrplus(Ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) } } - off += int32(len(pcln.Funcdata)) * int32(Thearch.Ptrsize) + off += int32(len(pcln.Funcdata)) * int32(SysArch.PtrSize) } if off != end { - Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), Thearch.Ptrsize) + Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), SysArch.PtrSize) errorexit() } @@ -357,14 +357,14 @@ func pclntab() { pclntabLastFunc = last // Final entry of table is just end pc. - setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) + setaddrplus(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), last, last.Size) // Start file table. start := int32(len(ftab.P)) - start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) + start += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1) pclntabFiletabOffset = start - setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) + setuint32(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint32(start)) Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index 56698361d0..0204b8c8c2 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -6,6 +6,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "encoding/binary" "fmt" "os" @@ -419,9 +420,9 @@ func chksectseg(h *IMAGE_SECTION_HEADER, s *Segment) { func Peinit() { var l int - switch Thearch.Thechar { + switch SysArch.Family { // 64-bit architectures - case '6': + case sys.AMD64: pe64 = 1 l = binary.Size(&oh64) @@ -506,7 +507,7 @@ func initdynimport() *Dll { if err != nil { Diag("failed to parse stdcall decoration: %v", err) } - m.argsize *= Thearch.Ptrsize + m.argsize *= SysArch.PtrSize s.Extname = s.Extname[:i] } @@ -520,10 +521,10 @@ func initdynimport() *Dll { for d := dr; d != nil; d = d.next { for m = d.ms; m != nil; m = m.next { m.s.Type = obj.SDATA - Symgrow(Ctxt, m.s, int64(Thearch.Ptrsize)) + Symgrow(Ctxt, m.s, int64(SysArch.PtrSize)) dynName := m.s.Extname // only windows/386 requires stdcall decoration - if Thearch.Thechar == '8' && m.argsize >= 0 { + if SysArch.Family == sys.I386 && m.argsize >= 0 { dynName += fmt.Sprintf("@%d", m.argsize) } dynSym := Linklookup(Ctxt, dynName, 0) @@ -532,7 +533,7 @@ func initdynimport() *Dll { r := Addrel(m.s) r.Sym = dynSym r.Off = 0 - r.Siz = uint8(Thearch.Ptrsize) + r.Siz = uint8(SysArch.PtrSize) r.Type = obj.R_ADDR } } @@ -546,10 +547,10 @@ func initdynimport() *Dll { m.s.Sub = dynamic.Sub dynamic.Sub = m.s m.s.Value = dynamic.Size - dynamic.Size += int64(Thearch.Ptrsize) + dynamic.Size += int64(SysArch.PtrSize) } - dynamic.Size += int64(Thearch.Ptrsize) + dynamic.Size += int64(SysArch.PtrSize) } } @@ -946,7 +947,7 @@ func writePESymTableRecords() int { } // only windows/386 requires underscore prefix on external symbols - if Thearch.Thechar == '8' && + if SysArch.Family == sys.I386 && Linkmode == LinkExternal && (s.Type != obj.SDYNIMPORT || s.Attr.CgoExport()) && s.Name == s.Extname && @@ -1002,7 +1003,7 @@ func writePESymTableRecords() int { for d := dr; d != nil; d = d.next { for m := d.ms; m != nil; m = m.next { s := m.s.R[0].Xsym - put(s, s.Name, 'U', 0, int64(Thearch.Ptrsize), 0, nil) + put(s, s.Name, 'U', 0, int64(SysArch.PtrSize), 0, nil) } } @@ -1129,12 +1130,12 @@ func addinitarray() (c *IMAGE_SECTION_HEADER) { } func Asmbpe() { - switch Thearch.Thechar { + switch SysArch.Family { default: - Exitf("unknown PE architecture: %v", Thearch.Thechar) - case '6': + Exitf("unknown PE architecture: %v", SysArch.Family) + case sys.AMD64: fh.Machine = IMAGE_FILE_MACHINE_AMD64 - case '8': + case sys.I386: fh.Machine = IMAGE_FILE_MACHINE_I386 } diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go index f48b54efda..b9902a5e5e 100644 --- a/src/cmd/link/internal/ld/pobj.go +++ b/src/cmd/link/internal/ld/pobj.go @@ -32,6 +32,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "flag" "fmt" "os" @@ -44,9 +45,7 @@ var ( ) func Ldmain() { - Ctxt = linknew(Thelinkarch) - Ctxt.Thechar = int32(Thearch.Thechar) - Ctxt.Thestring = Thestring + Ctxt = linknew(SysArch) Ctxt.Diag = Diag Ctxt.Bso = &Bso @@ -70,7 +69,7 @@ func Ldmain() { } } - if Thearch.Thechar == '6' && obj.Getgoos() == "plan9" { + if SysArch.Family == sys.AMD64 && obj.Getgoos() == "plan9" { obj.Flagcount("8", "use 64-bit addresses in symbol table", &Debug['8']) } obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) @@ -107,7 +106,7 @@ func Ldmain() { obj.Flagcount("race", "enable race detector", &flag_race) obj.Flagcount("s", "disable symbol table", &Debug['s']) var flagShared int - if Thearch.Thechar == '5' || Thearch.Thechar == '6' { + if SysArch.InFamily(sys.ARM, sys.AMD64) { obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &flagShared) } obj.Flagstr("tmpdir", "use `directory` for temporary files", &tmpdir) diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go index 3deb94644e..76fe7dab79 100644 --- a/src/cmd/link/internal/ld/sym.go +++ b/src/cmd/link/internal/ld/sym.go @@ -33,6 +33,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "log" "strconv" ) @@ -55,7 +56,7 @@ var headers = []struct { {"windowsgui", obj.Hwindows}, } -func linknew(arch *LinkArch) *Link { +func linknew(arch *sys.Arch) *Link { ctxt := &Link{ Hash: []map[string]*LSym{ // preallocate about 2mb for hash of @@ -98,33 +99,33 @@ func linknew(arch *LinkArch) *Link { obj.Hdragonfly, obj.Hsolaris: if obj.Getgoos() == "android" { - switch ctxt.Arch.Thechar { - case '6': + switch ctxt.Arch.Family { + case sys.AMD64: // Android/amd64 constant - offset from 0(FS) to our TLS slot. // Explained in src/runtime/cgo/gcc_android_*.c ctxt.Tlsoffset = 0x1d0 - case '8': + case sys.I386: // Android/386 constant - offset from 0(GS) to our TLS slot. ctxt.Tlsoffset = 0xf8 default: - ctxt.Tlsoffset = -1 * ctxt.Arch.Ptrsize + ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize } } else { - ctxt.Tlsoffset = -1 * ctxt.Arch.Ptrsize + ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize } case obj.Hnacl: - switch ctxt.Arch.Thechar { + switch ctxt.Arch.Family { default: log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name) - case '5': + case sys.ARM: ctxt.Tlsoffset = 0 - case '6': + case sys.AMD64: ctxt.Tlsoffset = 0 - case '8': + case sys.I386: ctxt.Tlsoffset = -8 } @@ -133,26 +134,26 @@ func linknew(arch *LinkArch) *Link { * Explained in src/runtime/cgo/gcc_darwin_*.c. */ case obj.Hdarwin: - switch ctxt.Arch.Thechar { + switch ctxt.Arch.Family { default: log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) - case '5': + case sys.ARM: ctxt.Tlsoffset = 0 // dummy value, not needed - case '6': + case sys.AMD64: ctxt.Tlsoffset = 0x8a0 - case '7': + case sys.ARM64: ctxt.Tlsoffset = 0 // dummy value, not needed - case '8': + case sys.I386: ctxt.Tlsoffset = 0x468 } } // On arm, record goarm. - if ctxt.Arch.Thechar == '5' { + if ctxt.Arch.Family == sys.ARM { ctxt.Goarm = obj.Getgoarm() } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 167176cc2d..ecd5c741bb 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -32,6 +32,7 @@ package ld import ( "cmd/internal/obj" + "cmd/internal/sys" "fmt" "path/filepath" "strings" @@ -160,7 +161,7 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L if x.Type&obj.SHIDDEN != 0 { other = STV_HIDDEN } - if (Buildmode == BuildmodePIE || DynlinkingGo()) && Thearch.Thechar == '9' && type_ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" { + if (Buildmode == BuildmodePIE || DynlinkingGo()) && SysArch.Family == sys.PPC64 && type_ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" { // On ppc64 the top three bits of the st_other field indicate how // many instructions separate the global and local entry points. In // our case it is two instructions, indicated by the value 3. @@ -229,7 +230,7 @@ func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ 'Z', 'm': l := 4 - if HEADTYPE == obj.Hplan9 && Thearch.Thechar == '6' && Debug['8'] == 0 { + if HEADTYPE == obj.Hplan9 && SysArch.Family == sys.AMD64 && Debug['8'] == 0 { Lputb(uint32(addr >> 32)) l = 8 } diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index d0977e9b00..9a145e373a 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -32,6 +32,7 @@ package mips64 import ( "cmd/internal/obj" + "cmd/internal/sys" "cmd/link/internal/ld" "encoding/binary" "fmt" @@ -82,8 +83,8 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { // the first instruction is always at the lower address, this is endian neutral; // but note that o1 and o2 should still use the target endian. - o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off:]) - o2 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off+4:]) + o1 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off:]) + o2 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off+4:]) o1 = o1&0xffff0000 | uint32(t>>16)&0xffff o2 = o2&0xffff0000 | uint32(t)&0xffff @@ -99,7 +100,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { obj.R_JMPMIPS: // Low 26 bits = (S + A) >> 2 t := ld.Symaddr(r.Sym) + r.Add - o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off:]) + o1 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off:]) *val = int64(o1&0xfc000000 | uint32(t>>2)&^0xfc000000) return 0 } @@ -214,7 +215,7 @@ func asmb() { default: case obj.Hplan9: /* plan 9 */ magic := uint32(4*18*18 + 7) - if ld.Thestring == "mips64le" { + if ld.SysArch == sys.ArchMIPS64LE { magic = uint32(4*26*26 + 7) } ld.Thearch.Lput(uint32(magic)) /* magic */ diff --git a/src/cmd/link/internal/mips64/l.go b/src/cmd/link/internal/mips64/l.go index 003ee5ce71..f4191e69ab 100644 --- a/src/cmd/link/internal/mips64/l.go +++ b/src/cmd/link/internal/mips64/l.go @@ -62,11 +62,9 @@ package mips64 // THE SOFTWARE. const ( - thechar = '0' MaxAlign = 32 // max data alignment MinAlign = 1 // min data alignment FuncAlign = 8 - MINLC = 4 ) /* Used by ../internal/ld/dwarf.go */ diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go index 57a1b2ab14..87bb3a079b 100644 --- a/src/cmd/link/internal/mips64/obj.go +++ b/src/cmd/link/internal/mips64/obj.go @@ -32,6 +32,7 @@ package mips64 import ( "cmd/internal/obj" + "cmd/internal/sys" "cmd/link/internal/ld" "fmt" "log" @@ -45,21 +46,15 @@ func Main() { } func linkarchinit() { - ld.Thestring = obj.Getgoarch() - if ld.Thestring == "mips64le" { - ld.Thelinkarch = &ld.Linkmips64le + if obj.Getgoarch() == "mips64le" { + ld.SysArch = sys.ArchMIPS64LE } else { - ld.Thelinkarch = &ld.Linkmips64 + ld.SysArch = sys.ArchMIPS64 } - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize ld.Thearch.Funcalign = FuncAlign ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minalign = MinAlign - ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP ld.Thearch.Dwarfreglr = DWARFREGLR @@ -72,7 +67,7 @@ func linkarchinit() { ld.Thearch.Elfsetupplt = elfsetupplt ld.Thearch.Gentext = gentext ld.Thearch.Machoreloc1 = machoreloc1 - if ld.Thelinkarch == &ld.Linkmips64le { + if ld.SysArch == sys.ArchMIPS64LE { ld.Thearch.Lput = ld.Lputl ld.Thearch.Wput = ld.Wputl ld.Thearch.Vput = ld.Vputl diff --git a/src/cmd/link/internal/ppc64/l.go b/src/cmd/link/internal/ppc64/l.go index 622d6bb12e..a720993fbc 100644 --- a/src/cmd/link/internal/ppc64/l.go +++ b/src/cmd/link/internal/ppc64/l.go @@ -62,11 +62,9 @@ package ppc64 // THE SOFTWARE. const ( - thechar = '9' MaxAlign = 32 // max data alignment MinAlign = 1 // min data alignment FuncAlign = 8 - MINLC = 4 ) /* Used by ../internal/ld/dwarf.go */ diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go index 539ab1ac02..a540ab85b5 100644 --- a/src/cmd/link/internal/ppc64/obj.go +++ b/src/cmd/link/internal/ppc64/obj.go @@ -32,6 +32,7 @@ package ppc64 import ( "cmd/internal/obj" + "cmd/internal/sys" "cmd/link/internal/ld" "fmt" "log" @@ -45,21 +46,15 @@ func Main() { } func linkarchinit() { - ld.Thestring = obj.Getgoarch() - if ld.Thestring == "ppc64le" { - ld.Thelinkarch = &ld.Linkppc64le + if obj.Getgoarch() == "ppc64le" { + ld.SysArch = sys.ArchPPC64LE } else { - ld.Thelinkarch = &ld.Linkppc64 + ld.SysArch = sys.ArchPPC64 } - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize ld.Thearch.Funcalign = FuncAlign ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minalign = MinAlign - ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP ld.Thearch.Dwarfreglr = DWARFREGLR @@ -72,7 +67,7 @@ func linkarchinit() { ld.Thearch.Elfsetupplt = elfsetupplt ld.Thearch.Gentext = gentext ld.Thearch.Machoreloc1 = machoreloc1 - if ld.Thelinkarch == &ld.Linkppc64le { + if ld.SysArch == sys.ArchPPC64LE { ld.Thearch.Lput = ld.Lputl ld.Thearch.Wput = ld.Wputl ld.Thearch.Vput = ld.Vputl @@ -150,7 +145,7 @@ func archinit() { } case obj.Hlinux: /* ppc64 elf */ - if ld.Thestring == "ppc64" { + if ld.SysArch == sys.ArchPPC64 { ld.Debug['d'] = 1 // TODO(austin): ELF ABI v1 not supported yet } ld.Elfinit() diff --git a/src/cmd/link/internal/s390x/l.go b/src/cmd/link/internal/s390x/l.go index 839a9849c8..42cf15ee85 100644 --- a/src/cmd/link/internal/s390x/l.go +++ b/src/cmd/link/internal/s390x/l.go @@ -62,14 +62,9 @@ package s390x // THE SOFTWARE. const ( - thechar = 'z' - PtrSize = 8 - IntSize = 8 - RegSize = 8 MaxAlign = 32 // max data alignment MinAlign = 2 // min data alignment FuncAlign = 16 - MINLC = 2 ) /* Used by ../internal/ld/dwarf.go */ diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go index ef88d22bbd..fdb9898181 100644 --- a/src/cmd/link/internal/s390x/obj.go +++ b/src/cmd/link/internal/s390x/obj.go @@ -32,6 +32,7 @@ package s390x import ( "cmd/internal/obj" + "cmd/internal/sys" "cmd/link/internal/ld" "fmt" ) @@ -44,17 +45,11 @@ func Main() { } func linkarchinit() { - ld.Thestring = obj.Getgoarch() - ld.Thelinkarch = &ld.Links390x + ld.SysArch = sys.ArchS390X - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize ld.Thearch.Funcalign = FuncAlign ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minalign = MinAlign - ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP ld.Thearch.Dwarfreglr = DWARFREGLR diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 7da5dd02be..91251de15e 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -292,7 +292,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) { return } - if ld.HEADTYPE == obj.Hdarwin && s.Size == PtrSize && r.Off == 0 { + if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.SysArch.PtrSize) && r.Off == 0 { // Mach-O relocations are a royal pain to lay out. // They use a compact stateful bytecode representation // that is too much bother to deal with. @@ -317,7 +317,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) { return } - if ld.HEADTYPE == obj.Hwindows && s.Size == PtrSize { + if ld.HEADTYPE == obj.Hwindows && s.Size == int64(ld.SysArch.PtrSize) { // nothing to do, the relocation will be laid out in pereloc1 return } diff --git a/src/cmd/link/internal/x86/l.go b/src/cmd/link/internal/x86/l.go index 068fed9c8d..2043f9bb4e 100644 --- a/src/cmd/link/internal/x86/l.go +++ b/src/cmd/link/internal/x86/l.go @@ -31,12 +31,9 @@ package x86 const ( - thechar = '8' - PtrSize = 4 MaxAlign = 32 // max data alignment MinAlign = 1 // min data alignment FuncAlign = 16 - MINLC = 1 ) /* Used by ../internal/ld/dwarf.go */ diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go index 4380c41ebb..574c0dad2d 100644 --- a/src/cmd/link/internal/x86/obj.go +++ b/src/cmd/link/internal/x86/obj.go @@ -32,6 +32,7 @@ package x86 import ( "cmd/internal/obj" + "cmd/internal/sys" "cmd/link/internal/ld" "fmt" "log" @@ -45,17 +46,11 @@ func Main() { } func linkarchinit() { - ld.Thestring = "386" - ld.Thelinkarch = &ld.Link386 + ld.SysArch = sys.Arch386 - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize ld.Thearch.Funcalign = FuncAlign ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minalign = MinAlign - ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP ld.Thearch.Dwarfreglr = DWARFREGLR -- cgit v1.3 From d481ffc1afeae8852caa3452a0e23b1cd90d1e10 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 6 Apr 2016 18:54:17 -0700 Subject: cmd/compile, cmd/link: eliminate uses of ArchFamily in error messages Two of these error messages are already dead code: cmd/compile.main and cmd/link.main already switch on $GOARCH, ensuring it must be a prefix of the sys.Arch.Family. The error message about uncompiled Go source files can be just be simplified: anyone who's manually constructing Go object file archives probably knows what tool to use to compile Go source files. Change-Id: Ia4a67c0a1d1158379c127c91e909226d3367f3c2 Reviewed-on: https://go-review.googlesource.com/21626 Run-TryBot: Matthew Dempsky TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/main.go | 9 +-------- src/cmd/link/internal/ld/lib.go | 7 ++----- 2 files changed, 3 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 72e6478afe..079f4916c7 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -93,14 +93,7 @@ func doversion() { func Main() { defer hidePanic() - // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, - // but not other values. - p := obj.Getgoarch() - - if !strings.HasPrefix(p, Thearch.LinkArch.Name) { - log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.LinkArch.Family, p) - } - goarch = p + goarch = obj.Getgoarch() Ctxt = obj.Linknew(Thearch.LinkArch) Ctxt.DiagFunc = Yyerror diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 3e0bd8ebc4..305a3bc0db 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1301,7 +1301,8 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when if !strings.HasPrefix(line, "go object ") { if strings.HasSuffix(pn, ".go") { - Exitf("%cl: input %s is not .%c file (use %cg to compile .go files)", SysArch.Family, pn, SysArch.Family, SysArch.Family) + Exitf("%s: uncompiled .go source file", pn) + return nil } if line == SysArch.Name { @@ -1559,10 +1560,6 @@ func mywhatsys() { goroot = obj.Getgoroot() goos = obj.Getgoos() goarch = obj.Getgoarch() - - if !strings.HasPrefix(goarch, SysArch.Name) { - log.Fatalf("cannot use %cc with GOARCH=%s", SysArch.Family, goarch) - } } // Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync. -- cgit v1.3 From 22ef687da815c4d651cef3c1b7d44f41100b6715 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 6 Apr 2016 20:06:12 -0700 Subject: cmd/link: remove dependency on sys.ArchFamily values Change-Id: I858054b72847f4f27a1ebbdaff82820a28c03743 Reviewed-on: https://go-review.googlesource.com/21627 Reviewed-by: Brad Fitzpatrick --- src/cmd/link/internal/ld/ldelf.go | 153 ++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index 3aee2d5ece..485599be62 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -927,7 +927,8 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { rp.Sym = sym.sym } - rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz)) + rp.Type = 256 + int32(info) + rp.Siz = relSize(pn, uint32(info)) if rela != 0 { rp.Add = int64(add) } else { @@ -1128,81 +1129,89 @@ func (x rbyoff) Less(i, j int) bool { return false } -func reltype(pn string, elftype int, siz *uint8) int { - // TODO(mdempsky): Remove dependency on ArchFamily char values. +func relSize(pn string, elftype uint32) uint8 { + // TODO(mdempsky): Replace this with a struct-valued switch statement + // once golang.org/issue/15164 is fixed or found to not impair cmd/link + // performance. - switch uint32(SysArch.Family) | uint32(elftype)<<24 { + const ( + AMD64 = uint32(sys.AMD64) + ARM = uint32(sys.ARM) + I386 = uint32(sys.I386) + PPC64 = uint32(sys.PPC64) + S390X = uint32(sys.S390X) + ) + + switch uint32(SysArch.Family) | elftype<<24 { default: Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype) fallthrough - case 'z' | R_390_8: - *siz = 1 - - case '9' | R_PPC64_TOC16<<24, - '9' | R_PPC64_TOC16_LO<<24, - '9' | R_PPC64_TOC16_HI<<24, - '9' | R_PPC64_TOC16_HA<<24, - '9' | R_PPC64_TOC16_DS<<24, - '9' | R_PPC64_TOC16_LO_DS<<24, - '9' | R_PPC64_REL16_LO<<24, - '9' | R_PPC64_REL16_HI<<24, - '9' | R_PPC64_REL16_HA<<24, - 'z' | R_390_16<<24, - 'z' | R_390_GOT16<<24, - 'z' | R_390_PC16<<24, - 'z' | R_390_PC16DBL<<24, - 'z' | R_390_PLT16DBL<<24: - *siz = 2 - - case '5' | R_ARM_ABS32<<24, - '5' | R_ARM_GOT32<<24, - '5' | R_ARM_PLT32<<24, - '5' | R_ARM_GOTOFF<<24, - '5' | R_ARM_GOTPC<<24, - '5' | R_ARM_THM_PC22<<24, - '5' | R_ARM_REL32<<24, - '5' | R_ARM_CALL<<24, - '5' | R_ARM_V4BX<<24, - '5' | R_ARM_GOT_PREL<<24, - '5' | R_ARM_PC24<<24, - '5' | R_ARM_JUMP24<<24, - '6' | R_X86_64_PC32<<24, - '6' | R_X86_64_PLT32<<24, - '6' | R_X86_64_GOTPCREL<<24, - '6' | R_X86_64_GOTPCRELX<<24, - '6' | R_X86_64_REX_GOTPCRELX<<24, - '8' | R_386_32<<24, - '8' | R_386_PC32<<24, - '8' | R_386_GOT32<<24, - '8' | R_386_PLT32<<24, - '8' | R_386_GOTOFF<<24, - '8' | R_386_GOTPC<<24, - '8' | R_386_GOT32X<<24, - '9' | R_PPC64_REL24<<24, - '9' | R_PPC_REL32<<24, - 'z' | R_390_32<<24, - 'z' | R_390_PC32<<24, - 'z' | R_390_GOT32<<24, - 'z' | R_390_PLT32<<24, - 'z' | R_390_PC32DBL<<24, - 'z' | R_390_PLT32DBL<<24, - 'z' | R_390_GOTPCDBL<<24, - 'z' | R_390_GOTENT<<24: - *siz = 4 - - case '6' | R_X86_64_64<<24, - '9' | R_PPC64_ADDR64<<24, - 'z' | R_390_GLOB_DAT<<24, - 'z' | R_390_RELATIVE<<24, - 'z' | R_390_GOTOFF<<24, - 'z' | R_390_GOTPC<<24, - 'z' | R_390_64<<24, - 'z' | R_390_PC64<<24, - 'z' | R_390_GOT64<<24, - 'z' | R_390_PLT64<<24: - *siz = 8 + case S390X | R_390_8<<24: + return 1 + + case PPC64 | R_PPC64_TOC16<<24, + PPC64 | R_PPC64_TOC16_LO<<24, + PPC64 | R_PPC64_TOC16_HI<<24, + PPC64 | R_PPC64_TOC16_HA<<24, + PPC64 | R_PPC64_TOC16_DS<<24, + PPC64 | R_PPC64_TOC16_LO_DS<<24, + PPC64 | R_PPC64_REL16_LO<<24, + PPC64 | R_PPC64_REL16_HI<<24, + PPC64 | R_PPC64_REL16_HA<<24, + S390X | R_390_16<<24, + S390X | R_390_GOT16<<24, + S390X | R_390_PC16<<24, + S390X | R_390_PC16DBL<<24, + S390X | R_390_PLT16DBL<<24: + return 2 + + case ARM | R_ARM_ABS32<<24, + ARM | R_ARM_GOT32<<24, + ARM | R_ARM_PLT32<<24, + ARM | R_ARM_GOTOFF<<24, + ARM | R_ARM_GOTPC<<24, + ARM | R_ARM_THM_PC22<<24, + ARM | R_ARM_REL32<<24, + ARM | R_ARM_CALL<<24, + ARM | R_ARM_V4BX<<24, + ARM | R_ARM_GOT_PREL<<24, + ARM | R_ARM_PC24<<24, + ARM | R_ARM_JUMP24<<24, + AMD64 | R_X86_64_PC32<<24, + AMD64 | R_X86_64_PLT32<<24, + AMD64 | R_X86_64_GOTPCREL<<24, + AMD64 | R_X86_64_GOTPCRELX<<24, + AMD64 | R_X86_64_REX_GOTPCRELX<<24, + I386 | R_386_32<<24, + I386 | R_386_PC32<<24, + I386 | R_386_GOT32<<24, + I386 | R_386_PLT32<<24, + I386 | R_386_GOTOFF<<24, + I386 | R_386_GOTPC<<24, + I386 | R_386_GOT32X<<24, + PPC64 | R_PPC64_REL24<<24, + PPC64 | R_PPC_REL32<<24, + S390X | R_390_32<<24, + S390X | R_390_PC32<<24, + S390X | R_390_GOT32<<24, + S390X | R_390_PLT32<<24, + S390X | R_390_PC32DBL<<24, + S390X | R_390_PLT32DBL<<24, + S390X | R_390_GOTPCDBL<<24, + S390X | R_390_GOTENT<<24: + return 4 + + case AMD64 | R_X86_64_64<<24, + PPC64 | R_PPC64_ADDR64<<24, + S390X | R_390_GLOB_DAT<<24, + S390X | R_390_RELATIVE<<24, + S390X | R_390_GOTOFF<<24, + S390X | R_390_GOTPC<<24, + S390X | R_390_64<<24, + S390X | R_390_PC64<<24, + S390X | R_390_GOT64<<24, + S390X | R_390_PLT64<<24: + return 8 } - - return 256 + elftype } -- cgit v1.3 From b17b95301a549d168d96e38310d06216f595e53b Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 6 Apr 2016 20:06:41 -0700 Subject: cmd/internal/sys: change ArchFamily constants to iotas RIP architecture characters. Change-Id: I36f53afdc311b14b9459ff3821bd6df54a057ded Reviewed-on: https://go-review.googlesource.com/21628 Reviewed-by: Brad Fitzpatrick Reviewed-by: Dave Cheney --- src/cmd/internal/sys/arch.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/cmd/internal/sys/arch.go b/src/cmd/internal/sys/arch.go index 0a7423aa9f..0dc7cb814b 100644 --- a/src/cmd/internal/sys/arch.go +++ b/src/cmd/internal/sys/arch.go @@ -10,13 +10,13 @@ import "encoding/binary" type ArchFamily byte const ( - AMD64 ArchFamily = '6' - ARM ArchFamily = '5' - ARM64 ArchFamily = '7' - I386 ArchFamily = '8' - MIPS64 ArchFamily = '0' - PPC64 ArchFamily = '9' - S390X ArchFamily = 'z' + AMD64 ArchFamily = iota + ARM + ARM64 + I386 + MIPS64 + PPC64 + S390X ) // Arch represents an individual architecture. @@ -30,6 +30,7 @@ type Arch struct { PtrSize int RegSize int + // MinLC is the minimum length of an instruction code. MinLC int } -- cgit v1.3 From 4b7e36cdfe8e0c3579a2503a81474fe43db4db69 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 6 Apr 2016 21:45:29 -0700 Subject: cmd: extract obj's Biobuf code into new bio package API could still be made more Go-ey. Updates #15165. Change-Id: I514ffceffa43c293ae5d7e5f1e9193fda0098865 Reviewed-on: https://go-review.googlesource.com/21644 Reviewed-by: Brad Fitzpatrick Run-TryBot: Matthew Dempsky TryBot-Result: Gobot Gobot --- src/cmd/asm/internal/asm/endtoend_test.go | 5 +- src/cmd/asm/main.go | 5 +- src/cmd/compile/internal/gc/bexport.go | 10 +- src/cmd/compile/internal/gc/export.go | 8 +- src/cmd/compile/internal/gc/go.go | 5 +- src/cmd/compile/internal/gc/main.go | 5 +- src/cmd/compile/internal/gc/obj.go | 31 +++--- src/cmd/dist/buildtool.go | 1 + src/cmd/internal/bio/buf.go | 150 ++++++++++++++++++++++++++++++ src/cmd/internal/obj/link.go | 7 +- src/cmd/internal/obj/objfile.go | 9 +- src/cmd/internal/obj/util.go | 140 ---------------------------- src/cmd/link/internal/ld/ar.go | 15 +-- src/cmd/link/internal/ld/go.go | 5 +- src/cmd/link/internal/ld/ldelf.go | 13 +-- src/cmd/link/internal/ld/ldmacho.go | 23 ++--- src/cmd/link/internal/ld/ldpe.go | 27 +++--- src/cmd/link/internal/ld/lib.go | 83 ++++++++--------- src/cmd/link/internal/ld/link.go | 4 +- src/cmd/link/internal/ld/objfile.go | 9 +- src/cmd/link/internal/ld/pobj.go | 3 +- 21 files changed, 291 insertions(+), 267 deletions(-) create mode 100644 src/cmd/internal/bio/buf.go (limited to 'src') diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index 1307c4243f..8986281f10 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -17,6 +17,7 @@ import ( "testing" "cmd/asm/internal/lex" + "cmd/internal/bio" "cmd/internal/obj" ) @@ -33,7 +34,7 @@ func testEndToEnd(t *testing.T, goarch, file string) { pList := obj.Linknewplist(ctxt) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. - ctxt.Bso = obj.Binitw(os.Stdout) + ctxt.Bso = bio.BufWriter(os.Stdout) defer ctxt.Bso.Flush() failed := false ctxt.DiagFunc = func(format string, args ...interface{}) { @@ -271,7 +272,7 @@ func testErrors(t *testing.T, goarch, file string) { pList := obj.Linknewplist(ctxt) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. - ctxt.Bso = obj.Binitw(os.Stdout) + ctxt.Bso = bio.BufWriter(os.Stdout) defer ctxt.Bso.Flush() failed := false var errBuf bytes.Buffer diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index 4e450bec98..75cb8f75d3 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -15,6 +15,7 @@ import ( "cmd/asm/internal/flags" "cmd/asm/internal/lex" + "cmd/internal/bio" "cmd/internal/obj" ) @@ -45,9 +46,9 @@ func main() { if *flags.Shared || *flags.Dynlink { ctxt.Flag_shared = 1 } - ctxt.Bso = obj.Binitw(os.Stdout) + ctxt.Bso = bio.BufWriter(os.Stdout) defer ctxt.Bso.Flush() - output := obj.Binitw(fd) + output := bio.BufWriter(fd) fmt.Fprintf(output, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion()) fmt.Fprintf(output, "!\n") diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 092cdac2f6..702090280f 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -92,7 +92,7 @@ package gc import ( "bytes" "cmd/compile/internal/big" - "cmd/internal/obj" + "cmd/internal/bio" "encoding/binary" "fmt" "sort" @@ -124,7 +124,7 @@ const exportVersion = "v0" const exportInlined = true // default: true type exporter struct { - out *obj.Biobuf + out *bio.Buf pkgIndex map[*Pkg]int typIndex map[*Type]int inlined []*Func @@ -136,7 +136,7 @@ type exporter struct { } // Export writes the exportlist for localpkg to out and returns the number of bytes written. -func Export(out *obj.Biobuf, trace bool) int { +func Export(out *bio.Buf, trace bool) int { p := exporter{ out: out, pkgIndex: make(map[*Pkg]int), @@ -1531,10 +1531,10 @@ func (p *exporter) byte(b byte) { fallthrough case '|': // write '|' as '|' '|' - obj.Bputc(p.out, '|') + p.out.WriteByte('|') p.written++ } - obj.Bputc(p.out, b) + p.out.WriteByte(b) p.written++ } diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 17311cf6af..5d4add8ff4 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -7,7 +7,7 @@ package gc import ( "bufio" "bytes" - "cmd/internal/obj" + "cmd/internal/bio" "fmt" "sort" "unicode" @@ -384,7 +384,7 @@ func dumpexport() { if debugFormat { // save a copy of the export data var copy bytes.Buffer - bcopy := obj.Binitw(©) + bcopy := bio.BufWriter(©) size = Export(bcopy, Debug_export != 0) bcopy.Flush() // flushing to bytes.Buffer cannot fail if n, err := bout.Write(copy.Bytes()); n != size || err != nil { @@ -577,7 +577,7 @@ func importtype(pt *Type, t *Type) { } func dumpasmhdr() { - b, err := obj.Bopenw(asmhdr) + b, err := bio.Create(asmhdr) if err != nil { Fatalf("%v", err) } @@ -604,5 +604,5 @@ func dumpasmhdr() { } } - obj.Bterm(b) + b.Close() } diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index ef8b516ea5..cd9db38fb4 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -6,6 +6,7 @@ package gc import ( "cmd/compile/internal/ssa" + "cmd/internal/bio" "cmd/internal/obj" ) @@ -132,7 +133,7 @@ var infile string var outfile string -var bout *obj.Biobuf +var bout *bio.Buf var nerrors int @@ -287,7 +288,7 @@ var Ctxt *obj.Link var writearchive int -var bstdout obj.Biobuf +var bstdout *bio.Buf var Nacl bool diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 079f4916c7..c8a778c34a 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -9,6 +9,7 @@ package gc import ( "bufio" "cmd/compile/internal/ssa" + "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/sys" "flag" @@ -97,8 +98,8 @@ func Main() { Ctxt = obj.Linknew(Thearch.LinkArch) Ctxt.DiagFunc = Yyerror - Ctxt.Bso = &bstdout - bstdout = *obj.Binitw(os.Stdout) + bstdout = bio.BufWriter(os.Stdout) + Ctxt.Bso = bstdout localpkg = mkpkg("") localpkg.Prefix = "\"\"" diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 99eb73bd94..3920e25224 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -5,6 +5,7 @@ package gc import ( + "cmd/internal/bio" "cmd/internal/obj" "crypto/sha256" "fmt" @@ -23,7 +24,7 @@ func formathdr(arhdr []byte, name string, size int64) { func dumpobj() { var err error - bout, err = obj.Bopenw(outfile) + bout, err = bio.Create(outfile) if err != nil { Flusherrors() fmt.Printf("can't create %s: %v\n", outfile, err) @@ -33,10 +34,10 @@ func dumpobj() { startobj := int64(0) var arhdr [ArhdrSize]byte if writearchive != 0 { - obj.Bwritestring(bout, "!\n") + bout.WriteString("!\n") arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) - startobj = obj.Boffset(bout) + startobj = bio.Boffset(bout) } fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) @@ -44,19 +45,19 @@ func dumpobj() { if writearchive != 0 { bout.Flush() - size := obj.Boffset(bout) - startobj + size := bio.Boffset(bout) - startobj if size&1 != 0 { - obj.Bputc(bout, 0) + bout.WriteByte(0) } - obj.Bseek(bout, startobj-ArhdrSize, 0) + bio.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "__.PKGDEF", size) bout.Write(arhdr[:]) bout.Flush() - obj.Bseek(bout, startobj+size+(size&1), 0) + bio.Bseek(bout, startobj+size+(size&1), 0) arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) - startobj = obj.Boffset(bout) + startobj = bio.Boffset(bout) fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) } @@ -91,16 +92,16 @@ func dumpobj() { if writearchive != 0 { bout.Flush() - size := obj.Boffset(bout) - startobj + size := bio.Boffset(bout) - startobj if size&1 != 0 { - obj.Bputc(bout, 0) + bout.WriteByte(0) } - obj.Bseek(bout, startobj-ArhdrSize, 0) + bio.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "_go_.o", size) bout.Write(arhdr[:]) } - obj.Bterm(bout) + bout.Close() } func dumpglobls() { @@ -132,9 +133,9 @@ func dumpglobls() { funcsyms = nil } -func Bputname(b *obj.Biobuf, s *obj.LSym) { - obj.Bwritestring(b, s.Name) - obj.Bputc(b, 0) +func Bputname(b *bio.Buf, s *obj.LSym) { + b.WriteString(s.Name) + b.WriteByte(0) } func Linksym(s *Sym) *obj.LSym { diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 123d5ccf82..777c92c726 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -38,6 +38,7 @@ var bootstrapDirs = []string{ "compile/internal/ppc64", "compile/internal/ssa", "compile/internal/x86", + "internal/bio", "internal/gcprog", "internal/obj", "internal/obj/arm", diff --git a/src/cmd/internal/bio/buf.go b/src/cmd/internal/bio/buf.go new file mode 100644 index 0000000000..a1df26ca9c --- /dev/null +++ b/src/cmd/internal/bio/buf.go @@ -0,0 +1,150 @@ +// Copyright 2015 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 bio implements seekable buffered I/O. +package bio + +import ( + "bufio" + "io" + "log" + "os" +) + +const EOF = -1 + +// Buf implements a seekable buffered I/O abstraction. +type Buf struct { + f *os.File + r *bufio.Reader + w *bufio.Writer +} + +func (b *Buf) Reader() *bufio.Reader { return b.r } +func (b *Buf) Writer() *bufio.Writer { return b.w } + +func Create(name string) (*Buf, error) { + f, err := os.Create(name) + if err != nil { + return nil, err + } + return &Buf{f: f, w: bufio.NewWriter(f)}, nil +} + +func Open(name string) (*Buf, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + return &Buf{f: f, r: bufio.NewReader(f)}, nil +} + +func BufWriter(w io.Writer) *Buf { + return &Buf{w: bufio.NewWriter(w)} +} + +func BufReader(r io.Reader) *Buf { + return &Buf{r: bufio.NewReader(r)} +} + +func (b *Buf) Write(p []byte) (int, error) { + return b.w.Write(p) +} + +func (b *Buf) WriteString(p string) (int, error) { + return b.w.WriteString(p) +} + +func Bseek(b *Buf, offset int64, whence int) int64 { + if b.w != nil { + if err := b.w.Flush(); err != nil { + log.Fatalf("writing output: %v", err) + } + } else if b.r != nil { + if whence == 1 { + offset -= int64(b.r.Buffered()) + } + } + off, err := b.f.Seek(offset, whence) + if err != nil { + log.Fatalf("seeking in output: %v", err) + } + if b.r != nil { + b.r.Reset(b.f) + } + return off +} + +func Boffset(b *Buf) int64 { + if b.w != nil { + if err := b.w.Flush(); err != nil { + log.Fatalf("writing output: %v", err) + } + } + off, err := b.f.Seek(0, 1) + if err != nil { + log.Fatalf("seeking in output [0, 1]: %v", err) + } + if b.r != nil { + off -= int64(b.r.Buffered()) + } + return off +} + +func (b *Buf) Flush() error { + return b.w.Flush() +} + +func (b *Buf) WriteByte(c byte) error { + return b.w.WriteByte(c) +} + +func Bread(b *Buf, p []byte) int { + n, err := io.ReadFull(b.r, p) + if n == 0 { + if err != nil && err != io.EOF { + n = -1 + } + } + return n +} + +func Bgetc(b *Buf) int { + c, err := b.r.ReadByte() + if err != nil { + if err != io.EOF { + log.Fatalf("reading input: %v", err) + } + return EOF + } + return int(c) +} + +func (b *Buf) Read(p []byte) (int, error) { + return b.r.Read(p) +} + +func (b *Buf) Peek(n int) ([]byte, error) { + return b.r.Peek(n) +} + +func Brdline(b *Buf, delim int) string { + s, err := b.r.ReadBytes(byte(delim)) + if err != nil { + log.Fatalf("reading input: %v", err) + } + return string(s) +} + +func (b *Buf) Close() error { + var err error + if b.w != nil { + err = b.w.Flush() + } + err1 := b.f.Close() + if err == nil { + err = err1 + } + return err +} diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 81a5689aef..2c81ca2f08 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -30,7 +30,10 @@ package obj -import "cmd/internal/sys" +import ( + "cmd/internal/bio" + "cmd/internal/sys" +) // An Addr is an argument to an instruction. // The general forms and their encodings are: @@ -626,7 +629,7 @@ type Link struct { Flag_shared int32 Flag_dynlink bool Flag_optimize bool - Bso *Biobuf + Bso *bio.Buf Pathname string Goroot string Goroot_final string diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index bdd3bfc826..405cbf446a 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -109,6 +109,7 @@ package obj import ( "bufio" + "cmd/internal/bio" "cmd/internal/sys" "fmt" "log" @@ -120,7 +121,7 @@ import ( // The Go and C compilers, and the assembler, call writeobj to write // out a Go object file. The linker does not call this; the linker // does not write out object files. -func Writeobjdirect(ctxt *Link, b *Biobuf) { +func Writeobjdirect(ctxt *Link, b *bio.Buf) { Flushplist(ctxt) WriteObjFile(ctxt, b) } @@ -373,16 +374,16 @@ func (w *objWriter) writeLengths() { w.writeInt(int64(w.nFile)) } -func newObjWriter(ctxt *Link, b *Biobuf) *objWriter { +func newObjWriter(ctxt *Link, b *bio.Buf) *objWriter { return &objWriter{ ctxt: ctxt, - wr: b.w, + wr: b.Writer(), vrefIdx: make(map[string]int), refIdx: make(map[string]int), } } -func WriteObjFile(ctxt *Link, b *Biobuf) { +func WriteObjFile(ctxt *Link, b *bio.Buf) { w := newObjWriter(ctxt, b) // Magic header diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 245fab9690..04e6a76e1a 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -5,10 +5,8 @@ package obj import ( - "bufio" "bytes" "fmt" - "io" "log" "os" "strings" @@ -26,144 +24,6 @@ func Cputime() float64 { return time.Since(start).Seconds() } -type Biobuf struct { - f *os.File - r *bufio.Reader - w *bufio.Writer - linelen int -} - -func (b *Biobuf) Reader() *bufio.Reader { return b.r } - -func Bopenw(name string) (*Biobuf, error) { - f, err := os.Create(name) - if err != nil { - return nil, err - } - return &Biobuf{f: f, w: bufio.NewWriter(f)}, nil -} - -func Bopenr(name string) (*Biobuf, error) { - f, err := os.Open(name) - if err != nil { - return nil, err - } - return &Biobuf{f: f, r: bufio.NewReader(f)}, nil -} - -func Binitw(w io.Writer) *Biobuf { - return &Biobuf{w: bufio.NewWriter(w)} -} - -func Binitr(r io.Reader) *Biobuf { - return &Biobuf{r: bufio.NewReader(r)} -} - -func (b *Biobuf) Write(p []byte) (int, error) { - return b.w.Write(p) -} - -func Bwritestring(b *Biobuf, p string) (int, error) { - return b.w.WriteString(p) -} - -func Bseek(b *Biobuf, offset int64, whence int) int64 { - if b.w != nil { - if err := b.w.Flush(); err != nil { - log.Fatalf("writing output: %v", err) - } - } else if b.r != nil { - if whence == 1 { - offset -= int64(b.r.Buffered()) - } - } - off, err := b.f.Seek(offset, whence) - if err != nil { - log.Fatalf("seeking in output: %v", err) - } - if b.r != nil { - b.r.Reset(b.f) - } - return off -} - -func Boffset(b *Biobuf) int64 { - if b.w != nil { - if err := b.w.Flush(); err != nil { - log.Fatalf("writing output: %v", err) - } - } - off, err := b.f.Seek(0, 1) - if err != nil { - log.Fatalf("seeking in output [0, 1]: %v", err) - } - if b.r != nil { - off -= int64(b.r.Buffered()) - } - return off -} - -func (b *Biobuf) Flush() error { - return b.w.Flush() -} - -func Bputc(b *Biobuf, c byte) { - b.w.WriteByte(c) -} - -const Beof = -1 - -func Bread(b *Biobuf, p []byte) int { - n, err := io.ReadFull(b.r, p) - if n == 0 { - if err != nil && err != io.EOF { - n = -1 - } - } - return n -} - -func Bgetc(b *Biobuf) int { - c, err := b.r.ReadByte() - if err != nil { - return -1 - } - return int(c) -} - -func (b *Biobuf) Read(p []byte) (int, error) { - return b.r.Read(p) -} - -func (b *Biobuf) Peek(n int) ([]byte, error) { - return b.r.Peek(n) -} - -func Brdline(b *Biobuf, delim int) string { - s, err := b.r.ReadBytes(byte(delim)) - if err != nil { - log.Fatalf("reading input: %v", err) - } - b.linelen = len(s) - return string(s) -} - -func Blinelen(b *Biobuf) int { - return b.linelen -} - -func Bterm(b *Biobuf) error { - var err error - if b.w != nil { - err = b.w.Flush() - } - err1 := b.f.Close() - if err == nil { - err = err1 - } - return err -} - func envOr(key, value string) string { if x := os.Getenv(key); x != "" { return x diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index d07756071d..205773c7f8 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -31,6 +31,7 @@ package ld import ( + "cmd/internal/bio" "cmd/internal/obj" "encoding/binary" "fmt" @@ -62,7 +63,7 @@ type ArHdr struct { // define them. This is used for the compiler support library // libgcc.a. func hostArchive(name string) { - f, err := obj.Bopenr(name) + f, err := bio.Open(name) if err != nil { if os.IsNotExist(err) { // It's OK if we don't have a libgcc file at all. @@ -73,15 +74,15 @@ func hostArchive(name string) { } Exitf("cannot open file %s: %v", name, err) } - defer obj.Bterm(f) + defer f.Close() magbuf := make([]byte, len(ARMAG)) - if obj.Bread(f, magbuf) != len(magbuf) { + if bio.Bread(f, magbuf) != len(magbuf) { Exitf("file %s too short", name) } var arhdr ArHdr - l := nextar(f, obj.Boffset(f), &arhdr) + l := nextar(f, bio.Boffset(f), &arhdr) if l <= 0 { Exitf("%s missing armap", name) } @@ -117,7 +118,7 @@ func hostArchive(name string) { l = atolwhex(arhdr.size) h := ldobj(f, "libgcc", l, pname, name, ArchiveObj) - obj.Bseek(f, h.off, 0) + bio.Bseek(f, h.off, 0) h.ld(f, h.pkg, h.length, h.pn) } @@ -130,7 +131,7 @@ func hostArchive(name string) { type archiveMap map[string]uint64 // readArmap reads the archive symbol map. -func readArmap(filename string, f *obj.Biobuf, arhdr ArHdr) archiveMap { +func readArmap(filename string, f *bio.Buf, arhdr ArHdr) archiveMap { is64 := arhdr.name == "/SYM64/" wordSize := 4 if is64 { @@ -139,7 +140,7 @@ func readArmap(filename string, f *obj.Biobuf, arhdr ArHdr) archiveMap { l := atolwhex(arhdr.size) contents := make([]byte, l) - if obj.Bread(f, contents) != int(l) { + if bio.Bread(f, contents) != int(l) { Exitf("short read from %s", filename) } diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 027e05d845..8bafaffd7c 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -8,6 +8,7 @@ package ld import ( "bytes" + "cmd/internal/bio" "cmd/internal/obj" "fmt" "os" @@ -26,7 +27,7 @@ func expandpkg(t0 string, pkg string) string { // once the dust settles, try to move some code to // libmach, so that other linkers and ar can share. -func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) { +func ldpkg(f *bio.Buf, pkg string, length int64, filename string, whence int) { var p0, p1 int if Debug['g'] != 0 { @@ -48,7 +49,7 @@ func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) } bdata := make([]byte, length) - if int64(obj.Bread(f, bdata)) != length { + if int64(bio.Bread(f, bdata)) != length { fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) if Debug['u'] != 0 { errorexit() diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index 485599be62..eafc6930d5 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -2,6 +2,7 @@ package ld import ( "bytes" + "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/sys" "encoding/binary" @@ -267,7 +268,7 @@ type ElfSect struct { } type ElfObj struct { - f *obj.Biobuf + f *bio.Buf base int64 // offset in f where ELF begins length int64 // length of ELF is64 int @@ -446,13 +447,13 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) { } } -func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { +func ldelf(f *bio.Buf, pkg string, length int64, pn string) { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) } Ctxt.IncVersion() - base := int32(obj.Boffset(f)) + base := int32(bio.Boffset(f)) var add uint64 var e binary.ByteOrder @@ -475,7 +476,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { var sect *ElfSect var sym ElfSym var symbols []*LSym - if obj.Bread(f, hdrbuf[:]) != len(hdrbuf) { + if bio.Bread(f, hdrbuf[:]) != len(hdrbuf) { goto bad } hdr = new(ElfHdrBytes) @@ -600,7 +601,7 @@ func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { elfobj.nsect = uint(elfobj.shnum) for i := 0; uint(i) < elfobj.nsect; i++ { - if obj.Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { + if bio.Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { goto bad } sect = &elfobj.sect[i] @@ -986,7 +987,7 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { sect.base = make([]byte, sect.size) err = fmt.Errorf("short read") - if obj.Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || obj.Bread(elfobj.f, sect.base) != len(sect.base) { + if bio.Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || bio.Bread(elfobj.f, sect.base) != len(sect.base) { return err } diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go index 9fbb2123af..6376116d04 100644 --- a/src/cmd/link/internal/ld/ldmacho.go +++ b/src/cmd/link/internal/ld/ldmacho.go @@ -1,6 +1,7 @@ package ld import ( + "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/sys" "encoding/binary" @@ -42,7 +43,7 @@ const ( ) type LdMachoObj struct { - f *obj.Biobuf + f *bio.Buf base int64 // off in f where Mach-O begins length int64 // length of Mach-O is64 bool @@ -298,7 +299,7 @@ func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { rel := make([]LdMachoRel, sect.nreloc) n := int(sect.nreloc * 8) buf := make([]byte, n) - if obj.Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || obj.Bread(m.f, buf) != n { + if bio.Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || bio.Bread(m.f, buf) != n { return -1 } var p []byte @@ -344,7 +345,7 @@ func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { n := int(d.nindirectsyms) p := make([]byte, n*4) - if obj.Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || obj.Bread(m.f, p) != len(p) { + if bio.Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || bio.Bread(m.f, p) != len(p) { return -1 } @@ -361,7 +362,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { } strbuf := make([]byte, symtab.strsize) - if obj.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || obj.Bread(m.f, strbuf) != len(strbuf) { + if bio.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || bio.Bread(m.f, strbuf) != len(strbuf) { return -1 } @@ -371,7 +372,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { } n := int(symtab.nsym * uint32(symsize)) symbuf := make([]byte, n) - if obj.Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || obj.Bread(m.f, symbuf) != len(symbuf) { + if bio.Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || bio.Bread(m.f, symbuf) != len(symbuf) { return -1 } sym := make([]LdMachoSym, symtab.nsym) @@ -401,7 +402,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { return 0 } -func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { +func ldmacho(f *bio.Buf, pkg string, length int64, pn string) { var err error var j int var is64 bool @@ -431,8 +432,8 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { var name string Ctxt.IncVersion() - base := obj.Boffset(f) - if obj.Bread(f, hdr[:]) != len(hdr) { + base := bio.Boffset(f) + if bio.Bread(f, hdr[:]) != len(hdr) { goto bad } @@ -455,7 +456,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { if is64 { var tmp [4]uint8 - obj.Bread(f, tmp[:4]) // skip reserved word in header + bio.Bread(f, tmp[:4]) // skip reserved word in header } m = new(LdMachoObj) @@ -493,7 +494,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { m.cmd = make([]LdMachoCmd, ncmd) off = uint32(len(hdr)) cmdp = make([]byte, cmdsz) - if obj.Bread(f, cmdp) != len(cmdp) { + if bio.Bread(f, cmdp) != len(cmdp) { err = fmt.Errorf("reading cmds: %v", err) goto bad } @@ -556,7 +557,7 @@ func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { } dat = make([]byte, c.seg.filesz) - if obj.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || obj.Bread(f, dat) != len(dat) { + if bio.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || bio.Bread(f, dat) != len(dat) { err = fmt.Errorf("cannot load object data: %v", err) goto bad } diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go index ea0c482838..e97e842e7f 100644 --- a/src/cmd/link/internal/ld/ldpe.go +++ b/src/cmd/link/internal/ld/ldpe.go @@ -5,6 +5,7 @@ package ld import ( + "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/sys" "encoding/binary" @@ -117,7 +118,7 @@ type PeSect struct { } type PeObj struct { - f *obj.Biobuf + f *bio.Buf name string base uint32 sect []PeSect @@ -128,14 +129,14 @@ type PeObj struct { snames []byte } -func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { +func ldpe(f *bio.Buf, pkg string, length int64, pn string) { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) } var sect *PeSect Ctxt.IncVersion() - base := int32(obj.Boffset(f)) + base := int32(bio.Boffset(f)) peobj := new(PeObj) peobj.f = f @@ -173,15 +174,15 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { // TODO return error if found .cormeta // load string table - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) - if obj.Bread(f, symbuf[:4]) != 4 { + if bio.Bread(f, symbuf[:4]) != 4 { goto bad } l = Le32(symbuf[:]) peobj.snames = make([]byte, l) - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) - if obj.Bread(f, peobj.snames) != len(peobj.snames) { + bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + if bio.Bread(f, peobj.snames) != len(peobj.snames) { goto bad } @@ -201,10 +202,10 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) peobj.npesym = uint(peobj.fh.NumberOfSymbols) - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) + bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) - if obj.Bread(f, symbuf[:]) != len(symbuf) { + bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) + if bio.Bread(f, symbuf[:]) != len(symbuf) { goto bad } @@ -289,10 +290,10 @@ func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { } r = make([]Reloc, rsect.sh.NumberOfRelocations) - obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) + bio.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { rp = &r[j] - if obj.Bread(f, symbuf[:10]) != 10 { + if bio.Bread(f, symbuf[:10]) != 10 { goto bad } rva := Le32(symbuf[0:]) @@ -465,7 +466,7 @@ func pemap(peobj *PeObj, sect *PeSect) int { if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file return 0 } - if obj.Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || obj.Bread(peobj.f, sect.base) != len(sect.base) { + if bio.Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || bio.Bread(peobj.f, sect.base) != len(sect.base) { return -1 } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 305a3bc0db..789eaef1a5 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -33,6 +33,7 @@ package ld import ( "bufio" "bytes" + "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/sys" "crypto/sha1" @@ -240,7 +241,7 @@ const ( var ( headstring string // buffered output - Bso obj.Biobuf + Bso bio.Buf ) type outBuf struct { @@ -738,13 +739,13 @@ func loadlib() { * look for the next file in an archive. * adapted from libmach. */ -func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 { +func nextar(bp *bio.Buf, off int64, a *ArHdr) int64 { if off&1 != 0 { off++ } - obj.Bseek(bp, off, 0) + bio.Bseek(bp, off, 0) buf := make([]byte, SAR_HDR) - if n := obj.Bread(bp, buf); n < len(buf) { + if n := bio.Bread(bp, buf); n < len(buf) { if n >= 0 { return 0 } @@ -773,25 +774,25 @@ func objfile(lib *Library) { fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg) } Bso.Flush() - f, err := obj.Bopenr(lib.File) + f, err := bio.Open(lib.File) if err != nil { Exitf("cannot open file %s: %v", lib.File, err) } magbuf := make([]byte, len(ARMAG)) - if obj.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { + if bio.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { /* load it as a regular file */ - l := obj.Bseek(f, 0, 2) + l := bio.Bseek(f, 0, 2) - obj.Bseek(f, 0, 0) + bio.Bseek(f, 0, 0) ldobj(f, pkg, l, lib.File, lib.File, FileObj) - obj.Bterm(f) + f.Close() return } /* process __.PKGDEF */ - off := obj.Boffset(f) + off := bio.Boffset(f) var arhdr ArHdr l := nextar(f, off, &arhdr) @@ -807,12 +808,12 @@ func objfile(lib *Library) { } if Buildmode == BuildmodeShared { - before := obj.Boffset(f) + before := bio.Boffset(f) pkgdefBytes := make([]byte, atolwhex(arhdr.size)) - obj.Bread(f, pkgdefBytes) + bio.Bread(f, pkgdefBytes) hash := sha1.Sum(pkgdefBytes) lib.hash = hash[:] - obj.Bseek(f, before, 0) + bio.Bseek(f, before, 0) } off += l @@ -848,11 +849,11 @@ func objfile(lib *Library) { } out: - obj.Bterm(f) + f.Close() } type Hostobj struct { - ld func(*obj.Biobuf, string, int64, string) + ld func(*bio.Buf, string, int64, string) pkg string pn string file string @@ -873,7 +874,7 @@ var internalpkg = []string{ "runtime/msan", } -func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg string, length int64, pn string, file string) *Hostobj { +func ldhostobj(ld func(*bio.Buf, string, int64, string), f *bio.Buf, pkg string, length int64, pn string, file string) *Hostobj { isinternal := false for i := 0; i < len(internalpkg); i++ { if pkg == internalpkg[i] { @@ -904,26 +905,26 @@ func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg s h.pkg = pkg h.pn = pn h.file = file - h.off = obj.Boffset(f) + h.off = bio.Boffset(f) h.length = length return h } func hostobjs() { - var f *obj.Biobuf + var f *bio.Buf var h *Hostobj for i := 0; i < len(hostobj); i++ { h = &hostobj[i] var err error - f, err = obj.Bopenr(h.file) + f, err = bio.Open(h.file) if f == nil { Exitf("cannot reopen %s: %v", h.pn, err) } - obj.Bseek(f, h.off, 0) + bio.Bseek(f, h.off, 0) h.ld(f, h.pkg, h.length, h.pn) - obj.Bterm(f) + f.Close() } } @@ -1265,15 +1266,15 @@ func hostlinkArchArgs() []string { // ldobj loads an input object. If it is a host object (an object // compiled by a non-Go compiler) it returns the Hostobj pointer. If // it is a Go object, it returns nil. -func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) *Hostobj { - eof := obj.Boffset(f) + length +func ldobj(f *bio.Buf, pkg string, length int64, pn string, file string, whence int) *Hostobj { + eof := bio.Boffset(f) + length - start := obj.Boffset(f) - c1 := obj.Bgetc(f) - c2 := obj.Bgetc(f) - c3 := obj.Bgetc(f) - c4 := obj.Bgetc(f) - obj.Bseek(f, start, 0) + start := bio.Boffset(f) + c1 := bio.Bgetc(f) + c2 := bio.Bgetc(f) + c3 := bio.Bgetc(f) + c4 := bio.Bgetc(f) + bio.Bseek(f, start, 0) magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) if magic == 0x7f454c46 { // \x7F E L F @@ -1289,12 +1290,8 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when } /* check the header */ - line := obj.Brdline(f, '\n') + line := bio.Brdline(f, '\n') if line == "" { - if obj.Blinelen(f) > 0 { - Diag("%s: not an object file", pn) - return nil - } Diag("truncated object file: %s", pn) return nil } @@ -1337,28 +1334,28 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when } /* skip over exports and other info -- ends with \n!\n */ - import0 := obj.Boffset(f) + import0 := bio.Boffset(f) c1 = '\n' // the last line ended in \n - c2 = obj.Bgetc(f) - c3 = obj.Bgetc(f) + c2 = bio.Bgetc(f) + c3 = bio.Bgetc(f) for c1 != '\n' || c2 != '!' || c3 != '\n' { c1 = c2 c2 = c3 - c3 = obj.Bgetc(f) - if c3 == obj.Beof { + c3 = bio.Bgetc(f) + if c3 == bio.EOF { Diag("truncated object file: %s", pn) return nil } } - import1 := obj.Boffset(f) + import1 := bio.Boffset(f) - obj.Bseek(f, import0, 0) + bio.Bseek(f, import0, 0) ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n - obj.Bseek(f, import1, 0) + bio.Bseek(f, import1, 0) - LoadObjFile(Ctxt, f, pkg, eof-obj.Boffset(f), pn) + LoadObjFile(Ctxt, f, pkg, eof-bio.Boffset(f), pn) return nil } diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index f0811389d2..d3f9ed3703 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -31,7 +31,7 @@ package ld import ( - "cmd/internal/obj" + "cmd/internal/bio" "cmd/internal/sys" "debug/elf" "fmt" @@ -165,7 +165,7 @@ type Link struct { Headtype int Arch *sys.Arch Debugvlog int32 - Bso *obj.Biobuf + Bso *bio.Buf Windows int32 Goroot string diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index 8a406d17a6..6f177861f0 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -110,6 +110,7 @@ package ld import ( "bufio" "bytes" + "cmd/internal/bio" "cmd/internal/obj" "io" "log" @@ -146,8 +147,8 @@ type objReader struct { file []*LSym } -func LoadObjFile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) { - start := obj.Boffset(f) +func LoadObjFile(ctxt *Link, f *bio.Buf, pkg string, length int64, pn string) { + start := bio.Boffset(f) r := &objReader{ rd: f.Reader(), pkg: pkg, @@ -156,8 +157,8 @@ func LoadObjFile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) dupSym: &LSym{Name: ".dup"}, } r.loadObjFile() - if obj.Boffset(f) != start+length { - log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length)) + if bio.Boffset(f) != start+length { + log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(bio.Boffset(f)), int64(start+length)) } } diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go index b9902a5e5e..bb48f13185 100644 --- a/src/cmd/link/internal/ld/pobj.go +++ b/src/cmd/link/internal/ld/pobj.go @@ -31,6 +31,7 @@ package ld import ( + "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/sys" "flag" @@ -49,7 +50,7 @@ func Ldmain() { Ctxt.Diag = Diag Ctxt.Bso = &Bso - Bso = *obj.Binitw(os.Stdout) + Bso = *bio.BufWriter(os.Stdout) Debug = [128]int{} nerrors = 0 outfile = "" -- cgit v1.3 From 438ce713a1c8e4d24aea547b6fcf907b5dbf0bec Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 6 Apr 2016 23:34:32 -0700 Subject: cmd/link/internal/amd64: remove empty source file Change-Id: I2da012ed996c669db513a462f014c6f3ffa396ee Reviewed-on: https://go-review.googlesource.com/21646 Reviewed-by: Mikio Hara --- src/cmd/link/internal/amd64/z.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/cmd/link/internal/amd64/z.go (limited to 'src') diff --git a/src/cmd/link/internal/amd64/z.go b/src/cmd/link/internal/amd64/z.go deleted file mode 100644 index f70035b9e3..0000000000 --- a/src/cmd/link/internal/amd64/z.go +++ /dev/null @@ -1 +0,0 @@ -package amd64 -- cgit v1.3 From 3b02c5b1b66df9cdb23d5a3243bb37b2c312ea1b Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Thu, 7 Apr 2016 07:29:22 +1000 Subject: runtime: merge lfstack{Pack,Unpack} into one file Merge the remaining lfstack{Pack,Unpack} implemetations into one file. unsafe.Sizeof(uintptr(0)) == 4 is a constant comparison so this branch folds away at compile time. Dmitry confirmed that the upper 17 bits of an address will be zero for a user mode pointer, so there is no need to sign extend on amd64 during unpack, so we can reuse the same implementation as all othe 64 bit archs. Change-Id: I99f589416d8b181ccde5364c9c2e78e4a5efc7f1 Reviewed-on: https://go-review.googlesource.com/21597 Run-TryBot: Dave Cheney TryBot-Result: Gobot Gobot Reviewed-by: Minux Ma --- src/runtime/lfstack.go | 35 +++++++++++++++++++++++++++++++++++ src/runtime/lfstack_32bit.go | 19 ------------------- src/runtime/lfstack_64bit.go | 33 --------------------------------- src/runtime/lfstack_amd64.go | 22 ---------------------- 4 files changed, 35 insertions(+), 74 deletions(-) delete mode 100644 src/runtime/lfstack_32bit.go delete mode 100644 src/runtime/lfstack_64bit.go delete mode 100644 src/runtime/lfstack_amd64.go (limited to 'src') diff --git a/src/runtime/lfstack.go b/src/runtime/lfstack.go index 1261f54d97..8a2d519402 100644 --- a/src/runtime/lfstack.go +++ b/src/runtime/lfstack.go @@ -41,3 +41,38 @@ func lfstackpop(head *uint64) unsafe.Pointer { } } } + +const ( + addrBits = 48 + cntBits = 64 - addrBits + 3 +) + +func lfstackPack(node *lfnode, cnt uintptr) uint64 { + if unsafe.Sizeof(uintptr(0)) == 4 { + // On 32-bit systems, the stored uint64 has a 32-bit pointer and 32-bit count. + return uint64(uintptr(unsafe.Pointer(node)))<<32 | uint64(cnt) + } + // On ppc64, Linux limits the user address space to 46 bits (see + // TASK_SIZE_USER64 in the Linux kernel). This has grown over time, + // so here we allow 48 bit addresses. + // + // On mips64, Linux limits the user address space to 40 bits (see + // TASK_SIZE64 in the Linux kernel). This has grown over time, + // so here we allow 48 bit addresses. + // + // On AMD64, virtual addresses are 48-bit numbers sign extended to 64. + // We shift the address left 16 to eliminate the sign extended part and make + // room in the bottom for the count. + // + // In addition to the 16 bits taken from the top, we can take 3 from the + // bottom, because node must be pointer-aligned, giving a total of 19 bits + // of count. + return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<> 32))) + } + return (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3))) +} diff --git a/src/runtime/lfstack_32bit.go b/src/runtime/lfstack_32bit.go deleted file mode 100644 index 2f59e0212e..0000000000 --- a/src/runtime/lfstack_32bit.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2014 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 386 arm nacl - -package runtime - -import "unsafe" - -// On 32-bit systems, the stored uint64 has a 32-bit pointer and 32-bit count. - -func lfstackPack(node *lfnode, cnt uintptr) uint64 { - return uint64(uintptr(unsafe.Pointer(node)))<<32 | uint64(cnt) -} - -func lfstackUnpack(val uint64) *lfnode { - return (*lfnode)(unsafe.Pointer(uintptr(val >> 32))) -} diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go deleted file mode 100644 index 07c2a141f0..0000000000 --- a/src/runtime/lfstack_64bit.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2014 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 arm64 mips64 mips64le ppc64 ppc64le - -package runtime - -import "unsafe" - -// On ppc64, Linux limits the user address space to 46 bits (see -// TASK_SIZE_USER64 in the Linux kernel). This has grown over time, -// so here we allow 48 bit addresses. -// -// On mips64, Linux limits the user address space to 40 bits (see -// TASK_SIZE64 in the Linux kernel). This has grown over time, -// so here we allow 48 bit addresses. -// -// In addition to the 16 bits taken from the top, we can take 3 from the -// bottom, because node must be pointer-aligned, giving a total of 19 bits -// of count. -const ( - addrBits = 48 - cntBits = 64 - addrBits + 3 -) - -func lfstackPack(node *lfnode, cnt uintptr) uint64 { - return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<> cntBits << 3))) -} diff --git a/src/runtime/lfstack_amd64.go b/src/runtime/lfstack_amd64.go deleted file mode 100644 index 6397e1d47f..0000000000 --- a/src/runtime/lfstack_amd64.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 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 runtime - -import "unsafe" - -// On AMD64, virtual addresses are 48-bit numbers sign extended to 64. -// We shift the address left 16 to eliminate the sign extended part and make -// room in the bottom for the count. -// In addition to the 16 bits taken from the top, we can take 3 from the -// bottom, because node must be pointer-aligned, giving a total of 19 bits -// of count. - -func lfstackPack(node *lfnode, cnt uintptr) uint64 { - return uint64(uintptr(unsafe.Pointer(node)))<<16 | uint64(cnt&(1<<19-1)) -} - -func lfstackUnpack(val uint64) *lfnode { - return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> 19 << 3))) -} -- cgit v1.3 From 121c434f7add815c3147b01a097a8998018bcc6b Mon Sep 17 00:00:00 2001 From: Richard Miller Date: Wed, 6 Apr 2016 18:58:22 +0100 Subject: runtime/pprof: make TestBlockProfile less timing dependent The test for profiling of channel blocking is timing dependent, and in particular the blockSelectRecvAsync case can fail on a slow builder (plan9_arm) when many tests are run in parallel. The child goroutine sleeps for a fixed period so the parent can be observed to block in a select call reading from the child; but if the OS process running the parent goroutine is delayed long enough, the child may wake again before the parent has reached the blocking point. By repeating the test three times, the likelihood of a blocking event is increased. Fixes #15096 Change-Id: I2ddb9576a83408d06b51ded682bf8e71e53ce59e Reviewed-on: https://go-review.googlesource.com/21604 Reviewed-by: Dmitry Vyukov Run-TryBot: Dmitry Vyukov TryBot-Result: Gobot Gobot --- src/runtime/pprof/pprof_test.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 23bc72c1e4..8b2f3d5291 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -530,15 +530,20 @@ func blockChanClose() { } func blockSelectRecvAsync() { + const numTries = 3 c := make(chan bool, 1) c2 := make(chan bool, 1) go func() { - time.Sleep(blockDelay) - c <- true + for i := 0; i < numTries; i++ { + time.Sleep(blockDelay) + c <- true + } }() - select { - case <-c: - case <-c2: + for i := 0; i < numTries; i++ { + select { + case <-c: + case <-c2: + } } } -- cgit v1.3 From 8448d3aace7f26bd6eca14e8b89c5a981c2ab9d3 Mon Sep 17 00:00:00 2001 From: Alexandru Moșoi Date: Tue, 5 Apr 2016 23:32:49 +0200 Subject: cmd/compile: fold CMPconst and SHR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fold the comparison when the SHR result is small. Useful for: - murmur mix like hashing where higher bits are desirable, i.e. hash = uint32(i * C) >> 18 - integer log2 via DeBruijn sequence: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn Change-Id: If70ae18cb86f4cc83ab6213f88ced03cc4986156 Reviewed-on: https://go-review.googlesource.com/21514 Run-TryBot: Alexandru Moșoi TryBot-Result: Gobot Gobot Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/gen/AMD64.rules | 2 ++ src/cmd/compile/internal/ssa/rewriteAMD64.go | 32 ++++++++++++++++++++++++++++ test/checkbce.go | 12 +++++++++++ 3 files changed, 46 insertions(+) (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index b37720eb39..d7f361dc2e 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -1101,6 +1101,8 @@ (CMPQconst (MOVBQZX _) [c]) && 0xFF < c -> (FlagLT_ULT) (CMPQconst (MOVWQZX _) [c]) && 0xFFFF < c -> (FlagLT_ULT) (CMPQconst (MOVLQZX _) [c]) && 0xFFFFFFFF < c -> (FlagLT_ULT) +(CMPLconst (SHRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1< (FlagLT_ULT) +(CMPQconst (SHRQconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 64 && (1< (FlagLT_ULT) (CMPQconst (ANDQconst _ [m]) [n]) && 0 <= m && m < n -> (FlagLT_ULT) (CMPLconst (ANDLconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT_ULT) (CMPWconst (ANDWconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < int16(n) -> (FlagLT_ULT) diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index a1d1e4edd9..34a393bbc5 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -2869,6 +2869,22 @@ func rewriteValueAMD64_OpAMD64CMPLconst(v *Value, config *Config) bool { v.reset(OpAMD64FlagGT_UGT) return true } + // match: (CMPLconst (SHRLconst _ [c]) [n]) + // cond: 0 <= n && 0 < c && c <= 32 && (1<>27]) + useInt(b[uint64(i*0x07C4ACDD)>>58]) + useInt(a[uint(i*0x07C4ACDD)>>59]) + + // The following bounds should removed as they can overflow. + useInt(a[uint32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$" + useInt(b[uint64(i*0x106297f105d0cc86)>>57]) // ERROR "Found IsInBounds$" + useInt(a[int32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$" + useInt(b[int64(i*0x106297f105d0cc86)>>57]) // ERROR "Found IsInBounds$" +} + func g1(a []int) { for i := range a { a[i] = i -- cgit v1.3 From 9cc9e95b288648d796d92f0b92cb713b35f20062 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Thu, 7 Apr 2016 14:05:06 +0000 Subject: Revert "runtime: merge lfstack{Pack,Unpack} into one file" This broke solaris, which apparently does use the upper 17 bits of the address space. This reverts commit 3b02c5b1b66df9cdb23d5a3243bb37b2c312ea1b. Change-Id: Iedfe54abd0384960845468205f20191a97751c0b Reviewed-on: https://go-review.googlesource.com/21652 Reviewed-by: Dave Cheney --- src/runtime/lfstack.go | 35 ----------------------------------- src/runtime/lfstack_32bit.go | 19 +++++++++++++++++++ src/runtime/lfstack_64bit.go | 33 +++++++++++++++++++++++++++++++++ src/runtime/lfstack_amd64.go | 22 ++++++++++++++++++++++ 4 files changed, 74 insertions(+), 35 deletions(-) create mode 100644 src/runtime/lfstack_32bit.go create mode 100644 src/runtime/lfstack_64bit.go create mode 100644 src/runtime/lfstack_amd64.go (limited to 'src') diff --git a/src/runtime/lfstack.go b/src/runtime/lfstack.go index 8a2d519402..1261f54d97 100644 --- a/src/runtime/lfstack.go +++ b/src/runtime/lfstack.go @@ -41,38 +41,3 @@ func lfstackpop(head *uint64) unsafe.Pointer { } } } - -const ( - addrBits = 48 - cntBits = 64 - addrBits + 3 -) - -func lfstackPack(node *lfnode, cnt uintptr) uint64 { - if unsafe.Sizeof(uintptr(0)) == 4 { - // On 32-bit systems, the stored uint64 has a 32-bit pointer and 32-bit count. - return uint64(uintptr(unsafe.Pointer(node)))<<32 | uint64(cnt) - } - // On ppc64, Linux limits the user address space to 46 bits (see - // TASK_SIZE_USER64 in the Linux kernel). This has grown over time, - // so here we allow 48 bit addresses. - // - // On mips64, Linux limits the user address space to 40 bits (see - // TASK_SIZE64 in the Linux kernel). This has grown over time, - // so here we allow 48 bit addresses. - // - // On AMD64, virtual addresses are 48-bit numbers sign extended to 64. - // We shift the address left 16 to eliminate the sign extended part and make - // room in the bottom for the count. - // - // In addition to the 16 bits taken from the top, we can take 3 from the - // bottom, because node must be pointer-aligned, giving a total of 19 bits - // of count. - return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<> 32))) - } - return (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3))) -} diff --git a/src/runtime/lfstack_32bit.go b/src/runtime/lfstack_32bit.go new file mode 100644 index 0000000000..2f59e0212e --- /dev/null +++ b/src/runtime/lfstack_32bit.go @@ -0,0 +1,19 @@ +// Copyright 2014 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 386 arm nacl + +package runtime + +import "unsafe" + +// On 32-bit systems, the stored uint64 has a 32-bit pointer and 32-bit count. + +func lfstackPack(node *lfnode, cnt uintptr) uint64 { + return uint64(uintptr(unsafe.Pointer(node)))<<32 | uint64(cnt) +} + +func lfstackUnpack(val uint64) *lfnode { + return (*lfnode)(unsafe.Pointer(uintptr(val >> 32))) +} diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go new file mode 100644 index 0000000000..07c2a141f0 --- /dev/null +++ b/src/runtime/lfstack_64bit.go @@ -0,0 +1,33 @@ +// Copyright 2014 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 arm64 mips64 mips64le ppc64 ppc64le + +package runtime + +import "unsafe" + +// On ppc64, Linux limits the user address space to 46 bits (see +// TASK_SIZE_USER64 in the Linux kernel). This has grown over time, +// so here we allow 48 bit addresses. +// +// On mips64, Linux limits the user address space to 40 bits (see +// TASK_SIZE64 in the Linux kernel). This has grown over time, +// so here we allow 48 bit addresses. +// +// In addition to the 16 bits taken from the top, we can take 3 from the +// bottom, because node must be pointer-aligned, giving a total of 19 bits +// of count. +const ( + addrBits = 48 + cntBits = 64 - addrBits + 3 +) + +func lfstackPack(node *lfnode, cnt uintptr) uint64 { + return uint64(uintptr(unsafe.Pointer(node)))<<(64-addrBits) | uint64(cnt&(1<> cntBits << 3))) +} diff --git a/src/runtime/lfstack_amd64.go b/src/runtime/lfstack_amd64.go new file mode 100644 index 0000000000..6397e1d47f --- /dev/null +++ b/src/runtime/lfstack_amd64.go @@ -0,0 +1,22 @@ +// Copyright 2014 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 runtime + +import "unsafe" + +// On AMD64, virtual addresses are 48-bit numbers sign extended to 64. +// We shift the address left 16 to eliminate the sign extended part and make +// room in the bottom for the count. +// In addition to the 16 bits taken from the top, we can take 3 from the +// bottom, because node must be pointer-aligned, giving a total of 19 bits +// of count. + +func lfstackPack(node *lfnode, cnt uintptr) uint64 { + return uint64(uintptr(unsafe.Pointer(node)))<<16 | uint64(cnt&(1<<19-1)) +} + +func lfstackUnpack(val uint64) *lfnode { + return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> 19 << 3))) +} -- cgit v1.3 From 95a895df0c64b0cd1283c4cf7794d491427d765c Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Thu, 7 Apr 2016 10:07:10 -0400 Subject: go/types: make Identical(nil, T) == Identical(T, nil) Fixes #15173 Change-Id: I353756f7bc36db0d2b24d40c80771481b7b18f6b Reviewed-on: https://go-review.googlesource.com/21585 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick --- src/go/types/api_test.go | 17 +++++++++++++++++ src/go/types/predicates.go | 2 ++ 2 files changed, 19 insertions(+) (limited to 'src') diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index c2feed3813..9573d80a17 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -1042,3 +1042,20 @@ func f() { } } } + +func TestIdentical_issue15173(t *testing.T) { + // Identical should allow nil arguments and be symmetric. + for _, test := range []struct { + x, y Type + want bool + }{ + {Typ[Int], Typ[Int], true}, + {Typ[Int], nil, false}, + {nil, Typ[Int], false}, + {nil, nil, true}, + } { + if got := Identical(test.x, test.y); got != test.want { + t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got) + } + } +} diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 993c6d290b..5509069fb6 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -277,6 +277,8 @@ func identical(x, y Type, p *ifacePair) bool { return x.obj == y.obj } + case nil: + default: unreachable() } -- cgit v1.3 From 7da42d75975044df37aa3aa2499623e2084a12df Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Sun, 20 Mar 2016 21:34:48 -0400 Subject: sync/atomic: add s390x implementations of atomic functions Load and store instructions are atomic on s390x. Change-Id: I33c641a75954f4fbd301b11a467cb57872038880 Reviewed-on: https://go-review.googlesource.com/20947 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/sync/atomic/asm_s390x.s | 143 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/sync/atomic/asm_s390x.s (limited to 'src') diff --git a/src/sync/atomic/asm_s390x.s b/src/sync/atomic/asm_s390x.s new file mode 100644 index 0000000000..b5389be38f --- /dev/null +++ b/src/sync/atomic/asm_s390x.s @@ -0,0 +1,143 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·SwapInt32(SB),NOSPLIT,$0-20 + BR ·SwapUint32(SB) + +TEXT ·SwapUint32(SB),NOSPLIT,$0-20 + MOVD addr+0(FP), R3 + MOVWZ new+8(FP), R4 + MOVWZ (R3), R5 +repeat: + CS R5, R4, (R3) // if (R3)==R5 then (R3)=R4 else R5=(R3) + BNE repeat + MOVW R5, old+16(FP) + RET + +TEXT ·SwapInt64(SB),NOSPLIT,$0-24 + BR ·SwapUint64(SB) + +TEXT ·SwapUint64(SB),NOSPLIT,$0-24 + MOVD addr+0(FP), R3 + MOVD new+8(FP), R4 + MOVD (R3), R5 +repeat: + CSG R5, R4, (R3) // if (R3)==R5 then (R3)=R4 else R5=(R3) + BNE repeat + MOVD R5, old+16(FP) + RET + +TEXT ·SwapUintptr(SB),NOSPLIT,$0-24 + BR ·SwapUint64(SB) + +TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0-17 + BR ·CompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-17 + MOVD ptr+0(FP), R3 + MOVWZ old+8(FP), R4 + MOVWZ new+12(FP), R5 + CS R4, R5, 0(R3) // if R4==(R3) then (R3)=R5 else R4=(R3) + BNE cas_fail + MOVB $1, ret+16(FP) + RET +cas_fail: + MOVB $0, ret+16(FP) + RET + +TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0-25 + BR ·CompareAndSwapUint64(SB) + +TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0-25 + BR ·CompareAndSwapUint64(SB) + +TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-25 + MOVD ptr+0(FP), R3 + MOVD old+8(FP), R4 + MOVD new+16(FP), R5 + CSG R4, R5, 0(R3) // if R4==(R3) then (R3)=R5 else R4=(R3) + BNE cas64_fail + MOVB $1, ret+24(FP) + RET +cas64_fail: + MOVB $0, ret+24(FP) + RET + +TEXT ·AddInt32(SB),NOSPLIT,$0-20 + BR ·AddUint32(SB) + +TEXT ·AddUint32(SB),NOSPLIT,$0-20 + MOVD ptr+0(FP), R4 + MOVWZ delta+8(FP), R5 + MOVWZ (R4), R3 +repeat: + ADD R3, R5, R6 + CS R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4) + BNE repeat + MOVW R6, ret+16(FP) + RET + +TEXT ·AddUintptr(SB),NOSPLIT,$0-24 + BR ·AddUint64(SB) + +TEXT ·AddInt64(SB),NOSPLIT,$0-24 + BR ·AddUint64(SB) + +TEXT ·AddUint64(SB),NOSPLIT,$0-24 + MOVD ptr+0(FP), R4 + MOVD delta+8(FP), R5 + MOVD (R4), R3 +repeat: + ADD R3, R5, R6 + CSG R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4) + BNE repeat + MOVD R6, ret+16(FP) + RET + +TEXT ·LoadInt32(SB),NOSPLIT,$0-12 + BR ·LoadUint32(SB) + +TEXT ·LoadUint32(SB),NOSPLIT,$0-12 + MOVD addr+0(FP), R3 + MOVW 0(R3), R4 + MOVW R4, val+8(FP) + RET + +TEXT ·LoadInt64(SB),NOSPLIT,$0-16 + BR ·LoadUint64(SB) + +TEXT ·LoadUint64(SB),NOSPLIT,$0-16 + MOVD addr+0(FP), R3 + MOVD 0(R3), R4 + MOVD R4, val+8(FP) + RET + +TEXT ·LoadUintptr(SB),NOSPLIT,$0-16 + BR ·LoadPointer(SB) + +TEXT ·LoadPointer(SB),NOSPLIT,$0-16 + BR ·LoadUint64(SB) + +TEXT ·StoreInt32(SB),NOSPLIT,$0-12 + BR ·StoreUint32(SB) + +TEXT ·StoreUint32(SB),NOSPLIT,$0-12 + MOVD ptr+0(FP), R3 + MOVW val+8(FP), R4 + MOVW R4, 0(R3) + RET + +TEXT ·StoreInt64(SB),NOSPLIT,$0-16 + BR ·StoreUint64(SB) + +TEXT ·StoreUint64(SB),NOSPLIT,$0-16 + MOVD addr+0(FP), R3 + MOVD val+8(FP), R4 + MOVD R4, 0(R3) + RET + +TEXT ·StoreUintptr(SB),NOSPLIT,$0-16 + BR ·StoreUint64(SB) -- cgit v1.3 From 1e7c61d8c3fc0916b73cb7411afa45e99b50aac4 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Sun, 20 Mar 2016 21:49:52 -0400 Subject: math/big: add s390x function implementations Change-Id: I2aadc885d6330460e494c687757f07c5e006f3b0 Reviewed-on: https://go-review.googlesource.com/20937 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/math/big/arith_s390x.s | 565 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 565 insertions(+) create mode 100644 src/math/big/arith_s390x.s (limited to 'src') diff --git a/src/math/big/arith_s390x.s b/src/math/big/arith_s390x.s new file mode 100644 index 0000000000..a691970810 --- /dev/null +++ b/src/math/big/arith_s390x.s @@ -0,0 +1,565 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !math_big_pure_go,s390x + +#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +TEXT ·mulWW(SB),NOSPLIT,$0 + MOVD x+0(FP), R3 + MOVD y+8(FP), R4 + MULHDU R3, R4 + MOVD R10, z1+16(FP) + MOVD R11, z0+24(FP) + RET + +// func divWW(x1, x0, y Word) (q, r Word) +TEXT ·divWW(SB),NOSPLIT,$0 + MOVD x1+0(FP), R10 + MOVD x0+8(FP), R11 + MOVD y+16(FP), R5 + WORD $0xb98700a5 // dlgr r10,r5 + MOVD R11, q+24(FP) + MOVD R10, r+32(FP) + RET + +// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11 +// func addVV(z, x, y []Word) (c Word) +TEXT ·addVV(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R2 + + MOVD $0, R4 // c = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v1 // if n < 0 goto v1 + +U1: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + ADDC R4, R4 // restore CF + MOVD 0(R9)(R10*1), R11 + ADDE R11, R5 + MOVD 8(R9)(R10*1), R11 + ADDE R11, R6 + MOVD 16(R9)(R10*1), R11 + ADDE R11, R7 + MOVD 24(R9)(R10*1), R11 + ADDE R11, R1 + MOVD R0, R4 + ADDE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + + ADD $32, R10 // i += 4 + SUB $4, R3 // n -= 4 + BGE U1 // if n >= 0 goto U1 + +v1: ADD $4, R3 // n += 4 + BLE E1 // if n <= 0 goto E1 + +L1: // n > 0 + ADDC R4, R4 // restore CF + MOVD 0(R8)(R10*1), R5 + MOVD 0(R9)(R10*1), R11 + ADDE R11, R5 + MOVD R5, 0(R2)(R10*1) + MOVD R0, R4 + ADDE R4, R4 // save CF + NEG R4, R4 + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L1 // if n > 0 goto L1 + +E1: NEG R4, R4 + MOVD R4, c+72(FP) // return c + RET + +// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11 +// func subVV(z, x, y []Word) (c Word) +// (same as addVV except for SUBC/SUBE instead of ADDC/ADDE and label names) +TEXT ·subVV(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z+0(FP), R2 + + MOVD $0, R4 // c = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v1 // if n < 0 goto v1 + +U1: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + MOVD R0, R11 + SUBC R4, R11 // restore CF + MOVD 0(R9)(R10*1), R11 + SUBE R11, R5 + MOVD 8(R9)(R10*1), R11 + SUBE R11, R6 + MOVD 16(R9)(R10*1), R11 + SUBE R11, R7 + MOVD 24(R9)(R10*1), R11 + SUBE R11, R1 + MOVD R0, R4 + SUBE R4, R4 // save CF + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + + ADD $32, R10 // i += 4 + SUB $4, R3 // n -= 4 + BGE U1 // if n >= 0 goto U1 + +v1: ADD $4, R3 // n += 4 + BLE E1 // if n <= 0 goto E1 + +L1: // n > 0 + MOVD R0, R11 + SUBC R4, R11 // restore CF + MOVD 0(R8)(R10*1), R5 + MOVD 0(R9)(R10*1), R11 + SUBE R11, R5 + MOVD R5, 0(R2)(R10*1) + MOVD R0, R4 + SUBE R4, R4 // save CF + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L1 // if n > 0 goto L1 + +E1: NEG R4, R4 + MOVD R4, c+72(FP) // return c + RET + + +// func addVW(z, x []Word, y Word) (c Word) +TEXT ·addVW(SB),NOSPLIT,$0 +//DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R4 // c = y + MOVD z+0(FP), R2 + MOVD $0, R0 // make sure it's 0 + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v4 // if n < 4 goto v4 + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + ADDC R4, R5 + ADDE R0, R6 + ADDE R0, R7 + ADDE R0, R1 + ADDE R0, R0 + MOVD R0, R4 // save CF + SUB R0, R0 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + ADD $32, R10 // i += 4 -> i +=32 + SUB $4, R3 // n -= 4 + BGE U4 // if n >= 0 goto U4 + +v4: ADD $4, R3 // n += 4 + BLE E4 // if n <= 0 goto E4 + +L4: // n > 0 + MOVD 0(R8)(R10*1), R5 + ADDC R4, R5 + ADDE R0, R0 + MOVD R0, R4 // save CF + SUB R0, R0 + MOVD R5, 0(R2)(R10*1) + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L4 // if n > 0 goto L4 + +E4: MOVD R4, c+56(FP) // return c + + RET + +//DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) +// func subVW(z, x []Word, y Word) (c Word) +// (same as addVW except for SUBC/SUBE instead of ADDC/ADDE and label names) +TEXT ·subVW(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R3 + MOVD x+24(FP), R8 + MOVD y+48(FP), R4 // c = y + MOVD z+0(FP), R2 + MOVD $0, R0 // make sure it's 0 + MOVD $0, R10 // i = 0 + + // s/JL/JMP/ below to disable the unrolled loop + SUB $4, R3 // n -= 4 + BLT v4 // if n < 4 goto v4 + +U4: // n >= 0 + // regular loop body unrolled 4x + MOVD 0(R8)(R10*1), R5 + MOVD 8(R8)(R10*1), R6 + MOVD 16(R8)(R10*1), R7 + MOVD 24(R8)(R10*1), R1 + SUBC R4, R5 //SLGR -> SUBC + SUBE R0, R6 //SLBGR -> SUBE + SUBE R0, R7 + SUBE R0, R1 + SUBE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + MOVD R6, 8(R2)(R10*1) + MOVD R7, 16(R2)(R10*1) + MOVD R1, 24(R2)(R10*1) + + ADD $32, R10 // i += 4 -> i +=32 + SUB $4, R3 // n -= 4 + BGE U4 // if n >= 0 goto U4 + +v4: ADD $4, R3 // n += 4 + BLE E4 // if n <= 0 goto E4 + +L4: // n > 0 + MOVD 0(R8)(R10*1), R5 + SUBC R4, R5 + SUBE R4, R4 // save CF + NEG R4, R4 + MOVD R5, 0(R2)(R10*1) + + ADD $8, R10 // i++ + SUB $1, R3 // n-- + BGT L4 // if n > 0 goto L4 + +E4: MOVD R4, c+56(FP) // return c + + RET + +// func shlVU(z, x []Word, s uint) (c Word) +TEXT ·shlVU(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R5 + SUB $1, R5 // n-- + BLT X8b // n < 0 (n <= 0) + + // n > 0 + MOVD s+48(FP), R4 + CMPBEQ R0, R4, Z80 //handle 0 case beq + MOVD $64, R6 + CMPBEQ R6, R4, Z864 //handle 64 case beq + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + SUB R4, R6, R7 + MOVD (R8)(R5*1), R10 // w1 = x[i-1] + SRD R7, R10, R3 + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E8 + + // i < n-1 +L8: MOVD R10, R3 // w = w1 + MOVD -8(R8)(R5*1), R10 // w1 = x[i+1] + + SLD R4, R3 // w<>ŝ + SRD R7, R10, R6 + OR R6, R3 + MOVD R3, (R2)(R5*1) // z[i] = w<>ŝ + SUB $8, R5 // i-- + +E8: CMPBGT R5, R0, L8 // i < n-1 + + // i >= n-1 +X8a: SLD R4, R10 // w1<= n-1 + MOVD R10, (R2)(R5*1) + RET + +Z864: MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + MOVD (R8)(R5*1), R3 // w1 = x[n-1] + MOVD R3, c+56(FP) // z[i] = x[n-1] + + BR E864 + + // i < n-1 +L864: MOVD -8(R8)(R5*1), R3 + + MOVD R3, (R2)(R5*1) // z[i] = x[n-1] + SUB $8, R5 // i-- + +E864: CMPBGT R5, R0, L864 // i < n-1 + + MOVD R0, (R2) // z[n-1] = 0 + RET + + +// CX = R4, r8 = r8, r10 = r2 , r11 = r5, DX = r3, AX = r10 , BX = R1 , 64-count = r7 (R0 set to 0) temp = R6 +// func shrVU(z, x []Word, s uint) (c Word) +TEXT ·shrVU(SB),NOSPLIT,$0 + MOVD z_len+8(FP), R5 + SUB $1, R5 // n-- + BLT X9b // n < 0 (n <= 0) + + // n > 0 + MOVD s+48(FP), R4 + CMPBEQ R0, R4, ZB0 //handle 0 case beq + MOVD $64, R6 + CMPBEQ R6, R4, ZB64 //handle 64 case beq + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + SUB R4, R6, R7 + MOVD (R8), R10 // w1 = x[0] + SLD R7, R10, R3 + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E9 + + // i < n-1 +L9: MOVD R10, R3 // w = w1 + MOVD 8(R8)(R1*1), R10 // w1 = x[i+1] + + SRD R4, R3 // w>>s | w1<>s | w1<= n-1 +X9a: SRD R4, R10 // w1>>s + MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s + RET + +X9b: MOVD R0, c+56(FP) + RET + +ZB0: MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + + MOVD (R8), R10 // w1 = x[0] + MOVD $0, R3 // R10 << 64 + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E9Z + + // i < n-1 +L9Z: MOVD R10, R3 // w = w1 + MOVD 8(R8)(R1*1), R10 // w1 = x[i+1] + + MOVD R3, (R2)(R1*1) // z[i] = w>>s | w1<= n-1 + MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s + RET + +ZB64: MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + SLD $3, R5 // n = n*8 + MOVD (R8), R3 // w1 = x[0] + MOVD R3, c+56(FP) + + MOVD $0, R1 // i = 0 + BR E964 + + // i < n-1 +L964: MOVD 8(R8)(R1*1), R3 // w1 = x[i+1] + + MOVD R3, (R2)(R1*1) // z[i] = w>>s | w1<= n-1 + MOVD $0, R10 // w1>>s + MOVD R10, (R2)(R5*1) // z[n-1] = w1>>s + RET + +// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, DX = r3, AX = r6 , BX = R1 , (R0 set to 0) + use R11 + use R7 for i +// func mulAddVWW(z, x []Word, y, r Word) (c Word) +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD r+56(FP), R4 // c = r + MOVD z_len+8(FP), R5 + MOVD $0, R1 // i = 0 + MOVD $0, R7 // i*8 = 0 + MOVD $0, R0 // make sure it's zero + BR E5 + +L5: MOVD (R8)(R1*1), R6 + MULHDU R9, R6 + ADDC R4, R11 //add to low order bits + ADDE R0, R6 + MOVD R11, (R2)(R1*1) + MOVD R6, R4 + ADD $8, R1 // i*8 + 8 + ADD $1, R7 // i++ + +E5: CMPBLT R7, R5, L5 // i < n + + MOVD R4, c+64(FP) + RET + +// func addMulVVW(z, x []Word, y Word) (c Word) +// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1 , (R0 set to 0) + use R11 + use R7 for i +TEXT ·addMulVVW(SB),NOSPLIT,$0 + MOVD z+0(FP), R2 + MOVD x+24(FP), R8 + MOVD y+48(FP), R9 + MOVD z_len+8(FP), R5 + + MOVD $0, R1 // i*8 = 0 + MOVD $0, R7 // i = 0 + MOVD $0, R0 // make sure it's zero + MOVD $0, R4 // c = 0 + + MOVD R5, R12 + AND $-2, R12 + CMPBGE R5, $2, A6 + BR E6 + +A6: MOVD (R8)(R1*1), R6 + MULHDU R9, R6 + MOVD (R2)(R1*1), R10 + ADDC R10, R11 //add to low order bits + ADDE R0, R6 + ADDC R4, R11 + ADDE R0, R6 + MOVD R6, R4 + MOVD R11, (R2)(R1*1) + + MOVD (8)(R8)(R1*1), R6 + MULHDU R9, R6 + MOVD (8)(R2)(R1*1), R10 + ADDC R10, R11 //add to low order bits + ADDE R0, R6 + ADDC R4, R11 + ADDE R0, R6 + MOVD R6, R4 + MOVD R11, (8)(R2)(R1*1) + + ADD $16, R1 // i*8 + 8 + ADD $2, R7 // i++ + + CMPBLT R7, R12, A6 + BR E6 + +L6: MOVD (R8)(R1*1), R6 + MULHDU R9, R6 + MOVD (R2)(R1*1), R10 + ADDC R10, R11 //add to low order bits + ADDE R0, R6 + ADDC R4, R11 + ADDE R0, R6 + MOVD R6, R4 + MOVD R11, (R2)(R1*1) + + ADD $8, R1 // i*8 + 8 + ADD $1, R7 // i++ + +E6: CMPBLT R7, R5, L6 // i < n + + MOVD R4, c+56(FP) + RET + +// func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) +// CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1(*8) , (R0 set to 0) + use R11 + use R7 for i +TEXT ·divWVW(SB),NOSPLIT,$0 + MOVD z+0(FP), R2 + MOVD xn+24(FP), R10 // r = xn + MOVD x+32(FP), R8 + MOVD y+56(FP), R9 + MOVD z_len+8(FP), R7 // i = z + SLD $3, R7, R1 // i*8 + MOVD $0, R0 // make sure it's zero + BR E7 + +L7: MOVD (R8)(R1*1), R11 + WORD $0xB98700A9 //DLGR R10,R9 + MOVD R11, (R2)(R1*1) + +E7: SUB $1, R7 // i-- + SUB $8, R1 + BGE L7 // i >= 0 + + MOVD R10, r+64(FP) + RET + +// func bitLen(x Word) (n int) +TEXT ·bitLen(SB),NOSPLIT,$0 + MOVD x+0(FP), R2 + WORD $0xb9830022 // FLOGR R2,R2 + MOVD $64, R3 + SUB R2, R3 + MOVD R3, n+8(FP) + RET -- cgit v1.3 From 187afdebef7953295189d4531e7dccdc0cb64500 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Mon, 4 Apr 2016 19:28:15 +0300 Subject: math/big: re-use memory in Int.GCD This improves TLS handshake performance. benchmark old ns/op new ns/op delta BenchmarkGCD10x10/WithoutXY-4 965 968 +0.31% BenchmarkGCD10x10/WithXY-4 1813 1391 -23.28% BenchmarkGCD10x100/WithoutXY-4 1093 1075 -1.65% BenchmarkGCD10x100/WithXY-4 2348 1676 -28.62% BenchmarkGCD10x1000/WithoutXY-4 1569 1565 -0.25% BenchmarkGCD10x1000/WithXY-4 4262 3242 -23.93% BenchmarkGCD10x10000/WithoutXY-4 6069 6066 -0.05% BenchmarkGCD10x10000/WithXY-4 12123 11331 -6.53% BenchmarkGCD10x100000/WithoutXY-4 52664 52610 -0.10% BenchmarkGCD10x100000/WithXY-4 97494 95649 -1.89% BenchmarkGCD100x100/WithoutXY-4 5244 5228 -0.31% BenchmarkGCD100x100/WithXY-4 22572 18630 -17.46% BenchmarkGCD100x1000/WithoutXY-4 6143 6233 +1.47% BenchmarkGCD100x1000/WithXY-4 24652 19357 -21.48% BenchmarkGCD100x10000/WithoutXY-4 15725 15804 +0.50% BenchmarkGCD100x10000/WithXY-4 60552 55973 -7.56% BenchmarkGCD100x100000/WithoutXY-4 107008 107853 +0.79% BenchmarkGCD100x100000/WithXY-4 349597 340994 -2.46% BenchmarkGCD1000x1000/WithoutXY-4 63785 64434 +1.02% BenchmarkGCD1000x1000/WithXY-4 373186 334035 -10.49% BenchmarkGCD1000x10000/WithoutXY-4 78038 78241 +0.26% BenchmarkGCD1000x10000/WithXY-4 543692 507034 -6.74% BenchmarkGCD1000x100000/WithoutXY-4 205607 207727 +1.03% BenchmarkGCD1000x100000/WithXY-4 2488113 2415323 -2.93% BenchmarkGCD10000x10000/WithoutXY-4 1731340 1714992 -0.94% BenchmarkGCD10000x10000/WithXY-4 10601046 7111329 -32.92% BenchmarkGCD10000x100000/WithoutXY-4 2239155 2212173 -1.21% BenchmarkGCD10000x100000/WithXY-4 30097040 26538887 -11.82% BenchmarkGCD100000x100000/WithoutXY-4 119845326 119863916 +0.02% BenchmarkGCD100000x100000/WithXY-4 768006543 426795966 -44.43% benchmark old allocs new allocs delta BenchmarkGCD10x10/WithoutXY-4 5 5 +0.00% BenchmarkGCD10x10/WithXY-4 17 9 -47.06% BenchmarkGCD10x100/WithoutXY-4 6 6 +0.00% BenchmarkGCD10x100/WithXY-4 21 9 -57.14% BenchmarkGCD10x1000/WithoutXY-4 6 6 +0.00% BenchmarkGCD10x1000/WithXY-4 30 12 -60.00% BenchmarkGCD10x10000/WithoutXY-4 6 6 +0.00% BenchmarkGCD10x10000/WithXY-4 26 12 -53.85% BenchmarkGCD10x100000/WithoutXY-4 6 6 +0.00% BenchmarkGCD10x100000/WithXY-4 28 12 -57.14% BenchmarkGCD100x100/WithoutXY-4 5 5 +0.00% BenchmarkGCD100x100/WithXY-4 183 61 -66.67% BenchmarkGCD100x1000/WithoutXY-4 8 8 +0.00% BenchmarkGCD100x1000/WithXY-4 170 47 -72.35% BenchmarkGCD100x10000/WithoutXY-4 8 8 +0.00% BenchmarkGCD100x10000/WithXY-4 200 67 -66.50% BenchmarkGCD100x100000/WithoutXY-4 8 8 +0.00% BenchmarkGCD100x100000/WithXY-4 188 65 -65.43% BenchmarkGCD1000x1000/WithoutXY-4 5 5 +0.00% BenchmarkGCD1000x1000/WithXY-4 2435 1193 -51.01% BenchmarkGCD1000x10000/WithoutXY-4 8 8 +0.00% BenchmarkGCD1000x10000/WithXY-4 2211 1076 -51.33% BenchmarkGCD1000x100000/WithoutXY-4 8 8 +0.00% BenchmarkGCD1000x100000/WithXY-4 2271 1108 -51.21% BenchmarkGCD10000x10000/WithoutXY-4 5 5 +0.00% BenchmarkGCD10000x10000/WithXY-4 23183 11605 -49.94% BenchmarkGCD10000x100000/WithoutXY-4 8 8 +0.00% BenchmarkGCD10000x100000/WithXY-4 23421 11717 -49.97% BenchmarkGCD100000x100000/WithoutXY-4 5 5 +0.00% BenchmarkGCD100000x100000/WithXY-4 232976 116815 -49.86% benchmark old bytes new bytes delta BenchmarkGCD10x10/WithoutXY-4 208 208 +0.00% BenchmarkGCD10x10/WithXY-4 736 432 -41.30% BenchmarkGCD10x100/WithoutXY-4 256 256 +0.00% BenchmarkGCD10x100/WithXY-4 896 432 -51.79% BenchmarkGCD10x1000/WithoutXY-4 368 368 +0.00% BenchmarkGCD10x1000/WithXY-4 1856 1152 -37.93% BenchmarkGCD10x10000/WithoutXY-4 1616 1616 +0.00% BenchmarkGCD10x10000/WithXY-4 7920 7376 -6.87% BenchmarkGCD10x100000/WithoutXY-4 13776 13776 +0.00% BenchmarkGCD10x100000/WithXY-4 68800 68176 -0.91% BenchmarkGCD100x100/WithoutXY-4 208 208 +0.00% BenchmarkGCD100x100/WithXY-4 6960 2112 -69.66% BenchmarkGCD100x1000/WithoutXY-4 544 560 +2.94% BenchmarkGCD100x1000/WithXY-4 7280 2400 -67.03% BenchmarkGCD100x10000/WithoutXY-4 2896 2912 +0.55% BenchmarkGCD100x10000/WithXY-4 15280 10002 -34.54% BenchmarkGCD100x100000/WithoutXY-4 27344 27365 +0.08% BenchmarkGCD100x100000/WithXY-4 88288 83427 -5.51% BenchmarkGCD1000x1000/WithoutXY-4 544 544 +0.00% BenchmarkGCD1000x1000/WithXY-4 178288 40043 -77.54% BenchmarkGCD1000x10000/WithoutXY-4 3344 3136 -6.22% BenchmarkGCD1000x10000/WithXY-4 188720 54432 -71.16% BenchmarkGCD1000x100000/WithoutXY-4 27792 27592 -0.72% BenchmarkGCD1000x100000/WithXY-4 373872 239447 -35.95% BenchmarkGCD10000x10000/WithoutXY-4 4288 4288 +0.00% BenchmarkGCD10000x10000/WithXY-4 11935584 481875 -95.96% BenchmarkGCD10000x100000/WithoutXY-4 31296 28834 -7.87% BenchmarkGCD10000x100000/WithXY-4 13237088 1662620 -87.44% BenchmarkGCD100000x100000/WithoutXY-4 40768 40768 +0.00% BenchmarkGCD100000x100000/WithXY-4 1165518864 14256010 -98.78% Change-Id: I652b3244bd074a03f3bc9a87c282330f9e5f1507 Reviewed-on: https://go-review.googlesource.com/21506 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/math/big/gcd_test.go | 16 +++++++++++++++- src/math/big/int.go | 4 ++-- src/math/big/nat.go | 29 ++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/math/big/gcd_test.go b/src/math/big/gcd_test.go index c0b9f58300..a929bf597f 100644 --- a/src/math/big/gcd_test.go +++ b/src/math/big/gcd_test.go @@ -20,13 +20,27 @@ func randInt(r *rand.Rand, size uint) *Int { } func runGCD(b *testing.B, aSize, bSize uint) { + b.Run("WithoutXY", func(b *testing.B) { + runGCDExt(b, aSize, bSize, false) + }) + b.Run("WithXY", func(b *testing.B) { + runGCDExt(b, aSize, bSize, true) + }) +} + +func runGCDExt(b *testing.B, aSize, bSize uint, calcXY bool) { b.StopTimer() var r = rand.New(rand.NewSource(1234)) aa := randInt(r, aSize) bb := randInt(r, bSize) + var x, y *Int + if calcXY { + x = new(Int) + y = new(Int) + } b.StartTimer() for i := 0; i < b.N; i++ { - new(Int).GCD(nil, nil, aa, bb) + new(Int).GCD(x, y, aa, bb) } } diff --git a/src/math/big/int.go b/src/math/big/int.go index 67ab7042ff..f2a75d1cd5 100644 --- a/src/math/big/int.go +++ b/src/math/big/int.go @@ -459,11 +459,11 @@ func (z *Int) GCD(x, y, a, b *Int) *Int { q := new(Int) temp := new(Int) + r := new(Int) for len(B.abs) > 0 { - r := new(Int) q, r = q.QuoRem(A, B, r) - A, B = B, r + A, B, r = B, r, A temp.Set(X) X.Mul(X, q) diff --git a/src/math/big/nat.go b/src/math/big/nat.go index 7668b6481b..2e65d2a7ef 100644 --- a/src/math/big/nat.go +++ b/src/math/big/nat.go @@ -8,7 +8,10 @@ package big -import "math/rand" +import ( + "math/rand" + "sync" +) // An unsigned integer x of the form // @@ -539,6 +542,21 @@ func (z nat) div(z2, u, v nat) (q, r nat) { return } +// getNat returns a nat of len n. The contents may not be zero. +func getNat(n int) nat { + var z nat + if v := natPool.Get(); v != nil { + z = v.(nat) + } + return z.make(n) +} + +func putNat(x nat) { + natPool.Put(x) +} + +var natPool sync.Pool + // q = (uIn-r)/v, with 0 <= r < y // Uses z as storage for q, and u as storage for r if possible. // See Knuth, Volume 2, section 4.3.1, Algorithm D. @@ -557,7 +575,7 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) { } q = z.make(m + 1) - qhatv := make(nat, n+1) + qhatv := getNat(n + 1) if alias(u, uIn) || alias(u, v) { u = nil // u is an alias for uIn or v - cannot reuse } @@ -565,10 +583,11 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) { u.clear() // TODO(gri) no need to clear if we allocated a new u // D1. + var v1 nat shift := nlz(v[n-1]) if shift > 0 { // do not modify v, it may be used by another goroutine simultaneously - v1 := make(nat, n) + v1 = getNat(n) shlVU(v1, v, shift) v = v1 } @@ -609,6 +628,10 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) { q[j] = qhat } + if v1 != nil { + putNat(v1) + } + putNat(qhatv) q = q.norm() shrVU(u, u, shift) -- cgit v1.3 From e2c09749af8c50fc2c0b515f2adc990cb0cb3cf6 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 7 Apr 2016 09:00:32 -0700 Subject: context: mark more tests as flaky on OpenBSD Updates #15158 Change-Id: I53e9e68d36efbf52736822e6caa047cfff501283 Reviewed-on: https://go-review.googlesource.com/21653 Reviewed-by: Matthew Dempsky Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/context/context_test.go | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/context/context_test.go b/src/context/context_test.go index 60020303c7..74af9a301c 100644 --- a/src/context/context_test.go +++ b/src/context/context_test.go @@ -242,6 +242,9 @@ func testDeadline(c Context, wait time.Duration, t *testing.T) { } func TestDeadline(t *testing.T) { + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 15158) + } c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) @@ -279,6 +282,9 @@ func TestTimeout(t *testing.T) { } func TestCanceledTimeout(t *testing.T) { + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 15158) + } c, _ := WithTimeout(Background(), 200*time.Millisecond) o := otherContext{c} c, cancel := WithTimeout(o, 400*time.Millisecond) -- cgit v1.3 From e6181eb9e1dc4ab9e297a102ed192997582ac46c Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Thu, 7 Apr 2016 14:00:00 -0400 Subject: cmd/link: disable DWARF when not generating symtab Fixes #15166 Change-Id: I30284e3c0fb2c80b26a2572e2fb249b8018e85f9 Reviewed-on: https://go-review.googlesource.com/21587 Run-TryBot: David Crawshaw Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/dwarf.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 230d146877..db8961676c 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1915,6 +1915,9 @@ func dwarfgeneratedebugsyms() { if Debug['w'] != 0 { // disable dwarf return } + if Debug['s'] != 0 && HEADTYPE != obj.Hdarwin { + return + } if HEADTYPE == obj.Hplan9 { return } -- cgit v1.3 From 9658b7ef83ae9c34f4a52680e7102d958577d5bb Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Thu, 7 Apr 2016 14:27:15 -0400 Subject: cmd/link: hide go.dwarf symbols Fixes #15179 Change-Id: I0f70b7ae1682eafaece7f22d8e76f0aa806f3ec9 Reviewed-on: https://go-review.googlesource.com/21589 Run-TryBot: David Crawshaw Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/dwarf.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index db8961676c..4741020a6d 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -509,6 +509,7 @@ func newdie(parent *DWDie, abbrev int, name string, version int) *DWDie { if name != "" && (abbrev <= DW_ABRV_VARIABLE || abbrev >= DW_ABRV_NULLTYPE) { if abbrev != DW_ABRV_VARIABLE || version == 0 { die.sym = Linklookup(Ctxt, infoprefix+name, version) + die.sym.Attr |= AttrHidden die.sym.Type = obj.SDWARFINFO } } @@ -814,6 +815,7 @@ func dotypedef(parent *DWDie, name string, def *DWDie) { } def.sym = Linklookup(Ctxt, def.sym.Name+".def", 0) + def.sym.Attr |= AttrHidden def.sym.Type = obj.SDWARFINFO // The typedef entry must be created after the def, -- cgit v1.3 From e6f36f0cd5b45b9ce7809a34c45aeb66a5ca64a4 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 18 Mar 2016 19:09:39 -0400 Subject: runtime: add s390x support (new files and lfstack_64bit.go modifications) Change-Id: I51c0a332e3cbdab348564e5dcd27583e75e4b881 Reviewed-on: https://go-review.googlesource.com/20946 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/runtime/asm_s390x.s | 1130 +++++++++++++++++++++++++++++++++++++ src/runtime/defs_linux_s390x.go | 167 ++++++ src/runtime/lfstack_64bit.go | 32 +- src/runtime/memclr_s390x.s | 122 ++++ src/runtime/memmove_s390x.s | 189 +++++++ src/runtime/os_linux_s390x.go | 46 ++ src/runtime/rt0_linux_s390x.s | 20 + src/runtime/signal_linux_s390x.go | 208 +++++++ src/runtime/sys_linux_s390x.s | 440 +++++++++++++++ src/runtime/sys_s390x.go | 45 ++ src/runtime/tls_s390x.s | 51 ++ 11 files changed, 2437 insertions(+), 13 deletions(-) create mode 100644 src/runtime/asm_s390x.s create mode 100644 src/runtime/defs_linux_s390x.go create mode 100644 src/runtime/memclr_s390x.s create mode 100644 src/runtime/memmove_s390x.s create mode 100644 src/runtime/os_linux_s390x.go create mode 100644 src/runtime/rt0_linux_s390x.s create mode 100644 src/runtime/signal_linux_s390x.go create mode 100644 src/runtime/sys_linux_s390x.s create mode 100644 src/runtime/sys_s390x.go create mode 100644 src/runtime/tls_s390x.s (limited to 'src') diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s new file mode 100644 index 0000000000..fc74b0ddf9 --- /dev/null +++ b/src/runtime/asm_s390x.s @@ -0,0 +1,1130 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go_asm.h" +#include "go_tls.h" +#include "funcdata.h" +#include "textflag.h" + +// Indicate the status of vector facility +// -1: init value +// 0: vector not installed +// 1: vector installed and enabled +// 2: vector installed but not enabled + +DATA runtime·vectorfacility+0x00(SB)/4, $-1 +GLOBL runtime·vectorfacility(SB), NOPTR, $4 + +TEXT runtime·checkvectorfacility(SB),NOSPLIT,$32-0 + MOVD $2, R0 + MOVD R1, tmp-32(SP) + MOVD $x-24(SP), R1 +// STFLE 0(R1) + WORD $0xB2B01000 + MOVBZ z-8(SP), R1 + AND $0x40, R1 + BNE vectorinstalled + MOVB $0, runtime·vectorfacility(SB) //Vector not installed + MOVD tmp-32(SP), R1 + MOVD $0, R0 + RET +vectorinstalled: + // check if the vector instruction has been enabled + VLEIB $0, $0xF, V16 + VLGVB $0, V16, R0 + CMPBEQ R0, $0xF, vectorenabled + MOVB $2, runtime·vectorfacility(SB) //Vector installed but not enabled + MOVD tmp-32(SP), R1 + MOVD $0, R0 + RET +vectorenabled: + MOVB $1, runtime·vectorfacility(SB) //Vector installed and enabled + MOVD tmp-32(SP), R1 + MOVD $0, R0 + RET + +TEXT runtime·rt0_go(SB),NOSPLIT,$0 + // R2 = argc; R3 = argv; R11 = temp; R13 = g; R15 = stack pointer + // C TLS base pointer in AR0:AR1 + + // initialize essential registers + XOR R0, R0 + + SUB $24, R15 + MOVW R2, 8(R15) // argc + MOVD R3, 16(R15) // argv + + // create istack out of the given (operating system) stack. + // _cgo_init may update stackguard. + MOVD $runtime·g0(SB), g + MOVD R15, R11 + SUB $(64*1024), R11 + MOVD R11, g_stackguard0(g) + MOVD R11, g_stackguard1(g) + MOVD R11, (g_stack+stack_lo)(g) + MOVD R15, (g_stack+stack_hi)(g) + + // if there is a _cgo_init, call it using the gcc ABI. + MOVD _cgo_init(SB), R11 + CMPBEQ R11, $0, nocgo + MOVW AR0, R4 // (AR0 << 32 | AR1) is the TLS base pointer; MOVD is translated to EAR + SLD $32, R4, R4 + MOVW AR1, R4 // arg 2: TLS base pointer + MOVD $setg_gcc<>(SB), R3 // arg 1: setg + MOVD g, R2 // arg 0: G + // C functions expect 160 bytes of space on caller stack frame + // and an 8-byte aligned stack pointer + MOVD R15, R9 // save current stack (R9 is preserved in the Linux ABI) + SUB $160, R15 // reserve 160 bytes + MOVD $~7, R6 + AND R6, R15 // 8-byte align + BL R11 // this call clobbers volatile registers according to Linux ABI (R0-R5, R14) + MOVD R9, R15 // restore stack + XOR R0, R0 // zero R0 + +nocgo: + // update stackguard after _cgo_init + MOVD (g_stack+stack_lo)(g), R2 + ADD $const__StackGuard, R2 + MOVD R2, g_stackguard0(g) + MOVD R2, g_stackguard1(g) + + // set the per-goroutine and per-mach "registers" + MOVD $runtime·m0(SB), R2 + + // save m->g0 = g0 + MOVD g, m_g0(R2) + // save m0 to g0->m + MOVD R2, g_m(g) + + BL runtime·check(SB) + + // argc/argv are already prepared on stack + BL runtime·args(SB) + BL runtime·osinit(SB) + BL runtime·schedinit(SB) + + // create a new goroutine to start program + MOVD $runtime·mainPC(SB), R2 // entry + SUB $24, R15 + MOVD R2, 16(R15) + MOVD R0, 8(R15) + MOVD R0, 0(R15) + BL runtime·newproc(SB) + ADD $24, R15 + + // start this M + BL runtime·mstart(SB) + + MOVD R0, 1(R0) + RET + +DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) +GLOBL runtime·mainPC(SB),RODATA,$8 + +TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 + MOVD R0, 2(R0) + RET + +TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 + RET + +/* + * go-routine + */ + +// void gosave(Gobuf*) +// save state in Gobuf; setjmp +TEXT runtime·gosave(SB), NOSPLIT, $-8-8 + MOVD buf+0(FP), R3 + MOVD R15, gobuf_sp(R3) + MOVD LR, gobuf_pc(R3) + MOVD g, gobuf_g(R3) + MOVD $0, gobuf_lr(R3) + MOVD $0, gobuf_ret(R3) + MOVD $0, gobuf_ctxt(R3) + RET + +// void gogo(Gobuf*) +// restore state from Gobuf; longjmp +TEXT runtime·gogo(SB), NOSPLIT, $-8-8 + MOVD buf+0(FP), R5 + MOVD gobuf_g(R5), g // make sure g is not nil + BL runtime·save_g(SB) + + MOVD 0(g), R4 + MOVD gobuf_sp(R5), R15 + MOVD gobuf_lr(R5), LR + MOVD gobuf_ret(R5), R3 + MOVD gobuf_ctxt(R5), R12 + MOVD $0, gobuf_sp(R5) + MOVD $0, gobuf_ret(R5) + MOVD $0, gobuf_lr(R5) + MOVD $0, gobuf_ctxt(R5) + CMP R0, R0 // set condition codes for == test, needed by stack split + MOVD gobuf_pc(R5), R6 + BR (R6) + +// void mcall(fn func(*g)) +// Switch to m->g0's stack, call fn(g). +// Fn must never return. It should gogo(&g->sched) +// to keep running g. +TEXT runtime·mcall(SB), NOSPLIT, $-8-8 + // Save caller state in g->sched + MOVD R15, (g_sched+gobuf_sp)(g) + MOVD LR, (g_sched+gobuf_pc)(g) + MOVD R0, (g_sched+gobuf_lr)(g) + MOVD g, (g_sched+gobuf_g)(g) + + // Switch to m->g0 & its stack, call fn. + MOVD g, R3 + MOVD g_m(g), R8 + MOVD m_g0(R8), g + BL runtime·save_g(SB) + CMP g, R3 + BNE 2(PC) + BR runtime·badmcall(SB) + MOVD fn+0(FP), R12 // context + MOVD 0(R12), R4 // code pointer + MOVD (g_sched+gobuf_sp)(g), R15 // sp = m->g0->sched.sp + SUB $16, R15 + MOVD R3, 8(R15) + MOVD $0, 0(R15) + BL (R4) + BR runtime·badmcall2(SB) + +// systemstack_switch is a dummy routine that systemstack leaves at the bottom +// of the G stack. We need to distinguish the routine that +// lives at the bottom of the G stack from the one that lives +// at the top of the system stack because the one at the top of +// the system stack terminates the stack walk (see topofstack()). +TEXT runtime·systemstack_switch(SB), NOSPLIT, $0-0 + UNDEF + BL (LR) // make sure this function is not leaf + RET + +// func systemstack(fn func()) +TEXT runtime·systemstack(SB), NOSPLIT, $0-8 + MOVD fn+0(FP), R3 // R3 = fn + MOVD R3, R12 // context + MOVD g_m(g), R4 // R4 = m + + MOVD m_gsignal(R4), R5 // R5 = gsignal + CMPBEQ g, R5, noswitch + + MOVD m_g0(R4), R5 // R5 = g0 + CMPBEQ g, R5, noswitch + + MOVD m_curg(R4), R6 + CMPBEQ g, R6, switch + + // Bad: g is not gsignal, not g0, not curg. What is it? + // Hide call from linker nosplit analysis. + MOVD $runtime·badsystemstack(SB), R3 + BL (R3) + +switch: + // save our state in g->sched. Pretend to + // be systemstack_switch if the G stack is scanned. + MOVD $runtime·systemstack_switch(SB), R6 + ADD $16, R6 // get past prologue + MOVD R6, (g_sched+gobuf_pc)(g) + MOVD R15, (g_sched+gobuf_sp)(g) + MOVD R0, (g_sched+gobuf_lr)(g) + MOVD g, (g_sched+gobuf_g)(g) + + // switch to g0 + MOVD R5, g + BL runtime·save_g(SB) + MOVD (g_sched+gobuf_sp)(g), R3 + // make it look like mstart called systemstack on g0, to stop traceback + SUB $8, R3 + MOVD $runtime·mstart(SB), R4 + MOVD R4, 0(R3) + MOVD R3, R15 + + // call target function + MOVD 0(R12), R3 // code pointer + BL (R3) + + // switch back to g + MOVD g_m(g), R3 + MOVD m_curg(R3), g + BL runtime·save_g(SB) + MOVD (g_sched+gobuf_sp)(g), R15 + MOVD $0, (g_sched+gobuf_sp)(g) + RET + +noswitch: + // already on m stack, just call directly + MOVD 0(R12), R3 // code pointer + BL (R3) + RET + +/* + * support for morestack + */ + +// Called during function prolog when more stack is needed. +// Caller has already loaded: +// R3: framesize, R4: argsize, R5: LR +// +// The traceback routines see morestack on a g0 as being +// the top of a stack (for example, morestack calling newstack +// calling the scheduler calling newm calling gc), so we must +// record an argument size. For that purpose, it has no arguments. +TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0 + // Cannot grow scheduler stack (m->g0). + MOVD g_m(g), R7 + MOVD m_g0(R7), R8 + CMPBNE g, R8, 2(PC) + BL runtime·abort(SB) + + // Cannot grow signal stack (m->gsignal). + MOVD m_gsignal(R7), R8 + CMP g, R8 + BNE 2(PC) + BL runtime·abort(SB) + + // Called from f. + // Set g->sched to context in f. + MOVD R12, (g_sched+gobuf_ctxt)(g) + MOVD R15, (g_sched+gobuf_sp)(g) + MOVD LR, R8 + MOVD R8, (g_sched+gobuf_pc)(g) + MOVD R5, (g_sched+gobuf_lr)(g) + + // Called from f. + // Set m->morebuf to f's caller. + MOVD R5, (m_morebuf+gobuf_pc)(R7) // f's caller's PC + MOVD R15, (m_morebuf+gobuf_sp)(R7) // f's caller's SP + MOVD g, (m_morebuf+gobuf_g)(R7) + + // Call newstack on m->g0's stack. + MOVD m_g0(R7), g + BL runtime·save_g(SB) + MOVD (g_sched+gobuf_sp)(g), R15 + BL runtime·newstack(SB) + + // Not reached, but make sure the return PC from the call to newstack + // is still in this function, and not the beginning of the next. + UNDEF + +TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + MOVD $0, R12 + BR runtime·morestack(SB) + +TEXT runtime·stackBarrier(SB),NOSPLIT,$0 + // We came here via a RET to an overwritten LR. + // R3 may be live. Other registers are available. + + // Get the original return PC, g.stkbar[g.stkbarPos].savedLRVal. + MOVD (g_stkbar+slice_array)(g), R4 + MOVD g_stkbarPos(g), R5 + MOVD $stkbar__size, R6 + MULLD R5, R6 + ADD R4, R6 + MOVD stkbar_savedLRVal(R6), R6 + // Record that this stack barrier was hit. + ADD $1, R5 + MOVD R5, g_stkbarPos(g) + // Jump to the original return PC. + BR (R6) + +// reflectcall: call a function with the given argument list +// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32). +// we don't have variable-sized frames, so we use a small number +// of constant-sized-frame functions to encode a few bits of size in the pc. +// Caution: ugly multiline assembly macros in your future! + +#define DISPATCH(NAME,MAXSIZE) \ + MOVD $MAXSIZE, R4; \ + CMP R3, R4; \ + BGT 3(PC); \ + MOVD $NAME(SB), R5; \ + BR (R5) +// Note: can't just "BR NAME(SB)" - bad inlining results. + +TEXT reflect·call(SB), NOSPLIT, $0-0 + BR ·reflectcall(SB) + +TEXT ·reflectcall(SB), NOSPLIT, $-8-32 + MOVWZ argsize+24(FP), R3 + // NOTE(rsc): No call16, because CALLFN needs four words + // of argument space to invoke callwritebarrier. + DISPATCH(runtime·call32, 32) + DISPATCH(runtime·call64, 64) + DISPATCH(runtime·call128, 128) + DISPATCH(runtime·call256, 256) + DISPATCH(runtime·call512, 512) + DISPATCH(runtime·call1024, 1024) + DISPATCH(runtime·call2048, 2048) + DISPATCH(runtime·call4096, 4096) + DISPATCH(runtime·call8192, 8192) + DISPATCH(runtime·call16384, 16384) + DISPATCH(runtime·call32768, 32768) + DISPATCH(runtime·call65536, 65536) + DISPATCH(runtime·call131072, 131072) + DISPATCH(runtime·call262144, 262144) + DISPATCH(runtime·call524288, 524288) + DISPATCH(runtime·call1048576, 1048576) + DISPATCH(runtime·call2097152, 2097152) + DISPATCH(runtime·call4194304, 4194304) + DISPATCH(runtime·call8388608, 8388608) + DISPATCH(runtime·call16777216, 16777216) + DISPATCH(runtime·call33554432, 33554432) + DISPATCH(runtime·call67108864, 67108864) + DISPATCH(runtime·call134217728, 134217728) + DISPATCH(runtime·call268435456, 268435456) + DISPATCH(runtime·call536870912, 536870912) + DISPATCH(runtime·call1073741824, 1073741824) + MOVD $runtime·badreflectcall(SB), R5 + BR (R5) + +#define CALLFN(NAME,MAXSIZE) \ +TEXT NAME(SB), WRAPPER, $MAXSIZE-24; \ + NO_LOCAL_POINTERS; \ + /* copy arguments to stack */ \ + MOVD arg+16(FP), R3; \ + MOVWZ argsize+24(FP), R4; \ + MOVD R15, R5; \ + ADD $(8-1), R5; \ + SUB $1, R3; \ + ADD R5, R4; \ + CMP R5, R4; \ + BEQ 6(PC); \ + ADD $1, R3; \ + ADD $1, R5; \ + MOVBZ 0(R3), R6; \ + MOVBZ R6, 0(R5); \ + BR -6(PC); \ + /* call function */ \ + MOVD f+8(FP), R12; \ + MOVD (R12), R8; \ + PCDATA $PCDATA_StackMapIndex, $0; \ + BL (R8); \ + /* copy return values back */ \ + MOVD arg+16(FP), R3; \ + MOVWZ n+24(FP), R4; \ + MOVWZ retoffset+28(FP), R6; \ + MOVD R15, R5; \ + ADD R6, R5; \ + ADD R6, R3; \ + SUB R6, R4; \ + ADD $(8-1), R5; \ + SUB $1, R3; \ + ADD R5, R4; \ +loop: \ + CMP R5, R4; \ + BEQ end; \ + ADD $1, R5; \ + ADD $1, R3; \ + MOVBZ 0(R5), R6; \ + MOVBZ R6, 0(R3); \ + BR loop; \ +end: \ + /* execute write barrier updates */ \ + MOVD argtype+0(FP), R7; \ + MOVD arg+16(FP), R3; \ + MOVWZ n+24(FP), R4; \ + MOVWZ retoffset+28(FP), R6; \ + MOVD R7, 8(R15); \ + MOVD R3, 16(R15); \ + MOVD R4, 24(R15); \ + MOVD R6, 32(R15); \ + BL runtime·callwritebarrier(SB); \ + RET + +CALLFN(·call32, 32) +CALLFN(·call64, 64) +CALLFN(·call128, 128) +CALLFN(·call256, 256) +CALLFN(·call512, 512) +CALLFN(·call1024, 1024) +CALLFN(·call2048, 2048) +CALLFN(·call4096, 4096) +CALLFN(·call8192, 8192) +CALLFN(·call16384, 16384) +CALLFN(·call32768, 32768) +CALLFN(·call65536, 65536) +CALLFN(·call131072, 131072) +CALLFN(·call262144, 262144) +CALLFN(·call524288, 524288) +CALLFN(·call1048576, 1048576) +CALLFN(·call2097152, 2097152) +CALLFN(·call4194304, 4194304) +CALLFN(·call8388608, 8388608) +CALLFN(·call16777216, 16777216) +CALLFN(·call33554432, 33554432) +CALLFN(·call67108864, 67108864) +CALLFN(·call134217728, 134217728) +CALLFN(·call268435456, 268435456) +CALLFN(·call536870912, 536870912) +CALLFN(·call1073741824, 1073741824) + +TEXT runtime·procyield(SB),NOSPLIT,$0-0 + RET + +// void jmpdefer(fv, sp); +// called from deferreturn. +// 1. grab stored LR for caller +// 2. sub 6 bytes to get back to BL deferreturn (size of BRASL instruction) +// 3. BR to fn +TEXT runtime·jmpdefer(SB),NOSPLIT|NOFRAME,$0-16 + MOVD 0(R15), R1 + SUB $6, R1, LR + + MOVD fv+0(FP), R12 + MOVD argp+8(FP), R15 + SUB $8, R15 + MOVD 0(R12), R3 + BR (R3) + +// Save state of caller into g->sched. Smashes R31. +TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0 + MOVD LR, (g_sched+gobuf_pc)(g) + MOVD R15, (g_sched+gobuf_sp)(g) + MOVD $0, (g_sched+gobuf_lr)(g) + MOVD $0, (g_sched+gobuf_ret)(g) + MOVD $0, (g_sched+gobuf_ctxt)(g) + RET + +// func asmcgocall(fn, arg unsafe.Pointer) int32 +// Call fn(arg) on the scheduler stack, +// aligned appropriately for the gcc ABI. +// See cgocall.go for more details. +TEXT ·asmcgocall(SB),NOSPLIT,$0-20 + // R2 = argc; R3 = argv; R11 = temp; R13 = g; R15 = stack pointer + // C TLS base pointer in AR0:AR1 + MOVD fn+0(FP), R3 + MOVD arg+8(FP), R4 + + MOVD R15, R2 // save original stack pointer + MOVD g, R5 + + // Figure out if we need to switch to m->g0 stack. + // We get called to create new OS threads too, and those + // come in on the m->g0 stack already. + MOVD g_m(g), R6 + MOVD m_g0(R6), R6 + CMPBEQ R6, g, g0 + BL gosave<>(SB) + MOVD R6, g + BL runtime·save_g(SB) + MOVD (g_sched+gobuf_sp)(g), R15 + + // Now on a scheduling stack (a pthread-created stack). +g0: + // Save room for two of our pointers, plus 160 bytes of callee + // save area that lives on the caller stack. + SUB $176, R15 + MOVD $~7, R6 + AND R6, R15 // 8-byte alignment for gcc ABI + MOVD R5, 168(R15) // save old g on stack + MOVD (g_stack+stack_hi)(R5), R5 + SUB R2, R5 + MOVD R5, 160(R15) // save depth in old g stack (can't just save SP, as stack might be copied during a callback) + MOVD R0, 0(R15) // clear back chain pointer (TODO can we give it real back trace information?) + MOVD R4, R2 // arg in R2 + BL R3 // can clobber: R0-R5, R14, F0-F3, F5, F7-F15 + + XOR R0, R0 // set R0 back to 0. + // Restore g, stack pointer. + MOVD 168(R15), g + BL runtime·save_g(SB) + MOVD (g_stack+stack_hi)(g), R5 + MOVD 160(R15), R6 + SUB R6, R5 + MOVD R5, R15 + + MOVW R2, ret+16(FP) + RET + +// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) +// Turn the fn into a Go func (by taking its address) and call +// cgocallback_gofunc. +TEXT runtime·cgocallback(SB),NOSPLIT,$24-24 + MOVD $fn+0(FP), R3 + MOVD R3, 8(R15) + MOVD frame+8(FP), R3 + MOVD R3, 16(R15) + MOVD framesize+16(FP), R3 + MOVD R3, 24(R15) + MOVD $runtime·cgocallback_gofunc(SB), R3 + BL (R3) + RET + +// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) +// See cgocall.go for more details. +TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24 + NO_LOCAL_POINTERS + + // Load m and g from thread-local storage. + MOVB runtime·iscgo(SB), R3 + CMPBEQ R3, $0, nocgo + BL runtime·load_g(SB) + +nocgo: + // If g is nil, Go did not create the current thread. + // Call needm to obtain one for temporary use. + // In this case, we're running on the thread stack, so there's + // lots of space, but the linker doesn't know. Hide the call from + // the linker analysis by using an indirect call. + CMPBEQ g, $0, needm + + MOVD g_m(g), R8 + MOVD R8, savedm-8(SP) + BR havem + +needm: + MOVD g, savedm-8(SP) // g is zero, so is m. + MOVD $runtime·needm(SB), R3 + BL (R3) + + // Set m->sched.sp = SP, so that if a panic happens + // during the function we are about to execute, it will + // have a valid SP to run on the g0 stack. + // The next few lines (after the havem label) + // will save this SP onto the stack and then write + // the same SP back to m->sched.sp. That seems redundant, + // but if an unrecovered panic happens, unwindm will + // restore the g->sched.sp from the stack location + // and then systemstack will try to use it. If we don't set it here, + // that restored SP will be uninitialized (typically 0) and + // will not be usable. + MOVD g_m(g), R8 + MOVD m_g0(R8), R3 + MOVD R15, (g_sched+gobuf_sp)(R3) + +havem: + // Now there's a valid m, and we're running on its m->g0. + // Save current m->g0->sched.sp on stack and then set it to SP. + // Save current sp in m->g0->sched.sp in preparation for + // switch back to m->curg stack. + // NOTE: unwindm knows that the saved g->sched.sp is at 8(R1) aka savedsp-16(SP). + MOVD m_g0(R8), R3 + MOVD (g_sched+gobuf_sp)(R3), R4 + MOVD R4, savedsp-16(SP) + MOVD R15, (g_sched+gobuf_sp)(R3) + + // Switch to m->curg stack and call runtime.cgocallbackg. + // Because we are taking over the execution of m->curg + // but *not* resuming what had been running, we need to + // save that information (m->curg->sched) so we can restore it. + // We can restore m->curg->sched.sp easily, because calling + // runtime.cgocallbackg leaves SP unchanged upon return. + // To save m->curg->sched.pc, we push it onto the stack. + // This has the added benefit that it looks to the traceback + // routine like cgocallbackg is going to return to that + // PC (because the frame we allocate below has the same + // size as cgocallback_gofunc's frame declared above) + // so that the traceback will seamlessly trace back into + // the earlier calls. + // + // In the new goroutine, -16(SP) and -8(SP) are unused. + MOVD m_curg(R8), g + BL runtime·save_g(SB) + MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 + MOVD (g_sched+gobuf_pc)(g), R5 + MOVD R5, -24(R4) + MOVD $-24(R4), R15 + BL runtime·cgocallbackg(SB) + + // Restore g->sched (== m->curg->sched) from saved values. + MOVD 0(R15), R5 + MOVD R5, (g_sched+gobuf_pc)(g) + MOVD $24(R15), R4 + MOVD R4, (g_sched+gobuf_sp)(g) + + // Switch back to m->g0's stack and restore m->g0->sched.sp. + // (Unlike m->curg, the g0 goroutine never uses sched.pc, + // so we do not have to restore it.) + MOVD g_m(g), R8 + MOVD m_g0(R8), g + BL runtime·save_g(SB) + MOVD (g_sched+gobuf_sp)(g), R15 + MOVD savedsp-16(SP), R4 + MOVD R4, (g_sched+gobuf_sp)(g) + + // If the m on entry was nil, we called needm above to borrow an m + // for the duration of the call. Since the call is over, return it with dropm. + MOVD savedm-8(SP), R6 + CMPBNE R6, $0, droppedm + MOVD $runtime·dropm(SB), R3 + BL (R3) +droppedm: + + // Done! + RET + +// void setg(G*); set g. for use by needm. +TEXT runtime·setg(SB), NOSPLIT, $0-8 + MOVD gg+0(FP), g + // This only happens if iscgo, so jump straight to save_g + BL runtime·save_g(SB) + RET + +// void setg_gcc(G*); set g in C TLS. +// Must obey the gcc calling convention. +TEXT setg_gcc<>(SB),NOSPLIT|NOFRAME,$0-0 + // The standard prologue clobbers LR (R14), which is callee-save in + // the C ABI, so we have to use NOFRAME and save LR ourselves. + MOVD LR, R1 + // Also save g, R10, and R11 since they're callee-save in C ABI + MOVD R10, R3 + MOVD g, R4 + MOVD R11, R5 + + MOVD R2, g + BL runtime·save_g(SB) + + MOVD R5, R11 + MOVD R4, g + MOVD R3, R10 + MOVD R1, LR + RET + +TEXT runtime·getcallerpc(SB),NOSPLIT,$8-16 + MOVD 16(R15), R3 // LR saved by caller + MOVD runtime·stackBarrierPC(SB), R4 + CMPBNE R3, R4, nobar + // Get original return PC. + BL runtime·nextBarrierPC(SB) + MOVD 8(R15), R3 +nobar: + MOVD R3, ret+8(FP) + RET + +TEXT runtime·setcallerpc(SB),NOSPLIT,$8-16 + MOVD pc+8(FP), R3 + MOVD 16(R15), R4 + MOVD runtime·stackBarrierPC(SB), R5 + CMPBEQ R4, R5, setbar + MOVD R3, 16(R15) // set LR in caller + RET +setbar: + // Set the stack barrier return PC. + MOVD R3, 8(R15) + BL runtime·setNextBarrierPC(SB) + RET + +TEXT runtime·getcallersp(SB),NOSPLIT,$0-16 + MOVD argp+0(FP), R3 + SUB $8, R3 + MOVD R3, ret+8(FP) + RET + +TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0 + MOVW (R0), R0 + UNDEF + +// int64 runtime·cputicks(void) +TEXT runtime·cputicks(SB),NOSPLIT,$0-8 + // The TOD clock on s390 counts from the year 1900 in ~250ps intervals. + // This means that since about 1972 the msb has been set, making the + // result of a call to STORE CLOCK (stck) a negative number. + // We clear the msb to make it positive. + STCK ret+0(FP) // serialises before and after call + MOVD ret+0(FP), R3 // R3 will wrap to 0 in the year 2043 + SLD $1, R3 + SRD $1, R3 + MOVD R3, ret+0(FP) + RET + +// memhash_varlen(p unsafe.Pointer, h seed) uintptr +// redirects to memhash(p, h, size) using the size +// stored in the closure. +TEXT runtime·memhash_varlen(SB),NOSPLIT,$40-24 + GO_ARGS + NO_LOCAL_POINTERS + MOVD p+0(FP), R3 + MOVD h+8(FP), R4 + MOVD 8(R12), R5 + MOVD R3, 8(R15) + MOVD R4, 16(R15) + MOVD R5, 24(R15) + BL runtime·memhash(SB) + MOVD 32(R15), R3 + MOVD R3, ret+16(FP) + RET + +// AES hashing not implemented for s390x +TEXT runtime·aeshash(SB),NOSPLIT|NOFRAME,$0-0 + MOVW (R0), R15 +TEXT runtime·aeshash32(SB),NOSPLIT|NOFRAME,$0-0 + MOVW (R0), R15 +TEXT runtime·aeshash64(SB),NOSPLIT|NOFRAME,$0-0 + MOVW (R0), R15 +TEXT runtime·aeshashstr(SB),NOSPLIT|NOFRAME,$0-0 + MOVW (R0), R15 + +// memequal(p, q unsafe.Pointer, size uintptr) bool +TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 + MOVD p+0(FP), R3 + MOVD q+8(FP), R5 + MOVD size+16(FP), R6 + LA ret+24(FP), R7 + BR runtime·memeqbody(SB) + +// memequal_varlen(a, b unsafe.Pointer) bool +TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17 + MOVD a+0(FP), R3 + MOVD b+8(FP), R5 + MOVD 8(R12), R6 // compiler stores size at offset 8 in the closure + LA ret+16(FP), R7 + BR runtime·memeqbody(SB) + +// eqstring tests whether two strings are equal. +// The compiler guarantees that strings passed +// to eqstring have equal length. +// See runtime_test.go:eqstring_generic for +// equivalent Go code. +TEXT runtime·eqstring(SB),NOSPLIT|NOFRAME,$0-33 + MOVD s1str+0(FP), R3 + MOVD s1len+8(FP), R6 + MOVD s2str+16(FP), R5 + LA ret+32(FP), R7 + BR runtime·memeqbody(SB) + +TEXT bytes·Equal(SB),NOSPLIT|NOFRAME,$0-49 + MOVD a_len+8(FP), R2 + MOVD b_len+32(FP), R6 + MOVD a+0(FP), R3 + MOVD b+24(FP), R5 + LA ret+48(FP), R7 + CMPBNE R2, R6, notequal + BR runtime·memeqbody(SB) +notequal: + MOVB $0, ret+48(FP) + RET + +// input: +// R3 = a +// R5 = b +// R6 = len +// R7 = address of output byte (stores 0 or 1 here) +// a and b have the same length +TEXT runtime·memeqbody(SB),NOSPLIT|NOFRAME,$0-0 + CMPBEQ R3, R5, equal +loop: + CMPBEQ R6, $0, equal + CMPBLT R6, $32, tiny + CMP R6, $256 + BLT tail + CLC $256, 0(R3), 0(R5) + BNE notequal + SUB $256, R6 + LA 256(R3), R3 + LA 256(R5), R5 + BR loop +tail: + SUB $1, R6, R8 + EXRL $runtime·memeqbodyclc(SB), R8 + BEQ equal +notequal: + MOVB $0, 0(R7) + RET +equal: + MOVB $1, 0(R7) + RET +tiny: + MOVD $0, R2 + CMPBLT R6, $16, lt16 + MOVD 0(R3), R8 + MOVD 0(R5), R9 + CMPBNE R8, R9, notequal + MOVD 8(R3), R8 + MOVD 8(R5), R9 + CMPBNE R8, R9, notequal + LA 16(R2), R2 + SUB $16, R6 +lt16: + CMPBLT R6, $8, lt8 + MOVD 0(R3)(R2*1), R8 + MOVD 0(R5)(R2*1), R9 + CMPBNE R8, R9, notequal + LA 8(R2), R2 + SUB $8, R6 +lt8: + CMPBLT R6, $4, lt4 + MOVWZ 0(R3)(R2*1), R8 + MOVWZ 0(R5)(R2*1), R9 + CMPBNE R8, R9, notequal + LA 4(R2), R2 + SUB $4, R6 +lt4: +#define CHECK(n) \ + CMPBEQ R6, $n, equal \ + MOVB n(R3)(R2*1), R8 \ + MOVB n(R5)(R2*1), R9 \ + CMPBNE R8, R9, notequal + CHECK(0) + CHECK(1) + CHECK(2) + CHECK(3) + BR equal + +TEXT runtime·memeqbodyclc(SB),NOSPLIT|NOFRAME,$0-0 + CLC $1, 0(R3), 0(R5) + RET + +TEXT runtime·fastrand1(SB), NOSPLIT, $0-4 + MOVD g_m(g), R4 + MOVWZ m_fastrand(R4), R3 + ADD R3, R3 + CMPW R3, $0 + BGE 2(PC) + XOR $0x88888eef, R3 + MOVW R3, m_fastrand(R4) + MOVW R3, ret+0(FP) + RET + +TEXT bytes·IndexByte(SB),NOSPLIT,$0-40 + MOVD s+0(FP), R3 // s => R3 + MOVD s_len+8(FP), R4 // s_len => R4 + MOVBZ c+24(FP), R5 // c => R5 + MOVD $ret+32(FP), R2 // &ret => R9 + BR runtime·indexbytebody(SB) + +TEXT strings·IndexByte(SB),NOSPLIT,$0-32 + MOVD s+0(FP), R3 // s => R3 + MOVD s_len+8(FP), R4 // s_len => R4 + MOVBZ c+16(FP), R5 // c => R5 + MOVD $ret+24(FP), R2 // &ret => R9 + BR runtime·indexbytebody(SB) + +// input: +// R3: s +// R4: s_len +// R5: c -- byte sought +// R2: &ret -- address to put index into +TEXT runtime·indexbytebody(SB),NOSPLIT,$0 + CMPBEQ R4, $0, notfound + MOVD R3, R6 // store base for later + ADD R3, R4, R8 // the address after the end of the string + //if the length is small, use loop; otherwise, use vector or srst search + CMPBGE R4, $16, large + +residual: + CMPBEQ R3, R8, notfound + MOVBZ 0(R3), R7 + LA 1(R3), R3 + CMPBNE R7, R5, residual + +found: + SUB R6, R3 + SUB $1, R3 + MOVD R3, 0(R2) + RET + +notfound: + MOVD $-1, 0(R2) + RET + +large: + MOVB runtime·vectorfacility(SB), R1 + CMPBEQ R1, $-1, checkvector // vectorfacility = -1, vector not checked yet +vectorchecked: + CMPBEQ R1, $1, vectorimpl // vectorfacility = 1, vector supported + +srstimpl: // vectorfacility != 1, not support or enable vector + MOVBZ R5, R0 // c needs to be in R0, leave until last minute as currently R0 is expected to be 0 +srstloop: + WORD $0xB25E0083 // srst %r8, %r3 (search the range [R3, R8)) + BVS srstloop // interrupted - continue + BGT notfoundr0 +foundr0: + XOR R0, R0 // reset R0 + SUB R6, R8 // remove base + MOVD R8, 0(R2) + RET +notfoundr0: + XOR R0, R0 // reset R0 + MOVD $-1, 0(R2) + RET + +vectorimpl: + //if the address is not 16byte aligned, use loop for the header + AND $15, R3, R8 + CMPBGT R8, $0, notaligned + +aligned: + ADD R6, R4, R8 + AND $-16, R8, R7 + // replicate c across V17 + VLVGB $0, R5, V19 + VREPB $0, V19, V17 + +vectorloop: + CMPBGE R3, R7, residual + VL 0(R3), V16 // load string to be searched into V16 + ADD $16, R3 + VFEEBS V16, V17, V18 // search V17 in V16 and set conditional code accordingly + BVS vectorloop + + // when vector search found c in the string + VLGVB $7, V18, R7 // load 7th element of V18 containing index into R7 + SUB $16, R3 + SUB R6, R3 + ADD R3, R7 + MOVD R7, 0(R2) + RET + +notaligned: + AND $-16, R3, R8 + ADD $16, R8 +notalignedloop: + CMPBEQ R3, R8, aligned + MOVBZ 0(R3), R7 + LA 1(R3), R3 + CMPBNE R7, R5, notalignedloop + BR found + +checkvector: + CALL runtime·checkvectorfacility(SB) + MOVB runtime·vectorfacility(SB), R1 + BR vectorchecked + +TEXT runtime·return0(SB), NOSPLIT, $0 + MOVW $0, R3 + RET + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT _cgo_topofstack(SB),NOSPLIT|NOFRAME,$0 + // g (R13), R10, R11 and LR (R14) are callee-save in the C ABI, so save them + MOVD g, R1 + MOVD R10, R3 + MOVD LR, R4 + MOVD R11, R5 + + BL runtime·load_g(SB) // clobbers g (R13), R10, R11 + MOVD g_m(g), R2 + MOVD m_curg(R2), R2 + MOVD (g_stack+stack_hi)(R2), R2 + + MOVD R1, g + MOVD R3, R10 + MOVD R4, LR + MOVD R5, R11 + RET + +// The top-most function running on a goroutine +// returns to goexit+PCQuantum. +TEXT runtime·goexit(SB),NOSPLIT|NOFRAME,$0-0 + BYTE $0x07; BYTE $0x00; // 2-byte nop + BL runtime·goexit1(SB) // does not return + // traceback from goexit1 must hit code range of goexit + BYTE $0x07; BYTE $0x00; // 2-byte nop + +TEXT runtime·prefetcht0(SB),NOSPLIT,$0-8 + RET + +TEXT runtime·prefetcht1(SB),NOSPLIT,$0-8 + RET + +TEXT runtime·prefetcht2(SB),NOSPLIT,$0-8 + RET + +TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8 + RET + +TEXT runtime·sigreturn(SB),NOSPLIT,$0-8 + RET + +TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0 + SYNC + RET + +TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40 + MOVD s1_base+0(FP), R3 + MOVD s1_len+8(FP), R4 + MOVD s2_base+16(FP), R5 + MOVD s2_len+24(FP), R6 + LA ret+32(FP), R7 + BR runtime·cmpbody(SB) + +TEXT bytes·Compare(SB),NOSPLIT|NOFRAME,$0-56 + MOVD s1+0(FP), R3 + MOVD s1+8(FP), R4 + MOVD s2+24(FP), R5 + MOVD s2+32(FP), R6 + LA res+48(FP), R7 + BR runtime·cmpbody(SB) + +// input: +// R3 = a +// R4 = alen +// R5 = b +// R6 = blen +// R7 = address of output word (stores -1/0/1 here) +TEXT runtime·cmpbody(SB),NOSPLIT|NOFRAME,$0-0 + CMPBEQ R3, R5, cmplengths + MOVD R4, R8 + CMPBLE R4, R6, amin + MOVD R6, R8 +amin: + CMPBEQ R8, $0, cmplengths + CMP R8, $256 + BLE tail +loop: + CLC $256, 0(R3), 0(R5) + BGT gt + BLT lt + SUB $256, R8 + CMP R8, $256 + BGT loop +tail: + SUB $1, R8 + EXRL $runtime·cmpbodyclc(SB), R8 + BGT gt + BLT lt +cmplengths: + CMP R4, R6 + BEQ eq + BLT lt +gt: + MOVD $1, 0(R7) + RET +lt: + MOVD $-1, 0(R7) + RET +eq: + MOVD $0, 0(R7) + RET + +TEXT runtime·cmpbodyclc(SB),NOSPLIT|NOFRAME,$0-0 + CLC $1, 0(R3), 0(R5) + RET + +// This is called from .init_array and follows the platform, not Go, ABI. +// We are overly conservative. We could only save the registers we use. +// However, since this function is only called once per loaded module +// performance is unimportant. +TEXT runtime·addmoduledata(SB),NOSPLIT|NOFRAME,$0-0 + // Save R6-R15, F0, F2, F4 and F6 in the + // register save area of the calling function + STMG R6, R15, 48(R15) + FMOVD F0, 128(R15) + FMOVD F2, 136(R15) + FMOVD F4, 144(R15) + FMOVD F6, 152(R15) + + // append the argument (passed in R2, as per the ELF ABI) to the + // moduledata linked list. + MOVD runtime·lastmoduledatap(SB), R1 + MOVD R2, moduledata_next(R1) + MOVD R2, runtime·lastmoduledatap(SB) + + // Restore R6-R15, F0, F2, F4 and F6 + LMG 48(R15), R6, R15 + FMOVD F0, 128(R15) + FMOVD F2, 136(R15) + FMOVD F4, 144(R15) + FMOVD F6, 152(R15) + RET + +TEXT ·checkASM(SB),NOSPLIT,$0-1 + MOVB $1, ret+0(FP) + RET diff --git a/src/runtime/defs_linux_s390x.go b/src/runtime/defs_linux_s390x.go new file mode 100644 index 0000000000..5f55d5a889 --- /dev/null +++ b/src/runtime/defs_linux_s390x.go @@ -0,0 +1,167 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +const ( + _EINTR = 0x4 + _EAGAIN = 0xb + _ENOMEM = 0xc + + _PROT_NONE = 0x0 + _PROT_READ = 0x1 + _PROT_WRITE = 0x2 + _PROT_EXEC = 0x4 + + _MAP_ANON = 0x20 + _MAP_PRIVATE = 0x2 + _MAP_FIXED = 0x10 + + _MADV_DONTNEED = 0x4 + _MADV_HUGEPAGE = 0xe + _MADV_NOHUGEPAGE = 0xf + + _SA_RESTART = 0x10000000 + _SA_ONSTACK = 0x8000000 + _SA_SIGINFO = 0x4 + + _SIGHUP = 0x1 + _SIGINT = 0x2 + _SIGQUIT = 0x3 + _SIGILL = 0x4 + _SIGTRAP = 0x5 + _SIGABRT = 0x6 + _SIGBUS = 0x7 + _SIGFPE = 0x8 + _SIGKILL = 0x9 + _SIGUSR1 = 0xa + _SIGSEGV = 0xb + _SIGUSR2 = 0xc + _SIGPIPE = 0xd + _SIGALRM = 0xe + _SIGSTKFLT = 0x10 + _SIGCHLD = 0x11 + _SIGCONT = 0x12 + _SIGSTOP = 0x13 + _SIGTSTP = 0x14 + _SIGTTIN = 0x15 + _SIGTTOU = 0x16 + _SIGURG = 0x17 + _SIGXCPU = 0x18 + _SIGXFSZ = 0x19 + _SIGVTALRM = 0x1a + _SIGPROF = 0x1b + _SIGWINCH = 0x1c + _SIGIO = 0x1d + _SIGPWR = 0x1e + _SIGSYS = 0x1f + + _FPE_INTDIV = 0x1 + _FPE_INTOVF = 0x2 + _FPE_FLTDIV = 0x3 + _FPE_FLTOVF = 0x4 + _FPE_FLTUND = 0x5 + _FPE_FLTRES = 0x6 + _FPE_FLTINV = 0x7 + _FPE_FLTSUB = 0x8 + + _BUS_ADRALN = 0x1 + _BUS_ADRERR = 0x2 + _BUS_OBJERR = 0x3 + + _SEGV_MAPERR = 0x1 + _SEGV_ACCERR = 0x2 + + _ITIMER_REAL = 0x0 + _ITIMER_VIRTUAL = 0x1 + _ITIMER_PROF = 0x2 + + _EPOLLIN = 0x1 + _EPOLLOUT = 0x4 + _EPOLLERR = 0x8 + _EPOLLHUP = 0x10 + _EPOLLRDHUP = 0x2000 + _EPOLLET = 0x80000000 + _EPOLL_CLOEXEC = 0x80000 + _EPOLL_CTL_ADD = 0x1 + _EPOLL_CTL_DEL = 0x2 + _EPOLL_CTL_MOD = 0x3 +) + +type timespec struct { + tv_sec int64 + tv_nsec int64 +} + +func (ts *timespec) set_sec(x int64) { + ts.tv_sec = x +} + +func (ts *timespec) set_nsec(x int32) { + ts.tv_nsec = int64(x) +} + +type timeval struct { + tv_sec int64 + tv_usec int64 +} + +func (tv *timeval) set_usec(x int32) { + tv.tv_usec = int64(x) +} + +type sigactiont struct { + sa_handler uintptr + sa_flags uint64 + sa_restorer uintptr + sa_mask uint64 +} + +type siginfo struct { + si_signo int32 + si_errno int32 + si_code int32 + // below here is a union; si_addr is the only field we use + si_addr uint64 +} + +type itimerval struct { + it_interval timeval + it_value timeval +} + +type epollevent struct { + events uint32 + pad_cgo_0 [4]byte + data [8]byte // unaligned uintptr +} + +const ( + _O_RDONLY = 0x0 + _O_CLOEXEC = 0x80000 + _SA_RESTORER = 0 +) + +type sigaltstackt struct { + ss_sp *byte + ss_flags int32 + ss_size uintptr +} + +type sigcontext struct { + psw_mask uint64 + psw_addr uint64 + gregs [16]uint64 + aregs [16]uint32 + fpc uint32 + fpregs [16]uint64 +} + +type ucontext struct { + uc_flags uint64 + uc_link *ucontext + uc_stack sigaltstackt + uc_mcontext sigcontext + uc_sigmask uint64 +} diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go index 07c2a141f0..8180b0a248 100644 --- a/src/runtime/lfstack_64bit.go +++ b/src/runtime/lfstack_64bit.go @@ -2,26 +2,32 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build arm64 mips64 mips64le ppc64 ppc64le +// +build arm64 mips64 mips64le ppc64 ppc64le s390x package runtime import "unsafe" -// On ppc64, Linux limits the user address space to 46 bits (see -// TASK_SIZE_USER64 in the Linux kernel). This has grown over time, -// so here we allow 48 bit addresses. -// -// On mips64, Linux limits the user address space to 40 bits (see -// TASK_SIZE64 in the Linux kernel). This has grown over time, -// so here we allow 48 bit addresses. -// -// In addition to the 16 bits taken from the top, we can take 3 from the -// bottom, because node must be pointer-aligned, giving a total of 19 bits -// of count. const ( + // addrBits is the number of bits needed to represent a virtual address. + // + // In Linux the user address space for each architecture is limited as + // follows (taken from the processor.h file for the architecture): + // + // Architecture Name Maximum Value (exclusive) + // --------------------------------------------------------------------- + // arm64 TASK_SIZE_64 Depends on configuration. + // ppc64{,le} TASK_SIZE_USER64 0x400000000000UL (46 bit addresses) + // mips64{,le} TASK_SIZE64 0x010000000000UL (40 bit addresses) + // s390x TASK_SIZE 0x020000000000UL (41 bit addresses) + // + // These values may increase over time. addrBits = 48 - cntBits = 64 - addrBits + 3 + + // In addition to the 16 bits taken from the top, we can take 3 from the + // bottom, because node must be pointer-aligned, giving a total of 19 bits + // of count. + cntBits = 64 - addrBits + 3 ) func lfstackPack(node *lfnode, cnt uintptr) uint64 { diff --git a/src/runtime/memclr_s390x.s b/src/runtime/memclr_s390x.s new file mode 100644 index 0000000000..86eafec0a9 --- /dev/null +++ b/src/runtime/memclr_s390x.s @@ -0,0 +1,122 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// void runtime·memclr(void*, uintptr) +TEXT runtime·memclr(SB),NOSPLIT|NOFRAME,$0-16 + MOVD ptr+0(FP), R4 + MOVD n+8(FP), R5 + +start: + CMPBLE R5, $3, clear0to3 + CMPBLE R5, $7, clear4to7 + CMPBLE R5, $11, clear8to11 + CMPBLE R5, $15, clear12to15 + CMP R5, $32 + BGE clearmt32 + MOVD R0, 0(R4) + MOVD R0, 8(R4) + ADD $16, R4 + SUB $16, R5 + BR start + +clear0to3: + CMPBEQ R5, $0, done + CMPBNE R5, $1, clear2 + MOVB R0, 0(R4) + RET +clear2: + CMPBNE R5, $2, clear3 + MOVH R0, 0(R4) + RET +clear3: + MOVH R0, 0(R4) + MOVB R0, 2(R4) + RET + +clear4to7: + CMPBNE R5, $4, clear5 + MOVW R0, 0(R4) + RET +clear5: + CMPBNE R5, $5, clear6 + MOVW R0, 0(R4) + MOVB R0, 4(R4) + RET +clear6: + CMPBNE R5, $6, clear7 + MOVW R0, 0(R4) + MOVH R0, 4(R4) + RET +clear7: + MOVW R0, 0(R4) + MOVH R0, 4(R4) + MOVB R0, 6(R4) + RET + +clear8to11: + CMPBNE R5, $8, clear9 + MOVD R0, 0(R4) + RET +clear9: + CMPBNE R5, $9, clear10 + MOVD R0, 0(R4) + MOVB R0, 8(R4) + RET +clear10: + CMPBNE R5, $10, clear11 + MOVD R0, 0(R4) + MOVH R0, 8(R4) + RET +clear11: + MOVD R0, 0(R4) + MOVH R0, 8(R4) + MOVB R0, 10(R4) + RET + +clear12to15: + CMPBNE R5, $12, clear13 + MOVD R0, 0(R4) + MOVW R0, 8(R4) + RET +clear13: + CMPBNE R5, $13, clear14 + MOVD R0, 0(R4) + MOVW R0, 8(R4) + MOVB R0, 12(R4) + RET +clear14: + CMPBNE R5, $14, clear15 + MOVD R0, 0(R4) + MOVW R0, 8(R4) + MOVH R0, 12(R4) + RET +clear15: + MOVD R0, 0(R4) + MOVW R0, 8(R4) + MOVH R0, 12(R4) + MOVB R0, 14(R4) + RET + +clearmt32: + CMP R5, $256 + BLT clearlt256 + XC $256, 0(R4), 0(R4) + ADD $256, R4 + ADD $-256, R5 + BR clearmt32 +clearlt256: + CMPBEQ R5, $0, done + ADD $-1, R5 + EXRL $runtime·memclr_s390x_exrl_xc(SB), R5 +done: + RET + +// DO NOT CALL - target for exrl (execute relative long) instruction. +TEXT runtime·memclr_s390x_exrl_xc(SB),NOSPLIT|NOFRAME,$0-0 + XC $1, 0(R4), 0(R4) + MOVD R0, 0(R0) + RET + diff --git a/src/runtime/memmove_s390x.s b/src/runtime/memmove_s390x.s new file mode 100644 index 0000000000..238f30891d --- /dev/null +++ b/src/runtime/memmove_s390x.s @@ -0,0 +1,189 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// void runtime·memmove(void*, void*, uintptr) +TEXT runtime·memmove(SB),NOSPLIT|NOFRAME,$0-24 + MOVD to+0(FP), R6 + MOVD from+8(FP), R4 + MOVD n+16(FP), R5 + + CMPBEQ R6, R4, done + +start: + CMPBLE R5, $3, move0to3 + CMPBLE R5, $7, move4to7 + CMPBLE R5, $11, move8to11 + CMPBLE R5, $15, move12to15 + CMPBNE R5, $16, movemt16 + MOVD 0(R4), R7 + MOVD 8(R4), R8 + MOVD R7, 0(R6) + MOVD R8, 8(R6) + RET + +movemt16: + CMPBGT R4, R6, forwards + ADD R5, R4, R7 + CMPBLE R7, R6, forwards + ADD R5, R6, R8 +backwards: + MOVD -8(R7), R3 + MOVD R3, -8(R8) + MOVD -16(R7), R3 + MOVD R3, -16(R8) + ADD $-16, R5 + ADD $-16, R7 + ADD $-16, R8 + CMP R5, $16 + BGE backwards + BR start + +forwards: + CMPBGT R5, $64, forwards_fast + MOVD 0(R4), R3 + MOVD R3, 0(R6) + MOVD 8(R4), R3 + MOVD R3, 8(R6) + ADD $16, R4 + ADD $16, R6 + ADD $-16, R5 + CMP R5, $16 + BGE forwards + BR start + +forwards_fast: + CMP R5, $256 + BLE forwards_small + MVC $256, 0(R4), 0(R6) + ADD $256, R4 + ADD $256, R6 + ADD $-256, R5 + BR forwards_fast + +forwards_small: + CMPBEQ R5, $0, done + ADD $-1, R5 + EXRL $runtime·memmove_s390x_exrl_mvc(SB), R5 + RET + +move0to3: + CMPBEQ R5, $0, done +move1: + CMPBNE R5, $1, move2 + MOVB 0(R4), R3 + MOVB R3, 0(R6) + RET +move2: + CMPBNE R5, $2, move3 + MOVH 0(R4), R3 + MOVH R3, 0(R6) + RET +move3: + MOVH 0(R4), R3 + MOVB 2(R4), R7 + MOVH R3, 0(R6) + MOVB R7, 2(R6) + RET + +move4to7: + CMPBNE R5, $4, move5 + MOVW 0(R4), R3 + MOVW R3, 0(R6) + RET +move5: + CMPBNE R5, $5, move6 + MOVW 0(R4), R3 + MOVB 4(R4), R7 + MOVW R3, 0(R6) + MOVB R7, 4(R6) + RET +move6: + CMPBNE R5, $6, move7 + MOVW 0(R4), R3 + MOVH 4(R4), R7 + MOVW R3, 0(R6) + MOVH R7, 4(R6) + RET +move7: + MOVW 0(R4), R3 + MOVH 4(R4), R7 + MOVB 6(R4), R8 + MOVW R3, 0(R6) + MOVH R7, 4(R6) + MOVB R8, 6(R6) + RET + +move8to11: + CMPBNE R5, $8, move9 + MOVD 0(R4), R3 + MOVD R3, 0(R6) + RET +move9: + CMPBNE R5, $9, move10 + MOVD 0(R4), R3 + MOVB 8(R4), R7 + MOVD R3, 0(R6) + MOVB R7, 8(R6) + RET +move10: + CMPBNE R5, $10, move11 + MOVD 0(R4), R3 + MOVH 8(R4), R7 + MOVD R3, 0(R6) + MOVH R7, 8(R6) + RET +move11: + MOVD 0(R4), R3 + MOVH 8(R4), R7 + MOVB 10(R4), R8 + MOVD R3, 0(R6) + MOVH R7, 8(R6) + MOVB R8, 10(R6) + RET + +move12to15: + CMPBNE R5, $12, move13 + MOVD 0(R4), R3 + MOVW 8(R4), R7 + MOVD R3, 0(R6) + MOVW R7, 8(R6) + RET +move13: + CMPBNE R5, $13, move14 + MOVD 0(R4), R3 + MOVW 8(R4), R7 + MOVB 12(R4), R8 + MOVD R3, 0(R6) + MOVW R7, 8(R6) + MOVB R8, 12(R6) + RET +move14: + CMPBNE R5, $14, move15 + MOVD 0(R4), R3 + MOVW 8(R4), R7 + MOVH 12(R4), R8 + MOVD R3, 0(R6) + MOVW R7, 8(R6) + MOVH R8, 12(R6) + RET +move15: + MOVD 0(R4), R3 + MOVW 8(R4), R7 + MOVH 12(R4), R8 + MOVB 14(R4), R10 + MOVD R3, 0(R6) + MOVW R7, 8(R6) + MOVH R8, 12(R6) + MOVB R10, 14(R6) +done: + RET + +// DO NOT CALL - target for exrl (execute relative long) instruction. +TEXT runtime·memmove_s390x_exrl_mvc(SB),NOSPLIT|NOFRAME,$0-0 + MVC $1, 0(R4), 0(R6) + MOVD R0, 0(R0) + RET + diff --git a/src/runtime/os_linux_s390x.go b/src/runtime/os_linux_s390x.go new file mode 100644 index 0000000000..e659dff716 --- /dev/null +++ b/src/runtime/os_linux_s390x.go @@ -0,0 +1,46 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +const ( + _SS_DISABLE = 2 + _NSIG = 65 + _SI_USER = 0 + _SIG_BLOCK = 0 + _SIG_UNBLOCK = 1 + _SIG_SETMASK = 2 + _RLIMIT_AS = 9 +) + +type sigset uint64 + +type rlimit struct { + rlim_cur uintptr + rlim_max uintptr +} + +var sigset_all = sigset(^uint64(0)) + +func sigaddset(mask *sigset, i int) { + if i > 64 { + throw("unexpected signal greater than 64") + } + *mask |= 1 << (uint(i) - 1) +} + +func sigdelset(mask *sigset, i int) { + if i > 64 { + throw("unexpected signal greater than 64") + } + *mask &^= 1 << (uint(i) - 1) +} + +func sigfillset(mask *uint64) { + *mask = ^uint64(0) +} + +func sigcopyset(mask *sigset, m sigmask) { + *mask = sigset(uint64(m[0]) | uint64(m[1])<<32) +} diff --git a/src/runtime/rt0_linux_s390x.s b/src/runtime/rt0_linux_s390x.s new file mode 100644 index 0000000000..aedd6c7ef2 --- /dev/null +++ b/src/runtime/rt0_linux_s390x.s @@ -0,0 +1,20 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT _rt0_s390x_linux(SB),NOSPLIT|NOFRAME,$0 + // In a statically linked binary, the stack contains argc, + // argv as argc string pointers followed by a NULL, envv as a + // sequence of string pointers followed by a NULL, and auxv. + // There is no TLS base pointer. + // + // TODO: Support dynamic linking entry point + MOVD 0(R15), R2 // argc + ADD $8, R15, R3 // argv + BR main(SB) + +TEXT main(SB),NOSPLIT|NOFRAME,$0 + MOVD $runtime·rt0_go(SB), R11 + BR R11 diff --git a/src/runtime/signal_linux_s390x.go b/src/runtime/signal_linux_s390x.go new file mode 100644 index 0000000000..155d3a326f --- /dev/null +++ b/src/runtime/signal_linux_s390x.go @@ -0,0 +1,208 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import ( + "runtime/internal/sys" + "unsafe" +) + +type sigctxt struct { + info *siginfo + ctxt unsafe.Pointer +} + +func (c *sigctxt) regs() *sigcontext { + return (*sigcontext)(unsafe.Pointer(&(*ucontext)(c.ctxt).uc_mcontext)) +} +func (c *sigctxt) r0() uint64 { return c.regs().gregs[0] } +func (c *sigctxt) r1() uint64 { return c.regs().gregs[1] } +func (c *sigctxt) r2() uint64 { return c.regs().gregs[2] } +func (c *sigctxt) r3() uint64 { return c.regs().gregs[3] } +func (c *sigctxt) r4() uint64 { return c.regs().gregs[4] } +func (c *sigctxt) r5() uint64 { return c.regs().gregs[5] } +func (c *sigctxt) r6() uint64 { return c.regs().gregs[6] } +func (c *sigctxt) r7() uint64 { return c.regs().gregs[7] } +func (c *sigctxt) r8() uint64 { return c.regs().gregs[8] } +func (c *sigctxt) r9() uint64 { return c.regs().gregs[9] } +func (c *sigctxt) r10() uint64 { return c.regs().gregs[10] } +func (c *sigctxt) r11() uint64 { return c.regs().gregs[11] } +func (c *sigctxt) r12() uint64 { return c.regs().gregs[12] } +func (c *sigctxt) r13() uint64 { return c.regs().gregs[13] } +func (c *sigctxt) r14() uint64 { return c.regs().gregs[14] } +func (c *sigctxt) r15() uint64 { return c.regs().gregs[15] } +func (c *sigctxt) link() uint64 { return c.regs().gregs[14] } +func (c *sigctxt) sp() uint64 { return c.regs().gregs[15] } +func (c *sigctxt) pc() uint64 { return c.regs().psw_addr } +func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) } +func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr } + +func (c *sigctxt) set_r0(x uint64) { c.regs().gregs[0] = x } +func (c *sigctxt) set_r13(x uint64) { c.regs().gregs[13] = x } +func (c *sigctxt) set_link(x uint64) { c.regs().gregs[14] = x } +func (c *sigctxt) set_sp(x uint64) { c.regs().gregs[15] = x } +func (c *sigctxt) set_pc(x uint64) { c.regs().psw_addr = x } +func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } +func (c *sigctxt) set_sigaddr(x uint64) { + *(*uintptr)(add(unsafe.Pointer(c.info), 2*sys.PtrSize)) = uintptr(x) +} + +func dumpregs(c *sigctxt) { + print("r0 ", hex(c.r0()), "\t") + print("r1 ", hex(c.r1()), "\n") + print("r2 ", hex(c.r2()), "\t") + print("r3 ", hex(c.r3()), "\n") + print("r4 ", hex(c.r4()), "\t") + print("r5 ", hex(c.r5()), "\n") + print("r6 ", hex(c.r6()), "\t") + print("r7 ", hex(c.r7()), "\n") + print("r8 ", hex(c.r8()), "\t") + print("r9 ", hex(c.r9()), "\n") + print("r10 ", hex(c.r10()), "\t") + print("r11 ", hex(c.r11()), "\n") + print("r12 ", hex(c.r12()), "\t") + print("r13 ", hex(c.r13()), "\n") + print("r14 ", hex(c.r14()), "\t") + print("r15 ", hex(c.r15()), "\n") + print("pc ", hex(c.pc()), "\t") + print("link ", hex(c.link()), "\n") +} + +var crashing int32 + +// May run during STW, so write barriers are not allowed. +// +//go:nowritebarrierrec +func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { + _g_ := getg() + c := &sigctxt{info, ctxt} + + if sig == _SIGPROF { + sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp, _g_.m) + return + } + flags := int32(_SigThrow) + if sig < uint32(len(sigtable)) { + flags = sigtable[sig].flags + } + if c.sigcode() != _SI_USER && flags&_SigPanic != 0 { + // Make it look like a call to the signal func. + // Have to pass arguments out of band since + // augmenting the stack frame would break + // the unwinding code. + gp.sig = sig + gp.sigcode0 = uintptr(c.sigcode()) + gp.sigcode1 = uintptr(c.sigaddr()) + gp.sigpc = uintptr(c.pc()) + + // We arrange link, and pc to pretend the panicking + // function calls sigpanic directly. + // Always save LINK to stack so that panics in leaf + // functions are correctly handled. This smashes + // the stack frame but we're not going back there + // anyway. + sp := c.sp() - sys.MinFrameSize + c.set_sp(sp) + *(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link() + + pc := uintptr(gp.sigpc) + + // If we don't recognize the PC as code + // but we do recognize the link register as code, + // then assume this was a call to non-code and treat like + // pc == 0, to make unwinding show the context. + if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil { + pc = 0 + } + + // Don't bother saving PC if it's zero, which is + // probably a call to a nil func: the old link register + // is more useful in the stack trace. + if pc != 0 { + c.set_link(uint64(pc)) + } + + // In case we are panicking from external C code + c.set_r0(0) + c.set_r13(uint64(uintptr(unsafe.Pointer(gp)))) + c.set_pc(uint64(funcPC(sigpanic))) + return + } + + if c.sigcode() == _SI_USER || flags&_SigNotify != 0 { + if sigsend(sig) { + return + } + } + + if c.sigcode() == _SI_USER && signal_ignored(sig) { + return + } + + if flags&_SigKill != 0 { + dieFromSignal(int32(sig)) + } + + if flags&_SigThrow == 0 { + return + } + + _g_.m.throwing = 1 + _g_.m.caughtsig.set(gp) + + if crashing == 0 { + startpanic() + } + + if sig < uint32(len(sigtable)) { + print(sigtable[sig].name, "\n") + } else { + print("Signal ", sig, "\n") + } + + print("PC=", hex(c.pc()), " m=", _g_.m.id, "\n") + if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 { + print("signal arrived during cgo execution\n") + gp = _g_.m.lockedg + } + print("\n") + + level, _, docrash := gotraceback() + if level > 0 { + goroutineheader(gp) + tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp) + if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning { + // tracebackothers on original m skipped this one; trace it now. + goroutineheader(_g_.m.curg) + traceback(^uintptr(0), ^uintptr(0), 0, gp) + } else if crashing == 0 { + tracebackothers(gp) + print("\n") + } + dumpregs(c) + } + + if docrash { + crashing++ + if crashing < sched.mcount { + // There are other m's that need to dump their stacks. + // Relay SIGQUIT to the next m by sending it to the current process. + // All m's that have already received SIGQUIT have signal masks blocking + // receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet. + // When the last m receives the SIGQUIT, it will fall through to the call to + // crash below. Just in case the relaying gets botched, each m involved in + // the relay sleeps for 5 seconds and then does the crash/exit itself. + // In expected operation, the last m has received the SIGQUIT and run + // crash/exit and the process is gone, all long before any of the + // 5-second sleeps have finished. + print("\n-----\n\n") + raiseproc(_SIGQUIT) + usleep(5 * 1000 * 1000) + } + crash() + } + + exit(2) +} diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s new file mode 100644 index 0000000000..f43792bd51 --- /dev/null +++ b/src/runtime/sys_linux_s390x.s @@ -0,0 +1,440 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// System calls and other system stuff for Linux s390x; see +// /usr/include/asm/unistd.h for the syscall number definitions. + +#include "go_asm.h" +#include "go_tls.h" +#include "textflag.h" + +#define SYS_exit 1 +#define SYS_read 3 +#define SYS_write 4 +#define SYS_open 5 +#define SYS_close 6 +#define SYS_getpid 20 +#define SYS_kill 37 +#define SYS_fcntl 55 +#define SYS_gettimeofday 78 +#define SYS_mmap 90 +#define SYS_munmap 91 +#define SYS_setitimer 104 +#define SYS_clone 120 +#define SYS_select 142 +#define SYS_sched_yield 158 +#define SYS_rt_sigreturn 173 +#define SYS_rt_sigaction 174 +#define SYS_rt_sigprocmask 175 +#define SYS_sigaltstack 186 +#define SYS_ugetrlimit 191 +#define SYS_madvise 219 +#define SYS_mincore 218 +#define SYS_gettid 236 +#define SYS_tkill 237 +#define SYS_futex 238 +#define SYS_sched_getaffinity 240 +#define SYS_exit_group 248 +#define SYS_epoll_create 249 +#define SYS_epoll_ctl 250 +#define SYS_epoll_wait 251 +#define SYS_clock_gettime 260 +#define SYS_epoll_create1 327 + +TEXT runtime·exit(SB),NOSPLIT|NOFRAME,$0-4 + MOVW code+0(FP), R2 + MOVW $SYS_exit_group, R1 + SYSCALL + RET + +TEXT runtime·exit1(SB),NOSPLIT|NOFRAME,$0-4 + MOVW code+0(FP), R2 + MOVW $SYS_exit, R1 + SYSCALL + RET + +TEXT runtime·open(SB),NOSPLIT|NOFRAME,$0-20 + MOVD name+0(FP), R2 + MOVW mode+8(FP), R3 + MOVW perm+12(FP), R4 + MOVW $SYS_open, R1 + SYSCALL + MOVD $-4095, R3 + CMPUBLT R2, R3, 2(PC) + MOVW $-1, R2 + MOVW R2, ret+16(FP) + RET + +TEXT runtime·closefd(SB),NOSPLIT|NOFRAME,$0-12 + MOVW fd+0(FP), R2 + MOVW $SYS_close, R1 + SYSCALL + MOVD $-4095, R3 + CMPUBLT R2, R3, 2(PC) + MOVW $-1, R2 + MOVW R2, ret+8(FP) + RET + +TEXT runtime·write(SB),NOSPLIT|NOFRAME,$0-28 + MOVD fd+0(FP), R2 + MOVD p+8(FP), R3 + 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 + +TEXT runtime·read(SB),NOSPLIT|NOFRAME,$0-28 + MOVW fd+0(FP), R2 + MOVD p+8(FP), R3 + 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 + +TEXT runtime·getrlimit(SB),NOSPLIT|NOFRAME,$0-20 + MOVW kind+0(FP), R2 + MOVD limit+8(FP), R3 + MOVW $SYS_ugetrlimit, R1 + SYSCALL + MOVW R2, ret+16(FP) + RET + +TEXT runtime·usleep(SB),NOSPLIT,$16-4 + MOVW usec+0(FP), R2 + MOVD R2, R4 + MOVW $1000000, R3 + DIVD R3, R2 + MOVD R2, 8(R15) + MULLD R2, R3 + SUB R3, R4 + MOVD R4, 16(R15) + + // select(0, 0, 0, 0, &tv) + MOVW $0, R2 + MOVW $0, R3 + MOVW $0, R4 + MOVW $0, R5 + ADD $8, R15, R6 + MOVW $SYS_select, R1 + SYSCALL + RET + +TEXT runtime·gettid(SB),NOSPLIT,$0-4 + MOVW $SYS_gettid, R1 + SYSCALL + MOVW R2, ret+0(FP) + RET + +TEXT runtime·raise(SB),NOSPLIT|NOFRAME,$0 + MOVW $SYS_gettid, R1 + SYSCALL + MOVW R2, R2 // arg 1 tid + MOVW sig+0(FP), R3 // arg 2 + MOVW $SYS_tkill, R1 + SYSCALL + RET + +TEXT runtime·raiseproc(SB),NOSPLIT|NOFRAME,$0 + MOVW $SYS_getpid, R1 + SYSCALL + MOVW R2, R2 // arg 1 pid + MOVW sig+0(FP), R3 // arg 2 + MOVW $SYS_kill, R1 + SYSCALL + RET + +TEXT runtime·setitimer(SB),NOSPLIT|NOFRAME,$0-24 + MOVW mode+0(FP), R2 + MOVD new+8(FP), R3 + MOVD old+16(FP), R4 + MOVW $SYS_setitimer, R1 + SYSCALL + RET + +TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28 + MOVD addr+0(FP), R2 + MOVD n+8(FP), R3 + MOVD dst+16(FP), R4 + MOVW $SYS_mincore, R1 + SYSCALL + MOVW R2, ret+24(FP) + RET + +// func now() (sec int64, nsec int32) +TEXT time·now(SB),NOSPLIT,$16 + MOVD $0(R15), R2 + MOVD $0, R3 + MOVW $SYS_gettimeofday, R1 + SYSCALL + MOVD 0(R15), R2 // sec + MOVD 8(R15), R4 // usec + MOVD $1000, R3 + MULLD R3, R4 + MOVD R2, sec+0(FP) + MOVW R4, nsec+8(FP) + RET + +TEXT runtime·nanotime(SB),NOSPLIT,$16 + MOVW $1, R2 // CLOCK_MONOTONIC + MOVD $0(R15), R3 + MOVW $SYS_clock_gettime, R1 + SYSCALL + MOVD 0(R15), R2 // sec + MOVD 8(R15), R4 // nsec + // sec is in R2, nsec in R4 + // return nsec in R2 + MOVD $1000000000, R3 + MULLD R3, R2 + ADD R4, R2 + MOVD R2, ret+0(FP) + RET + +TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 + MOVW sig+0(FP), R2 + MOVD new+8(FP), R3 + MOVD old+16(FP), R4 + MOVW size+24(FP), R5 + MOVW $SYS_rt_sigprocmask, R1 + SYSCALL + MOVD $-4095, R3 + CMPUBLT R2, R3, 2(PC) + MOVD R0, 0(R0) // crash + RET + +TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36 + MOVD sig+0(FP), R2 + MOVD new+8(FP), R3 + MOVD old+16(FP), R4 + MOVD size+24(FP), R5 + MOVW $SYS_rt_sigaction, R1 + SYSCALL + MOVW R2, ret+32(FP) + RET + +TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 + MOVW sig+8(FP), R2 + MOVD info+16(FP), R3 + MOVD ctx+24(FP), R4 + MOVD fn+0(FP), R5 + BL R5 + RET + +TEXT runtime·sigtramp(SB),NOSPLIT,$64 + // initialize essential registers (just in case) + XOR R0, R0 + + // this might be called in external code context, + // where g is not set. + MOVB runtime·iscgo(SB), R6 + CMPBEQ R6, $0, 2(PC) + BL runtime·load_g(SB) + + MOVW R2, 8(R15) + MOVD R3, 16(R15) + MOVD R4, 24(R15) + MOVD $runtime·sigtrampgo(SB), R5 + BL R5 + RET + +TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0 + BR runtime·sigtramp(SB) + +// func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer +TEXT runtime·mmap(SB),NOSPLIT,$48-40 + MOVD addr+0(FP), R2 + MOVD n+8(FP), R3 + MOVW prot+16(FP), R4 + MOVW flags+20(FP), R5 + MOVW fd+24(FP), R6 + MOVWZ off+28(FP), R7 + + // s390x uses old_mmap, so the arguments need to be placed into + // a struct and a pointer to the struct passed to mmap. + MOVD R2, addr-48(SP) + MOVD R3, n-40(SP) + MOVD R4, prot-32(SP) + MOVD R5, flags-24(SP) + MOVD R6, fd-16(SP) + MOVD R7, off-8(SP) + + MOVD $addr-48(SP), R2 + MOVW $SYS_mmap, R1 + SYSCALL + MOVD $-4095, R3 + CMPUBLT R2, R3, 2(PC) + NEG R2 + MOVD R2, ret+32(FP) + RET + +TEXT runtime·munmap(SB),NOSPLIT|NOFRAME,$0 + MOVD addr+0(FP), R2 + MOVD n+8(FP), R3 + MOVW $SYS_munmap, R1 + SYSCALL + MOVD $-4095, R3 + CMPUBLT R2, R3, 2(PC) + MOVD R0, 0(R0) // crash + RET + +TEXT runtime·madvise(SB),NOSPLIT|NOFRAME,$0 + MOVD addr+0(FP), R2 + MOVD n+8(FP), R3 + MOVW flags+16(FP), R4 + MOVW $SYS_madvise, R1 + SYSCALL + // ignore failure - maybe pages are locked + RET + +// int64 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT runtime·futex(SB),NOSPLIT|NOFRAME,$0 + MOVD addr+0(FP), R2 + MOVW op+8(FP), R3 + MOVW val+12(FP), R4 + MOVD ts+16(FP), R5 + MOVD addr2+24(FP), R6 + MOVW val3+32(FP), R7 + MOVW $SYS_futex, R1 + SYSCALL + MOVW R2, ret+40(FP) + RET + +// int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void)); +TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0 + MOVW flags+0(FP), R3 + MOVD stk+8(FP), R2 + + // Copy mp, gp, fn off parent stack for use by child. + // Careful: Linux system call clobbers ???. + MOVD mm+16(FP), R7 + MOVD gg+24(FP), R8 + MOVD fn+32(FP), R9 + + MOVD R7, -8(R2) + MOVD R8, -16(R2) + MOVD R9, -24(R2) + MOVD $1234, R7 + MOVD R7, -32(R2) + + SYSCALL $SYS_clone + + // In parent, return. + CMPBEQ R2, $0, 3(PC) + MOVW R2, ret+40(FP) + RET + + // In child, on new stack. + // initialize essential registers + XOR R0, R0 + MOVD -32(R15), R7 + CMP R7, $1234 + BEQ 2(PC) + MOVD R0, 0(R0) + + // Initialize m->procid to Linux tid + SYSCALL $SYS_gettid + + MOVD -24(R15), R9 // fn + MOVD -16(R15), R8 // g + MOVD -8(R15), R7 // m + + CMPBEQ R7, $0, nog + CMP R8, $0 + BEQ nog + + MOVD R2, m_procid(R7) + + // In child, set up new stack + MOVD R7, g_m(R8) + MOVD R8, g + //CALL runtime·stackcheck(SB) + +nog: + // Call fn + BL R9 + + // It shouldn't return. If it does, exit that thread. + MOVW $111, R2 + MOVW $SYS_exit, R1 + SYSCALL + BR -2(PC) // keep exiting + +TEXT runtime·sigaltstack(SB),NOSPLIT|NOFRAME,$0 + MOVD new+0(FP), R2 + MOVD old+8(FP), R3 + MOVW $SYS_sigaltstack, R1 + SYSCALL + MOVD $-4095, R3 + CMPUBLT R2, R3, 2(PC) + MOVD R0, 0(R0) // crash + RET + +TEXT runtime·osyield(SB),NOSPLIT|NOFRAME,$0 + MOVW $SYS_sched_yield, R1 + SYSCALL + RET + +TEXT runtime·sched_getaffinity(SB),NOSPLIT|NOFRAME,$0 + MOVD pid+0(FP), R2 + MOVD len+8(FP), R3 + MOVD buf+16(FP), R4 + MOVW $SYS_sched_getaffinity, R1 + SYSCALL + MOVW R2, ret+24(FP) + RET + +// int32 runtime·epollcreate(int32 size); +TEXT runtime·epollcreate(SB),NOSPLIT|NOFRAME,$0 + MOVW size+0(FP), R2 + MOVW $SYS_epoll_create, R1 + SYSCALL + MOVW R2, ret+8(FP) + RET + +// int32 runtime·epollcreate1(int32 flags); +TEXT runtime·epollcreate1(SB),NOSPLIT|NOFRAME,$0 + MOVW flags+0(FP), R2 + MOVW $SYS_epoll_create1, R1 + SYSCALL + MOVW R2, ret+8(FP) + RET + +// func epollctl(epfd, op, fd int32, ev *epollEvent) int +TEXT runtime·epollctl(SB),NOSPLIT|NOFRAME,$0 + MOVW epfd+0(FP), R2 + MOVW op+4(FP), R3 + MOVW fd+8(FP), R4 + MOVD ev+16(FP), R5 + MOVW $SYS_epoll_ctl, R1 + SYSCALL + MOVW R2, ret+24(FP) + RET + +// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout); +TEXT runtime·epollwait(SB),NOSPLIT|NOFRAME,$0 + MOVW epfd+0(FP), R2 + MOVD ev+8(FP), R3 + MOVW nev+16(FP), R4 + MOVW timeout+20(FP), R5 + MOVW $SYS_epoll_wait, R1 + SYSCALL + MOVW R2, ret+24(FP) + RET + +// void runtime·closeonexec(int32 fd); +TEXT runtime·closeonexec(SB),NOSPLIT|NOFRAME,$0 + MOVW fd+0(FP), R2 // fd + MOVD $2, R3 // F_SETFD + MOVD $1, R4 // FD_CLOEXEC + MOVW $SYS_fcntl, R1 + SYSCALL + RET diff --git a/src/runtime/sys_s390x.go b/src/runtime/sys_s390x.go new file mode 100644 index 0000000000..2aa81e75c0 --- /dev/null +++ b/src/runtime/sys_s390x.go @@ -0,0 +1,45 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import "unsafe" + +// adjust Gobuf as if it executed a call to fn with context ctxt +// and then did an immediate Gosave. +func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) { + if buf.lr != 0 { + throw("invalid use of gostartcall") + } + buf.lr = buf.pc + buf.pc = uintptr(fn) + buf.ctxt = ctxt +} + +// Called to rewind context saved during morestack back to beginning of function. +// To help us, the linker emits a jmp back to the beginning right after the +// call to morestack. We just have to decode and apply that jump. +func rewindmorestack(buf *gobuf) { + var inst uint64 + if buf.pc&1 == 0 && buf.pc != 0 { + inst = *(*uint64)(unsafe.Pointer(buf.pc)) + switch inst >> 48 { + case 0xa7f4: // BRC (branch relative on condition) instruction. + inst >>= 32 + inst &= 0xFFFF + offset := int64(int16(inst)) + offset <<= 1 + buf.pc += uintptr(offset) + return + case 0xc0f4: // BRCL (branch relative on condition long) instruction. + inst >>= 16 + inst = inst & 0xFFFFFFFF + inst = (inst << 1) & 0xFFFFFFFF + buf.pc += uintptr(int32(inst)) + return + } + } + print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n") + throw("runtime: misuse of rewindmorestack") +} diff --git a/src/runtime/tls_s390x.s b/src/runtime/tls_s390x.s new file mode 100644 index 0000000000..cb6a21c114 --- /dev/null +++ b/src/runtime/tls_s390x.s @@ -0,0 +1,51 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go_asm.h" +#include "go_tls.h" +#include "funcdata.h" +#include "textflag.h" + +// We have to resort to TLS variable to save g (R13). +// One reason is that external code might trigger +// SIGSEGV, and our runtime.sigtramp don't even know we +// are in external code, and will continue to use R13, +// this might well result in another SIGSEGV. + +// save_g saves the g register into pthread-provided +// thread-local memory, so that we can call externally compiled +// s390x code that will overwrite this register. +// +// If !iscgo, this is a no-op. +// +// NOTE: setg_gcc<> assume this clobbers only R10 and R11. +TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0 + MOVB runtime·iscgo(SB), R10 + CMPBEQ R10, $0, nocgo + MOVW AR0, R11 + SLD $32, R11 + MOVW AR1, R11 + MOVD runtime·tls_g(SB), R10 + MOVD g, 0(R10)(R11*1) +nocgo: + RET + +// load_g loads the g register from pthread-provided +// thread-local memory, for use after calling externally compiled +// s390x code that overwrote those registers. +// +// This is never called directly from C code (it doesn't have to +// follow the C ABI), but it may be called from a C context, where the +// usual Go registers aren't set up. +// +// NOTE: _cgo_topofstack assumes this only clobbers g (R13), R10 and R11. +TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0 + MOVW AR0, R11 + SLD $32, R11 + MOVW AR1, R11 + MOVD runtime·tls_g(SB), R10 + MOVD 0(R10)(R11*1), g + RET + +GLOBL runtime·tls_g+0(SB),TLSBSS,$8 -- cgit v1.3 From e88f89028a55acf9c8b76b7f6ca284c3f9eb4cbd Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Thu, 31 Mar 2016 16:05:23 -0700 Subject: bytes, string: add Reset method to Reader Currently, there is no easy allocation-free way to turn a []byte or string into an io.Reader. Thus, we add a Reset method to bytes.Reader and strings.Reader to allow the reuse of these Readers with another []byte or string. This is consistent with the fact that many standard library io.Readers already support a Reset method of some type: bufio.Reader flate.Reader gzip.Reader zlib.Reader debug/dwarf.LineReader bytes.Buffer crypto/rc4.Cipher Fixes #15033 Change-Id: I456fd1af77af6ef0b4ac6228b058ac1458ff3d19 Reviewed-on: https://go-review.googlesource.com/21386 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/bytes/reader.go | 3 +++ src/bytes/reader_test.go | 20 ++++++++++++++++++++ src/strings/reader.go | 3 +++ src/strings/reader_test.go | 20 ++++++++++++++++++++ 4 files changed, 46 insertions(+) (limited to 'src') diff --git a/src/bytes/reader.go b/src/bytes/reader.go index 5941ebdab4..7aa30578b3 100644 --- a/src/bytes/reader.go +++ b/src/bytes/reader.go @@ -146,5 +146,8 @@ func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { return } +// Reset resets the Reader to be reading from b. +func (r *Reader) Reset(b []byte) { *r = Reader{b, 0, -1} } + // NewReader returns a new Reader reading from b. func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} } diff --git a/src/bytes/reader_test.go b/src/bytes/reader_test.go index b929a28260..add985d57e 100644 --- a/src/bytes/reader_test.go +++ b/src/bytes/reader_test.go @@ -256,3 +256,23 @@ func TestReaderLenSize(t *testing.T) { t.Errorf("Size = %d; want 3", r.Size()) } } + +func TestReaderReset(t *testing.T) { + r := NewReader([]byte("世界")) + if _, _, err := r.ReadRune(); err != nil { + t.Errorf("ReadRune: unexpected error: %v", err) + } + + const want = "abcdef" + r.Reset([]byte(want)) + if err := r.UnreadRune(); err == nil { + t.Errorf("UnreadRune: expected error, got nil") + } + buf, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("ReadAll: unexpected error: %v", err) + } + if got := string(buf); got != want { + t.Errorf("ReadAll: got %q, want %q", got, want) + } +} diff --git a/src/strings/reader.go b/src/strings/reader.go index 248e55245c..737873c099 100644 --- a/src/strings/reader.go +++ b/src/strings/reader.go @@ -145,6 +145,9 @@ func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { return } +// Reset resets the Reader to be reading from s. +func (r *Reader) Reset(s string) { *r = Reader{s, 0, -1} } + // NewReader returns a new Reader reading from s. // It is similar to bytes.NewBufferString but more efficient and read-only. func NewReader(s string) *Reader { return &Reader{s, 0, -1} } diff --git a/src/strings/reader_test.go b/src/strings/reader_test.go index 5003a37be4..7bca2e89a1 100644 --- a/src/strings/reader_test.go +++ b/src/strings/reader_test.go @@ -170,3 +170,23 @@ func TestReaderLenSize(t *testing.T) { t.Errorf("Size = %d; want 3", r.Size()) } } + +func TestReaderReset(t *testing.T) { + r := strings.NewReader("世界") + if _, _, err := r.ReadRune(); err != nil { + t.Errorf("ReadRune: unexpected error: %v", err) + } + + const want = "abcdef" + r.Reset(want) + if err := r.UnreadRune(); err == nil { + t.Errorf("UnreadRune: expected error, got nil") + } + buf, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("ReadAll: unexpected error: %v", err) + } + if got := string(buf); got != want { + t.Errorf("ReadAll: got %q, want %q", got, want) + } +} -- cgit v1.3 From dfded57819dd9111afffc25360cd3e147859d354 Mon Sep 17 00:00:00 2001 From: Cheng-Lung Sung Date: Wed, 6 Apr 2016 23:05:20 +0800 Subject: cmd/go: revise importPath when ImportPath is 'command-line-arguments' Fixes #14613 Change-Id: I40d9696db3879549e78373ef17f6c92bd4b3470b Reviewed-on: https://go-review.googlesource.com/21596 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/go/pkg.go | 10 ++++++++-- src/cmd/go/vendor_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index fa923c8873..30ef02beff 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -434,6 +434,12 @@ func vendoredImportPath(parent *Package, path string) (found string) { } targ := filepath.Join(dir[:i], vpath) if isDir(targ) && hasGoFiles(targ) { + importPath := parent.ImportPath + if importPath == "command-line-arguments" { + // If parent.ImportPath is 'command-line-arguments'. + // set to relative directory to root (also chopped root directory) + importPath = dir[len(root)+1:] + } // We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy. // We know the import path for parent's dir. // We chopped off some number of path elements and @@ -443,14 +449,14 @@ func vendoredImportPath(parent *Package, path string) (found string) { // (actually the same number of bytes) from parent's import path // and then append /vendor/path. chopped := len(dir) - i - if chopped == len(parent.ImportPath)+1 { + if chopped == len(importPath)+1 { // We walked up from c:\gopath\src\foo\bar // and found c:\gopath\src\vendor\path. // We chopped \foo\bar (length 8) but the import path is "foo/bar" (length 7). // Use "vendor/path" without any prefix. return vpath } - return parent.ImportPath[:len(parent.ImportPath)-chopped] + "/" + vpath + return importPath[:len(importPath)-chopped] + "/" + vpath } } return path diff --git a/src/cmd/go/vendor_test.go b/src/cmd/go/vendor_test.go index 199eab4471..bcb5082311 100644 --- a/src/cmd/go/vendor_test.go +++ b/src/cmd/go/vendor_test.go @@ -232,6 +232,32 @@ func TestVendorTest2(t *testing.T) { tg.run("test", "github.com/rsc/go-get-issue-11864/vendor/vendor.org/tx2") } +func TestVendorTest3(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + tg.run("get", "github.com/clsung/go-vendor-issue-14613") + + tg.run("build", "-i", "github.com/clsung/go-vendor-issue-14613") + + // test folder should work + tg.run("test", "-i", "github.com/clsung/go-vendor-issue-14613") + tg.run("test", "github.com/clsung/go-vendor-issue-14613") + + // test with specified _test.go should work too + tg.cd(filepath.Join(tg.path("."), "src")) + tg.run("test", "-i", "github.com/clsung/go-vendor-issue-14613/vendor_test.go") + tg.run("test", "github.com/clsung/go-vendor-issue-14613/vendor_test.go") + + // test with imported and not used + tg.run("test", "-i", "github.com/clsung/go-vendor-issue-14613/vendor/mylibtesttest/myapp/myapp_test.go") + tg.runFail("test", "github.com/clsung/go-vendor-issue-14613/vendor/mylibtesttest/myapp/myapp_test.go") + tg.grepStderr("imported and not used:", `should say "imported and not used"`) +} + func TestVendorList(t *testing.T) { testenv.MustHaveExternalNetwork(t) -- cgit v1.3 From 68ac1f774624faf99e7f6ec6592acb50f23b7874 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 7 Apr 2016 10:21:35 -0700 Subject: cmd/compile: Fix constant-folding of unsigned shifts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure the results of unsigned constant-folded shifts are sign-extended into the AuxInt field. Fixes #15175 Change-Id: I3490d1bc3d9b2e1578ed30964645508577894f58 Reviewed-on: https://go-review.googlesource.com/21586 Reviewed-by: Alexandru Moșoi Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/ssa/gen/generic.rules | 6 +-- src/cmd/compile/internal/ssa/rewritegeneric.go | 12 ++--- test/fixedbugs/issue15175.go | 66 ++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 test/fixedbugs/issue15175.go (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index b56e3f1b2d..dacc2007c8 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -81,13 +81,13 @@ (Rsh64Ux64 (Const64 [c]) (Const64 [d])) -> (Const64 [int64(uint64(c) >> uint64(d))]) (Lsh32x64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(int32(c) << uint64(d))]) (Rsh32x64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(int32(c) >> uint64(d))]) -(Rsh32Ux64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(uint32(c) >> uint64(d))]) +(Rsh32Ux64 (Const32 [c]) (Const64 [d])) -> (Const32 [int64(int32(uint32(c) >> uint64(d)))]) (Lsh16x64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(int16(c) << uint64(d))]) (Rsh16x64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(int16(c) >> uint64(d))]) -(Rsh16Ux64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(uint16(c) >> uint64(d))]) +(Rsh16Ux64 (Const16 [c]) (Const64 [d])) -> (Const16 [int64(int16(uint16(c) >> uint64(d)))]) (Lsh8x64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(int8(c) << uint64(d))]) (Rsh8x64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(int8(c) >> uint64(d))]) -(Rsh8Ux64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(uint8(c) >> uint64(d))]) +(Rsh8Ux64 (Const8 [c]) (Const64 [d])) -> (Const8 [int64(int8(uint8(c) >> uint64(d)))]) (Lsh64x64 (Const64 [0]) _) -> (Const64 [0]) (Rsh64x64 (Const64 [0]) _) -> (Const64 [0]) diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 932cb42235..9b0f43c414 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -6185,7 +6185,7 @@ func rewriteValuegeneric_OpRsh16Ux64(v *Value, config *Config) bool { _ = b // match: (Rsh16Ux64 (Const16 [c]) (Const64 [d])) // cond: - // result: (Const16 [int64(uint16(c) >> uint64(d))]) + // result: (Const16 [int64(int16(uint16(c) >> uint64(d)))]) for { v_0 := v.Args[0] if v_0.Op != OpConst16 { @@ -6198,7 +6198,7 @@ func rewriteValuegeneric_OpRsh16Ux64(v *Value, config *Config) bool { } d := v_1.AuxInt v.reset(OpConst16) - v.AuxInt = int64(uint16(c) >> uint64(d)) + v.AuxInt = int64(int16(uint16(c) >> uint64(d))) return true } // match: (Rsh16Ux64 (Const16 [0]) _) @@ -6547,7 +6547,7 @@ func rewriteValuegeneric_OpRsh32Ux64(v *Value, config *Config) bool { _ = b // match: (Rsh32Ux64 (Const32 [c]) (Const64 [d])) // cond: - // result: (Const32 [int64(uint32(c) >> uint64(d))]) + // result: (Const32 [int64(int32(uint32(c) >> uint64(d)))]) for { v_0 := v.Args[0] if v_0.Op != OpConst32 { @@ -6560,7 +6560,7 @@ func rewriteValuegeneric_OpRsh32Ux64(v *Value, config *Config) bool { } d := v_1.AuxInt v.reset(OpConst32) - v.AuxInt = int64(uint32(c) >> uint64(d)) + v.AuxInt = int64(int32(uint32(c) >> uint64(d))) return true } // match: (Rsh32Ux64 (Const32 [0]) _) @@ -7353,7 +7353,7 @@ func rewriteValuegeneric_OpRsh8Ux64(v *Value, config *Config) bool { _ = b // match: (Rsh8Ux64 (Const8 [c]) (Const64 [d])) // cond: - // result: (Const8 [int64(uint8(c) >> uint64(d))]) + // result: (Const8 [int64(int8(uint8(c) >> uint64(d)))]) for { v_0 := v.Args[0] if v_0.Op != OpConst8 { @@ -7366,7 +7366,7 @@ func rewriteValuegeneric_OpRsh8Ux64(v *Value, config *Config) bool { } d := v_1.AuxInt v.reset(OpConst8) - v.AuxInt = int64(uint8(c) >> uint64(d)) + v.AuxInt = int64(int8(uint8(c) >> uint64(d))) return true } // match: (Rsh8Ux64 (Const8 [0]) _) diff --git a/test/fixedbugs/issue15175.go b/test/fixedbugs/issue15175.go new file mode 100644 index 0000000000..c6cab532f8 --- /dev/null +++ b/test/fixedbugs/issue15175.go @@ -0,0 +1,66 @@ +// run + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Make sure unsigned shift results get sign-extended correctly. +package main + +import "fmt" + +func main() { + failed := false + a6 := uint8(253) + if got := a6 >> 0; got != 253 { + fmt.Printf("uint8(253)>>0 = %v, wanted 253\n", got) + failed = true + } + if got := f1(0, 2, 1, 0, 0, 1, true); got != 255 { + fmt.Printf("f1(...) = %v, wanted 255\n", got) + failed = true + } + if got := f2(1); got != 242 { + fmt.Printf("f2(...) = %v, wanted 242\n", got) + failed = true + } + if got := f3(false, 0, 0); got != 254 { + fmt.Printf("f3(...) = %v, wanted 254\n", got) + failed = true + } + if failed { + panic("bad") + } +} + +func f1(a1 uint, a2 int8, a3 int8, a4 int8, a5 uint8, a6 int, a7 bool) uint8 { + a5-- + a4 += (a2 << a1 << 2) | (a4 ^ a4<<(a1&a1)) - a3 // int8 + a6 -= a6 >> (2 + uint32(a2)>>3) // int + a1 += a1 // uint + a3 *= a4 << (a1 | a1) << (uint16(3) >> 2 & (1 - 0) & (uint16(1) << a5 << 3)) // int8 + a7 = a7 || ((a2 == a4) || (a7 && a7) || ((a5 == a5) || (a7 || a7))) // bool + return a5 >> a1 +} + +func f2(a1 uint8) uint8 { + a1-- + a1-- + a1 -= a1 + (a1 << 1) - (a1*a1*a1)<<(2-0+(3|3)-1) // uint8 + v1 := 0 * ((2 * 1) ^ 1) & ((uint(0) >> a1) + (2+0)*(uint(2)+0)) // uint + _ = v1 + return a1 >> (((2 ^ 2) >> (v1 | 2)) + 0) +} + +func f3(a1 bool, a2 uint, a3 int64) uint8 { + a3-- + v1 := 1 & (2 & 1 * (1 ^ 2) & (uint8(3*1) >> 0)) // uint8 + _ = v1 + v1 += v1 - (v1 >> a2) + (v1 << (a2 ^ a2) & v1) // uint8 + v1 *= v1 // uint8 + a3-- + v1 += v1 & v1 // uint8 + v1-- + v1 = ((v1 << 0) | v1>>0) + v1 // uint8 + return v1 >> 0 +} -- cgit v1.3 From 853f1a1a63b759686421196d419ddaa626a44bf5 Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke Date: Thu, 7 Apr 2016 19:59:59 -0700 Subject: net/http: fixed trivial go vet warnings Updates #15177 Change-Id: I748f025461f313b5b426821ead695f90d3011a6b Reviewed-on: https://go-review.googlesource.com/21677 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/net/http/client_test.go | 2 +- src/net/http/httptest/httptest_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index b9e17c5270..a9b30b1bf5 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -311,7 +311,7 @@ func TestClientRedirectContext(t *testing.T) { _, err := c.Do(req) ue, ok := err.(*url.Error) if !ok { - t.Fatalf("got error %T; want *url.Error") + t.Fatalf("got error %T; want *url.Error", err) } if ue.Err != ExportErrRequestCanceled && ue.Err != ExportErrRequestCanceledConn { t.Errorf("url.Error.Err = %v; want errRequestCanceled or errRequestCanceledConn", ue.Err) diff --git a/src/net/http/httptest/httptest_test.go b/src/net/http/httptest/httptest_test.go index 18ba73880e..4f9ecbd8bb 100644 --- a/src/net/http/httptest/httptest_test.go +++ b/src/net/http/httptest/httptest_test.go @@ -155,10 +155,10 @@ func TestNewRequest(t *testing.T) { got := NewRequest(tt.method, tt.uri, tt.body) slurp, err := ioutil.ReadAll(got.Body) if err != nil { - t.Errorf("%i. ReadAll: %v", i, err) + t.Errorf("%d. ReadAll: %v", i, err) } if string(slurp) != tt.wantBody { - t.Errorf("%i. Body = %q; want %q", i, slurp, tt.wantBody) + t.Errorf("%d. Body = %q; want %q", i, slurp, tt.wantBody) } got.Body = nil // before DeepEqual if !reflect.DeepEqual(got.URL, tt.want.URL) { -- cgit v1.3 From 4dae828f77a37ed87401f7877998b241f0d2c33e Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Fri, 8 Apr 2016 15:09:45 +1000 Subject: cmd/go: fix failing tests since vet was moved from x/tools Change-Id: I3276a118ced78f3efd8f1bc5fb8b8fa2fde52496 Reviewed-on: https://go-review.googlesource.com/21704 Reviewed-by: Brad Fitzpatrick --- src/cmd/go/go_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 3e595d187f..8a0416089c 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -2320,8 +2320,7 @@ func TestGoVetWithExternalTests(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.makeTempdir() - tg.setenv("GOPATH", tg.path(".")) - tg.run("get", "golang.org/x/tools/cmd/vet") + tg.run("install", "cmd/vet") tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.runFail("vet", "vetpkg") tg.grepBoth("missing argument for Printf", "go vet vetpkg did not find missing argument for Printf") @@ -2333,8 +2332,7 @@ func TestGoVetWithTags(t *testing.T) { tg := testgo(t) defer tg.cleanup() tg.makeTempdir() - tg.setenv("GOPATH", tg.path(".")) - tg.run("get", "golang.org/x/tools/cmd/vet") + tg.run("install", "cmd/vet") tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.runFail("vet", "-tags", "tagtest", "vetpkg") tg.grepBoth(`c\.go.*wrong number of args for format`, "go get vetpkg did not run scan tagged file") -- cgit v1.3 From 49e07f2b7e25a1f7a050f73fbb7807185e09e46b Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Fri, 8 Apr 2016 20:09:10 +1000 Subject: cmd/compile/internal/gc: unexport Export Export does not need to be exported. Change-Id: I252f0c024732f1d056817cab13e8e3c589b54d13 Reviewed-on: https://go-review.googlesource.com/21721 Run-TryBot: Dave Cheney TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/bexport.go | 4 ++-- src/cmd/compile/internal/gc/export.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 702090280f..909ff14982 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -135,8 +135,8 @@ type exporter struct { trace bool } -// Export writes the exportlist for localpkg to out and returns the number of bytes written. -func Export(out *bio.Buf, trace bool) int { +// export writes the exportlist for localpkg to out and returns the number of bytes written. +func export(out *bio.Buf, trace bool) int { p := exporter{ out: out, pkgIndex: make(map[*Pkg]int), diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 5d4add8ff4..2f94b9c62f 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -385,7 +385,7 @@ func dumpexport() { // save a copy of the export data var copy bytes.Buffer bcopy := bio.BufWriter(©) - size = Export(bcopy, Debug_export != 0) + size = export(bcopy, Debug_export != 0) bcopy.Flush() // flushing to bytes.Buffer cannot fail if n, err := bout.Write(copy.Bytes()); n != size || err != nil { Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err) @@ -407,7 +407,7 @@ func dumpexport() { pkgs = savedPkgs pkgMap = savedPkgMap } else { - size = Export(bout, Debug_export != 0) + size = export(bout, Debug_export != 0) } exportf("\n$$\n") } else { -- cgit v1.3 From d22357ce9dc650a69e78b37a6b25be1ee0b8b26c Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Thu, 7 Apr 2016 15:31:49 -0400 Subject: cmd/compile: cleanup -dynlink/-shared support check Moves the list of architectures that support shared libraries into a function. Also adds s390x to that list. Change-Id: I99c8a9f6cd4816ce3d53abaabaf8d002e25e6b28 Reviewed-on: https://go-review.googlesource.com/21661 Reviewed-by: Matthew Dempsky Reviewed-by: Michael Hudson-Doyle Run-TryBot: Michael Munday --- src/cmd/compile/internal/gc/main.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index c8a778c34a..03143f5d0a 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -91,6 +91,12 @@ func doversion() { os.Exit(0) } +// supportsDynlink reports whether or not the code generator for the given +// architecture supports the -shared and -dynlink flags. +func supportsDynlink(arch *sys.Arch) bool { + return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X) +} + func Main() { defer hidePanic() @@ -195,15 +201,13 @@ func Main() { obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y']) var flag_shared int var flag_dynlink bool - if Thearch.LinkArch.InFamily(sys.ARM, sys.AMD64, sys.ARM64, sys.I386, sys.PPC64) { + if supportsDynlink(Thearch.LinkArch.Arch) { obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared) + flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") } if Thearch.LinkArch.Family == sys.AMD64 { obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) } - if Thearch.LinkArch.InFamily(sys.ARM, sys.AMD64, sys.ARM64, sys.I386, sys.PPC64) { - flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") - } obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) -- cgit v1.3 From 8f2edf11998a30b497586ac0e9f75036a318280a Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Fri, 8 Apr 2016 19:14:03 +1000 Subject: cmd: replace bio.Buf with bio.Reader and bio.Writer Replace the bidirectional bio.Buf type with a pair of unidirectional buffered seekable Reader and Writers. Change-Id: I86664a06f93c94595dc67c2cbd21356feb6680ef Reviewed-on: https://go-review.googlesource.com/21720 Reviewed-by: Brad Fitzpatrick Run-TryBot: Dave Cheney TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/bexport.go | 4 +- src/cmd/compile/internal/gc/go.go | 4 +- src/cmd/compile/internal/gc/obj.go | 16 ++-- src/cmd/internal/bio/buf.go | 137 +++++++++++++++++++-------------- src/cmd/internal/obj/link.go | 2 +- src/cmd/internal/obj/objfile.go | 6 +- src/cmd/link/internal/ld/ar.go | 6 +- src/cmd/link/internal/ld/go.go | 2 +- src/cmd/link/internal/ld/ldelf.go | 10 +-- src/cmd/link/internal/ld/ldmacho.go | 16 ++-- src/cmd/link/internal/ld/ldpe.go | 18 ++--- src/cmd/link/internal/ld/lib.go | 49 ++++++------ src/cmd/link/internal/ld/link.go | 7 +- src/cmd/link/internal/ld/objfile.go | 8 +- 14 files changed, 154 insertions(+), 131 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 909ff14982..bb0a34e67b 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -124,7 +124,7 @@ const exportVersion = "v0" const exportInlined = true // default: true type exporter struct { - out *bio.Buf + out *bio.Writer pkgIndex map[*Pkg]int typIndex map[*Type]int inlined []*Func @@ -136,7 +136,7 @@ type exporter struct { } // export writes the exportlist for localpkg to out and returns the number of bytes written. -func export(out *bio.Buf, trace bool) int { +func export(out *bio.Writer, trace bool) int { p := exporter{ out: out, pkgIndex: make(map[*Pkg]int), diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index cd9db38fb4..ec7e219d95 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -133,7 +133,7 @@ var infile string var outfile string -var bout *bio.Buf +var bout *bio.Writer var nerrors int @@ -288,7 +288,7 @@ var Ctxt *obj.Link var writearchive int -var bstdout *bio.Buf +var bstdout *bio.Writer var Nacl bool diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 3920e25224..23c8be645c 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -37,7 +37,7 @@ func dumpobj() { bout.WriteString("!\n") arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) - startobj = bio.Boffset(bout) + startobj = bout.Offset() } fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) @@ -45,19 +45,19 @@ func dumpobj() { if writearchive != 0 { bout.Flush() - size := bio.Boffset(bout) - startobj + size := bout.Offset() - startobj if size&1 != 0 { bout.WriteByte(0) } - bio.Bseek(bout, startobj-ArhdrSize, 0) + bout.Seek(startobj-ArhdrSize, 0) formathdr(arhdr[:], "__.PKGDEF", size) bout.Write(arhdr[:]) bout.Flush() - bio.Bseek(bout, startobj+size+(size&1), 0) + bout.Seek(startobj+size+(size&1), 0) arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) - startobj = bio.Boffset(bout) + startobj = bout.Offset() fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) } @@ -92,11 +92,11 @@ func dumpobj() { if writearchive != 0 { bout.Flush() - size := bio.Boffset(bout) - startobj + size := bout.Offset() - startobj if size&1 != 0 { bout.WriteByte(0) } - bio.Bseek(bout, startobj-ArhdrSize, 0) + bout.Seek(startobj-ArhdrSize, 0) formathdr(arhdr[:], "_go_.o", size) bout.Write(arhdr[:]) } @@ -133,7 +133,7 @@ func dumpglobls() { funcsyms = nil } -func Bputname(b *bio.Buf, s *obj.LSym) { +func Bputname(b *bio.Writer, s *obj.LSym) { b.WriteString(s.Name) b.WriteByte(0) } diff --git a/src/cmd/internal/bio/buf.go b/src/cmd/internal/bio/buf.go index a1df26ca9c..0bd4658cdd 100644 --- a/src/cmd/internal/bio/buf.go +++ b/src/cmd/internal/bio/buf.go @@ -14,94 +14,116 @@ import ( const EOF = -1 -// Buf implements a seekable buffered I/O abstraction. -type Buf struct { +// Reader implements a seekable buffered io.Reader. +type Reader struct { f *os.File r *bufio.Reader +} + +// Writer implements a seekable buffered io.Writer. +type Writer struct { + f *os.File w *bufio.Writer } -func (b *Buf) Reader() *bufio.Reader { return b.r } -func (b *Buf) Writer() *bufio.Writer { return b.w } +// Reader returns this Reader's underlying bufio.Reader. +func (r *Reader) Reader() *bufio.Reader { return r.r } -func Create(name string) (*Buf, error) { +// Writer returns this Writer's underlying bufio.Writer. +func (w *Writer) Writer() *bufio.Writer { return w.w } + +// Create creates the file named name and returns a Writer +// for that file. +func Create(name string) (*Writer, error) { f, err := os.Create(name) if err != nil { return nil, err } - return &Buf{f: f, w: bufio.NewWriter(f)}, nil + return &Writer{f: f, w: bufio.NewWriter(f)}, nil } -func Open(name string) (*Buf, error) { +// Open returns a Reader for the file named name. +func Open(name string) (*Reader, error) { f, err := os.Open(name) if err != nil { return nil, err } - return &Buf{f: f, r: bufio.NewReader(f)}, nil + return &Reader{f: f, r: bufio.NewReader(f)}, nil } -func BufWriter(w io.Writer) *Buf { - return &Buf{w: bufio.NewWriter(w)} +// BufWriter returns a Writer on top of w. +// TODO(dfc) remove this method and replace caller with bufio.Writer. +func BufWriter(w io.Writer) *Writer { + return &Writer{w: bufio.NewWriter(w)} } -func BufReader(r io.Reader) *Buf { - return &Buf{r: bufio.NewReader(r)} +// BufWriter returns a Reader on top of r. +// TODO(dfc) remove this method and replace caller with bufio.Reader. +func BufReader(r io.Reader) *Reader { + return &Reader{r: bufio.NewReader(r)} } -func (b *Buf) Write(p []byte) (int, error) { - return b.w.Write(p) +func (w *Writer) Write(p []byte) (int, error) { + return w.w.Write(p) } -func (b *Buf) WriteString(p string) (int, error) { - return b.w.WriteString(p) +func (w *Writer) WriteString(p string) (int, error) { + return w.w.WriteString(p) } -func Bseek(b *Buf, offset int64, whence int) int64 { - if b.w != nil { - if err := b.w.Flush(); err != nil { - log.Fatalf("writing output: %v", err) - } - } else if b.r != nil { - if whence == 1 { - offset -= int64(b.r.Buffered()) - } +func (r *Reader) Seek(offset int64, whence int) int64 { + if whence == 1 { + offset -= int64(r.r.Buffered()) } - off, err := b.f.Seek(offset, whence) + off, err := r.f.Seek(offset, whence) if err != nil { log.Fatalf("seeking in output: %v", err) } - if b.r != nil { - b.r.Reset(b.f) - } + r.r.Reset(r.f) return off } -func Boffset(b *Buf) int64 { - if b.w != nil { - if err := b.w.Flush(); err != nil { - log.Fatalf("writing output: %v", err) - } +func (w *Writer) Seek(offset int64, whence int) int64 { + if err := w.w.Flush(); err != nil { + log.Fatalf("writing output: %v", err) } - off, err := b.f.Seek(0, 1) + off, err := w.f.Seek(offset, whence) + if err != nil { + log.Fatalf("seeking in output: %v", err) + } + return off +} + +func (r *Reader) Offset() int64 { + off, err := r.f.Seek(0, 1) if err != nil { log.Fatalf("seeking in output [0, 1]: %v", err) } - if b.r != nil { - off -= int64(b.r.Buffered()) + off -= int64(r.r.Buffered()) + return off +} + +func (w *Writer) Offset() int64 { + if err := w.w.Flush(); err != nil { + log.Fatalf("writing output: %v", err) + } + off, err := w.f.Seek(0, 1) + if err != nil { + log.Fatalf("seeking in output [0, 1]: %v", err) } return off } -func (b *Buf) Flush() error { - return b.w.Flush() +func (w *Writer) Flush() error { + return w.w.Flush() } -func (b *Buf) WriteByte(c byte) error { - return b.w.WriteByte(c) +func (w *Writer) WriteByte(c byte) error { + return w.w.WriteByte(c) } -func Bread(b *Buf, p []byte) int { - n, err := io.ReadFull(b.r, p) +func Bread(r *Reader, p []byte) int { + n, err := io.ReadFull(r.r, p) if n == 0 { if err != nil && err != io.EOF { n = -1 @@ -110,8 +132,8 @@ func Bread(b *Buf, p []byte) int { return n } -func Bgetc(b *Buf) int { - c, err := b.r.ReadByte() +func Bgetc(r *Reader) int { + c, err := r.r.ReadByte() if err != nil { if err != io.EOF { log.Fatalf("reading input: %v", err) @@ -121,28 +143,29 @@ func Bgetc(b *Buf) int { return int(c) } -func (b *Buf) Read(p []byte) (int, error) { - return b.r.Read(p) +func (r *Reader) Read(p []byte) (int, error) { + return r.r.Read(p) } -func (b *Buf) Peek(n int) ([]byte, error) { - return b.r.Peek(n) +func (r *Reader) Peek(n int) ([]byte, error) { + return r.r.Peek(n) } -func Brdline(b *Buf, delim int) string { - s, err := b.r.ReadBytes(byte(delim)) +func Brdline(r *Reader, delim int) string { + s, err := r.r.ReadBytes(byte(delim)) if err != nil { log.Fatalf("reading input: %v", err) } return string(s) } -func (b *Buf) Close() error { - var err error - if b.w != nil { - err = b.w.Flush() - } - err1 := b.f.Close() +func (r *Reader) Close() error { + return r.f.Close() +} + +func (w *Writer) Close() error { + err := w.w.Flush() + err1 := w.f.Close() if err == nil { err = err1 } diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 2c81ca2f08..c48c3d807f 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -629,7 +629,7 @@ type Link struct { Flag_shared int32 Flag_dynlink bool Flag_optimize bool - Bso *bio.Buf + Bso *bio.Writer Pathname string Goroot string Goroot_final string diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 405cbf446a..ed6d75eba3 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -121,7 +121,7 @@ import ( // The Go and C compilers, and the assembler, call writeobj to write // out a Go object file. The linker does not call this; the linker // does not write out object files. -func Writeobjdirect(ctxt *Link, b *bio.Buf) { +func Writeobjdirect(ctxt *Link, b *bio.Writer) { Flushplist(ctxt) WriteObjFile(ctxt, b) } @@ -374,7 +374,7 @@ func (w *objWriter) writeLengths() { w.writeInt(int64(w.nFile)) } -func newObjWriter(ctxt *Link, b *bio.Buf) *objWriter { +func newObjWriter(ctxt *Link, b *bio.Writer) *objWriter { return &objWriter{ ctxt: ctxt, wr: b.Writer(), @@ -383,7 +383,7 @@ func newObjWriter(ctxt *Link, b *bio.Buf) *objWriter { } } -func WriteObjFile(ctxt *Link, b *bio.Buf) { +func WriteObjFile(ctxt *Link, b *bio.Writer) { w := newObjWriter(ctxt, b) // Magic header diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index 205773c7f8..6a0aeb121f 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -82,7 +82,7 @@ func hostArchive(name string) { } var arhdr ArHdr - l := nextar(f, bio.Boffset(f), &arhdr) + l := nextar(f, f.Offset(), &arhdr) if l <= 0 { Exitf("%s missing armap", name) } @@ -118,7 +118,7 @@ func hostArchive(name string) { l = atolwhex(arhdr.size) h := ldobj(f, "libgcc", l, pname, name, ArchiveObj) - bio.Bseek(f, h.off, 0) + f.Seek(h.off, 0) h.ld(f, h.pkg, h.length, h.pn) } @@ -131,7 +131,7 @@ func hostArchive(name string) { type archiveMap map[string]uint64 // readArmap reads the archive symbol map. -func readArmap(filename string, f *bio.Buf, arhdr ArHdr) archiveMap { +func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap { is64 := arhdr.name == "/SYM64/" wordSize := 4 if is64 { diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 8bafaffd7c..5dad90dae6 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -27,7 +27,7 @@ func expandpkg(t0 string, pkg string) string { // once the dust settles, try to move some code to // libmach, so that other linkers and ar can share. -func ldpkg(f *bio.Buf, pkg string, length int64, filename string, whence int) { +func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int) { var p0, p1 int if Debug['g'] != 0 { diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index eafc6930d5..55884c07a2 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -268,7 +268,7 @@ type ElfSect struct { } type ElfObj struct { - f *bio.Buf + f *bio.Reader base int64 // offset in f where ELF begins length int64 // length of ELF is64 int @@ -447,13 +447,13 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) { } } -func ldelf(f *bio.Buf, pkg string, length int64, pn string) { +func ldelf(f *bio.Reader, pkg string, length int64, pn string) { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) } Ctxt.IncVersion() - base := int32(bio.Boffset(f)) + base := f.Offset() var add uint64 var e binary.ByteOrder @@ -601,7 +601,7 @@ func ldelf(f *bio.Buf, pkg string, length int64, pn string) { elfobj.nsect = uint(elfobj.shnum) for i := 0; uint(i) < elfobj.nsect; i++ { - if bio.Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { + if f.Seek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { goto bad } sect = &elfobj.sect[i] @@ -987,7 +987,7 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { sect.base = make([]byte, sect.size) err = fmt.Errorf("short read") - if bio.Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || bio.Bread(elfobj.f, sect.base) != len(sect.base) { + if elfobj.f.Seek(int64(uint64(elfobj.base)+sect.off), 0) < 0 || bio.Bread(elfobj.f, sect.base) != len(sect.base) { return err } diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go index 6376116d04..dffe6f69ce 100644 --- a/src/cmd/link/internal/ld/ldmacho.go +++ b/src/cmd/link/internal/ld/ldmacho.go @@ -43,7 +43,7 @@ const ( ) type LdMachoObj struct { - f *bio.Buf + f *bio.Reader base int64 // off in f where Mach-O begins length int64 // length of Mach-O is64 bool @@ -299,7 +299,7 @@ func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { rel := make([]LdMachoRel, sect.nreloc) n := int(sect.nreloc * 8) buf := make([]byte, n) - if bio.Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || bio.Bread(m.f, buf) != n { + if m.f.Seek(m.base+int64(sect.reloff), 0) < 0 || bio.Bread(m.f, buf) != n { return -1 } var p []byte @@ -345,7 +345,7 @@ func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { n := int(d.nindirectsyms) p := make([]byte, n*4) - if bio.Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || bio.Bread(m.f, p) != len(p) { + if m.f.Seek(m.base+int64(d.indirectsymoff), 0) < 0 || bio.Bread(m.f, p) != len(p) { return -1 } @@ -362,7 +362,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { } strbuf := make([]byte, symtab.strsize) - if bio.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || bio.Bread(m.f, strbuf) != len(strbuf) { + if m.f.Seek(m.base+int64(symtab.stroff), 0) < 0 || bio.Bread(m.f, strbuf) != len(strbuf) { return -1 } @@ -372,7 +372,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { } n := int(symtab.nsym * uint32(symsize)) symbuf := make([]byte, n) - if bio.Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || bio.Bread(m.f, symbuf) != len(symbuf) { + if m.f.Seek(m.base+int64(symtab.symoff), 0) < 0 || bio.Bread(m.f, symbuf) != len(symbuf) { return -1 } sym := make([]LdMachoSym, symtab.nsym) @@ -402,7 +402,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { return 0 } -func ldmacho(f *bio.Buf, pkg string, length int64, pn string) { +func ldmacho(f *bio.Reader, pkg string, length int64, pn string) { var err error var j int var is64 bool @@ -432,7 +432,7 @@ func ldmacho(f *bio.Buf, pkg string, length int64, pn string) { var name string Ctxt.IncVersion() - base := bio.Boffset(f) + base := f.Offset() if bio.Bread(f, hdr[:]) != len(hdr) { goto bad } @@ -557,7 +557,7 @@ func ldmacho(f *bio.Buf, pkg string, length int64, pn string) { } dat = make([]byte, c.seg.filesz) - if bio.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || bio.Bread(f, dat) != len(dat) { + if f.Seek(m.base+int64(c.seg.fileoff), 0) < 0 || bio.Bread(f, dat) != len(dat) { err = fmt.Errorf("cannot load object data: %v", err) goto bad } diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go index e97e842e7f..ba5b928ea0 100644 --- a/src/cmd/link/internal/ld/ldpe.go +++ b/src/cmd/link/internal/ld/ldpe.go @@ -118,7 +118,7 @@ type PeSect struct { } type PeObj struct { - f *bio.Buf + f *bio.Reader name string base uint32 sect []PeSect @@ -129,14 +129,14 @@ type PeObj struct { snames []byte } -func ldpe(f *bio.Buf, pkg string, length int64, pn string) { +func ldpe(f *bio.Reader, pkg string, length int64, pn string) { if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) } var sect *PeSect Ctxt.IncVersion() - base := int32(bio.Boffset(f)) + base := f.Offset() peobj := new(PeObj) peobj.f = f @@ -174,14 +174,14 @@ func ldpe(f *bio.Buf, pkg string, length int64, pn string) { // TODO return error if found .cormeta // load string table - bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if bio.Bread(f, symbuf[:4]) != 4 { goto bad } l = Le32(symbuf[:]) peobj.snames = make([]byte, l) - bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) if bio.Bread(f, peobj.snames) != len(peobj.snames) { goto bad } @@ -202,9 +202,9 @@ func ldpe(f *bio.Buf, pkg string, length int64, pn string) { peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) peobj.npesym = uint(peobj.fh.NumberOfSymbols) - bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) + f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { - bio.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) + f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) if bio.Bread(f, symbuf[:]) != len(symbuf) { goto bad } @@ -290,7 +290,7 @@ func ldpe(f *bio.Buf, pkg string, length int64, pn string) { } r = make([]Reloc, rsect.sh.NumberOfRelocations) - bio.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) + f.Seek(int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { rp = &r[j] if bio.Bread(f, symbuf[:10]) != 10 { @@ -466,7 +466,7 @@ func pemap(peobj *PeObj, sect *PeSect) int { if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file return 0 } - if bio.Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || bio.Bread(peobj.f, sect.base) != len(sect.base) { + if peobj.f.Seek(int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || bio.Bread(peobj.f, sect.base) != len(sect.base) { return -1 } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 789eaef1a5..f8cc995c30 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -241,9 +241,10 @@ const ( var ( headstring string // buffered output - Bso bio.Buf + Bso bio.Writer ) +// TODO(dfc) outBuf duplicates bio.Writer type outBuf struct { w *bufio.Writer f *os.File @@ -739,11 +740,11 @@ func loadlib() { * look for the next file in an archive. * adapted from libmach. */ -func nextar(bp *bio.Buf, off int64, a *ArHdr) int64 { +func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 { if off&1 != 0 { off++ } - bio.Bseek(bp, off, 0) + bp.Seek(off, 0) buf := make([]byte, SAR_HDR) if n := bio.Bread(bp, buf); n < len(buf) { if n >= 0 { @@ -782,9 +783,9 @@ func objfile(lib *Library) { magbuf := make([]byte, len(ARMAG)) if bio.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { /* load it as a regular file */ - l := bio.Bseek(f, 0, 2) + l := f.Seek(0, 2) - bio.Bseek(f, 0, 0) + f.Seek(0, 0) ldobj(f, pkg, l, lib.File, lib.File, FileObj) f.Close() @@ -792,7 +793,7 @@ func objfile(lib *Library) { } /* process __.PKGDEF */ - off := bio.Boffset(f) + off := f.Offset() var arhdr ArHdr l := nextar(f, off, &arhdr) @@ -808,12 +809,12 @@ func objfile(lib *Library) { } if Buildmode == BuildmodeShared { - before := bio.Boffset(f) + before := f.Offset() pkgdefBytes := make([]byte, atolwhex(arhdr.size)) bio.Bread(f, pkgdefBytes) hash := sha1.Sum(pkgdefBytes) lib.hash = hash[:] - bio.Bseek(f, before, 0) + f.Seek(before, 0) } off += l @@ -853,7 +854,7 @@ out: } type Hostobj struct { - ld func(*bio.Buf, string, int64, string) + ld func(*bio.Reader, string, int64, string) pkg string pn string file string @@ -874,7 +875,7 @@ var internalpkg = []string{ "runtime/msan", } -func ldhostobj(ld func(*bio.Buf, string, int64, string), f *bio.Buf, pkg string, length int64, pn string, file string) *Hostobj { +func ldhostobj(ld func(*bio.Reader, string, int64, string), f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj { isinternal := false for i := 0; i < len(internalpkg); i++ { if pkg == internalpkg[i] { @@ -905,24 +906,22 @@ func ldhostobj(ld func(*bio.Buf, string, int64, string), f *bio.Buf, pkg string, h.pkg = pkg h.pn = pn h.file = file - h.off = bio.Boffset(f) + h.off = f.Offset() h.length = length return h } func hostobjs() { - var f *bio.Buf var h *Hostobj for i := 0; i < len(hostobj); i++ { h = &hostobj[i] - var err error - f, err = bio.Open(h.file) - if f == nil { + f, err := bio.Open(h.file) + if err != nil { Exitf("cannot reopen %s: %v", h.pn, err) } - bio.Bseek(f, h.off, 0) + f.Seek(h.off, 0) h.ld(f, h.pkg, h.length, h.pn) f.Close() } @@ -1266,15 +1265,15 @@ func hostlinkArchArgs() []string { // ldobj loads an input object. If it is a host object (an object // compiled by a non-Go compiler) it returns the Hostobj pointer. If // it is a Go object, it returns nil. -func ldobj(f *bio.Buf, pkg string, length int64, pn string, file string, whence int) *Hostobj { - eof := bio.Boffset(f) + length +func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, whence int) *Hostobj { + eof := f.Offset() + length - start := bio.Boffset(f) + start := f.Offset() c1 := bio.Bgetc(f) c2 := bio.Bgetc(f) c3 := bio.Bgetc(f) c4 := bio.Bgetc(f) - bio.Bseek(f, start, 0) + f.Seek(start, 0) magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) if magic == 0x7f454c46 { // \x7F E L F @@ -1334,7 +1333,7 @@ func ldobj(f *bio.Buf, pkg string, length int64, pn string, file string, whence } /* skip over exports and other info -- ends with \n!\n */ - import0 := bio.Boffset(f) + import0 := f.Offset() c1 = '\n' // the last line ended in \n c2 = bio.Bgetc(f) @@ -1349,13 +1348,13 @@ func ldobj(f *bio.Buf, pkg string, length int64, pn string, file string, whence } } - import1 := bio.Boffset(f) + import1 := f.Offset() - bio.Bseek(f, import0, 0) + f.Seek(import0, 0) ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n - bio.Bseek(f, import1, 0) + f.Seek(import1, 0) - LoadObjFile(Ctxt, f, pkg, eof-bio.Boffset(f), pn) + LoadObjFile(Ctxt, f, pkg, eof-f.Offset(), pn) return nil } diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index d3f9ed3703..cbcc979c85 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -165,9 +165,10 @@ type Link struct { Headtype int Arch *sys.Arch Debugvlog int32 - Bso *bio.Buf - Windows int32 - Goroot string + + Bso *bio.Writer + Windows int32 + Goroot string // Symbol lookup based on name and indexed by version. Hash []map[string]*LSym diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index 6f177861f0..61a67cf94c 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -147,8 +147,8 @@ type objReader struct { file []*LSym } -func LoadObjFile(ctxt *Link, f *bio.Buf, pkg string, length int64, pn string) { - start := bio.Boffset(f) +func LoadObjFile(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { + start := f.Offset() r := &objReader{ rd: f.Reader(), pkg: pkg, @@ -157,8 +157,8 @@ func LoadObjFile(ctxt *Link, f *bio.Buf, pkg string, length int64, pn string) { dupSym: &LSym{Name: ".dup"}, } r.loadObjFile() - if bio.Boffset(f) != start+length { - log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(bio.Boffset(f)), int64(start+length)) + if f.Offset() != start+length { + log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length) } } -- cgit v1.3 From 01360a64c5895a9ec8b8c34140415fe34c3de201 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Wed, 6 Apr 2016 20:19:47 -0700 Subject: io: change the name of ReadAtSizer to SizedReaderAt This is a proposal. The old name is pretty poor. The new one describes it better and may be easier to remember. It does not start with Read, though I think that inconsistency is worthwhile. Reworded the comment a bit for clarity. Change-Id: Icb4f9c663cc68958e0363d7ff78a0b29cc521f98 Reviewed-on: https://go-review.googlesource.com/21629 Reviewed-by: Andrew Gerrand Reviewed-by: Brad Fitzpatrick --- src/io/io.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/io/io.go b/src/io/io.go index 23401dae93..023473c79b 100644 --- a/src/io/io.go +++ b/src/io/io.go @@ -274,10 +274,11 @@ type RuneScanner interface { UnreadRune() error } -// ReadAtSizer is the interface that groups the basic ReadAt and Size -// methods, representing a sized data source that supports random +// SizedReaderAt is the interface that groups the basic ReadAt method +// with a Size method that reports the total size of the underlying +// object. It represents a fixed-size data source that supports random // access by multiple concurrent goroutines. -type ReadAtSizer interface { +type SizedReaderAt interface { ReaderAt // Size reports the length of the data source in bytes. Size() int64 -- cgit v1.3 From 78715cebcfcca3aaaaba4dd41ef6b82a46d7b93d Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 8 Apr 2016 11:55:05 -0400 Subject: cmd/link: add s390x to link tool main function Change-Id: I83bc2b4a00216b069f133113e4ae9ad76c98a708 Reviewed-on: https://go-review.googlesource.com/21741 Run-TryBot: Michael Munday Reviewed-by: Brad Fitzpatrick --- src/cmd/link/main.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go index e52b718699..f92e02eac3 100644 --- a/src/cmd/link/main.go +++ b/src/cmd/link/main.go @@ -11,6 +11,7 @@ import ( "cmd/link/internal/arm64" "cmd/link/internal/mips64" "cmd/link/internal/ppc64" + "cmd/link/internal/s390x" "cmd/link/internal/x86" "fmt" "os" @@ -33,5 +34,7 @@ func main() { mips64.Main() case "ppc64", "ppc64le": ppc64.Main() + case "s390x": + s390x.Main() } } -- cgit v1.3 From 59d186832b94349d683431e01e084d6ce460f476 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 8 Apr 2016 17:50:03 +0000 Subject: context: disable more flaky tests on openbsd Updates #15158 Change-Id: Icb3788152a7a5a9b0d56ea38da46d770ffdce413 Reviewed-on: https://go-review.googlesource.com/21763 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Matthew Dempsky --- src/context/context_test.go | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/context/context_test.go b/src/context/context_test.go index 74af9a301c..573470e084 100644 --- a/src/context/context_test.go +++ b/src/context/context_test.go @@ -494,6 +494,9 @@ func TestLayersTimeout(t *testing.T) { } func testLayers(t *testing.T, seed int64, testTimeout bool) { + if runtime.GOOS == "openbsd" { + testenv.SkipFlaky(t, 15158) + } rand.Seed(seed) errorf := func(format string, a ...interface{}) { t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) -- cgit v1.3 From 6c5352f181846b73d532c039df3017befe657d6a Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 8 Apr 2016 11:40:51 -0400 Subject: syscall: add assembly for Linux on s390x Change-Id: I42ade65a91f3effc03dd663ee449410baa9f8ca8 Reviewed-on: https://go-review.googlesource.com/21740 Reviewed-by: Bill O'Farrell Reviewed-by: Minux Ma Run-TryBot: Minux Ma TryBot-Result: Gobot Gobot --- src/syscall/asm_linux_s390x.s | 156 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/syscall/asm_linux_s390x.s (limited to 'src') diff --git a/src/syscall/asm_linux_s390x.s b/src/syscall/asm_linux_s390x.s new file mode 100644 index 0000000000..e22a92b966 --- /dev/null +++ b/src/syscall/asm_linux_s390x.s @@ -0,0 +1,156 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// +// System calls for s390x, Linux +// + +// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64) +TEXT ·Syscall(SB),NOSPLIT,$0-56 + BL runtime·entersyscall(SB) + MOVD a1+8(FP), R2 + MOVD a2+16(FP), R3 + MOVD a3+24(FP), R4 + MOVD $0, R5 + MOVD $0, R6 + MOVD $0, R7 + MOVD trap+0(FP), R1 // syscall entry + SYSCALL + MOVD $0xfffffffffffff001, R8 + CMPUBLT R2, R8, ok + MOVD $-1, r1+32(FP) + MOVD $0, r2+40(FP) + NEG R2, R2 + MOVD R2, err+48(FP) // errno + BL runtime·exitsyscall(SB) + RET +ok: + MOVD R2, r1+32(FP) + MOVD R3, r2+40(FP) + MOVD $0, err+48(FP) // errno + BL runtime·exitsyscall(SB) + RET + +// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-80 + BL runtime·entersyscall(SB) + MOVD a1+8(FP), R2 + MOVD a2+16(FP), R3 + MOVD a3+24(FP), R4 + MOVD a4+32(FP), R5 + MOVD a5+40(FP), R6 + MOVD a6+48(FP), R7 + MOVD trap+0(FP), R1 // syscall entry + SYSCALL + MOVD $0xfffffffffffff001, R8 + CMPUBLT R2, R8, ok6 + MOVD $-1, r1+56(FP) + MOVD $0, r2+64(FP) + NEG R2, R2 + MOVD R2, err+72(FP) // errno + BL runtime·exitsyscall(SB) + RET +ok6: + MOVD R2, r1+56(FP) + MOVD R3, r2+64(FP) + MOVD $0, err+72(FP) // errno + BL runtime·exitsyscall(SB) + RET + +// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) +TEXT ·RawSyscall(SB),NOSPLIT,$0-56 + MOVD a1+8(FP), R2 + MOVD a2+16(FP), R3 + MOVD a3+24(FP), R4 + MOVD $0, R5 + MOVD $0, R6 + MOVD $0, R7 + MOVD trap+0(FP), R1 // syscall entry + SYSCALL + MOVD $0xfffffffffffff001, R8 + CMPUBLT R2, R8, ok1 + MOVD $-1, r1+32(FP) + MOVD $0, r2+40(FP) + NEG R2, R2 + MOVD R2, err+48(FP) // errno + RET +ok1: + MOVD R2, r1+32(FP) + MOVD R3, r2+40(FP) + MOVD $0, err+48(FP) // errno + RET + +// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +TEXT ·RawSyscall6(SB),NOSPLIT,$0-80 + MOVD a1+8(FP), R2 + MOVD a2+16(FP), R3 + MOVD a3+24(FP), R4 + MOVD a4+32(FP), R5 + MOVD a5+40(FP), R6 + MOVD a6+48(FP), R7 + MOVD trap+0(FP), R1 // syscall entry + SYSCALL + MOVD $0xfffffffffffff001, R8 + CMPUBLT R2, R8, ok2 + MOVD $-1, r1+56(FP) + MOVD $0, r2+64(FP) + NEG R2, R2 + MOVD R2, err+72(FP) // errno + RET +ok2: + MOVD R2, r1+56(FP) + MOVD R3, r2+64(FP) + MOVD $0, err+72(FP) // errno + RET + +#define SYS_SOCKETCALL 102 /* from zsysnum_linux_s390x.go */ + +// func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int) +// Kernel interface gets call sub-number and pointer to a0. +TEXT ·socketcall(SB),NOSPLIT,$0-72 + BL runtime·entersyscall(SB) + MOVD $SYS_SOCKETCALL, R1 // syscall entry + MOVD call+0(FP), R2 // socket call number + MOVD $a0+8(FP), R3 // pointer to call arguments + MOVD $0, R4 + MOVD $0, R5 + MOVD $0, R6 + MOVD $0, R7 + SYSCALL + MOVD $0xfffffffffffff001, R8 + CMPUBLT R2, R8, oksock + MOVD $-1, n+56(FP) + NEG R2, R2 + MOVD R2, err+64(FP) + BL runtime·exitsyscall(SB) + RET +oksock: + MOVD R2, n+56(FP) + MOVD $0, err+64(FP) + CALL runtime·exitsyscall(SB) + RET + +// func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int) +// Kernel interface gets call sub-number and pointer to a0. +TEXT ·rawsocketcall(SB),NOSPLIT,$0-72 + MOVD $SYS_SOCKETCALL, R1 // syscall entry + MOVD call+0(FP), R2 // socket call number + MOVD $a0+8(FP), R3 // pointer to call arguments + MOVD $0, R4 + MOVD $0, R5 + MOVD $0, R6 + MOVD $0, R7 + SYSCALL + MOVD $0xfffffffffffff001, R8 + CMPUBLT R2, R8, oksock1 + MOVD $-1, n+56(FP) + NEG R2, R2 + MOVD R2, err+64(FP) + RET +oksock1: + MOVD R2, n+56(FP) + MOVD $0, err+64(FP) + RET -- cgit v1.3 From 59af53d681845a8b0be2a728ca1b59aee5ad9ea6 Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke Date: Thu, 7 Apr 2016 23:22:30 -0700 Subject: bytes: add ContainsRune Make package bytes consistent with strings by adding missing function ContainsRune. Fixes #15189 Change-Id: Ie09080b389e55bbe070c57aa3bd134053a805423 Reviewed-on: https://go-review.googlesource.com/21710 Run-TryBot: Rob Pike TryBot-Result: Gobot Gobot Reviewed-by: Rob Pike --- src/bytes/bytes.go | 5 +++++ src/bytes/bytes_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'src') diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go index 698d881c9d..305c85d9f4 100644 --- a/src/bytes/bytes.go +++ b/src/bytes/bytes.go @@ -88,6 +88,11 @@ func ContainsAny(b []byte, chars string) bool { return IndexAny(b, chars) >= 0 } +// ContainsRune reports whether the Unicode code point r is within b. +func ContainsRune(b []byte, r rune) bool { + return IndexRune(b, r) >= 0 +} + // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. func Index(s, sep []byte) int { n := len(sep) diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go index 40e8d09b59..620cfd1bce 100644 --- a/src/bytes/bytes_test.go +++ b/src/bytes/bytes_test.go @@ -1245,6 +1245,30 @@ func TestContainsAny(t *testing.T) { } } +var ContainsRuneTests = []struct { + b []byte + r rune + expected bool +}{ + {[]byte(""), 'a', false}, + {[]byte("a"), 'a', true}, + {[]byte("aaa"), 'a', true}, + {[]byte("abc"), 'y', false}, + {[]byte("abc"), 'c', true}, + {[]byte("a☺b☻c☹d"), 'x', false}, + {[]byte("a☺b☻c☹d"), '☻', true}, + {[]byte("aRegExp*"), '*', true}, +} + +func TestContainsRune(t *testing.T) { + for _, ct := range ContainsRuneTests { + if ContainsRune(ct.b, ct.r) != ct.expected { + t.Errorf("ContainsRune(%q, %q) = %v, want %v", + ct.b, ct.r, !ct.expected, ct.expected) + } + } +} + var makeFieldsInput = func() []byte { x := make([]byte, 1<<20) // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. -- cgit v1.3 From 9ada88aec271a2f08c998e9669331145803e7d5a Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 8 Apr 2016 13:02:49 -0400 Subject: cmd/cgo: increase s390x int type size to 8 bytes The size of the int type in Go on s390x is 8 bytes, not 4. Change-Id: I1a71ce8c9925f3499abb61c1aa4f6fa2d2ec0d7e Reviewed-on: https://go-review.googlesource.com/21760 Reviewed-by: Bill O'Farrell Reviewed-by: Brad Fitzpatrick --- src/cmd/cgo/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 5e863549d6..2dc36c20db 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -156,7 +156,7 @@ var intSizeMap = map[string]int64{ "ppc64": 8, "ppc64le": 8, "s390": 4, - "s390x": 4, + "s390x": 8, } var cPrefix string -- cgit v1.3 From 0fb7b4cccd02df10f239ed77d6d85566b6388b83 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 8 Apr 2016 15:14:37 +0200 Subject: runtime: emit file:line info into traces This makes traces self-contained and simplifies trace workflow in modern cloud environments where it is simpler to reach a service via HTTP than to obtain the binary. Change-Id: I6ff3ca694dc698270f1e29da37d5efaf4e843a0d Reviewed-on: https://go-review.googlesource.com/21732 Run-TryBot: Dmitry Vyukov TryBot-Result: Gobot Gobot Reviewed-by: Hyang-Ah Hana Kim --- src/cmd/trace/main.go | 5 - src/internal/trace/parser.go | 174 +++++++++++++--------------------- src/runtime/trace.go | 98 ++++++++++++++++--- src/runtime/trace/trace_stack_test.go | 7 +- 4 files changed, 156 insertions(+), 128 deletions(-) (limited to 'src') diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index e493be91b7..12bf8c3c16 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -104,11 +104,6 @@ func parseEvents() ([]*trace.Event, error) { loader.err = fmt.Errorf("failed to parse trace: %v", err) return } - err = trace.Symbolize(events, programBinary) - if err != nil { - loader.err = fmt.Errorf("failed to symbolize trace: %v", err) - return - } loader.events = events }) return loader.events, loader.err diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go index e325678733..5db3fc317e 100644 --- a/src/internal/trace/parser.go +++ b/src/internal/trace/parser.go @@ -5,15 +5,10 @@ package trace import ( - "bufio" "bytes" "fmt" "io" - "os" - "os/exec" "sort" - "strconv" - "strings" ) // Event describes one event in the trace. @@ -59,11 +54,11 @@ const ( // Parse parses, post-processes and verifies the trace. func Parse(r io.Reader) ([]*Event, error) { - rawEvents, err := readTrace(r) + rawEvents, strings, err := readTrace(r) if err != nil { return nil, err } - events, err := parseEvents(rawEvents) + events, stacks, err := parseEvents(rawEvents, strings) if err != nil { return nil, err } @@ -75,6 +70,12 @@ func Parse(r io.Reader) ([]*Event, error) { if err != nil { return nil, err } + // Attach stack traces. + for _, ev := range events { + if ev.StkID != 0 { + ev.Stk = stacks[ev.StkID] + } + } return events, nil } @@ -87,19 +88,20 @@ type rawEvent struct { // readTrace does wire-format parsing and verification. // It does not care about specific event types and argument meaning. -func readTrace(r io.Reader) ([]rawEvent, error) { +func readTrace(r io.Reader) ([]rawEvent, map[uint64]string, error) { // Read and validate trace header. var buf [16]byte off, err := r.Read(buf[:]) if off != 16 || err != nil { - return nil, fmt.Errorf("failed to read header: read %v, err %v", off, err) + return nil, nil, fmt.Errorf("failed to read header: read %v, err %v", off, err) } if !bytes.Equal(buf[:], []byte("go 1.5 trace\x00\x00\x00\x00")) { - return nil, fmt.Errorf("not a trace file") + return nil, nil, fmt.Errorf("not a trace file") } // Read events. var events []rawEvent + strings := make(map[uint64]string) for { // Read event type and number of arguments (1 byte). off0 := off @@ -108,18 +110,51 @@ func readTrace(r io.Reader) ([]rawEvent, error) { break } if err != nil || n != 1 { - return nil, fmt.Errorf("failed to read trace at offset 0x%x: n=%v err=%v", off0, n, err) + return nil, nil, fmt.Errorf("failed to read trace at offset 0x%x: n=%v err=%v", off0, n, err) } off += n typ := buf[0] << 2 >> 2 narg := buf[0] >> 6 + if typ == EvString { + // String dictionary entry [ID, length, string]. + var id uint64 + id, off, err = readVal(r, off) + if err != nil { + return nil, nil, err + } + if id == 0 { + return nil, nil, fmt.Errorf("string at offset %d has invalid id 0", off) + } + if strings[id] != "" { + return nil, nil, fmt.Errorf("string at offset %d has duplicate id %v", off, id) + } + var ln uint64 + ln, off, err = readVal(r, off) + if err != nil { + return nil, nil, err + } + if ln == 0 { + return nil, nil, fmt.Errorf("string at offset %d has invalie length 0", off) + } + if ln > 1e6 { + return nil, nil, fmt.Errorf("string at offset %d has too large length %v", off, ln) + } + buf := make([]byte, ln) + n, err := io.ReadFull(r, buf) + if err != nil { + return nil, nil, fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, ln, err) + } + off += n + strings[id] = string(buf) + continue + } ev := rawEvent{typ: typ, off: off0} if narg < 3 { for i := 0; i < int(narg)+2; i++ { // sequence number and time stamp are present but not counted in narg var v uint64 v, off, err = readVal(r, off) if err != nil { - return nil, err + return nil, nil, err } ev.args = append(ev.args, v) } @@ -128,34 +163,34 @@ func readTrace(r io.Reader) ([]rawEvent, error) { var v uint64 v, off, err = readVal(r, off) if err != nil { - return nil, err + return nil, nil, err } evLen := v off1 := off for evLen > uint64(off-off1) { v, off, err = readVal(r, off) if err != nil { - return nil, err + return nil, nil, err } ev.args = append(ev.args, v) } if evLen != uint64(off-off1) { - return nil, fmt.Errorf("event has wrong length at offset 0x%x: want %v, got %v", off0, evLen, off-off1) + return nil, nil, fmt.Errorf("event has wrong length at offset 0x%x: want %v, got %v", off0, evLen, off-off1) } } events = append(events, ev) } - return events, nil + return events, strings, nil } // Parse events transforms raw events into events. // It does analyze and verify per-event-type arguments. -func parseEvents(rawEvents []rawEvent) (events []*Event, err error) { +func parseEvents(rawEvents []rawEvent, strings map[uint64]string) (events []*Event, stacks map[uint64][]*Frame, err error) { var ticksPerSec, lastSeq, lastTs int64 var lastG, timerGoid uint64 var lastP int lastGs := make(map[int]uint64) // last goroutine running on P - stacks := make(map[uint64][]*Frame) + stacks = make(map[uint64][]*Frame) for _, raw := range rawEvents { if raw.typ == EvNone || raw.typ >= EvCount { err = fmt.Errorf("unknown event type %v at offset 0x%x", raw.typ, raw.off) @@ -211,16 +246,20 @@ func parseEvents(rawEvents []rawEvent) (events []*Event, err error) { raw.off, size) return } - if uint64(len(raw.args)) != size+2 { + if want := 2 + 4*size; uint64(len(raw.args)) != want { err = fmt.Errorf("EvStack has wrong number of arguments at offset 0x%x: want %v, got %v", - raw.off, size+2, len(raw.args)) + raw.off, want, len(raw.args)) return } id := raw.args[0] if id != 0 && size > 0 { stk := make([]*Frame, size) for i := 0; i < int(size); i++ { - stk[i] = &Frame{PC: raw.args[i+2]} + pc := raw.args[2+i*4+0] + fn := raw.args[2+i*4+1] + file := raw.args[2+i*4+2] + line := raw.args[2+i*4+3] + stk[i] = &Frame{PC: pc, Fn: strings[fn], File: strings[file], Line: int(line)} } stacks[id] = stk } @@ -263,13 +302,6 @@ func parseEvents(rawEvents []rawEvent) (events []*Event, err error) { return } - // Attach stack traces. - for _, ev := range events { - if ev.StkID != 0 { - ev.Stk = stacks[ev.StkID] - } - } - // Sort by sequence number and translate cpu ticks to real time. sort.Sort(eventList(events)) if ticksPerSec == 0 { @@ -478,8 +510,7 @@ func postProcessTrace(events []*Event) error { g.evStart = ev p.g = ev.G if g.evCreate != nil { - // +1 because symbolizer expects return pc. - ev.Stk = []*Frame{{PC: g.evCreate.Args[1] + 1}} + ev.StkID = g.evCreate.Args[1] g.evCreate = nil } @@ -580,79 +611,6 @@ func postProcessTrace(events []*Event) error { return nil } -// symbolizeTrace attaches func/file/line info to stack traces. -func Symbolize(events []*Event, bin string) error { - // First, collect and dedup all pcs. - pcs := make(map[uint64]*Frame) - for _, ev := range events { - for _, f := range ev.Stk { - pcs[f.PC] = nil - } - } - - // Start addr2line. - cmd := exec.Command("go", "tool", "addr2line", bin) - in, err := cmd.StdinPipe() - if err != nil { - return fmt.Errorf("failed to pipe addr2line stdin: %v", err) - } - cmd.Stderr = os.Stderr - out, err := cmd.StdoutPipe() - if err != nil { - return fmt.Errorf("failed to pipe addr2line stdout: %v", err) - } - err = cmd.Start() - if err != nil { - return fmt.Errorf("failed to start addr2line: %v", err) - } - outb := bufio.NewReader(out) - - // Write all pcs to addr2line. - // Need to copy pcs to an array, because map iteration order is non-deterministic. - var pcArray []uint64 - for pc := range pcs { - pcArray = append(pcArray, pc) - _, err := fmt.Fprintf(in, "0x%x\n", pc-1) - if err != nil { - return fmt.Errorf("failed to write to addr2line: %v", err) - } - } - in.Close() - - // Read in answers. - for _, pc := range pcArray { - fn, err := outb.ReadString('\n') - if err != nil { - return fmt.Errorf("failed to read from addr2line: %v", err) - } - file, err := outb.ReadString('\n') - if err != nil { - return fmt.Errorf("failed to read from addr2line: %v", err) - } - f := &Frame{PC: pc} - f.Fn = fn[:len(fn)-1] - f.File = file[:len(file)-1] - if colon := strings.LastIndex(f.File, ":"); colon != -1 { - ln, err := strconv.Atoi(f.File[colon+1:]) - if err == nil { - f.File = f.File[:colon] - f.Line = ln - } - } - pcs[pc] = f - } - cmd.Wait() - - // Replace frames in events array. - for _, ev := range events { - for i, f := range ev.Stk { - ev.Stk[i] = pcs[f.PC] - } - } - - return nil -} - // readVal reads unsigned base-128 value from r. func readVal(r io.Reader, off0 int) (v uint64, off int, err error) { off = off0 @@ -704,7 +662,7 @@ const ( EvNone = 0 // unused EvBatch = 1 // start of per-P batch of events [pid, timestamp] EvFrequency = 2 // contains tracer timer frequency [frequency (ticks per second)] - EvStack = 3 // stack [stack id, number of PCs, array of PCs] + EvStack = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] EvGomaxprocs = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] EvProcStart = 5 // start of P [timestamp, thread id] EvProcStop = 6 // stop of P [timestamp] @@ -714,7 +672,7 @@ const ( EvGCScanDone = 10 // GC scan done [timestamp] EvGCSweepStart = 11 // GC sweep start [timestamp, stack id] EvGCSweepDone = 12 // GC sweep done [timestamp] - EvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, start PC, stack id] + EvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, new start id, stack id] EvGoStart = 14 // goroutine starts running [timestamp, goroutine id] EvGoEnd = 15 // goroutine ends [timestamp] EvGoStop = 16 // goroutine stops (like in select{}) [timestamp, stack] @@ -738,7 +696,8 @@ const ( EvNextGC = 34 // memstats.next_gc change [timestamp, next_gc] EvTimerGoroutine = 35 // denotes timer goroutine [timer goroutine id] EvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] - EvCount = 37 + EvString = 37 // string dictionary entry [ID, length, string] + EvCount = 38 ) var EventDescriptions = [EvCount]struct { @@ -759,7 +718,7 @@ var EventDescriptions = [EvCount]struct { EvGCScanDone: {"GCScanDone", false, []string{}}, EvGCSweepStart: {"GCSweepStart", true, []string{}}, EvGCSweepDone: {"GCSweepDone", false, []string{}}, - EvGoCreate: {"GoCreate", true, []string{"g", "pc"}}, + EvGoCreate: {"GoCreate", true, []string{"g", "stack"}}, EvGoStart: {"GoStart", false, []string{"g"}}, EvGoEnd: {"GoEnd", false, []string{}}, EvGoStop: {"GoStop", true, []string{}}, @@ -783,4 +742,5 @@ var EventDescriptions = [EvCount]struct { EvNextGC: {"NextGC", false, []string{"mem"}}, EvTimerGoroutine: {"TimerGoroutine", false, []string{"g", "unused"}}, EvFutileWakeup: {"FutileWakeup", false, []string{}}, + EvString: {"String", false, []string{}}, } diff --git a/src/runtime/trace.go b/src/runtime/trace.go index 805c34f483..f54e5e0a7e 100644 --- a/src/runtime/trace.go +++ b/src/runtime/trace.go @@ -23,7 +23,7 @@ const ( traceEvNone = 0 // unused traceEvBatch = 1 // start of per-P batch of events [pid, timestamp] traceEvFrequency = 2 // contains tracer timer frequency [frequency (ticks per second)] - traceEvStack = 3 // stack [stack id, number of PCs, array of PCs] + traceEvStack = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] traceEvGomaxprocs = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] traceEvProcStart = 5 // start of P [timestamp, thread id] traceEvProcStop = 6 // stop of P [timestamp] @@ -33,7 +33,7 @@ const ( traceEvGCScanDone = 10 // GC scan done [timestamp] traceEvGCSweepStart = 11 // GC sweep start [timestamp, stack id] traceEvGCSweepDone = 12 // GC sweep done [timestamp] - traceEvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, start PC, stack id] + traceEvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, new start id, stack id] traceEvGoStart = 14 // goroutine starts running [timestamp, goroutine id] traceEvGoEnd = 15 // goroutine ends [timestamp] traceEvGoStop = 16 // goroutine stops (like in select{}) [timestamp, stack] @@ -57,7 +57,8 @@ const ( traceEvNextGC = 34 // memstats.next_gc change [timestamp, next_gc] traceEvTimerGoroutine = 35 // denotes timer goroutine [timer goroutine id] traceEvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] - traceEvCount = 37 + traceEvString = 37 // string dictionary entry [ID, length, string] + traceEvCount = 38 ) const ( @@ -111,6 +112,12 @@ var trace struct { reader *g // goroutine that called ReadTrace, or nil stackTab traceStackTable // maps stack traces to unique ids + // Dictionary for traceEvString. + // Currently this is used only for func/file:line info after tracing session, + // so we assume single-threaded access. + strings map[string]uint64 + stringSeq uint64 + bufLock mutex // protects buf buf traceBufPtr // global trace buffer, used when running without a p } @@ -191,6 +198,8 @@ func StartTrace() error { trace.timeStart = nanotime() trace.headerWritten = false trace.footerWritten = false + trace.strings = make(map[string]uint64) + trace.stringSeq = 0 // Can't set trace.enabled yet. While the world is stopped, exitsyscall could // already emit a delayed event (see exitTicks in exitsyscall) if we set trace.enabled here. @@ -272,8 +281,6 @@ func StopTrace() { trace.enabled = false trace.shutdown = true - trace.stackTab.dump() - unlock(&trace.bufLock) startTheWorld() @@ -309,6 +316,7 @@ func StopTrace() { trace.empty = buf.ptr().link sysFree(unsafe.Pointer(buf), unsafe.Sizeof(*buf.ptr()), &memstats.other_sys) } + trace.strings = nil trace.shutdown = false unlock(&trace.lock) } @@ -380,6 +388,9 @@ func ReadTrace() []byte { data = traceAppend(data, uint64(timers.gp.goid)) data = traceAppend(data, 0) } + // This will emit a bunch of full buffers, we will pick them up + // on the next iteration. + trace.stackTab.dump() return data } // Done. @@ -603,6 +614,29 @@ func traceFlush(buf traceBufPtr) traceBufPtr { return buf } +func traceString(buf *traceBuf, s string) (uint64, *traceBuf) { + if s == "" { + return 0, buf + } + if id, ok := trace.strings[s]; ok { + return id, buf + } + + trace.stringSeq++ + id := trace.stringSeq + trace.strings[s] = id + + size := 1 + 2*traceBytesPerNumber + len(s) + if len(buf.arr)-buf.pos < size { + buf = traceFlush(traceBufPtrOf(buf)).ptr() + } + buf.byte(traceEvString) + buf.varint(id) + buf.varint(uint64(len(s))) + buf.pos += copy(buf.arr[buf.pos:], s) + return id, buf +} + // traceAppend appends v to buf in little-endian-base-128 encoding. func traceAppend(buf []byte, v uint64) []byte { for ; v >= 0x80; v >>= 7 { @@ -716,23 +750,28 @@ func (tab *traceStackTable) newStack(n int) *traceStack { // dump writes all previously cached stacks to trace buffers, // releases all memory and resets state. func (tab *traceStackTable) dump() { - var tmp [(2 + traceStackSize) * traceBytesPerNumber]byte + frames := make(map[uintptr]traceFrame) + var tmp [(2 + 4*traceStackSize) * traceBytesPerNumber]byte buf := traceFlush(0).ptr() for _, stk := range tab.tab { stk := stk.ptr() for ; stk != nil; stk = stk.link.ptr() { - maxSize := 1 + (3+stk.n)*traceBytesPerNumber - if len(buf.arr)-buf.pos < maxSize { - buf = traceFlush(traceBufPtrOf(buf)).ptr() - } - // Form the event in the temp buffer, we need to know the actual length. tmpbuf := tmp[:0] tmpbuf = traceAppend(tmpbuf, uint64(stk.id)) tmpbuf = traceAppend(tmpbuf, uint64(stk.n)) for _, pc := range stk.stack() { + var frame traceFrame + frame, buf = traceFrameForPC(buf, frames, pc) tmpbuf = traceAppend(tmpbuf, uint64(pc)) + tmpbuf = traceAppend(tmpbuf, uint64(frame.funcID)) + tmpbuf = traceAppend(tmpbuf, uint64(frame.fileID)) + tmpbuf = traceAppend(tmpbuf, uint64(frame.line)) } // Now copy to the buffer. + size := 1 + traceBytesPerNumber + len(tmpbuf) + if len(buf.arr)-buf.pos < size { + buf = traceFlush(traceBufPtrOf(buf)).ptr() + } buf.byte(traceEvStack | 3< maxLen { + fn = fn[len(fn)-maxLen:] + } + frame.funcID, buf = traceString(buf, fn) + file, line := funcline(f, pc-sys.PCQuantum) + frame.line = uint64(line) + if len(file) > maxLen { + file = file[len(file)-maxLen:] + } + frame.fileID, buf = traceString(buf, file) + return frame, buf +} + // traceAlloc is a non-thread-safe region allocator. // It holds a linked list of traceAllocBlock. type traceAlloc struct { @@ -844,7 +916,9 @@ func traceGCSweepDone() { } func traceGoCreate(newg *g, pc uintptr) { - traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(pc)) + // +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum. + id := trace.stackTab.put([]uintptr{pc + sys.PCQuantum}) + traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(id)) } func traceGoStart() { diff --git a/src/runtime/trace/trace_stack_test.go b/src/runtime/trace/trace_stack_test.go index b99ec687d5..c3fb0f6fee 100644 --- a/src/runtime/trace/trace_stack_test.go +++ b/src/runtime/trace/trace_stack_test.go @@ -129,10 +129,6 @@ func TestTraceSymbolize(t *testing.T) { if err != nil { t.Fatalf("failed to parse trace: %v", err) } - err = trace.Symbolize(events, os.Args[0]) - if err != nil { - t.Fatalf("failed to symbolize trace: %v", err) - } // Now check that the stacks are correct. type frame struct { @@ -149,6 +145,9 @@ func TestTraceSymbolize(t *testing.T) { {"runtime/trace_test.TestTraceSymbolize", 106}, {"testing.tRunner", 0}, }}, + {trace.EvGoStart, []frame{ + {"runtime/trace_test.TestTraceSymbolize.func1", 37}, + }}, {trace.EvGoSched, []frame{ {"runtime/trace_test.TestTraceSymbolize", 107}, {"testing.tRunner", 0}, -- cgit v1.3 From c3b3e7b4ef9dff1fc0cc504f81465ded5663b4e4 Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 8 Apr 2016 13:33:43 -0400 Subject: cmd/compile: insert instrumentation more carefully in racewalk Be more careful about inserting instrumentation in racewalk. If the node being instrumented is an OAS, and it has a non- empty Ninit, then append instrumentation to the Ninit list rather than letting it be inserted before the OAS (and the compilation of its init list). This deals with the case that the Ninit list defines a variable used in the RHS of the OAS. Fixes #15091. Change-Id: Iac91696d9104d07f0bf1bd3499bbf56b2e1ef073 Reviewed-on: https://go-review.googlesource.com/21771 Reviewed-by: Josh Bleecher Snyder Run-TryBot: David Chase --- src/cmd/compile/internal/gc/fmt.go | 3 +++ src/cmd/compile/internal/gc/racewalk.go | 8 +++++++- src/cmd/compile/internal/gc/ssa.go | 4 ++++ src/cmd/compile/internal/ssa/regalloc.go | 4 ++-- test/fixedbugs/issue15091.go | 25 +++++++++++++++++++++++++ 5 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 test/fixedbugs/issue15091.go (limited to 'src') diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 5c5503619f..19f109055d 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -737,6 +737,9 @@ func typefmt(t *Type, flag FmtFlag) string { Fatalf("cannot use TDDDFIELD with old exporter") } return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.DDDField()) + + case Txxx: + return "Txxx" } if fmtmode == FExp { diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go index 09889a40f3..f6e65146d6 100644 --- a/src/cmd/compile/internal/gc/racewalk.go +++ b/src/cmd/compile/internal/gc/racewalk.go @@ -164,7 +164,13 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) { var outn Nodes outn.Set(out) instrumentnode(&ls[i], &outn, 0, 0) - out = append(outn.Slice(), ls[i]) + if ls[i].Op != OAS || ls[i].Ninit.Len() == 0 { + out = append(outn.Slice(), ls[i]) + } else { + // Splice outn onto end of ls[i].Ninit + ls[i].Ninit.AppendNodes(&outn) + out = append(out, ls[i]) + } } } n.List.Set(out) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 90c4d4e95e..7c5f906d76 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -3699,6 +3699,10 @@ func (s *state) resolveFwdRef(v *ssa.Value) { if b == s.f.Entry { // Live variable at start of function. if s.canSSA(name) { + if strings.HasPrefix(name.Sym.Name, "autotmp_") { + // It's likely that this is an uninitialized variable in the entry block. + s.Fatalf("Treating auto as if it were arg, func %s, node %v, value %v", b.Func.Name, name, v) + } v.Op = ssa.OpArg v.Aux = name return diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 22b9d12c19..aec23a1368 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -417,7 +417,7 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line // Load v from its spill location. case vi.spill != nil: if s.f.pass.debug > logSpills { - s.f.Config.Warnl(vi.spill.Line, "load spill") + s.f.Config.Warnl(vi.spill.Line, "load spill for %v from %v", v, vi.spill) } c = s.curBlock.NewValue1(line, OpLoadReg, v.Type, vi.spill) vi.spillUsed = true @@ -1078,7 +1078,7 @@ func (s *regAllocState) regalloc(f *Func) { vi := s.values[i] if vi.spillUsed { if s.f.pass.debug > logSpills { - s.f.Config.Warnl(vi.spill.Line, "spilled value") + s.f.Config.Warnl(vi.spill.Line, "spilled value at %v remains", vi.spill) } continue } diff --git a/test/fixedbugs/issue15091.go b/test/fixedbugs/issue15091.go new file mode 100644 index 0000000000..346e906171 --- /dev/null +++ b/test/fixedbugs/issue15091.go @@ -0,0 +1,25 @@ +// errorcheck -0 -race + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sample + +type Html struct { + headerIDs map[string]int +} + +// We don't want to see: +// internal error: (*Html).xyzzy autotmp_3 (type *int) recorded as live on entry, p.Pc=0 +// or (now, with the error caught earlier) +// Treating auto as if it were arg, func (*Html).xyzzy, node ... +// caused by racewalker inserting instrumentation before an OAS where the Ninit +// of the OAS defines part of its right-hand-side. (I.e., the race instrumentation +// references a variable before it is defined.) +func (options *Html) xyzzy(id string) string { + for count, found := options.headerIDs[id]; found; count, found = options.headerIDs[id] { + _ = count + } + return "" +} -- cgit v1.3 From 092ef8a2ca60e1a7573442757b02ec1efc456c2c Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Fri, 8 Apr 2016 14:27:35 +1200 Subject: cmd/cgo: fix cgo with gccgo Change-Id: I1780899255e22c16d7f8e9947609a1c284d7c42e Reviewed-on: https://go-review.googlesource.com/21690 Reviewed-by: David Crawshaw Run-TryBot: Michael Hudson-Doyle TryBot-Result: Gobot Gobot --- src/cmd/cgo/out.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 621c41c6b2..88b0147364 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1451,7 +1451,7 @@ const char *_cgoPREFIX_Cfunc_CString(struct __go_string s) { void *_cgoPREFIX_Cfunc_CBytes(struct __go_open_array b) { char *p = malloc(b.__count); - memmove(p, b.__data, b.__count); + memmove(p, b.__values, b.__count); return p; } -- cgit v1.3 From 6fee4aa5c76612d133d2b01441e608cfa696bae9 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Sat, 9 Apr 2016 12:54:45 +1000 Subject: cmd/link/internal: make ld.Bso a *bio.Writer This is a pre requesite of CL 21722 and removes a lot of unidiomatic boilerplate in the linker. Change-Id: If7491b88212b2be7b0c8c588e9c196839131f8ad Reviewed-on: https://go-review.googlesource.com/21780 Run-TryBot: Dave Cheney Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/link/internal/amd64/asm.go | 16 ++++++------- src/cmd/link/internal/arm/asm.go | 12 +++++----- src/cmd/link/internal/arm64/asm.go | 12 +++++----- src/cmd/link/internal/ld/ar.go | 2 +- src/cmd/link/internal/ld/data.go | 46 ++++++++++++++++++------------------- src/cmd/link/internal/ld/dwarf.go | 2 +- src/cmd/link/internal/ld/ldelf.go | 8 +++---- src/cmd/link/internal/ld/ldpe.go | 2 +- src/cmd/link/internal/ld/lib.go | 34 +++++++++++++-------------- src/cmd/link/internal/ld/pcln.go | 2 +- src/cmd/link/internal/ld/pobj.go | 15 ++++++------ src/cmd/link/internal/mips64/asm.go | 12 +++++----- src/cmd/link/internal/ppc64/asm.go | 12 +++++----- src/cmd/link/internal/s390x/asm.go | 14 +++++------ src/cmd/link/internal/x86/asm.go | 14 +++++------ 15 files changed, 102 insertions(+), 101 deletions(-) (limited to 'src') diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index 2b219da881..8cecd422e1 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -611,12 +611,12 @@ func addgotsym(s *ld.LSym) { func asmb() { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f codeblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f codeblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -634,7 +634,7 @@ func asmb() { if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -643,7 +643,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -688,7 +688,7 @@ func asmb() { symo := int64(0) if ld.Debug['s'] == 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { @@ -725,7 +725,7 @@ func asmb() { ld.Cwrite(ld.Elfstrdat) if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } if ld.Linkmode == ld.LinkExternal { @@ -749,7 +749,7 @@ func asmb() { case obj.Hwindows: if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } case obj.Hdarwin: @@ -760,7 +760,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f headr\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index e2718bfac8..b89cb20bdf 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -563,7 +563,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) { func asmb() { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() @@ -581,7 +581,7 @@ func asmb() { if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -590,7 +590,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -613,7 +613,7 @@ func asmb() { if ld.Debug['s'] == 0 { // TODO: rationalize if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { @@ -635,7 +635,7 @@ func asmb() { default: if ld.Iself { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() @@ -669,7 +669,7 @@ func asmb() { ld.Ctxt.Cursym = nil if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index fd28e8693b..fd8929dd99 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -401,7 +401,7 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { func asmb() { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() @@ -419,7 +419,7 @@ func asmb() { if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -428,7 +428,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -451,7 +451,7 @@ func asmb() { if ld.Debug['s'] == 0 { // TODO: rationalize if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { @@ -473,7 +473,7 @@ func asmb() { default: if ld.Iself { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() @@ -507,7 +507,7 @@ func asmb() { ld.Ctxt.Cursym = nil if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index 6a0aeb121f..f9357392d7 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -68,7 +68,7 @@ func hostArchive(name string) { if os.IsNotExist(err) { // It's OK if we don't have a libgcc file at all. if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "skipping libgcc file: %v\n", err) + fmt.Fprintf(Bso, "skipping libgcc file: %v\n", err) } return } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index ae430b4e45..cd910b54c0 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -638,7 +638,7 @@ func relocsym(s *LSym) { func reloc() { if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) + fmt.Fprintf(Bso, "%5.2f reloc\n", obj.Cputime()) } Bso.Flush() @@ -717,7 +717,7 @@ func dynreloc() { return } if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) + fmt.Fprintf(Bso, "%5.2f reloc\n", obj.Cputime()) } Bso.Flush() @@ -785,7 +785,7 @@ func blk(start *LSym, addr int64, size int64) { func Codeblk(addr int64, size int64) { if Debug['a'] != 0 { - fmt.Fprintf(&Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + fmt.Fprintf(Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) } blk(Ctxt.Textp, addr, size) @@ -816,32 +816,32 @@ func Codeblk(addr int64, size int64) { } if addr < sym.Value { - fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) + fmt.Fprintf(Bso, "%-20s %.8x|", "_", uint64(int64(addr))) for ; addr < sym.Value; addr++ { - fmt.Fprintf(&Bso, " %.2x", 0) + fmt.Fprintf(Bso, " %.2x", 0) } - fmt.Fprintf(&Bso, "\n") + fmt.Fprintf(Bso, "\n") } - fmt.Fprintf(&Bso, "%.6x\t%-20s\n", uint64(int64(addr)), sym.Name) + fmt.Fprintf(Bso, "%.6x\t%-20s\n", uint64(int64(addr)), sym.Name) q = sym.P for len(q) >= 16 { - fmt.Fprintf(&Bso, "%.6x\t% x\n", uint64(addr), q[:16]) + fmt.Fprintf(Bso, "%.6x\t% x\n", uint64(addr), q[:16]) addr += 16 q = q[16:] } if len(q) > 0 { - fmt.Fprintf(&Bso, "%.6x\t% x\n", uint64(addr), q) + fmt.Fprintf(Bso, "%.6x\t% x\n", uint64(addr), q) addr += int64(len(q)) } } if addr < eaddr { - fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) + fmt.Fprintf(Bso, "%-20s %.8x|", "_", uint64(int64(addr))) for ; addr < eaddr; addr++ { - fmt.Fprintf(&Bso, " %.2x", 0) + fmt.Fprintf(Bso, " %.2x", 0) } } @@ -850,7 +850,7 @@ func Codeblk(addr int64, size int64) { func Datblk(addr int64, size int64) { if Debug['a'] != 0 { - fmt.Fprintf(&Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + fmt.Fprintf(Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) } blk(datap, addr, size) @@ -879,26 +879,26 @@ func Datblk(addr int64, size int64) { break } if addr < sym.Value { - fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint64(addr)) + fmt.Fprintf(Bso, "\t%.8x| 00 ...\n", uint64(addr)) addr = sym.Value } - fmt.Fprintf(&Bso, "%s\n\t%.8x|", sym.Name, uint(addr)) + fmt.Fprintf(Bso, "%s\n\t%.8x|", sym.Name, uint(addr)) p = sym.P ep = p[len(sym.P):] for -cap(p) < -cap(ep) { if -cap(p) > -cap(sym.P) && int(-cap(p)+cap(sym.P))%16 == 0 { - fmt.Fprintf(&Bso, "\n\t%.8x|", uint(addr+int64(-cap(p)+cap(sym.P)))) + fmt.Fprintf(Bso, "\n\t%.8x|", uint(addr+int64(-cap(p)+cap(sym.P)))) } - fmt.Fprintf(&Bso, " %.2x", p[0]) + fmt.Fprintf(Bso, " %.2x", p[0]) p = p[1:] } addr += int64(len(sym.P)) for ; addr < sym.Value+sym.Size; addr++ { - fmt.Fprintf(&Bso, " %.2x", 0) + fmt.Fprintf(Bso, " %.2x", 0) } - fmt.Fprintf(&Bso, "\n") + fmt.Fprintf(Bso, "\n") if Linkmode == LinkExternal { for i = 0; i < int64(len(sym.R)); i++ { @@ -919,20 +919,20 @@ func Datblk(addr int64, size int64) { typ = "call" } - fmt.Fprintf(&Bso, "\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, int64(r.Add), int64(r.Sym.Value+r.Add)) + fmt.Fprintf(Bso, "\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, int64(r.Add), int64(r.Sym.Value+r.Add)) } } } if addr < eaddr { - fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint(addr)) + fmt.Fprintf(Bso, "\t%.8x| 00 ...\n", uint(addr)) } - fmt.Fprintf(&Bso, "\t%.8x|\n", uint(eaddr)) + fmt.Fprintf(Bso, "\t%.8x|\n", uint(eaddr)) } func Dwarfblk(addr int64, size int64) { if Debug['a'] != 0 { - fmt.Fprintf(&Bso, "dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + fmt.Fprintf(Bso, "dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) } blk(dwarfp, addr, size) @@ -1248,7 +1248,7 @@ func dataSort(head *LSym) *LSym { func dodata() { if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime()) + fmt.Fprintf(Bso, "%5.2f dodata\n", obj.Cputime()) } Bso.Flush() diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 4741020a6d..de2d50a1a9 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1931,7 +1931,7 @@ func dwarfgeneratedebugsyms() { } if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f dwarf\n", obj.Cputime()) + fmt.Fprintf(Bso, "%5.2f dwarf\n", obj.Cputime()) } // For diagnostic messages. diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index 55884c07a2..1c55daa392 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -405,7 +405,7 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) { ehdr.flags = 0x5000202 } if data[0] != 'A' { - fmt.Fprintf(&Bso, ".ARM.attributes has unexpected format %c\n", data[0]) + fmt.Fprintf(Bso, ".ARM.attributes has unexpected format %c\n", data[0]) return } data = data[1:] @@ -416,7 +416,7 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) { nulIndex := bytes.IndexByte(sectiondata, 0) if nulIndex < 0 { - fmt.Fprintf(&Bso, "corrupt .ARM.attributes (section name not NUL-terminated)\n") + fmt.Fprintf(Bso, "corrupt .ARM.attributes (section name not NUL-terminated)\n") return } name := string(sectiondata[:nulIndex]) @@ -440,7 +440,7 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) { } } if attrList.err != nil { - fmt.Fprintf(&Bso, "could not parse .ARM.attributes\n") + fmt.Fprintf(Bso, "could not parse .ARM.attributes\n") } } } @@ -449,7 +449,7 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) { func ldelf(f *bio.Reader, pkg string, length int64, pn string) { if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) + fmt.Fprintf(Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) } Ctxt.IncVersion() diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go index ba5b928ea0..37a550d5c9 100644 --- a/src/cmd/link/internal/ld/ldpe.go +++ b/src/cmd/link/internal/ld/ldpe.go @@ -131,7 +131,7 @@ type PeObj struct { func ldpe(f *bio.Reader, pkg string, length int64, pn string) { if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) + fmt.Fprintf(Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) } var sect *PeSect diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index f8cc995c30..e35306dd0e 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -241,7 +241,7 @@ const ( var ( headstring string // buffered output - Bso bio.Writer + Bso *bio.Writer ) // TODO(dfc) outBuf duplicates bio.Writer @@ -469,7 +469,7 @@ func loadinternal(name string) { if Linkshared { shlibname := filepath.Join(Ctxt.Libdir[i], name+".shlibname") if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, shlibname) + fmt.Fprintf(Bso, "searching for %s.a in %s\n", name, shlibname) } if _, err := os.Stat(shlibname); err == nil { addlibpath(Ctxt, "internal", "internal", "", name, shlibname) @@ -479,7 +479,7 @@ func loadinternal(name string) { } pname := filepath.Join(Ctxt.Libdir[i], name+".a") if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, pname) + fmt.Fprintf(Bso, "searching for %s.a in %s\n", name, pname) } if _, err := os.Stat(pname); err == nil { addlibpath(Ctxt, "internal", "internal", pname, name, "") @@ -489,7 +489,7 @@ func loadinternal(name string) { } if found == 0 { - fmt.Fprintf(&Bso, "warning: unable to find %s.a\n", name) + fmt.Fprintf(Bso, "warning: unable to find %s.a\n", name) } } @@ -521,7 +521,7 @@ func loadlib() { iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo" if Ctxt.Library[i].Shlib == "" { if Debug['v'] > 1 { - fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref) + fmt.Fprintf(Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref) } objfile(Ctxt.Library[i]) } @@ -530,7 +530,7 @@ func loadlib() { for i = 0; i < len(Ctxt.Library); i++ { if Ctxt.Library[i].Shlib != "" { if Debug['v'] > 1 { - fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].Shlib, Ctxt.Library[i].Objref) + fmt.Fprintf(Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].Shlib, Ctxt.Library[i].Objref) } ldshlibsyms(Ctxt.Library[i].Shlib) } @@ -693,13 +693,13 @@ func loadlib() { args := hostlinkArchArgs() args = append(args, "--print-libgcc-file-name") if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%s %v\n", extld, args) + fmt.Fprintf(Bso, "%s %v\n", extld, args) } out, err := exec.Command(extld, args...).Output() if err != nil { if Debug['v'] != 0 { - fmt.Fprintln(&Bso, "not using a libgcc file because compiler failed") - fmt.Fprintf(&Bso, "%v\n%s\n", err, out) + fmt.Fprintln(Bso, "not using a libgcc file because compiler failed") + fmt.Fprintf(Bso, "%v\n%s\n", err, out) } libgccfile = "none" } else { @@ -772,7 +772,7 @@ func objfile(lib *Library) { pkg := pathtoprefix(lib.Pkg) if Debug['v'] > 1 { - fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg) + fmt.Fprintf(Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg) } Bso.Flush() f, err := bio.Open(lib.File) @@ -1035,7 +1035,7 @@ func archive() { argv = append(argv, hostobjCopy()...) if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "archive: %s\n", strings.Join(argv, " ")) + fmt.Fprintf(Bso, "archive: %s\n", strings.Join(argv, " ")) Bso.Flush() } @@ -1204,18 +1204,18 @@ func hostlink() { } if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "host link:") + fmt.Fprintf(Bso, "host link:") for _, v := range argv { - fmt.Fprintf(&Bso, " %q", v) + fmt.Fprintf(Bso, " %q", v) } - fmt.Fprintf(&Bso, "\n") + fmt.Fprintf(Bso, "\n") Bso.Flush() } if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { Exitf("running %s failed: %v\n%s", argv[0], err, out) } else if Debug['v'] != 0 && len(out) > 0 { - fmt.Fprintf(&Bso, "%s", out) + fmt.Fprintf(Bso, "%s", out) Bso.Flush() } @@ -2007,7 +2007,7 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { // Otherwise, off is addressing the saved program counter. // Something underhanded is going on. Say nothing. if Debug['v'] != 0 || Debug['n'] != 0 { - fmt.Fprintf(&Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize)) + fmt.Fprintf(Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize)) } Bso.Flush() } @@ -2098,7 +2098,7 @@ func callgraph() { continue } if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM || r.Type == obj.R_CALLPOWER || r.Type == obj.R_CALLMIPS) && r.Sym.Type == obj.STEXT { - fmt.Fprintf(&Bso, "%s calls %s\n", s.Name, r.Sym.Name) + fmt.Fprintf(Bso, "%s calls %s\n", s.Name, r.Sym.Name) } } } diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 471dda712f..a5fea3db76 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -375,7 +375,7 @@ func pclntab() { ftab.Size = int64(len(ftab.P)) if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) + fmt.Fprintf(Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) } } diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go index bb48f13185..50066d32d7 100644 --- a/src/cmd/link/internal/ld/pobj.go +++ b/src/cmd/link/internal/ld/pobj.go @@ -46,11 +46,12 @@ var ( ) func Ldmain() { + Bso = bio.BufWriter(os.Stdout) + Ctxt = linknew(SysArch) Ctxt.Diag = Diag - Ctxt.Bso = &Bso + Ctxt.Bso = Bso - Bso = *bio.BufWriter(os.Stdout) Debug = [128]int{} nerrors = 0 outfile = "" @@ -122,7 +123,7 @@ func Ldmain() { obj.Flagparse(usage) startProfile() - Ctxt.Bso = &Bso + Ctxt.Bso = Bso Ctxt.Debugvlog = int32(Debug['v']) if flagShared != 0 { if Buildmode == BuildmodeUnset { @@ -163,7 +164,7 @@ func Ldmain() { } if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND)) + fmt.Fprintf(Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND)) } Bso.Flush() @@ -214,9 +215,9 @@ func Ldmain() { hostlink() archive() if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime()) - fmt.Fprintf(&Bso, "%d symbols\n", len(Ctxt.Allsym)) - fmt.Fprintf(&Bso, "%d liveness data\n", liveness) + fmt.Fprintf(Bso, "%5.2f cpu time\n", obj.Cputime()) + fmt.Fprintf(Bso, "%d symbols\n", len(Ctxt.Allsym)) + fmt.Fprintf(Bso, "%d liveness data\n", liveness) } Bso.Flush() diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 9a145e373a..027736cc11 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -114,7 +114,7 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { func asmb() { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() @@ -132,7 +132,7 @@ func asmb() { if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -141,7 +141,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -159,7 +159,7 @@ func asmb() { if ld.Debug['s'] == 0 { // TODO: rationalize if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { @@ -178,7 +178,7 @@ func asmb() { default: if ld.Iself { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() @@ -207,7 +207,7 @@ func asmb() { ld.Ctxt.Cursym = nil if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 4c2131dfc6..13d80545c7 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -834,7 +834,7 @@ func ensureglinkresolver() *ld.LSym { func asmb() { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() @@ -852,7 +852,7 @@ func asmb() { if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -861,7 +861,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -879,7 +879,7 @@ func asmb() { if ld.Debug['s'] == 0 { // TODO: rationalize if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { @@ -898,7 +898,7 @@ func asmb() { default: if ld.Iself { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() @@ -927,7 +927,7 @@ func asmb() { ld.Ctxt.Cursym = nil if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index c5e2d187b8..30b1e5a3e1 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -505,7 +505,7 @@ func addgotsym(s *ld.LSym) { func asmb() { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() @@ -523,7 +523,7 @@ func asmb() { if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -532,7 +532,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -552,7 +552,7 @@ func asmb() { ld.Diag("unsupported executable format") } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen) @@ -560,14 +560,14 @@ func asmb() { ld.Cseek(int64(symo)) if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() ld.Cwrite(ld.Elfstrdat) if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } if ld.Linkmode == ld.LinkExternal { @@ -577,7 +577,7 @@ func asmb() { ld.Ctxt.Cursym = nil if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 91251de15e..a786ba5a48 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -609,7 +609,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) { func asmb() { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime()) } ld.Bso.Flush() @@ -627,7 +627,7 @@ func asmb() { if ld.Segrodata.Filelen > 0 { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -636,7 +636,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime()) } ld.Bso.Flush() @@ -658,7 +658,7 @@ func asmb() { if ld.Debug['s'] == 0 { // TODO: rationalize if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime()) } ld.Bso.Flush() switch ld.HEADTYPE { @@ -684,7 +684,7 @@ func asmb() { default: if ld.Iself { if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime()) } ld.Asmelfsym() ld.Cflush() @@ -711,7 +711,7 @@ func asmb() { case obj.Hwindows: if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime()) } case obj.Hdarwin: @@ -722,7 +722,7 @@ func asmb() { } if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) + fmt.Fprintf(ld.Bso, "%5.2f headr\n", obj.Cputime()) } ld.Bso.Flush() ld.Cseek(0) -- cgit v1.3 From ca397bb68e4b548843d2886e374f96ec3bb0f9c0 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Fri, 8 Apr 2016 19:30:41 +1000 Subject: cmd: remove bio.BufReader and bio.BufWriter bio.BufReader was never used. bio.BufWriter was used to wrap an existing io.Writer, but the bio.Writer returned would not be seekable, so replace all occurences with bufio.Reader instead. Change-Id: I9c6779e35c63178aa4e104c17bb5bb8b52de0359 Reviewed-on: https://go-review.googlesource.com/21722 Reviewed-by: Brad Fitzpatrick Run-TryBot: Dave Cheney TryBot-Result: Gobot Gobot --- src/cmd/asm/internal/asm/endtoend_test.go | 6 +++--- src/cmd/asm/main.go | 15 ++++++++------- src/cmd/compile/internal/gc/bexport.go | 6 +++--- src/cmd/compile/internal/gc/export.go | 4 ++-- src/cmd/compile/internal/gc/go.go | 3 ++- src/cmd/compile/internal/gc/main.go | 3 +-- src/cmd/internal/bio/buf.go | 12 ------------ src/cmd/internal/obj/link.go | 4 ++-- src/cmd/link/internal/ld/lib.go | 2 +- src/cmd/link/internal/ld/link.go | 9 ++++----- src/cmd/link/internal/ld/pobj.go | 4 ++-- 11 files changed, 28 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go index 8986281f10..bc992a7c99 100644 --- a/src/cmd/asm/internal/asm/endtoend_test.go +++ b/src/cmd/asm/internal/asm/endtoend_test.go @@ -5,6 +5,7 @@ package asm import ( + "bufio" "bytes" "fmt" "io/ioutil" @@ -17,7 +18,6 @@ import ( "testing" "cmd/asm/internal/lex" - "cmd/internal/bio" "cmd/internal/obj" ) @@ -34,7 +34,7 @@ func testEndToEnd(t *testing.T, goarch, file string) { pList := obj.Linknewplist(ctxt) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. - ctxt.Bso = bio.BufWriter(os.Stdout) + ctxt.Bso = bufio.NewWriter(os.Stdout) defer ctxt.Bso.Flush() failed := false ctxt.DiagFunc = func(format string, args ...interface{}) { @@ -272,7 +272,7 @@ func testErrors(t *testing.T, goarch, file string) { pList := obj.Linknewplist(ctxt) var ok bool testOut = new(bytes.Buffer) // The assembler writes test output to this buffer. - ctxt.Bso = bio.BufWriter(os.Stdout) + ctxt.Bso = bufio.NewWriter(os.Stdout) defer ctxt.Bso.Flush() failed := false var errBuf bytes.Buffer diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index 75cb8f75d3..f010ca93f1 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -5,6 +5,7 @@ package main import ( + "bufio" "flag" "fmt" "log" @@ -32,11 +33,6 @@ func main() { flags.Parse() - // Create object file, write header. - fd, err := os.Create(*flags.OutputFile) - if err != nil { - log.Fatal(err) - } ctxt := obj.Linknew(architecture.LinkArch) if *flags.PrintOut { ctxt.Debugasm = 1 @@ -46,9 +42,14 @@ func main() { if *flags.Shared || *flags.Dynlink { ctxt.Flag_shared = 1 } - ctxt.Bso = bio.BufWriter(os.Stdout) + ctxt.Bso = bufio.NewWriter(os.Stdout) defer ctxt.Bso.Flush() - output := bio.BufWriter(fd) + + // Create object file, write header. + output, err := bio.Create(*flags.OutputFile) + if err != nil { + log.Fatal(err) + } fmt.Fprintf(output, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion()) fmt.Fprintf(output, "!\n") diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index bb0a34e67b..15e5e3ada6 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -90,9 +90,9 @@ importer. package gc import ( + "bufio" "bytes" "cmd/compile/internal/big" - "cmd/internal/bio" "encoding/binary" "fmt" "sort" @@ -124,7 +124,7 @@ const exportVersion = "v0" const exportInlined = true // default: true type exporter struct { - out *bio.Writer + out *bufio.Writer pkgIndex map[*Pkg]int typIndex map[*Type]int inlined []*Func @@ -136,7 +136,7 @@ type exporter struct { } // export writes the exportlist for localpkg to out and returns the number of bytes written. -func export(out *bio.Writer, trace bool) int { +func export(out *bufio.Writer, trace bool) int { p := exporter{ out: out, pkgIndex: make(map[*Pkg]int), diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 2f94b9c62f..dc7c0869bf 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -384,7 +384,7 @@ func dumpexport() { if debugFormat { // save a copy of the export data var copy bytes.Buffer - bcopy := bio.BufWriter(©) + bcopy := bufio.NewWriter(©) size = export(bcopy, Debug_export != 0) bcopy.Flush() // flushing to bytes.Buffer cannot fail if n, err := bout.Write(copy.Bytes()); n != size || err != nil { @@ -407,7 +407,7 @@ func dumpexport() { pkgs = savedPkgs pkgMap = savedPkgMap } else { - size = export(bout, Debug_export != 0) + size = export(bout.Writer(), Debug_export != 0) } exportf("\n$$\n") } else { diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index ec7e219d95..d9b28ff8e6 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -5,6 +5,7 @@ package gc import ( + "bufio" "cmd/compile/internal/ssa" "cmd/internal/bio" "cmd/internal/obj" @@ -288,7 +289,7 @@ var Ctxt *obj.Link var writearchive int -var bstdout *bio.Writer +var bstdout *bufio.Writer var Nacl bool diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 03143f5d0a..26acf8861f 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -9,7 +9,6 @@ package gc import ( "bufio" "cmd/compile/internal/ssa" - "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/sys" "flag" @@ -104,7 +103,7 @@ func Main() { Ctxt = obj.Linknew(Thearch.LinkArch) Ctxt.DiagFunc = Yyerror - bstdout = bio.BufWriter(os.Stdout) + bstdout = bufio.NewWriter(os.Stdout) Ctxt.Bso = bstdout localpkg = mkpkg("") diff --git a/src/cmd/internal/bio/buf.go b/src/cmd/internal/bio/buf.go index 0bd4658cdd..983ce46627 100644 --- a/src/cmd/internal/bio/buf.go +++ b/src/cmd/internal/bio/buf.go @@ -51,18 +51,6 @@ func Open(name string) (*Reader, error) { return &Reader{f: f, r: bufio.NewReader(f)}, nil } -// BufWriter returns a Writer on top of w. -// TODO(dfc) remove this method and replace caller with bufio.Writer. -func BufWriter(w io.Writer) *Writer { - return &Writer{w: bufio.NewWriter(w)} -} - -// BufWriter returns a Reader on top of r. -// TODO(dfc) remove this method and replace caller with bufio.Reader. -func BufReader(r io.Reader) *Reader { - return &Reader{r: bufio.NewReader(r)} -} - func (w *Writer) Write(p []byte) (int, error) { return w.w.Write(p) } diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index c48c3d807f..62175f9ed8 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -31,7 +31,7 @@ package obj import ( - "cmd/internal/bio" + "bufio" "cmd/internal/sys" ) @@ -629,7 +629,7 @@ type Link struct { Flag_shared int32 Flag_dynlink bool Flag_optimize bool - Bso *bio.Writer + Bso *bufio.Writer Pathname string Goroot string Goroot_final string diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index e35306dd0e..01dca9fc31 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -241,7 +241,7 @@ const ( var ( headstring string // buffered output - Bso *bio.Writer + Bso *bufio.Writer ) // TODO(dfc) outBuf duplicates bio.Writer diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index cbcc979c85..52b52f1cc0 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -31,7 +31,7 @@ package ld import ( - "cmd/internal/bio" + "bufio" "cmd/internal/sys" "debug/elf" "fmt" @@ -165,10 +165,9 @@ type Link struct { Headtype int Arch *sys.Arch Debugvlog int32 - - Bso *bio.Writer - Windows int32 - Goroot string + Bso *bufio.Writer + Windows int32 + Goroot string // Symbol lookup based on name and indexed by version. Hash []map[string]*LSym diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go index 50066d32d7..f4fb4d4845 100644 --- a/src/cmd/link/internal/ld/pobj.go +++ b/src/cmd/link/internal/ld/pobj.go @@ -31,7 +31,7 @@ package ld import ( - "cmd/internal/bio" + "bufio" "cmd/internal/obj" "cmd/internal/sys" "flag" @@ -46,7 +46,7 @@ var ( ) func Ldmain() { - Bso = bio.BufWriter(os.Stdout) + Bso = bufio.NewWriter(os.Stdout) Ctxt = linknew(SysArch) Ctxt.Diag = Diag -- cgit v1.3 From 93368be61ebaf8069d0d70034097de580441c412 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Fri, 8 Apr 2016 20:37:54 +1000 Subject: cmd/internal/bio: embed bufio.{Reader,Writer} in bio.{Reader,Writer} Change-Id: Ie95b0b0d4f724f4769cf2d4f8063cb5019fa9bc9 Reviewed-on: https://go-review.googlesource.com/21781 Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/export.go | 2 +- src/cmd/internal/bio/buf.go | 56 ++++++++--------------------------- src/cmd/internal/obj/objfile.go | 2 +- src/cmd/link/internal/ld/objfile.go | 2 +- 4 files changed, 16 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index dc7c0869bf..ae36657a65 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -407,7 +407,7 @@ func dumpexport() { pkgs = savedPkgs pkgMap = savedPkgMap } else { - size = export(bout.Writer(), Debug_export != 0) + size = export(bout.Writer, Debug_export != 0) } exportf("\n$$\n") } else { diff --git a/src/cmd/internal/bio/buf.go b/src/cmd/internal/bio/buf.go index 983ce46627..564ac77cbf 100644 --- a/src/cmd/internal/bio/buf.go +++ b/src/cmd/internal/bio/buf.go @@ -17,21 +17,15 @@ const EOF = -1 // Reader implements a seekable buffered io.Reader. type Reader struct { f *os.File - r *bufio.Reader + *bufio.Reader } // Writer implements a seekable buffered io.Writer. type Writer struct { f *os.File - w *bufio.Writer + *bufio.Writer } -// Reader returns this Reader's underlying bufio.Reader. -func (r *Reader) Reader() *bufio.Reader { return r.r } - -// Writer returns this Writer's underlying bufio.Writer. -func (w *Writer) Writer() *bufio.Writer { return w.w } - // Create creates the file named name and returns a Writer // for that file. func Create(name string) (*Writer, error) { @@ -39,7 +33,7 @@ func Create(name string) (*Writer, error) { if err != nil { return nil, err } - return &Writer{f: f, w: bufio.NewWriter(f)}, nil + return &Writer{f: f, Writer: bufio.NewWriter(f)}, nil } // Open returns a Reader for the file named name. @@ -48,31 +42,23 @@ func Open(name string) (*Reader, error) { if err != nil { return nil, err } - return &Reader{f: f, r: bufio.NewReader(f)}, nil -} - -func (w *Writer) Write(p []byte) (int, error) { - return w.w.Write(p) -} - -func (w *Writer) WriteString(p string) (int, error) { - return w.w.WriteString(p) + return &Reader{f: f, Reader: bufio.NewReader(f)}, nil } func (r *Reader) Seek(offset int64, whence int) int64 { if whence == 1 { - offset -= int64(r.r.Buffered()) + offset -= int64(r.Buffered()) } off, err := r.f.Seek(offset, whence) if err != nil { log.Fatalf("seeking in output: %v", err) } - r.r.Reset(r.f) + r.Reset(r.f) return off } func (w *Writer) Seek(offset int64, whence int) int64 { - if err := w.w.Flush(); err != nil { + if err := w.Flush(); err != nil { log.Fatalf("writing output: %v", err) } off, err := w.f.Seek(offset, whence) @@ -87,12 +73,12 @@ func (r *Reader) Offset() int64 { if err != nil { log.Fatalf("seeking in output [0, 1]: %v", err) } - off -= int64(r.r.Buffered()) + off -= int64(r.Buffered()) return off } func (w *Writer) Offset() int64 { - if err := w.w.Flush(); err != nil { + if err := w.Flush(); err != nil { log.Fatalf("writing output: %v", err) } off, err := w.f.Seek(0, 1) @@ -102,16 +88,8 @@ func (w *Writer) Offset() int64 { return off } -func (w *Writer) Flush() error { - return w.w.Flush() -} - -func (w *Writer) WriteByte(c byte) error { - return w.w.WriteByte(c) -} - func Bread(r *Reader, p []byte) int { - n, err := io.ReadFull(r.r, p) + n, err := io.ReadFull(r, p) if n == 0 { if err != nil && err != io.EOF { n = -1 @@ -121,7 +99,7 @@ func Bread(r *Reader, p []byte) int { } func Bgetc(r *Reader) int { - c, err := r.r.ReadByte() + c, err := r.ReadByte() if err != nil { if err != io.EOF { log.Fatalf("reading input: %v", err) @@ -131,16 +109,8 @@ func Bgetc(r *Reader) int { return int(c) } -func (r *Reader) Read(p []byte) (int, error) { - return r.r.Read(p) -} - -func (r *Reader) Peek(n int) ([]byte, error) { - return r.r.Peek(n) -} - func Brdline(r *Reader, delim int) string { - s, err := r.r.ReadBytes(byte(delim)) + s, err := r.ReadBytes(byte(delim)) if err != nil { log.Fatalf("reading input: %v", err) } @@ -152,7 +122,7 @@ func (r *Reader) Close() error { } func (w *Writer) Close() error { - err := w.w.Flush() + err := w.Flush() err1 := w.f.Close() if err == nil { err = err1 diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index ed6d75eba3..ee21f39d10 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -377,7 +377,7 @@ func (w *objWriter) writeLengths() { func newObjWriter(ctxt *Link, b *bio.Writer) *objWriter { return &objWriter{ ctxt: ctxt, - wr: b.Writer(), + wr: b.Writer, vrefIdx: make(map[string]int), refIdx: make(map[string]int), } diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go index 61a67cf94c..578afd4c74 100644 --- a/src/cmd/link/internal/ld/objfile.go +++ b/src/cmd/link/internal/ld/objfile.go @@ -150,7 +150,7 @@ type objReader struct { func LoadObjFile(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) { start := f.Offset() r := &objReader{ - rd: f.Reader(), + rd: f.Reader, pkg: pkg, ctxt: ctxt, pn: pn, -- cgit v1.3 From bce9747ed00c53e7ddeea102e87aede1b3ec9bd3 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Sat, 9 Apr 2016 15:04:45 +1000 Subject: cmd: remove unused code Generated with honnef.co/go/unused There is a large amount of unused code in cmd/internal/obj/s390x but that can wait til the s390x port is merged. There is some unused code in cmd/internal/unvendor/golang.org/x/arch/arm/armasm but that should be addressed upstream and a new revision imported. Change-Id: I252c0f9ea8c5bb1a0b530a374ef13a0a20ea56aa Reviewed-on: https://go-review.googlesource.com/21782 Reviewed-by: Brad Fitzpatrick Run-TryBot: Dave Cheney --- src/cmd/internal/goobj/read.go | 7 ++---- src/cmd/internal/obj/mips/asm0.go | 4 ---- src/cmd/internal/obj/x86/asm6.go | 5 ----- src/cmd/internal/obj/x86/obj6_test.go | 1 - src/cmd/internal/objfile/pe.go | 2 -- src/cmd/link/internal/amd64/asm.go | 6 ----- src/cmd/link/internal/arm/asm.go | 5 ----- src/cmd/link/internal/arm64/asm.go | 4 ---- src/cmd/link/internal/ld/data.go | 4 ---- src/cmd/link/internal/ld/deadcode.go | 4 +--- src/cmd/link/internal/ld/decodesym.go | 18 --------------- src/cmd/link/internal/ld/go.go | 30 +------------------------ src/cmd/link/internal/ld/macho.go | 7 ++---- src/cmd/link/internal/ld/macho_combine_dwarf.go | 4 +--- src/cmd/link/internal/mips64/asm.go | 4 ---- src/cmd/link/internal/ppc64/asm.go | 4 ---- src/cmd/link/internal/x86/asm.go | 4 ---- 17 files changed, 7 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go index 5434661384..698d58efe0 100644 --- a/src/cmd/internal/goobj/read.go +++ b/src/cmd/internal/goobj/read.go @@ -229,11 +229,8 @@ var ( errCorruptArchive = errors.New("corrupt archive") errTruncatedArchive = errors.New("truncated archive") - errNotArchive = errors.New("unrecognized archive format") - - errCorruptObject = errors.New("corrupt object file") - errTruncatedObject = errors.New("truncated object file") - errNotObject = errors.New("unrecognized object file format") + errCorruptObject = errors.New("corrupt object file") + errNotObject = errors.New("unrecognized object file format") ) // An objReader is an object file reader. diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go index 521cb66dec..5cb5d1cfd9 100644 --- a/src/cmd/internal/obj/mips/asm0.go +++ b/src/cmd/internal/obj/mips/asm0.go @@ -974,10 +974,6 @@ func OP_JMP(op uint32, i uint32) uint32 { return op | i&0x3FFFFFF } -func oclass(a *obj.Addr) int { - return int(a.Class) - 1 -} - func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) { o1 := uint32(0) o2 := uint32(0) diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index b940094b8b..c15b59b5e8 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -884,11 +884,6 @@ var yvex_vpbroadcast = []ytab{ {Yxm, Ynone, Yyr, Zvex_rm_v_r, 2}, } -var yvex_xxmyxm = []ytab{ - {Yxr, Ynone, Yxm, Zvex_r_v_rm, 2}, - {Yyr, Ynone, Yxm, Zvex_r_v_rm, 2}, -} - var ymmxmm0f38 = []ytab{ {Ymm, Ynone, Ymr, Zlitm_r, 3}, {Yxm, Ynone, Yxr, Zlitm_r, 5}, diff --git a/src/cmd/internal/obj/x86/obj6_test.go b/src/cmd/internal/obj/x86/obj6_test.go index a5c80cea3b..fe1f95cc0d 100644 --- a/src/cmd/internal/obj/x86/obj6_test.go +++ b/src/cmd/internal/obj/x86/obj6_test.go @@ -76,7 +76,6 @@ func parseTestData(t *testing.T) *ParsedTestData { } var spaces_re *regexp.Regexp = regexp.MustCompile("\\s+") -var marker_re *regexp.Regexp = regexp.MustCompile("MOVQ \\$([0-9]+), AX") func normalize(s string) string { return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ") diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go index 1b319941ac..c024762371 100644 --- a/src/cmd/internal/objfile/pe.go +++ b/src/cmd/internal/objfile/pe.go @@ -69,8 +69,6 @@ func (f *peFile) symbols() ([]Sym, error) { text = 0x20 data = 0x40 bss = 0x80 - permX = 0x20000000 - permR = 0x40000000 permW = 0x80000000 ) ch := sect.Characteristics diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index 8cecd422e1..a6dce6c2c9 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -99,12 +99,6 @@ func gentext() { ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) } -func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { - ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) - ld.Adduint64(ld.Ctxt, rela, ld.R_X86_64_RELATIVE) - ld.Addaddrplus(ld.Ctxt, rela, r.Sym, r.Add) // Addend -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index b89cb20bdf..1188615716 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -114,11 +114,6 @@ func braddoff(a int32, b int32) int32 { return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) } -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off)) - ld.Adduint32(ld.Ctxt, rel, ld.R_ARM_RELATIVE) -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index fd8929dd99..d3ba5ff3f3 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -91,10 +91,6 @@ func gentext() { ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) } -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { log.Fatalf("adddynrel not implemented") } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index cd910b54c0..2c8cc9ca4f 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -236,10 +236,6 @@ func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 { * Used for the data block. */ -func listnextp(s *LSym) **LSym { - return &s.Next -} - func listsubp(s *LSym) **LSym { return &s.Sub } diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index b17b96001e..8b2d0d447e 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -170,9 +170,7 @@ type methodref struct { r [3]*Reloc // R_METHOD relocations to fields of runtime.method } -func (m methodref) mtyp() *LSym { return m.r[0].Sym } -func (m methodref) ifn() *LSym { return m.r[1].Sym } -func (m methodref) tfn() *LSym { return m.r[2].Sym } +func (m methodref) ifn() *LSym { return m.r[1].Sym } func (m methodref) isExported() bool { for _, r := range m.m { diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index bc29938590..1066d220f7 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -56,11 +56,6 @@ func decodetype_kind(s *LSym) uint8 { return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindMask) // 0x13 / 0x1f } -// Type.commonType.kind -func decodetype_noptr(s *LSym) uint8 { - return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindNoPointers) // 0x13 / 0x1f -} - // Type.commonType.kind func decodetype_usegcprog(s *LSym) uint8 { return uint8(s.P[2*SysArch.PtrSize+7] & obj.KindGCProg) // 0x13 / 0x1f @@ -216,19 +211,6 @@ func decodetype_structfieldarrayoff(s *LSym, i int) int { return off } -func decodetype_stringptr(s *LSym, off int) string { - s = decode_reloc_sym(s, int32(off)) - if s == nil { - return "" - } - r := decode_reloc(s, 0) // s has a pointer to the string data at offset 0 - if r == nil { // shouldn't happen. - return "" - } - strlen := int64(decode_inuxi(s.P[SysArch.PtrSize:], SysArch.IntSize)) - return string(r.Sym.P[r.Add : r.Add+strlen]) -} - // decodetype_name decodes the name from a reflect.name. func decodetype_name(s *LSym, off int) string { r := decode_reloc(s, int32(off)) diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 5dad90dae6..3af5f7a046 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -419,35 +419,7 @@ type Pkg struct { impby []*Pkg } -var ( - // pkgmap records the imported-by relationship between packages. - // Entries are keyed by package path (e.g., "runtime" or "net/url"). - pkgmap = map[string]*Pkg{} - - pkgall []*Pkg -) - -func lookupPkg(path string) *Pkg { - if p, ok := pkgmap[path]; ok { - return p - } - p := &Pkg{path: path} - pkgmap[path] = p - pkgall = append(pkgall, p) - return p -} - -// imported records that package pkg imports package imp. -func imported(pkg, imp string) { - // everyone imports runtime, even runtime. - if imp == "runtime" { - return - } - - p := lookupPkg(pkg) - i := lookupPkg(imp) - i.impby = append(i.impby, p) -} +var pkgall []*Pkg func (p *Pkg) cycle() *Pkg { if p.checked { diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 25d48fbf22..1d9a1a9324 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -586,11 +586,8 @@ func Asmbmacho() { // and we can assume OS X. // // See golang.org/issues/12941. - const ( - LC_VERSION_MIN_MACOSX = 0x24 - LC_VERSION_MIN_IPHONEOS = 0x25 - LC_VERSION_MIN_WATCHOS = 0x30 - ) + const LC_VERSION_MIN_MACOSX = 0x24 + ml := newMachoLoad(LC_VERSION_MIN_MACOSX, 2) ml.data[0] = 10<<16 | 7<<8 | 0<<0 // OS X version 10.7.0 ml.data[1] = 10<<16 | 7<<8 | 0<<0 // SDK 10.7.0 diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go index b5a5a8d429..dcc371ec05 100644 --- a/src/cmd/link/internal/ld/macho_combine_dwarf.go +++ b/src/cmd/link/internal/ld/macho_combine_dwarf.go @@ -15,11 +15,9 @@ import ( "unsafe" ) -var fakedwarf, realdwarf, linkseg *macho.Segment +var realdwarf, linkseg *macho.Segment var dwarfstart, linkstart int64 var linkoffset uint32 -var machHeader *macho.FileHeader -var mappedHeader []byte const ( LC_ID_DYLIB = 0xd diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 027736cc11..ad6a1f7524 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -41,10 +41,6 @@ import ( func gentext() {} -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { log.Fatalf("adddynrel not implemented") } diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index 13d80545c7..3970f3c5f9 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -265,10 +265,6 @@ func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) { ld.Adduint32(ld.Ctxt, stub, 0x4e800420) // bctr } -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index a786ba5a48..19a8917ec8 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -139,10 +139,6 @@ func gentext() { ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) } -func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - func adddynrel(s *ld.LSym, r *ld.Reloc) { targ := r.Sym ld.Ctxt.Cursym = s -- cgit v1.3 From 9d4efdfd12f47f1ed8ce482ebeeb4d4e30a2dbc6 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Fri, 8 Apr 2016 20:44:56 +1000 Subject: cmd/internal/bio: move Bgetc to link/internal/ld Also, remove bio.Brdline. Change-Id: I3e0caed27a373fd71737cf6892de5e8fc208b776 Reviewed-on: https://go-review.googlesource.com/21783 Reviewed-by: Brad Fitzpatrick Run-TryBot: Dave Cheney --- src/cmd/internal/bio/buf.go | 21 --------------------- src/cmd/link/internal/ld/lib.go | 33 ++++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/cmd/internal/bio/buf.go b/src/cmd/internal/bio/buf.go index 564ac77cbf..6a5d821d45 100644 --- a/src/cmd/internal/bio/buf.go +++ b/src/cmd/internal/bio/buf.go @@ -12,8 +12,6 @@ import ( "os" ) -const EOF = -1 - // Reader implements a seekable buffered io.Reader. type Reader struct { f *os.File @@ -98,25 +96,6 @@ func Bread(r *Reader, p []byte) int { return n } -func Bgetc(r *Reader) int { - c, err := r.ReadByte() - if err != nil { - if err != io.EOF { - log.Fatalf("reading input: %v", err) - } - return EOF - } - return int(c) -} - -func Brdline(r *Reader, delim int) string { - s, err := r.ReadBytes(byte(delim)) - if err != nil { - log.Fatalf("reading input: %v", err) - } - return string(s) -} - func (r *Reader) Close() error { return r.f.Close() } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 01dca9fc31..6e33ec3b05 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1269,10 +1269,10 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when eof := f.Offset() + length start := f.Offset() - c1 := bio.Bgetc(f) - c2 := bio.Bgetc(f) - c3 := bio.Bgetc(f) - c4 := bio.Bgetc(f) + c1 := bgetc(f) + c2 := bgetc(f) + c3 := bgetc(f) + c4 := bgetc(f) f.Seek(start, 0) magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) @@ -1289,9 +1289,9 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when } /* check the header */ - line := bio.Brdline(f, '\n') - if line == "" { - Diag("truncated object file: %s", pn) + line, err := f.ReadString('\n') + if err != nil { + Diag("truncated object file: %s: %v", pn, err) return nil } @@ -1336,13 +1336,13 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when import0 := f.Offset() c1 = '\n' // the last line ended in \n - c2 = bio.Bgetc(f) - c3 = bio.Bgetc(f) + c2 = bgetc(f) + c3 = bgetc(f) for c1 != '\n' || c2 != '!' || c3 != '\n' { c1 = c2 c2 = c3 - c3 = bio.Bgetc(f) - if c3 == bio.EOF { + c3 = bgetc(f) + if c3 == -1 { Diag("truncated object file: %s", pn) return nil } @@ -2133,3 +2133,14 @@ func Rnd(v int64, r int64) int64 { v -= c return v } + +func bgetc(r *bio.Reader) int { + c, err := r.ReadByte() + if err != nil { + if err != io.EOF { + log.Fatalf("reading input: %v", err) + } + return -1 + } + return int(c) +} -- cgit v1.3 From 0435e88a119fd057aa7209591ba3dff122c9f24c Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 18 Mar 2016 10:56:23 +0100 Subject: runtime: revert "do not call timeBeginPeriod on windows" This reverts commit ab4c9298b8185a056ff1152f2c7bd9b38d3d06f3. Sysmon critically depends on system timer resolution for retaking of Ps blocked in system calls. See #14790 for an example of a program where execution time goes from 2ms to 30ms if timeBeginPeriod(1) is not used. We can remove timeBeginPeriod(1) when we support UMS (#7876). Update #14790 Change-Id: I362b56154359b2c52d47f9f2468fe012b481cf6d Reviewed-on: https://go-review.googlesource.com/20834 Reviewed-by: Austin Clements Run-TryBot: Dmitry Vyukov TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman --- misc/cgo/testcarchive/carchive_test.go | 2 +- src/runtime/export_windows_test.go | 7 ++-- src/runtime/os_windows.go | 9 ++++- src/runtime/syscall_windows_test.go | 66 ++++++---------------------------- 4 files changed, 25 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go index 47e0ceb5c9..72e9ef1d59 100644 --- a/misc/cgo/testcarchive/carchive_test.go +++ b/misc/cgo/testcarchive/carchive_test.go @@ -120,7 +120,7 @@ func goEnv(key string) string { func compilemain(t *testing.T, libgo string) { ccArgs := append(cc, "-o", "testp"+exeSuffix, "main.c") if GOOS == "windows" { - ccArgs = append(ccArgs, "main_windows.c", libgo, "-lntdll", "-lws2_32") + ccArgs = append(ccArgs, "main_windows.c", libgo, "-lntdll", "-lws2_32", "-lwinmm") } else { ccArgs = append(ccArgs, "main_unix.c", libgo) } diff --git a/src/runtime/export_windows_test.go b/src/runtime/export_windows_test.go index 66c103709c..536b398fd7 100644 --- a/src/runtime/export_windows_test.go +++ b/src/runtime/export_windows_test.go @@ -8,8 +8,11 @@ package runtime import "unsafe" -var TestingWER = &testingWER -var OsYield = osyield +var ( + TestingWER = &testingWER + OsYield = osyield + TimeBeginPeriodRetValue = &timeBeginPeriodRetValue +) func NumberOfProcessors() int32 { var info systeminfo diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 7244706b92..9147091a49 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -53,6 +53,7 @@ const ( //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll" //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" +//go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod%1 "winmm.dll" type stdFunction unsafe.Pointer @@ -98,7 +99,9 @@ var ( _WSAGetOverlappedResult, _WaitForSingleObject, _WriteConsoleW, - _WriteFile stdFunction + _WriteFile, + _timeBeginPeriod, + _ stdFunction // Following syscalls are only available on some Windows PCs. // We will load syscalls, if available, before using them. @@ -228,6 +231,8 @@ func setlasterror(err uint32) // flags can be used with LoadLibraryEx." var useLoadLibraryEx bool +var timeBeginPeriodRetValue uint32 + func osinit() { asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall)) usleep2Addr = unsafe.Pointer(funcPC(usleep2)) @@ -247,6 +252,8 @@ func osinit() { stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1) + timeBeginPeriodRetValue = uint32(stdcall1(_timeBeginPeriod, 1)) + ncpu = getproccount() // Windows dynamic priority boosting assumes that a process has different types diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go index ff045338c1..4a10749682 100644 --- a/src/runtime/syscall_windows_test.go +++ b/src/runtime/syscall_windows_test.go @@ -622,6 +622,13 @@ uintptr_t cfunc(callback f, uintptr_t n) { } } +func TestTimeBeginPeriod(t *testing.T) { + const TIMERR_NOERROR = 0 + if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR { + t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue) + } +} + // removeOneCPU removes one (any) cpu from affinity mask. // It returns new affinity mask. func removeOneCPU(mask uintptr) (uintptr, error) { @@ -874,21 +881,10 @@ var ( modwinmm = syscall.NewLazyDLL("winmm.dll") modkernel32 = syscall.NewLazyDLL("kernel32.dll") - proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod") - proctimeEndPeriod = modwinmm.NewProc("timeEndPeriod") - procCreateEvent = modkernel32.NewProc("CreateEventW") procSetEvent = modkernel32.NewProc("SetEvent") ) -func timeBeginPeriod(period uint32) { - syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0) -} - -func timeEndPeriod(period uint32) { - syscall.Syscall(proctimeEndPeriod.Addr(), 1, uintptr(period), 0, 0) -} - func createEvent() (syscall.Handle, error) { r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0) if r0 == 0 { @@ -905,7 +901,7 @@ func setEvent(h syscall.Handle) error { return nil } -func benchChanToSyscallPing(b *testing.B) { +func BenchmarkChanToSyscallPing(b *testing.B) { n := b.N ch := make(chan int) event, err := createEvent() @@ -927,17 +923,7 @@ func benchChanToSyscallPing(b *testing.B) { } } -func BenchmarkChanToSyscallPing1ms(b *testing.B) { - timeBeginPeriod(1) - benchChanToSyscallPing(b) - timeEndPeriod(1) -} - -func BenchmarkChanToSyscallPing15ms(b *testing.B) { - benchChanToSyscallPing(b) -} - -func benchSyscallToSyscallPing(b *testing.B) { +func BenchmarkSyscallToSyscallPing(b *testing.B) { n := b.N event1, err := createEvent() if err != nil { @@ -965,17 +951,7 @@ func benchSyscallToSyscallPing(b *testing.B) { } } -func BenchmarkSyscallToSyscallPing1ms(b *testing.B) { - timeBeginPeriod(1) - benchSyscallToSyscallPing(b) - timeEndPeriod(1) -} - -func BenchmarkSyscallToSyscallPing15ms(b *testing.B) { - benchSyscallToSyscallPing(b) -} - -func benchChanToChanPing(b *testing.B) { +func BenchmarkChanToChanPing(b *testing.B) { n := b.N ch1 := make(chan int) ch2 := make(chan int) @@ -991,28 +967,8 @@ func benchChanToChanPing(b *testing.B) { } } -func BenchmarkChanToChanPing1ms(b *testing.B) { - timeBeginPeriod(1) - benchChanToChanPing(b) - timeEndPeriod(1) -} - -func BenchmarkChanToChanPing15ms(b *testing.B) { - benchChanToChanPing(b) -} - -func benchOsYield(b *testing.B) { +func BenchmarkOsYield(b *testing.B) { for i := 0; i < b.N; i++ { runtime.OsYield() } } - -func BenchmarkOsYield1ms(b *testing.B) { - timeBeginPeriod(1) - benchOsYield(b) - timeEndPeriod(1) -} - -func BenchmarkOsYield15ms(b *testing.B) { - benchOsYield(b) -} -- cgit v1.3 From c31fdd4ee9fccd24a274cebd82dcc7123ad43d0e Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sat, 9 Apr 2016 22:54:26 +0000 Subject: cmd/go: fix typo in comment Thanks to deafgoat. Fixes #15215 Change-Id: I9fababc7ecd201ce86020a438e4faee95e7623a8 Reviewed-on: https://go-review.googlesource.com/21792 Reviewed-by: Emmanuel Odeke Reviewed-by: Brad Fitzpatrick --- src/cmd/go/pkg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 30ef02beff..9b0c657236 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -658,7 +658,7 @@ func disallowVendorVisibility(srcDir string, p *Package, stk *importStack) *Pack // findVendor looks for the last non-terminating "vendor" path element in the given import path. // If there isn't one, findVendor returns ok=false. -// Otherwise, findInternal returns ok=true and the index of the "vendor". +// Otherwise, findVendor returns ok=true and the index of the "vendor". // // Note that terminating "vendor" elements don't count: "x/vendor" is its own package, // not the vendored copy of an import "" (the empty import path). -- cgit v1.3 From 3598d4b8387a9e1c9afd522e0d18201f855f613b Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke Date: Sat, 9 Apr 2016 00:34:18 -0700 Subject: net/http/httputil: DumpRequest dumps Content-Length if set in header Fixes #7215 Change-Id: I108171ef18cac66d0dc11ce3553c26fc49529807 Reviewed-on: https://go-review.googlesource.com/21790 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick --- src/net/http/httputil/dump.go | 1 - src/net/http/httputil/dump_test.go | 40 +++++++++++++++++++++++++++++++++++ src/net/http/httputil/example_test.go | 2 +- 3 files changed, 41 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/net/http/httputil/dump.go b/src/net/http/httputil/dump.go index ddde11a0e4..692ab62c9b 100644 --- a/src/net/http/httputil/dump.go +++ b/src/net/http/httputil/dump.go @@ -163,7 +163,6 @@ func valueOrDefault(value, def string) string { var reqWriteExcludeHeaderDump = map[string]bool{ "Host": true, // not in Header map anyway - "Content-Length": true, "Transfer-Encoding": true, "Trailer": true, } diff --git a/src/net/http/httputil/dump_test.go b/src/net/http/httputil/dump_test.go index fc884347a6..2e980d39f8 100644 --- a/src/net/http/httputil/dump_test.go +++ b/src/net/http/httputil/dump_test.go @@ -122,6 +122,10 @@ var dumpTests = []dumpTest{ Host: "post.tld", Path: "/", }, + Header: http.Header{ + "Content-Length": []string{"8193"}, + }, + ContentLength: 8193, ProtoMajor: 1, ProtoMinor: 1, @@ -135,6 +139,10 @@ var dumpTests = []dumpTest{ "Content-Length: 8193\r\n" + "Accept-Encoding: gzip\r\n\r\n" + strings.Repeat("a", 8193), + WantDump: "POST / HTTP/1.1\r\n" + + "Host: post.tld\r\n" + + "Content-Length: 8193\r\n\r\n" + + strings.Repeat("a", 8193), }, { @@ -144,6 +152,38 @@ var dumpTests = []dumpTest{ WantDump: "GET http://foo.com/ HTTP/1.1\r\n" + "User-Agent: blah\r\n\r\n", }, + + // Issue #7215. DumpRequest should return the "Content-Length" when set + { + Req: *mustReadRequest("POST /v2/api/?login HTTP/1.1\r\n" + + "Host: passport.myhost.com\r\n" + + "Content-Length: 3\r\n" + + "\r\nkey1=name1&key2=name2"), + WantDump: "POST /v2/api/?login HTTP/1.1\r\n" + + "Host: passport.myhost.com\r\n" + + "Content-Length: 3\r\n" + + "\r\nkey", + }, + + // Issue #7215. DumpRequest should return the "Content-Length" in ReadRequest + { + Req: *mustReadRequest("POST /v2/api/?login HTTP/1.1\r\n" + + "Host: passport.myhost.com\r\n" + + "Content-Length: 0\r\n" + + "\r\nkey1=name1&key2=name2"), + WantDump: "POST /v2/api/?login HTTP/1.1\r\n" + + "Host: passport.myhost.com\r\n" + + "Content-Length: 0\r\n\r\n", + }, + + // Issue #7215. DumpRequest should not return the "Content-Length" if unset + { + Req: *mustReadRequest("POST /v2/api/?login HTTP/1.1\r\n" + + "Host: passport.myhost.com\r\n" + + "\r\nkey1=name1&key2=name2"), + WantDump: "POST /v2/api/?login HTTP/1.1\r\n" + + "Host: passport.myhost.com\r\n\r\n", + }, } func TestDumpRequest(t *testing.T) { diff --git a/src/net/http/httputil/example_test.go b/src/net/http/httputil/example_test.go index f856135742..6191603674 100644 --- a/src/net/http/httputil/example_test.go +++ b/src/net/http/httputil/example_test.go @@ -47,7 +47,7 @@ func ExampleDumpRequest() { fmt.Printf("%s", b) // Output: - // "POST / HTTP/1.1\r\nHost: www.example.org\r\nAccept-Encoding: gzip\r\nUser-Agent: Go-http-client/1.1\r\n\r\nGo is a general-purpose language designed with systems programming in mind." + // "POST / HTTP/1.1\r\nHost: www.example.org\r\nAccept-Encoding: gzip\r\nContent-Length: 75\r\nUser-Agent: Go-http-client/1.1\r\n\r\nGo is a general-purpose language designed with systems programming in mind." } func ExampleDumpRequestOut() { -- cgit v1.3 From 824d8c10fe5e1026c15cbece41ee372b1fd333f3 Mon Sep 17 00:00:00 2001 From: Wisdom Omuya Date: Sat, 9 Apr 2016 19:23:01 -0400 Subject: cmd/go: fix typo in findInternal documentation Fixes #15217 Change-Id: Ib8f7af714197fd209e743f61f28a5b07c04a7f5c Reviewed-on: https://go-review.googlesource.com/21793 Reviewed-by: Brad Fitzpatrick --- src/cmd/go/pkg.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 9b0c657236..f330b4db43 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -523,7 +523,7 @@ func disallowInternal(srcDir string, p *Package, stk *importStack) *Package { return p } - // Check for "internal" element: four cases depending on begin of string and/or end of string. + // Check for "internal" element: three cases depending on begin of string and/or end of string. i, ok := findInternal(p.ImportPath) if !ok { return p @@ -560,7 +560,7 @@ func disallowInternal(srcDir string, p *Package, stk *importStack) *Package { // If there isn't one, findInternal returns ok=false. // Otherwise, findInternal returns ok=true and the index of the "internal". func findInternal(path string) (index int, ok bool) { - // Four cases, depending on internal at start/end of string or not. + // Three cases, depending on internal at start/end of string or not. // The order matters: we must return the index of the final element, // because the final one produces the most restrictive requirement // on the importer. -- cgit v1.3 From e4f1d9cf2e948eb0f0bb91d7c253ab61dfff3a59 Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke Date: Sun, 27 Mar 2016 17:29:53 -0700 Subject: runtime: make execution error panic values implement the Error interface Make execution panics implement Error as mandated by https://golang.org/ref/spec#Run_time_panics, instead of panics with strings. Fixes #14965 Change-Id: I7827f898b9b9c08af541db922cc24fa0800ff18a Reviewed-on: https://go-review.googlesource.com/21214 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/runtime/chan.go | 10 +++++----- src/runtime/crash_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/runtime/error.go | 13 ++++++++++++- src/runtime/hashmap.go | 4 ++-- src/runtime/malloc.go | 2 +- src/runtime/proc.go | 2 +- src/runtime/select.go | 2 +- 7 files changed, 68 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 954b389f47..8543cb4c9c 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -64,7 +64,7 @@ func makechan(t *chantype, size int64) *hchan { throw("makechan: bad alignment") } if size < 0 || int64(uintptr(size)) != size || (elem.size > 0 && uintptr(size) > (_MaxMem-hchanSize)/elem.size) { - panic("makechan: size out of range") + panic(plainError("makechan: size out of range")) } var c *hchan @@ -171,7 +171,7 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin if c.closed != 0 { unlock(&c.lock) - panic("send on closed channel") + panic(plainError("send on closed channel")) } if sg := c.recvq.dequeue(); sg != nil { @@ -231,7 +231,7 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin if c.closed == 0 { throw("chansend: spurious wakeup") } - panic("send on closed channel") + panic(plainError("send on closed channel")) } gp.param = nil if mysg.releasetime > 0 { @@ -302,13 +302,13 @@ func sendDirect(t *_type, sg *sudog, src unsafe.Pointer) { func closechan(c *hchan) { if c == nil { - panic("close of nil channel") + panic(plainError("close of nil channel")) } lock(&c.lock) if c.closed != 0 { unlock(&c.lock) - panic("close of closed channel") + panic(plainError("close of closed channel")) } if raceenabled { diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 85fcc69fed..2941b8e8f8 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -273,6 +273,52 @@ func TestGoexitInPanic(t *testing.T) { } } +// Issue 14965: Runtime panics should be of type runtime.Error +func TestRuntimePanicWithRuntimeError(t *testing.T) { + testCases := [...]func(){ + 0: func() { + var m map[uint64]bool + m[1234] = true + }, + 1: func() { + ch := make(chan struct{}) + close(ch) + close(ch) + }, + 2: func() { + var ch = make(chan struct{}) + close(ch) + ch <- struct{}{} + }, + 3: func() { + var s = make([]int, 2) + _ = s[2] + }, + 4: func() { + n := -1 + _ = make(chan bool, n) + }, + 5: func() { + close((chan bool)(nil)) + }, + } + + for i, fn := range testCases { + got := panicValue(fn) + if _, ok := got.(runtime.Error); !ok { + t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got) + } + } +} + +func panicValue(fn func()) (recovered interface{}) { + defer func() { + recovered = recover() + }() + fn() + return +} + func TestPanicAfterGoexit(t *testing.T) { // an uncaught panic should still work after goexit output := runTestProg(t, "testprog", "PanicAfterGoexit") diff --git a/src/runtime/error.go b/src/runtime/error.go index 3e1ec4bc5a..15f6bdf014 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -50,6 +50,17 @@ func (e errorString) Error() string { return "runtime error: " + string(e) } +// plainError represents a runtime error described a string without +// the prefix "runtime error: " after invoking errorString.Error(). +// See Issue #14965. +type plainError string + +func (e plainError) RuntimeError() {} + +func (e plainError) Error() string { + return string(e) +} + type stringer interface { String() string } @@ -82,5 +93,5 @@ func printany(i interface{}) { // called from generated code func panicwrap(pkg, typ, meth string) { - panic("value method " + pkg + "." + typ + "." + meth + " called using nil *" + typ + " pointer") + panic(plainError("value method " + pkg + "." + typ + "." + meth + " called using nil *" + typ + " pointer")) } diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 80b2b5338c..9e18192cd8 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -194,7 +194,7 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap { } if hint < 0 || int64(int32(hint)) != hint { - panic("makemap: size out of range") + panic(plainError("makemap: size out of range")) // TODO: make hint an int, then none of this nonsense } @@ -428,7 +428,7 @@ func mapaccessK(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, unsafe func mapassign1(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) { if h == nil { - panic("assignment to entry in nil map") + panic(plainError("assignment to entry in nil map")) } if raceenabled { callerpc := getcallerpc(unsafe.Pointer(&t)) diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index 5f1e2f64c0..ee4728c9a5 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -793,7 +793,7 @@ func newarray(typ *_type, n uintptr) unsafe.Pointer { flags |= flagNoScan } if int(n) < 0 || (typ.size > 0 && n > _MaxMem/typ.size) { - panic("runtime: allocation size out of range") + panic(plainError("runtime: allocation size out of range")) } return mallocgc(typ.size*n, typ, flags) } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 1f55b0fa21..1a9dbd6c53 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -381,7 +381,7 @@ func badmcall2(fn func(*g)) { } func badreflectcall() { - panic("runtime: arg size to reflect.call more than 1GB") + panic(plainError("arg size to reflect.call more than 1GB")) } func lockedOSThread() bool { diff --git a/src/runtime/select.go b/src/runtime/select.go index c80c833b15..9810db5453 100644 --- a/src/runtime/select.go +++ b/src/runtime/select.go @@ -594,7 +594,7 @@ retc: sclose: // send on closed channel selunlock(scases, lockorder) - panic("send on closed channel") + panic(plainError("send on closed channel")) } func (c *hchan) sortkey() uintptr { -- cgit v1.3 From de7ee57c7ead59899d5b412a839c995de0e813b5 Mon Sep 17 00:00:00 2001 From: Marvin Stenger Date: Fri, 8 Apr 2016 18:19:10 +0200 Subject: cmd: remove bio.Bread Replace calls to bio.Bread with calls to io.ReadFull. Change-Id: I2ee8739d01e04a4da9c20b6ce7d1d5b89914b8ad Reviewed-on: https://go-review.googlesource.com/21750 Reviewed-by: Dave Cheney --- src/cmd/internal/bio/buf.go | 11 ----------- src/cmd/link/internal/ld/ar.go | 10 +++++----- src/cmd/link/internal/ld/go.go | 3 ++- src/cmd/link/internal/ld/ldelf.go | 10 ++++++---- src/cmd/link/internal/ld/ldmacho.go | 34 +++++++++++++++++++++++++--------- src/cmd/link/internal/ld/ldpe.go | 14 +++++++++----- src/cmd/link/internal/ld/lib.go | 21 +++++++++++++-------- 7 files changed, 60 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/cmd/internal/bio/buf.go b/src/cmd/internal/bio/buf.go index 6a5d821d45..7a077041c2 100644 --- a/src/cmd/internal/bio/buf.go +++ b/src/cmd/internal/bio/buf.go @@ -7,7 +7,6 @@ package bio import ( "bufio" - "io" "log" "os" ) @@ -86,16 +85,6 @@ func (w *Writer) Offset() int64 { return off } -func Bread(r *Reader, p []byte) int { - n, err := io.ReadFull(r, p) - if n == 0 { - if err != nil && err != io.EOF { - n = -1 - } - } - return n -} - func (r *Reader) Close() error { return r.f.Close() } diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go index f9357392d7..323dfbefc5 100644 --- a/src/cmd/link/internal/ld/ar.go +++ b/src/cmd/link/internal/ld/ar.go @@ -35,6 +35,7 @@ import ( "cmd/internal/obj" "encoding/binary" "fmt" + "io" "os" ) @@ -76,8 +77,8 @@ func hostArchive(name string) { } defer f.Close() - magbuf := make([]byte, len(ARMAG)) - if bio.Bread(f, magbuf) != len(magbuf) { + var magbuf [len(ARMAG)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { Exitf("file %s too short", name) } @@ -138,9 +139,8 @@ func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap { wordSize = 8 } - l := atolwhex(arhdr.size) - contents := make([]byte, l) - if bio.Bread(f, contents) != int(l) { + contents := make([]byte, atolwhex(arhdr.size)) + if _, err := io.ReadFull(f, contents); err != nil { Exitf("short read from %s", filename) } diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 3af5f7a046..425c75571f 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -11,6 +11,7 @@ import ( "cmd/internal/bio" "cmd/internal/obj" "fmt" + "io" "os" "strings" ) @@ -49,7 +50,7 @@ func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int) } bdata := make([]byte, length) - if int64(bio.Bread(f, bdata)) != length { + if _, err := io.ReadFull(f, bdata); err != nil { fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) if Debug['u'] != 0 { errorexit() diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index 1c55daa392..d9581a5189 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -476,7 +476,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) { var sect *ElfSect var sym ElfSym var symbols []*LSym - if bio.Bread(f, hdrbuf[:]) != len(hdrbuf) { + if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { goto bad } hdr = new(ElfHdrBytes) @@ -986,9 +986,11 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { } sect.base = make([]byte, sect.size) - err = fmt.Errorf("short read") - if elfobj.f.Seek(int64(uint64(elfobj.base)+sect.off), 0) < 0 || bio.Bread(elfobj.f, sect.base) != len(sect.base) { - return err + if elfobj.f.Seek(int64(uint64(elfobj.base)+sect.off), 0) < 0 { + return fmt.Errorf("short read: seek not successful") + } + if _, err := io.ReadFull(elfobj.f, sect.base); err != nil { + return fmt.Errorf("short read: %v", err) } return nil diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go index dffe6f69ce..8dc4033bbc 100644 --- a/src/cmd/link/internal/ld/ldmacho.go +++ b/src/cmd/link/internal/ld/ldmacho.go @@ -6,6 +6,7 @@ import ( "cmd/internal/sys" "encoding/binary" "fmt" + "io" "log" "sort" ) @@ -299,7 +300,10 @@ func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { rel := make([]LdMachoRel, sect.nreloc) n := int(sect.nreloc * 8) buf := make([]byte, n) - if m.f.Seek(m.base+int64(sect.reloff), 0) < 0 || bio.Bread(m.f, buf) != n { + if m.f.Seek(m.base+int64(sect.reloff), 0) < 0 { + return -1 + } + if _, err := io.ReadFull(m.f, buf); err != nil { return -1 } var p []byte @@ -345,7 +349,10 @@ func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { n := int(d.nindirectsyms) p := make([]byte, n*4) - if m.f.Seek(m.base+int64(d.indirectsymoff), 0) < 0 || bio.Bread(m.f, p) != len(p) { + if m.f.Seek(m.base+int64(d.indirectsymoff), 0) < 0 { + return -1 + } + if _, err := io.ReadFull(m.f, p); err != nil { return -1 } @@ -362,7 +369,10 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { } strbuf := make([]byte, symtab.strsize) - if m.f.Seek(m.base+int64(symtab.stroff), 0) < 0 || bio.Bread(m.f, strbuf) != len(strbuf) { + if m.f.Seek(m.base+int64(symtab.stroff), 0) < 0 { + return -1 + } + if _, err := io.ReadFull(m.f, strbuf); err != nil { return -1 } @@ -372,7 +382,10 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { } n := int(symtab.nsym * uint32(symsize)) symbuf := make([]byte, n) - if m.f.Seek(m.base+int64(symtab.symoff), 0) < 0 || bio.Bread(m.f, symbuf) != len(symbuf) { + if m.f.Seek(m.base+int64(symtab.symoff), 0) < 0 { + return -1 + } + if _, err := io.ReadFull(m.f, symbuf); err != nil { return -1 } sym := make([]LdMachoSym, symtab.nsym) @@ -433,7 +446,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) { Ctxt.IncVersion() base := f.Offset() - if bio.Bread(f, hdr[:]) != len(hdr) { + if _, err := io.ReadFull(f, hdr[:]); err != nil { goto bad } @@ -455,8 +468,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) { } if is64 { - var tmp [4]uint8 - bio.Bread(f, tmp[:4]) // skip reserved word in header + f.Seek(4, 1) // skip reserved word in header } m = new(LdMachoObj) @@ -494,7 +506,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) { m.cmd = make([]LdMachoCmd, ncmd) off = uint32(len(hdr)) cmdp = make([]byte, cmdsz) - if bio.Bread(f, cmdp) != len(cmdp) { + if _, err2 := io.ReadFull(f, cmdp); err2 != nil { err = fmt.Errorf("reading cmds: %v", err) goto bad } @@ -557,7 +569,11 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) { } dat = make([]byte, c.seg.filesz) - if f.Seek(m.base+int64(c.seg.fileoff), 0) < 0 || bio.Bread(f, dat) != len(dat) { + if f.Seek(m.base+int64(c.seg.fileoff), 0) < 0 { + err = fmt.Errorf("cannot load object data: %v", err) + goto bad + } + if _, err2 := io.ReadFull(f, dat); err2 != nil { err = fmt.Errorf("cannot load object data: %v", err) goto bad } diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go index 37a550d5c9..7f7121ff94 100644 --- a/src/cmd/link/internal/ld/ldpe.go +++ b/src/cmd/link/internal/ld/ldpe.go @@ -10,6 +10,7 @@ import ( "cmd/internal/sys" "encoding/binary" "fmt" + "io" "log" "sort" "strconv" @@ -176,13 +177,13 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) { // load string table f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) - if bio.Bread(f, symbuf[:4]) != 4 { + if _, err := io.ReadFull(f, symbuf[:4]); err != nil { goto bad } l = Le32(symbuf[:]) peobj.snames = make([]byte, l) f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) - if bio.Bread(f, peobj.snames) != len(peobj.snames) { + if _, err := io.ReadFull(f, peobj.snames); err != nil { goto bad } @@ -205,7 +206,7 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) { f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { f.Seek(int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) - if bio.Bread(f, symbuf[:]) != len(symbuf) { + if _, err := io.ReadFull(f, symbuf[:]); err != nil { goto bad } @@ -293,7 +294,7 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) { f.Seek(int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { rp = &r[j] - if bio.Bread(f, symbuf[:10]) != 10 { + if _, err := io.ReadFull(f, symbuf[:10]); err != nil { goto bad } rva := Le32(symbuf[0:]) @@ -466,7 +467,10 @@ func pemap(peobj *PeObj, sect *PeSect) int { if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file return 0 } - if peobj.f.Seek(int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || bio.Bread(peobj.f, sect.base) != len(sect.base) { + if peobj.f.Seek(int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 { + return -1 + } + if _, err := io.ReadFull(peobj.f, sect.base); err != nil { return -1 } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 6e33ec3b05..1f2df8b9c5 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -745,12 +745,12 @@ func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 { off++ } bp.Seek(off, 0) - buf := make([]byte, SAR_HDR) - if n := bio.Bread(bp, buf); n < len(buf) { - if n >= 0 { - return 0 + var buf [SAR_HDR]byte + if n, err := io.ReadFull(bp, buf[:]); err != nil { + if n == 0 && err != io.EOF { + return -1 } - return -1 + return 0 } a.name = artrim(buf[0:16]) @@ -780,8 +780,11 @@ func objfile(lib *Library) { Exitf("cannot open file %s: %v", lib.File, err) } - magbuf := make([]byte, len(ARMAG)) - if bio.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { + for i := 0; i < len(ARMAG); i++ { + if c, err := f.ReadByte(); err == nil && c == ARMAG[i] { + continue + } + /* load it as a regular file */ l := f.Seek(0, 2) @@ -811,7 +814,9 @@ func objfile(lib *Library) { if Buildmode == BuildmodeShared { before := f.Offset() pkgdefBytes := make([]byte, atolwhex(arhdr.size)) - bio.Bread(f, pkgdefBytes) + if _, err := io.ReadFull(f, pkgdefBytes); err != nil { + Diag("%s: short read on archive file symbol header: %v", lib.File, err) + } hash := sha1.Sum(pkgdefBytes) lib.hash = hash[:] f.Seek(before, 0) -- cgit v1.3 From 527ffebb2c9fe432a0ef0aa0c2449d83cd8a23cb Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sun, 10 Apr 2016 12:49:40 +0200 Subject: internal/trace: fix a typo in error message Change-Id: Id79eaa6d49dae80c334c7243b0a5bbcdcb9397d3 Reviewed-on: https://go-review.googlesource.com/21758 Reviewed-by: Mikio Hara --- src/internal/trace/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go index 5db3fc317e..65530b15c3 100644 --- a/src/internal/trace/parser.go +++ b/src/internal/trace/parser.go @@ -134,7 +134,7 @@ func readTrace(r io.Reader) ([]rawEvent, map[uint64]string, error) { return nil, nil, err } if ln == 0 { - return nil, nil, fmt.Errorf("string at offset %d has invalie length 0", off) + return nil, nil, fmt.Errorf("string at offset %d has invalid length 0", off) } if ln > 1e6 { return nil, nil, fmt.Errorf("string at offset %d has too large length %v", off, ln) -- cgit v1.3 From 012557b3769f9286b9488fbfd4bddfeee66b6a55 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sun, 10 Apr 2016 08:48:55 +0200 Subject: all: replace magic 0x80 with named constant utf8.RuneSelf Change-Id: Id1c2e8e9d60588de866e8b6ca59cc83dd28f848f Reviewed-on: https://go-review.googlesource.com/21756 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/bufio/bufio.go | 2 +- src/cmd/compile/internal/gc/fmt.go | 2 +- src/encoding/asn1/asn1.go | 2 +- src/go/build/build.go | 2 +- src/go/build/read.go | 3 ++- src/go/scanner/scanner.go | 6 +++--- src/html/template/css.go | 2 +- src/net/http/cookiejar/punycode.go | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index d2ccc74f52..3b30b8b80c 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -266,7 +266,7 @@ func (b *Reader) ReadRune() (r rune, size int, err error) { return 0, 0, b.readErr() } r, size = rune(b.buf[b.r]), 1 - if r >= 0x80 { + if r >= utf8.RuneSelf { r, size = utf8.DecodeRune(b.buf[b.r:b.w]) } b.r += size diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 19f109055d..41d696574c 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -337,7 +337,7 @@ func Vconv(v Val, flag FmtFlag) string { case CTRUNE: x := v.U.(*Mpint).Int64() - if ' ' <= x && x < 0x80 && x != '\\' && x != '\'' { + if ' ' <= x && x < utf8.RuneSelf && x != '\\' && x != '\'' { return fmt.Sprintf("'%c'", int(x)) } if 0 <= x && x < 1<<16 { diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index bd2c96d887..2b5ad08551 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -393,7 +393,7 @@ func isPrintable(b byte) bool { // byte slice and returns it. func parseIA5String(bytes []byte) (ret string, err error) { for _, b := range bytes { - if b >= 0x80 { + if b >= utf8.RuneSelf { err = SyntaxError{"IA5String contains invalid character"} return } diff --git a/src/go/build/build.go b/src/go/build/build.go index e61d564fa3..04a41a6c2e 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -1266,7 +1266,7 @@ func safeCgoName(s string, spaces bool) bool { safe = safe[len(safeSpaces):] } for i := 0; i < len(s); i++ { - if c := s[i]; c < 0x80 && bytes.IndexByte(safe, c) < 0 { + if c := s[i]; c < utf8.RuneSelf && bytes.IndexByte(safe, c) < 0 { return false } } diff --git a/src/go/build/read.go b/src/go/build/read.go index d411c1980e..29b8cdc786 100644 --- a/src/go/build/read.go +++ b/src/go/build/read.go @@ -8,6 +8,7 @@ import ( "bufio" "errors" "io" + "unicode/utf8" ) type importReader struct { @@ -20,7 +21,7 @@ type importReader struct { } func isIdent(c byte) bool { - return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= 0x80 + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf } var ( diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go index 4041d9aa47..ce660c71d5 100644 --- a/src/go/scanner/scanner.go +++ b/src/go/scanner/scanner.go @@ -64,7 +64,7 @@ func (s *Scanner) next() { switch { case r == 0: s.error(s.offset, "illegal character NUL") - case r >= 0x80: + case r >= utf8.RuneSelf: // not ASCII r, w = utf8.DecodeRune(s.src[s.rdOffset:]) if r == utf8.RuneError && w == 1 { @@ -255,11 +255,11 @@ func (s *Scanner) findLineEnd() bool { } func isLetter(ch rune) bool { - return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch) } func isDigit(ch rune) bool { - return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) + return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch) } func (s *Scanner) scanIdentifier() string { diff --git a/src/html/template/css.go b/src/html/template/css.go index 4c27cce85a..9154d8636d 100644 --- a/src/html/template/css.go +++ b/src/html/template/css.go @@ -243,7 +243,7 @@ func cssValueFilter(args ...interface{}) string { return filterFailsafe } default: - if c < 0x80 && isCSSNmchar(rune(c)) { + if c < utf8.RuneSelf && isCSSNmchar(rune(c)) { id = append(id, c) } } diff --git a/src/net/http/cookiejar/punycode.go b/src/net/http/cookiejar/punycode.go index ea7ceb5ef3..a9cc666e8c 100644 --- a/src/net/http/cookiejar/punycode.go +++ b/src/net/http/cookiejar/punycode.go @@ -37,7 +37,7 @@ func encode(prefix, s string) (string, error) { delta, n, bias := int32(0), initialN, initialBias b, remaining := int32(0), int32(0) for _, r := range s { - if r < 0x80 { + if r < utf8.RuneSelf { b++ output = append(output, byte(r)) } else { -- cgit v1.3 From f20b1809f213c662932106a68c76ea3545eab1ee Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Sun, 10 Apr 2016 13:43:24 +0200 Subject: compress/flate: eliminate most common bounds checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses the SSA compiler to eliminate various unneeded bounds checks in loops and various lookups. This fixes the low hanging fruit, without any major code changes. name old time/op new time/op delta EncodeDigitsHuffman1e4-8 49.9µs ± 1% 48.1µs ± 1% -3.74% (p=0.000 n=10+9) EncodeDigitsHuffman1e5-8 476µs ± 1% 458µs ± 1% -3.58% (p=0.000 n=10+10) EncodeDigitsHuffman1e6-8 4.80ms ± 2% 4.56ms ± 1% -5.07% (p=0.000 n=10+9) EncodeDigitsSpeed1e4-8 305µs ± 3% 290µs ± 2% -5.03% (p=0.000 n=10+9) EncodeDigitsSpeed1e5-8 3.67ms ± 2% 3.49ms ± 2% -4.78% (p=0.000 n=9+10) EncodeDigitsSpeed1e6-8 38.3ms ± 2% 35.8ms ± 1% -6.58% (p=0.000 n=9+10) EncodeDigitsDefault1e4-8 361µs ± 2% 346µs ± 3% -4.12% (p=0.000 n=10+9) EncodeDigitsDefault1e5-8 5.24ms ± 2% 4.96ms ± 3% -5.38% (p=0.000 n=10+10) EncodeDigitsDefault1e6-8 56.5ms ± 3% 52.2ms ± 2% -7.68% (p=0.000 n=10+10) EncodeDigitsCompress1e4-8 362µs ± 2% 343µs ± 1% -5.20% (p=0.000 n=10+9) EncodeDigitsCompress1e5-8 5.26ms ± 3% 4.98ms ± 2% -5.48% (p=0.000 n=10+10) EncodeDigitsCompress1e6-8 56.0ms ± 4% 52.1ms ± 1% -7.01% (p=0.000 n=10+10) EncodeTwainHuffman1e4-8 70.9µs ± 3% 64.7µs ± 1% -8.68% (p=0.000 n=10+9) EncodeTwainHuffman1e5-8 556µs ± 2% 524µs ± 2% -5.84% (p=0.000 n=10+10) EncodeTwainHuffman1e6-8 5.54ms ± 3% 5.22ms ± 2% -5.70% (p=0.000 n=10+10) EncodeTwainSpeed1e4-8 294µs ± 3% 284µs ± 1% -3.71% (p=0.000 n=10+10) EncodeTwainSpeed1e5-8 2.59ms ± 2% 2.48ms ± 1% -4.14% (p=0.000 n=10+9) EncodeTwainSpeed1e6-8 25.6ms ± 1% 24.3ms ± 1% -5.28% (p=0.000 n=9+10) EncodeTwainDefault1e4-8 419µs ± 2% 396µs ± 1% -5.59% (p=0.000 n=10+9) EncodeTwainDefault1e5-8 6.23ms ± 4% 5.75ms ± 1% -7.83% (p=0.000 n=10+9) EncodeTwainDefault1e6-8 66.2ms ± 2% 61.4ms ± 1% -7.22% (p=0.000 n=10+10) EncodeTwainCompress1e4-8 426µs ± 1% 405µs ± 1% -4.97% (p=0.000 n=9+10) EncodeTwainCompress1e5-8 6.80ms ± 1% 6.32ms ± 1% -6.97% (p=0.000 n=9+10) EncodeTwainCompress1e6-8 74.6ms ± 3% 68.7ms ± 1% -7.90% (p=0.000 n=10+9) name old speed new speed delta EncodeDigitsHuffman1e4-8 200MB/s ± 1% 208MB/s ± 1% +3.88% (p=0.000 n=10+9) EncodeDigitsHuffman1e5-8 210MB/s ± 1% 218MB/s ± 1% +3.71% (p=0.000 n=10+10) EncodeDigitsHuffman1e6-8 208MB/s ± 2% 219MB/s ± 1% +5.32% (p=0.000 n=10+9) EncodeDigitsSpeed1e4-8 32.8MB/s ± 3% 34.5MB/s ± 2% +5.29% (p=0.000 n=10+9) EncodeDigitsSpeed1e5-8 27.2MB/s ± 2% 28.6MB/s ± 2% +5.29% (p=0.000 n=10+10) EncodeDigitsSpeed1e6-8 26.1MB/s ± 2% 27.9MB/s ± 1% +7.02% (p=0.000 n=9+10) EncodeDigitsDefault1e4-8 27.7MB/s ± 2% 28.9MB/s ± 3% +4.30% (p=0.000 n=10+9) EncodeDigitsDefault1e5-8 19.1MB/s ± 2% 20.2MB/s ± 3% +5.69% (p=0.000 n=10+10) EncodeDigitsDefault1e6-8 17.7MB/s ± 3% 19.2MB/s ± 2% +8.31% (p=0.000 n=10+10) EncodeDigitsCompress1e4-8 27.6MB/s ± 2% 29.1MB/s ± 1% +5.47% (p=0.000 n=10+9) EncodeDigitsCompress1e5-8 19.0MB/s ± 3% 20.1MB/s ± 2% +5.78% (p=0.000 n=10+10) EncodeDigitsCompress1e6-8 17.9MB/s ± 4% 19.2MB/s ± 1% +7.50% (p=0.000 n=10+10) EncodeTwainHuffman1e4-8 141MB/s ± 3% 154MB/s ± 1% +9.46% (p=0.000 n=10+9) EncodeTwainHuffman1e5-8 180MB/s ± 2% 191MB/s ± 2% +6.19% (p=0.000 n=10+10) EncodeTwainHuffman1e6-8 181MB/s ± 3% 192MB/s ± 2% +6.02% (p=0.000 n=10+10) EncodeTwainSpeed1e4-8 34.0MB/s ± 3% 35.3MB/s ± 1% +3.84% (p=0.000 n=10+10) EncodeTwainSpeed1e5-8 38.7MB/s ± 2% 40.3MB/s ± 1% +4.30% (p=0.000 n=10+9) EncodeTwainSpeed1e6-8 39.1MB/s ± 1% 41.2MB/s ± 1% +5.57% (p=0.000 n=9+10) EncodeTwainDefault1e4-8 23.9MB/s ± 2% 25.3MB/s ± 1% +5.91% (p=0.000 n=10+9) EncodeTwainDefault1e5-8 16.0MB/s ± 4% 17.4MB/s ± 1% +8.47% (p=0.000 n=10+9) EncodeTwainDefault1e6-8 15.1MB/s ± 2% 16.3MB/s ± 1% +7.76% (p=0.000 n=10+10) EncodeTwainCompress1e4-8 23.5MB/s ± 1% 24.7MB/s ± 1% +5.24% (p=0.000 n=9+10) EncodeTwainCompress1e5-8 14.7MB/s ± 1% 15.8MB/s ± 1% +7.50% (p=0.000 n=9+10) EncodeTwainCompress1e6-8 13.4MB/s ± 3% 14.6MB/s ± 1% +8.57% (p=0.000 n=10+9) Change-Id: I5c7e84c2f9ea4d38a2115995705eebb93387e22f Reviewed-on: https://go-review.googlesource.com/21759 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/compress/flate/deflate.go | 37 ++++++++++----------- src/compress/flate/huffman_bit_writer.go | 55 +++++++++++++++++--------------- src/compress/flate/huffman_code.go | 4 +-- 3 files changed, 48 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/compress/flate/deflate.go b/src/compress/flate/deflate.go index 3bb8b5e02a..d8bbffbc66 100644 --- a/src/compress/flate/deflate.go +++ b/src/compress/flate/deflate.go @@ -73,8 +73,8 @@ type compressor struct { // hashPrev[hashHead[hashValue] & windowMask] contains the previous index // with the same hash value. chainHead int - hashHead []uint32 - hashPrev []uint32 + hashHead [hashSize]uint32 + hashPrev [windowSize]uint32 hashOffset int // input window: unprocessed data is window[index:windowEnd] @@ -188,12 +188,13 @@ func (d *compressor) fillWindow(b []byte) { var newH uint32 for i, val := range dst { di := i + index - newH = val & hashMask + newH = val + hh := &d.hashHead[newH&hashMask] // Get previous value with the same hash. // Our chain should point to the previous value. - d.hashPrev[di&windowMask] = d.hashHead[newH] + d.hashPrev[di&windowMask] = *hh // Set the head of the hash chain to us. - d.hashHead[newH] = uint32(di + d.hashOffset) + *hh = uint32(di + d.hashOffset) } d.hash = newH } @@ -293,6 +294,7 @@ func bulkHash4(b []byte, dst []uint32) { // bytes in size. func matchLen(a, b []byte, max int) int { a = a[:max] + b = b[:len(a)] for i, av := range a { if b[i] != av { return i @@ -302,8 +304,6 @@ func matchLen(a, b []byte, max int) int { } func (d *compressor) initDeflate() { - d.hashHead = make([]uint32, hashSize) - d.hashPrev = make([]uint32, windowSize) d.window = make([]byte, 2*windowSize) d.hashOffset = 1 d.tokens = make([]token, 0, maxFlateBlockTokens+1) @@ -358,9 +358,10 @@ Loop: if d.index < d.maxInsertIndex { // Update the hash d.hash = hash4(d.window[d.index : d.index+minMatchLength]) - d.chainHead = int(d.hashHead[d.hash]) + hh := &d.hashHead[d.hash&hashMask] + d.chainHead = int(*hh) d.hashPrev[d.index&windowMask] = uint32(d.chainHead) - d.hashHead[d.hash] = uint32(d.index + d.hashOffset) + *hh = uint32(d.index + d.hashOffset) } prevLength := d.length prevOffset := d.offset @@ -404,9 +405,10 @@ Loop: d.hash = hash4(d.window[d.index : d.index+minMatchLength]) // Get previous value with the same hash. // Our chain should point to the previous value. - d.hashPrev[d.index&windowMask] = d.hashHead[d.hash] + hh := &d.hashHead[d.hash&hashMask] + d.hashPrev[d.index&windowMask] = *hh // Set the head of the hash chain to us. - d.hashHead[d.hash] = uint32(d.index + d.hashOffset) + *hh = uint32(d.index + d.hashOffset) } } if d.fastSkipHashing == skipNever { @@ -531,9 +533,6 @@ func (d *compressor) init(w io.Writer, level int) (err error) { return nil } -// hzeroes is used for zeroing the hash slice. -var hzeroes [256]uint32 - func (d *compressor) reset(w io.Writer) { d.w.reset(w) d.sync = false @@ -543,15 +542,13 @@ func (d *compressor) reset(w io.Writer) { d.windowEnd = 0 default: d.chainHead = -1 - for s := d.hashHead; len(s) > 0; { - n := copy(s, hzeroes[:]) - s = s[n:] + for i := range d.hashHead { + d.hashHead[i] = 0 } - for s := d.hashPrev; len(s) > 0; s = s[len(hzeroes):] { - copy(s, hzeroes[:]) + for i := range d.hashPrev { + d.hashPrev[i] = 0 } d.hashOffset = 1 - d.index, d.windowEnd = 0, 0 d.blockStart, d.byteAvailable = 0, false d.tokens = d.tokens[:0] diff --git a/src/compress/flate/huffman_bit_writer.go b/src/compress/flate/huffman_bit_writer.go index b99f86ea13..23f242f88e 100644 --- a/src/compress/flate/huffman_bit_writer.go +++ b/src/compress/flate/huffman_bit_writer.go @@ -84,11 +84,11 @@ type huffmanBitWriter struct { bits uint64 nbits uint bytes [bufferSize]byte + codegenFreq [codegenCodeCount]int32 nbytes int literalFreq []int32 offsetFreq []int32 codegen []uint8 - codegenFreq []int32 literalEncoding *huffmanEncoder offsetEncoding *huffmanEncoder codegenEncoding *huffmanEncoder @@ -101,7 +101,6 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { literalFreq: make([]int32, maxNumLit), offsetFreq: make([]int32, offsetCodeCount), codegen: make([]uint8, maxNumLit+offsetCodeCount+1), - codegenFreq: make([]int32, codegenCodeCount), literalEncoding: newHuffmanEncoder(maxNumLit), codegenEncoding: newHuffmanEncoder(codegenCodeCount), offsetEncoding: newHuffmanEncoder(offsetCodeCount), @@ -143,12 +142,13 @@ func (w *huffmanBitWriter) writeBits(b int32, nb uint) { w.bits >>= 48 w.nbits -= 48 n := w.nbytes - w.bytes[n+0] = byte(bits) - w.bytes[n+1] = byte(bits >> 8) - w.bytes[n+2] = byte(bits >> 16) - w.bytes[n+3] = byte(bits >> 24) - w.bytes[n+4] = byte(bits >> 32) - w.bytes[n+5] = byte(bits >> 40) + bytes := w.bytes[n : n+6] + bytes[0] = byte(bits) + bytes[1] = byte(bits >> 8) + bytes[2] = byte(bits >> 16) + bytes[3] = byte(bits >> 24) + bytes[4] = byte(bits >> 32) + bytes[5] = byte(bits >> 40) n += 6 if n >= bufferFlushSize { _, w.err = w.w.Write(w.bytes[:n]) @@ -293,12 +293,13 @@ func (w *huffmanBitWriter) writeCode(c hcode) { w.bits >>= 48 w.nbits -= 48 n := w.nbytes - w.bytes[n+0] = byte(bits) - w.bytes[n+1] = byte(bits >> 8) - w.bytes[n+2] = byte(bits >> 16) - w.bytes[n+3] = byte(bits >> 24) - w.bytes[n+4] = byte(bits >> 32) - w.bytes[n+5] = byte(bits >> 40) + bytes := w.bytes[n : n+6] + bytes[0] = byte(bits) + bytes[1] = byte(bits >> 8) + bytes[2] = byte(bits >> 16) + bytes[3] = byte(bits >> 24) + bytes[4] = byte(bits >> 32) + bytes[5] = byte(bits >> 40) n += 6 if n >= bufferFlushSize { _, w.err = w.w.Write(w.bytes[:n]) @@ -428,13 +429,13 @@ func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) { // Generate codegen and codegenFrequencies, which indicates how to encode // the literalEncoding and the offsetEncoding. w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) - w.codegenEncoding.generate(w.codegenFreq, 7) + w.codegenEncoding.generate(w.codegenFreq[:], 7) numCodegens = len(w.codegenFreq) for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { numCodegens-- } dynamicHeader := int64(3+5+5+4+(3*numCodegens)) + - w.codegenEncoding.bitLength(w.codegenFreq) + + w.codegenEncoding.bitLength(w.codegenFreq[:]) + int64(extraBits) + int64(w.codegenFreq[16]*2) + int64(w.codegenFreq[17]*3) + @@ -482,7 +483,7 @@ func (w *huffmanBitWriter) writeBlockDynamic(tokens []token, eof bool, input []b // Generate codegen and codegenFrequencies, which indicates how to encode // the literalEncoding and the offsetEncoding. w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) - w.codegenEncoding.generate(w.codegenFreq, 7) + w.codegenEncoding.generate(w.codegenFreq[:], 7) numCodegens := len(w.codegenFreq) for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { numCodegens-- @@ -609,13 +610,13 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) { // Generate codegen and codegenFrequencies, which indicates how to encode // the literalEncoding and the offsetEncoding. w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, huffOffset) - w.codegenEncoding.generate(w.codegenFreq, 7) + w.codegenEncoding.generate(w.codegenFreq[:], 7) numCodegens = len(w.codegenFreq) for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { numCodegens-- } headerSize := int64(3+5+5+4+(3*numCodegens)) + - w.codegenEncoding.bitLength(w.codegenFreq) + + w.codegenEncoding.bitLength(w.codegenFreq[:]) + int64(w.codegenFreq[16]*2) + int64(w.codegenFreq[17]*3) + int64(w.codegenFreq[18]*7) @@ -639,7 +640,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) { // Huffman. w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) - encoding := w.literalEncoding.codes + encoding := w.literalEncoding.codes[:257] n := w.nbytes for _, t := range input { // Bitwriting inlined, ~30% speedup @@ -653,12 +654,13 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) { bits := w.bits w.bits >>= 48 w.nbits -= 48 - w.bytes[n+0] = byte(bits) - w.bytes[n+1] = byte(bits >> 8) - w.bytes[n+2] = byte(bits >> 16) - w.bytes[n+3] = byte(bits >> 24) - w.bytes[n+4] = byte(bits >> 32) - w.bytes[n+5] = byte(bits >> 40) + bytes := w.bytes[n : n+6] + bytes[0] = byte(bits) + bytes[1] = byte(bits >> 8) + bytes[2] = byte(bits >> 16) + bytes[3] = byte(bits >> 24) + bytes[4] = byte(bits >> 32) + bytes[5] = byte(bits >> 40) n += 6 if n < bufferFlushSize { continue @@ -677,6 +679,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) { // // len(h) must be >= 256, and h's elements must be all zeroes. func histogram(b []byte, h []int32) { + h = h[:256] for _, t := range b { h[t]++ } diff --git a/src/compress/flate/huffman_code.go b/src/compress/flate/huffman_code.go index b0328c6e08..20fb19090d 100644 --- a/src/compress/flate/huffman_code.go +++ b/src/compress/flate/huffman_code.go @@ -96,8 +96,8 @@ func generateFixedLiteralEncoding() *huffmanEncoder { func generateFixedOffsetEncoding() *huffmanEncoder { h := newHuffmanEncoder(30) codes := h.codes - for ch := uint16(0); ch < 30; ch++ { - codes[ch] = hcode{code: reverseBits(ch, 5), len: 5} + for ch := range codes { + codes[ch] = hcode{code: reverseBits(uint16(ch), 5), len: 5} } return h } -- cgit v1.3 From a56f5a032217e1e28c005886a98054caf7dc8201 Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Sun, 10 Apr 2016 17:16:07 +0200 Subject: compress/flate: improve short writer error test This improves the short version of the writer test. First of all, it has a much quicker setup. Previously that could take up towards 0.5 second. Secondly, it will test all compression levels in short mode as well. Execution time is 1.7s/0.03s for normal/short mode. Change-Id: I275a21f712daff6f7125cc6a493415e86439cb19 Reviewed-on: https://go-review.googlesource.com/21800 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/compress/flate/writer_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/compress/flate/writer_test.go b/src/compress/flate/writer_test.go index 633cadf2b7..e4c5c8cc72 100644 --- a/src/compress/flate/writer_test.go +++ b/src/compress/flate/writer_test.go @@ -86,14 +86,18 @@ func (e *errorWriter) Write(b []byte) (int, error) { // Test if errors from the underlying writer is passed upwards. func TestWriteError(t *testing.T) { buf := new(bytes.Buffer) - for i := 0; i < 1024*1024; i++ { + n := 65536 + if !testing.Short() { + n *= 4 + } + for i := 0; i < n; i++ { buf.WriteString(fmt.Sprintf("asdasfasf%d%dfghfgujyut%dyutyu\n", i, i, i)) } in := buf.Bytes() // We create our own buffer to control number of writes. - copyBuffer := make([]byte, 1024) + copyBuffer := make([]byte, 128) for l := 0; l < 10; l++ { - for fail := 1; fail <= 512; fail *= 2 { + for fail := 1; fail <= 256; fail *= 2 { // Fail after 'fail' writes ew := &errorWriter{N: fail} w, err := NewWriter(ew, l) -- cgit v1.3 From a44c4256ae958b0aacecd5fd0b0e7f1156f8bcf4 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 10 Apr 2016 14:51:07 +0000 Subject: html: fix typo in UnescapeString string docs Fixes #15221 Change-Id: I9e927a2f604213338b4572f1a32d0247c58bdc60 Reviewed-on: https://go-review.googlesource.com/21798 Reviewed-by: Ian Lance Taylor --- src/html/escape.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/html/escape.go b/src/html/escape.go index 71906ac586..8dd1f4ad2f 100644 --- a/src/html/escape.go +++ b/src/html/escape.go @@ -181,7 +181,7 @@ func EscapeString(s string) string { // UnescapeString unescapes entities like "<" to become "<". It unescapes a // larger range of entities than EscapeString escapes. For example, "á" -// unescapes to "á", as does "á" and "&xE1;". +// unescapes to "á", as does "á" and "á". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func UnescapeString(s string) string { -- cgit v1.3 From 974c201f74f730737964e5239da473fc548b408e Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Sun, 10 Apr 2016 10:43:04 -0700 Subject: runtime: avoid unnecessary map iteration write barrier Update #14921 Change-Id: I5c5816d0193757bf7465b1e09c27ca06897df4bf Reviewed-on: https://go-review.googlesource.com/21814 Run-TryBot: Josh Bleecher Snyder TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/runtime/hashmap.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 9e18192cd8..d549ce4194 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -790,7 +790,9 @@ next: } } it.bucket = bucket - it.bptr = b + if it.bptr != b { // avoid unnecessary write barrier; see issue 14921 + it.bptr = b + } it.i = i + 1 it.checkBucket = checkBucket return -- cgit v1.3 From 6b33b0e98e9be77d98b026ae2adf10dd71be5a1b Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Sun, 10 Apr 2016 09:08:00 -0700 Subject: cmd/compile: avoid a spill in append fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of spilling newlen, recalculate it. This removes a spill from the fast path, at the cost of a cheap recalculation on the (rare) growth path. This uses 8 bytes less of stack space. It generates two more bytes of code, but that is due to suboptimal register allocation; see far below. Runtime append microbenchmarks are all over the map, presumably due to incidental code movement. Sample code: func s(b []byte) []byte { b = append(b, 1, 2, 3) return b } Before: "".s t=1 size=160 args=0x30 locals=0x48 0x0000 00000 (append.go:8) TEXT "".s(SB), $72-48 0x0000 00000 (append.go:8) MOVQ (TLS), CX 0x0009 00009 (append.go:8) CMPQ SP, 16(CX) 0x000d 00013 (append.go:8) JLS 149 0x0013 00019 (append.go:8) SUBQ $72, SP 0x0017 00023 (append.go:8) FUNCDATA $0, gclocals·6432f8c6a0d23fa7bee6c5d96f21a92a(SB) 0x0017 00023 (append.go:8) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0017 00023 (append.go:9) MOVQ "".b+88(FP), CX 0x001c 00028 (append.go:9) LEAQ 3(CX), DX 0x0020 00032 (append.go:9) MOVQ DX, "".autotmp_0+64(SP) 0x0025 00037 (append.go:9) MOVQ "".b+96(FP), BX 0x002a 00042 (append.go:9) CMPQ DX, BX 0x002d 00045 (append.go:9) JGT $0, 86 0x002f 00047 (append.go:8) MOVQ "".b+80(FP), AX 0x0034 00052 (append.go:9) MOVB $1, (AX)(CX*1) 0x0038 00056 (append.go:9) MOVB $2, 1(AX)(CX*1) 0x003d 00061 (append.go:9) MOVB $3, 2(AX)(CX*1) 0x0042 00066 (append.go:10) MOVQ AX, "".~r1+104(FP) 0x0047 00071 (append.go:10) MOVQ DX, "".~r1+112(FP) 0x004c 00076 (append.go:10) MOVQ BX, "".~r1+120(FP) 0x0051 00081 (append.go:10) ADDQ $72, SP 0x0055 00085 (append.go:10) RET 0x0056 00086 (append.go:9) LEAQ type.[]uint8(SB), AX 0x005d 00093 (append.go:9) MOVQ AX, (SP) 0x0061 00097 (append.go:9) MOVQ "".b+80(FP), BP 0x0066 00102 (append.go:9) MOVQ BP, 8(SP) 0x006b 00107 (append.go:9) MOVQ CX, 16(SP) 0x0070 00112 (append.go:9) MOVQ BX, 24(SP) 0x0075 00117 (append.go:9) MOVQ DX, 32(SP) 0x007a 00122 (append.go:9) PCDATA $0, $0 0x007a 00122 (append.go:9) CALL runtime.growslice(SB) 0x007f 00127 (append.go:9) MOVQ 40(SP), AX 0x0084 00132 (append.go:9) MOVQ 56(SP), BX 0x0089 00137 (append.go:8) MOVQ "".b+88(FP), CX 0x008e 00142 (append.go:9) MOVQ "".autotmp_0+64(SP), DX 0x0093 00147 (append.go:9) JMP 52 0x0095 00149 (append.go:9) NOP 0x0095 00149 (append.go:8) CALL runtime.morestack_noctxt(SB) 0x009a 00154 (append.go:8) JMP 0 After: "".s t=1 size=176 args=0x30 locals=0x40 0x0000 00000 (append.go:8) TEXT "".s(SB), $64-48 0x0000 00000 (append.go:8) MOVQ (TLS), CX 0x0009 00009 (append.go:8) CMPQ SP, 16(CX) 0x000d 00013 (append.go:8) JLS 151 0x0013 00019 (append.go:8) SUBQ $64, SP 0x0017 00023 (append.go:8) FUNCDATA $0, gclocals·6432f8c6a0d23fa7bee6c5d96f21a92a(SB) 0x0017 00023 (append.go:8) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0017 00023 (append.go:9) MOVQ "".b+80(FP), CX 0x001c 00028 (append.go:9) LEAQ 3(CX), DX 0x0020 00032 (append.go:9) MOVQ "".b+88(FP), BX 0x0025 00037 (append.go:9) CMPQ DX, BX 0x0028 00040 (append.go:9) JGT $0, 81 0x002a 00042 (append.go:8) MOVQ "".b+72(FP), AX 0x002f 00047 (append.go:9) MOVB $1, (AX)(CX*1) 0x0033 00051 (append.go:9) MOVB $2, 1(AX)(CX*1) 0x0038 00056 (append.go:9) MOVB $3, 2(AX)(CX*1) 0x003d 00061 (append.go:10) MOVQ AX, "".~r1+96(FP) 0x0042 00066 (append.go:10) MOVQ DX, "".~r1+104(FP) 0x0047 00071 (append.go:10) MOVQ BX, "".~r1+112(FP) 0x004c 00076 (append.go:10) ADDQ $64, SP 0x0050 00080 (append.go:10) RET 0x0051 00081 (append.go:9) LEAQ type.[]uint8(SB), AX 0x0058 00088 (append.go:9) MOVQ AX, (SP) 0x005c 00092 (append.go:9) MOVQ "".b+72(FP), BP 0x0061 00097 (append.go:9) MOVQ BP, 8(SP) 0x0066 00102 (append.go:9) MOVQ CX, 16(SP) 0x006b 00107 (append.go:9) MOVQ BX, 24(SP) 0x0070 00112 (append.go:9) MOVQ DX, 32(SP) 0x0075 00117 (append.go:9) PCDATA $0, $0 0x0075 00117 (append.go:9) CALL runtime.growslice(SB) 0x007a 00122 (append.go:9) MOVQ 40(SP), AX 0x007f 00127 (append.go:9) MOVQ 48(SP), CX 0x0084 00132 (append.go:9) MOVQ 56(SP), BX 0x0089 00137 (append.go:9) ADDQ $3, CX 0x008d 00141 (append.go:9) MOVQ CX, DX 0x0090 00144 (append.go:8) MOVQ "".b+80(FP), CX 0x0095 00149 (append.go:9) JMP 47 0x0097 00151 (append.go:9) NOP 0x0097 00151 (append.go:8) CALL runtime.morestack_noctxt(SB) 0x009c 00156 (append.go:8) JMP 0 Observe that in the following sequence, we should use DX directly instead of using CX as a temporary register, which would make the new code a strict improvement on the old: 0x007f 00127 (append.go:9) MOVQ 48(SP), CX 0x0084 00132 (append.go:9) MOVQ 56(SP), BX 0x0089 00137 (append.go:9) ADDQ $3, CX 0x008d 00141 (append.go:9) MOVQ CX, DX 0x0090 00144 (append.go:8) MOVQ "".b+80(FP), CX Change-Id: I4ee50b18fa53865901d2d7f86c2cbb54c6fa6924 Reviewed-on: https://go-review.googlesource.com/21812 Run-TryBot: Josh Bleecher Snyder TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/ssa.go | 30 +++++++++++++++++------------- src/runtime/slice.go | 6 ++++++ 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7c5f906d76..d69559d945 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -337,12 +337,13 @@ var ( memVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "mem"}} // dummy nodes for temporary variables - ptrVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ptr"}} - capVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "cap"}} - typVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "typ"}} - idataVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "idata"}} - okVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ok"}} - deltaVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "delta"}} + ptrVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ptr"}} + newlenVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "newlen"}} + capVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "cap"}} + typVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "typ"}} + idataVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "idata"}} + okVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ok"}} + deltaVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "delta"}} ) // startBlock sets the current block we're generating code in to b. @@ -2089,15 +2090,16 @@ func (s *state) expr(n *Node) *ssa.Value { // exprAppend converts an OAPPEND node n to an ssa.Value, adds it to s, and returns the Value. func (s *state) exprAppend(n *Node) *ssa.Value { // append(s, e1, e2, e3). Compile like: - // ptr,len,cap := s + // ptr, len, cap := s // newlen := len + 3 // if newlen > s.cap { - // ptr,_,cap = growslice(s, newlen) + // ptr, len, cap = growslice(s, newlen) + // newlen = len + 3 // recalculate to avoid a spill // } // *(ptr+len) = e1 // *(ptr+len+1) = e2 // *(ptr+len+2) = e3 - // makeslice(ptr,newlen,cap) + // makeslice(ptr, newlen, cap) et := n.Type.Elem() pt := Ptrto(et) @@ -2117,6 +2119,7 @@ func (s *state) exprAppend(n *Node) *ssa.Value { nl := s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], l, s.constInt(Types[TINT], nargs)) cmp := s.newValue2(s.ssaOp(OGT, Types[TINT]), Types[TBOOL], nl, c) s.vars[&ptrVar] = p + s.vars[&newlenVar] = nl s.vars[&capVar] = c b := s.endBlock() b.Kind = ssa.BlockIf @@ -2132,8 +2135,7 @@ func (s *state) exprAppend(n *Node) *ssa.Value { r := s.rtcall(growslice, true, []*Type{pt, Types[TINT], Types[TINT]}, taddr, p, l, c, nl) s.vars[&ptrVar] = r[0] - // Note: we don't need to read r[1], the result's length. It will be nl. - // (or maybe we should, we just have to spill/restore nl otherwise?) + s.vars[&newlenVar] = s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], r[1], s.constInt(Types[TINT], nargs)) s.vars[&capVar] = r[2] b = s.endBlock() b.AddEdgeTo(assign) @@ -2154,8 +2156,9 @@ func (s *state) exprAppend(n *Node) *ssa.Value { } } - p = s.variable(&ptrVar, pt) // generates phi for ptr - c = s.variable(&capVar, Types[TINT]) // generates phi for cap + p = s.variable(&ptrVar, pt) // generates phi for ptr + nl = s.variable(&newlenVar, Types[TINT]) // generates phi for nl + c = s.variable(&capVar, Types[TINT]) // generates phi for cap p2 := s.newValue2(ssa.OpPtrIndex, pt, p, l) // TODO: just one write barrier call for all of these writes? // TODO: maybe just one writeBarrier.enabled check? @@ -2178,6 +2181,7 @@ func (s *state) exprAppend(n *Node) *ssa.Value { // make result delete(s.vars, &ptrVar) + delete(s.vars, &newlenVar) delete(s.vars, &capVar) return s.newValue3(ssa.OpSliceMake, n.Type, p, nl, c) } diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 0bc0299f72..4ab221056c 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -37,6 +37,12 @@ func makeslice(t *slicetype, len64, cap64 int64) slice { // It is passed the slice type, the old slice, and the desired new minimum capacity, // and it returns a new slice with at least that capacity, with the old data // copied into it. +// The new slice's length is set to the old slice's length, +// NOT to the new requested capacity. +// This is for codegen convenience. The old slice's length is used immediately +// to calculate where to write new values during an append. +// TODO: When the old backend is gone, reconsider this decision. +// The SSA backend might prefer the new length or to return only ptr/cap and save stack space. func growslice(t *slicetype, old slice, cap int) slice { if raceenabled { callerpc := getcallerpc(unsafe.Pointer(&t)) -- cgit v1.3 From 0004f34cefcdaad13a5131e3494fb2ff04877cd2 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 10 Apr 2016 08:26:43 -0700 Subject: cmd/compile: regalloc enforces 2-address instructions Instead of being a hint, resultInArg0 is now enforced by regalloc. This allows us to delete all the code from amd64/ssa.go which deals with converting from a semantically three-address instruction into some copies plus a two-address instruction. Change-Id: Id4f39a80be4b678718bfd42a229f9094ab6ecd7c Reviewed-on: https://go-review.googlesource.com/21816 Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/amd64/ssa.go | 240 ++++---------------- src/cmd/compile/internal/ssa/gen/AMD64.rules | 6 + src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 28 +-- src/cmd/compile/internal/ssa/gen/main.go | 8 +- src/cmd/compile/internal/ssa/op.go | 2 +- src/cmd/compile/internal/ssa/opGen.go | 328 +++++++++++++-------------- src/cmd/compile/internal/ssa/regalloc.go | 47 +++- src/cmd/compile/internal/ssa/rewriteAMD64.go | 47 ++++ 8 files changed, 310 insertions(+), 396 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index 3f8e0ece12..723a2ddec5 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -192,74 +192,23 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = r } - // 2-address opcode arithmetic, symmetric - case ssa.OpAMD64ADDSS, ssa.OpAMD64ADDSD, + // 2-address opcode arithmetic + case ssa.OpAMD64SUBQ, ssa.OpAMD64SUBL, ssa.OpAMD64SUBW, ssa.OpAMD64SUBB, + ssa.OpAMD64MULQ, ssa.OpAMD64MULL, ssa.OpAMD64MULW, ssa.OpAMD64MULB, ssa.OpAMD64ANDQ, ssa.OpAMD64ANDL, ssa.OpAMD64ANDW, ssa.OpAMD64ANDB, ssa.OpAMD64ORQ, ssa.OpAMD64ORL, ssa.OpAMD64ORW, ssa.OpAMD64ORB, ssa.OpAMD64XORQ, ssa.OpAMD64XORL, ssa.OpAMD64XORW, ssa.OpAMD64XORB, - ssa.OpAMD64MULQ, ssa.OpAMD64MULL, ssa.OpAMD64MULW, ssa.OpAMD64MULB, - ssa.OpAMD64MULSS, ssa.OpAMD64MULSD, ssa.OpAMD64PXOR: - r := gc.SSARegNum(v) - x := gc.SSARegNum(v.Args[0]) - y := gc.SSARegNum(v.Args[1]) - if x != r && y != r { - opregreg(moveByType(v.Type), r, x) - x = r - } - p := gc.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_REG - p.To.Type = obj.TYPE_REG - p.To.Reg = r - if x == r { - p.From.Reg = y - } else { - p.From.Reg = x - } - // 2-address opcode arithmetic, not symmetric - case ssa.OpAMD64SUBQ, ssa.OpAMD64SUBL, ssa.OpAMD64SUBW, ssa.OpAMD64SUBB: - r := gc.SSARegNum(v) - x := gc.SSARegNum(v.Args[0]) - y := gc.SSARegNum(v.Args[1]) - var neg bool - if y == r { - // compute -(y-x) instead - x, y = y, x - neg = true - } - if x != r { - opregreg(moveByType(v.Type), r, x) - } - opregreg(v.Op.Asm(), r, y) - - if neg { - if v.Op == ssa.OpAMD64SUBQ { - p := gc.Prog(x86.ANEGQ) - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } else { // Avoids partial registers write - p := gc.Prog(x86.ANEGL) - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } - } - case ssa.OpAMD64SUBSS, ssa.OpAMD64SUBSD, ssa.OpAMD64DIVSS, ssa.OpAMD64DIVSD: + ssa.OpAMD64SHLQ, ssa.OpAMD64SHLL, ssa.OpAMD64SHLW, ssa.OpAMD64SHLB, + ssa.OpAMD64SHRQ, ssa.OpAMD64SHRL, ssa.OpAMD64SHRW, ssa.OpAMD64SHRB, + ssa.OpAMD64SARQ, ssa.OpAMD64SARL, ssa.OpAMD64SARW, ssa.OpAMD64SARB, + ssa.OpAMD64ADDSS, ssa.OpAMD64ADDSD, ssa.OpAMD64SUBSS, ssa.OpAMD64SUBSD, + ssa.OpAMD64MULSS, ssa.OpAMD64MULSD, ssa.OpAMD64DIVSS, ssa.OpAMD64DIVSD, + ssa.OpAMD64PXOR: r := gc.SSARegNum(v) - x := gc.SSARegNum(v.Args[0]) - y := gc.SSARegNum(v.Args[1]) - if y == r && x != r { - // r/y := x op r/y, need to preserve x and rewrite to - // r/y := r/y op x15 - x15 := int16(x86.REG_X15) - // register move y to x15 - // register move x to y - // rename y with x15 - opregreg(moveByType(v.Type), x15, y) - opregreg(moveByType(v.Type), r, x) - y = x15 - } else if x != r { - opregreg(moveByType(v.Type), r, x) + if r != gc.SSARegNum(v.Args[0]) { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) } - opregreg(v.Op.Asm(), r, y) + opregreg(v.Op.Asm(), r, gc.SSARegNum(v.Args[1])) case ssa.OpAMD64DIVQ, ssa.OpAMD64DIVL, ssa.OpAMD64DIVW, ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU, @@ -372,47 +321,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { // Do a 64-bit add, the overflow goes into the carry. // Shift right once and pull the carry back into the 63rd bit. r := gc.SSARegNum(v) - x := gc.SSARegNum(v.Args[0]) - y := gc.SSARegNum(v.Args[1]) - if x != r && y != r { - opregreg(moveByType(v.Type), r, x) - x = r + if r != gc.SSARegNum(v.Args[0]) { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) } p := gc.Prog(x86.AADDQ) p.From.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG p.To.Reg = r - if x == r { - p.From.Reg = y - } else { - p.From.Reg = x - } + p.From.Reg = gc.SSARegNum(v.Args[1]) p = gc.Prog(x86.ARCRQ) p.From.Type = obj.TYPE_CONST p.From.Offset = 1 p.To.Type = obj.TYPE_REG p.To.Reg = r - case ssa.OpAMD64SHLQ, ssa.OpAMD64SHLL, ssa.OpAMD64SHLW, ssa.OpAMD64SHLB, - ssa.OpAMD64SHRQ, ssa.OpAMD64SHRL, ssa.OpAMD64SHRW, ssa.OpAMD64SHRB, - ssa.OpAMD64SARQ, ssa.OpAMD64SARL, ssa.OpAMD64SARW, ssa.OpAMD64SARB: - x := gc.SSARegNum(v.Args[0]) - r := gc.SSARegNum(v) - if x != r { - if r == x86.REG_CX { - v.Fatalf("can't implement %s, target and shift both in CX", v.LongString()) - } - p := gc.Prog(moveByType(v.Type)) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } - p := gc.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_REG - p.From.Reg = gc.SSARegNum(v.Args[1]) // should be CX - p.To.Type = obj.TYPE_REG - p.To.Reg = r case ssa.OpAMD64ADDQconst, ssa.OpAMD64ADDLconst, ssa.OpAMD64ADDWconst, ssa.OpAMD64ADDBconst: r := gc.SSARegNum(v) a := gc.SSARegNum(v.Args[0]) @@ -433,7 +355,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = r return - } else if v.AuxInt == -1 { + } + if v.AuxInt == -1 { var asm obj.As if v.Op == ssa.OpAMD64ADDQconst { asm = x86.ADECQ @@ -444,14 +367,13 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = r return - } else { - p := gc.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_CONST - p.From.Offset = v.AuxInt - p.To.Type = obj.TYPE_REG - p.To.Reg = r - return } + p := gc.Prog(v.Op.Asm()) + p.From.Type = obj.TYPE_CONST + p.From.Offset = v.AuxInt + p.To.Type = obj.TYPE_REG + p.To.Reg = r + return } var asm obj.As if v.Op == ssa.OpAMD64ADDQconst { @@ -469,17 +391,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { case ssa.OpAMD64CMOVQEQconst, ssa.OpAMD64CMOVLEQconst, ssa.OpAMD64CMOVWEQconst, ssa.OpAMD64CMOVQNEconst, ssa.OpAMD64CMOVLNEconst, ssa.OpAMD64CMOVWNEconst: r := gc.SSARegNum(v) - x := gc.SSARegNum(v.Args[0]) - // Arg0 is in/out, move in to out if not already same - if r != x { - p := gc.Prog(moveByType(v.Type)) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = r + if r != gc.SSARegNum(v.Args[0]) { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) } - // Constant into AX, after arg0 movement in case arg0 is in AX + // Constant into AX p := gc.Prog(moveByType(v.Type)) p.From.Type = obj.TYPE_CONST p.From.Offset = v.AuxInt @@ -494,13 +410,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { case ssa.OpAMD64MULQconst, ssa.OpAMD64MULLconst, ssa.OpAMD64MULWconst, ssa.OpAMD64MULBconst: r := gc.SSARegNum(v) - x := gc.SSARegNum(v.Args[0]) - if r != x { - p := gc.Prog(moveByType(v.Type)) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = r + if r != gc.SSARegNum(v.Args[0]) { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) } p := gc.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_CONST @@ -508,87 +419,22 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.To.Type = obj.TYPE_REG p.To.Reg = r // TODO: Teach doasm to compile the three-address multiply imul $c, r1, r2 - // instead of using the MOVQ above. + // then we don't need to use resultInArg0 for these ops. //p.From3 = new(obj.Addr) //p.From3.Type = obj.TYPE_REG //p.From3.Reg = gc.SSARegNum(v.Args[0]) - case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst, ssa.OpAMD64SUBWconst, ssa.OpAMD64SUBBconst: - x := gc.SSARegNum(v.Args[0]) - r := gc.SSARegNum(v) - // We have 3-op add (lea), so transforming a = b - const into - // a = b + (- const), saves us 1 instruction. We can't fit - // - (-1 << 31) into 4 bytes offset in lea. - // We handle 2-address just fine below. - if v.AuxInt == -1<<31 || x == r { - if x != r { - // This code compensates for the fact that the register allocator - // doesn't understand 2-address instructions yet. TODO: fix that. - p := gc.Prog(moveByType(v.Type)) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } - p := gc.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_CONST - p.From.Offset = v.AuxInt - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } else if x == r && v.AuxInt == -1 { - var asm obj.As - // x = x - (-1) is the same as x++ - // See OpAMD64ADDQconst comments about inc vs add $1,reg - if v.Op == ssa.OpAMD64SUBQconst { - asm = x86.AINCQ - } else { - asm = x86.AINCL - } - p := gc.Prog(asm) - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } else if x == r && v.AuxInt == 1 { - var asm obj.As - if v.Op == ssa.OpAMD64SUBQconst { - asm = x86.ADECQ - } else { - asm = x86.ADECL - } - p := gc.Prog(asm) - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } else { - var asm obj.As - if v.Op == ssa.OpAMD64SUBQconst { - asm = x86.ALEAQ - } else { - asm = x86.ALEAL - } - p := gc.Prog(asm) - p.From.Type = obj.TYPE_MEM - p.From.Reg = x - p.From.Offset = -v.AuxInt - p.To.Type = obj.TYPE_REG - p.To.Reg = r - } - case ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst, ssa.OpAMD64ANDWconst, ssa.OpAMD64ANDBconst, + case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst, ssa.OpAMD64SUBWconst, ssa.OpAMD64SUBBconst, + ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst, ssa.OpAMD64ANDWconst, ssa.OpAMD64ANDBconst, ssa.OpAMD64ORQconst, ssa.OpAMD64ORLconst, ssa.OpAMD64ORWconst, ssa.OpAMD64ORBconst, ssa.OpAMD64XORQconst, ssa.OpAMD64XORLconst, ssa.OpAMD64XORWconst, ssa.OpAMD64XORBconst, - ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst, ssa.OpAMD64SHLWconst, - ssa.OpAMD64SHLBconst, ssa.OpAMD64SHRQconst, ssa.OpAMD64SHRLconst, ssa.OpAMD64SHRWconst, - ssa.OpAMD64SHRBconst, ssa.OpAMD64SARQconst, ssa.OpAMD64SARLconst, ssa.OpAMD64SARWconst, - ssa.OpAMD64SARBconst, ssa.OpAMD64ROLQconst, ssa.OpAMD64ROLLconst, ssa.OpAMD64ROLWconst, - ssa.OpAMD64ROLBconst: - // This code compensates for the fact that the register allocator - // doesn't understand 2-address instructions yet. TODO: fix that. - x := gc.SSARegNum(v.Args[0]) + ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst, ssa.OpAMD64SHLWconst, ssa.OpAMD64SHLBconst, + ssa.OpAMD64SHRQconst, ssa.OpAMD64SHRLconst, ssa.OpAMD64SHRWconst, ssa.OpAMD64SHRBconst, + ssa.OpAMD64SARQconst, ssa.OpAMD64SARLconst, ssa.OpAMD64SARWconst, ssa.OpAMD64SARBconst, + ssa.OpAMD64ROLQconst, ssa.OpAMD64ROLLconst, ssa.OpAMD64ROLWconst, ssa.OpAMD64ROLBconst: r := gc.SSARegNum(v) - if x != r { - p := gc.Prog(moveByType(v.Type)) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = r + if r != gc.SSARegNum(v.Args[0]) { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) } p := gc.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_CONST @@ -821,9 +667,6 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { p.To.Offset = v.AuxInt case ssa.OpCopy, ssa.OpAMD64MOVQconvert: // TODO: use MOVQreg for reg->reg copies instead of OpCopy? - if v.Type.IsMemory() { - return - } x := gc.SSARegNum(v.Args[0]) y := gc.SSARegNum(v) if x != y { @@ -969,14 +812,9 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL, ssa.OpAMD64NEGW, ssa.OpAMD64NEGB, ssa.OpAMD64BSWAPQ, ssa.OpAMD64BSWAPL, ssa.OpAMD64NOTQ, ssa.OpAMD64NOTL, ssa.OpAMD64NOTW, ssa.OpAMD64NOTB: - x := gc.SSARegNum(v.Args[0]) r := gc.SSARegNum(v) - if x != r { - p := gc.Prog(moveByType(v.Type)) - p.From.Type = obj.TYPE_REG - p.From.Reg = x - p.To.Type = obj.TYPE_REG - p.To.Reg = r + if r != gc.SSARegNum(v.Args[0]) { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) } p := gc.Prog(v.Op.Asm()) p.To.Type = obj.TYPE_REG diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index d7f361dc2e..dcd5e6a5e1 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -1273,6 +1273,12 @@ (XORWconst [c] x) && int16(c)==0 -> x (XORBconst [c] x) && int8(c)==0 -> x +// Convert constant subtracts to constant adds +(SUBQconst [c] x) && c != -(1<<31) -> (ADDQconst [-c] x) +(SUBLconst [c] x) -> (ADDLconst [int64(int32(-c))] x) +(SUBWconst [c] x) -> (ADDWconst [int64(int16(-c))] x) +(SUBBconst [c] x) -> (ADDBconst [int64(int8(-c))] x) + // generic constant folding // TODO: more of this (ADDQconst [c] (MOVQconst [d])) -> (MOVQconst [c+d]) diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index b1698c0cf1..88bb6bc542 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -111,12 +111,14 @@ func init() { // Common regInfo var ( gp01 = regInfo{inputs: []regMask{}, outputs: gponly} - gp11 = regInfo{inputs: []regMask{gpsp}, outputs: gponly, clobbers: flags} + gp11 = regInfo{inputs: []regMask{gp}, outputs: gponly, clobbers: flags} + gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly, clobbers: flags} gp11nf = regInfo{inputs: []regMask{gpsp}, outputs: gponly} // nf: no flags clobbered gp11sb = regInfo{inputs: []regMask{gpspsb}, outputs: gponly} - gp21 = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: gponly, clobbers: flags} + gp21 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly, clobbers: flags} + gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly, clobbers: flags} gp21sb = regInfo{inputs: []regMask{gpspsb, gpsp}, outputs: gponly} - gp21shift = regInfo{inputs: []regMask{gpsp, cx}, outputs: []regMask{gp &^ cx}, clobbers: flags} + gp21shift = regInfo{inputs: []regMask{gp, cx}, outputs: []regMask{gp}, clobbers: flags} gp11div = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{ax}, clobbers: dx | flags} gp11hmul = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx}, @@ -128,8 +130,8 @@ func init() { gp1flags = regInfo{inputs: []regMask{gpsp}, outputs: flagsonly} flagsgp = regInfo{inputs: flagsonly, outputs: gponly} - // for CMOVconst -- uses AX to hold constant temporary. AX input is moved before temp. - gp1flagsgp = regInfo{inputs: []regMask{gp, flags}, clobbers: ax | flags, outputs: []regMask{gp &^ ax}} + // for CMOVconst -- uses AX to hold constant temporary. + gp1flagsgp = regInfo{inputs: []regMask{gp &^ ax, flags}, clobbers: ax | flags, outputs: []regMask{gp &^ ax}} readflags = regInfo{inputs: flagsonly, outputs: gponly} flagsgpax = regInfo{inputs: flagsonly, clobbers: ax | flags, outputs: []regMask{gp &^ ax}} @@ -186,14 +188,14 @@ func init() { {name: "MOVSDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"}, // fp64 indexed by 8i store // binary ops - {name: "ADDQ", argLength: 2, reg: gp21, asm: "ADDQ", commutative: true, resultInArg0: true}, // arg0 + arg1 - {name: "ADDL", argLength: 2, reg: gp21, asm: "ADDL", commutative: true, resultInArg0: true}, // arg0 + arg1 - {name: "ADDW", argLength: 2, reg: gp21, asm: "ADDL", commutative: true, resultInArg0: true}, // arg0 + arg1 - {name: "ADDB", argLength: 2, reg: gp21, asm: "ADDL", commutative: true, resultInArg0: true}, // arg0 + arg1 - {name: "ADDQconst", argLength: 1, reg: gp11, asm: "ADDQ", aux: "Int64", resultInArg0: true, typ: "UInt64"}, // arg0 + auxint - {name: "ADDLconst", argLength: 1, reg: gp11, asm: "ADDL", aux: "Int32", resultInArg0: true}, // arg0 + auxint - {name: "ADDWconst", argLength: 1, reg: gp11, asm: "ADDL", aux: "Int16", resultInArg0: true}, // arg0 + auxint - {name: "ADDBconst", argLength: 1, reg: gp11, asm: "ADDL", aux: "Int8", resultInArg0: true}, // arg0 + auxint + {name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true}, // arg0 + arg1 + {name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true}, // arg0 + arg1 + {name: "ADDW", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true}, // arg0 + arg1 + {name: "ADDB", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true}, // arg0 + arg1 + {name: "ADDQconst", argLength: 1, reg: gp11sp, asm: "ADDQ", aux: "Int64", typ: "UInt64"}, // arg0 + auxint + {name: "ADDLconst", argLength: 1, reg: gp11sp, asm: "ADDL", aux: "Int32"}, // arg0 + auxint + {name: "ADDWconst", argLength: 1, reg: gp11sp, asm: "ADDL", aux: "Int16"}, // arg0 + auxint + {name: "ADDBconst", argLength: 1, reg: gp11sp, asm: "ADDL", aux: "Int8"}, // arg0 + auxint {name: "SUBQ", argLength: 2, reg: gp21, asm: "SUBQ", resultInArg0: true}, // arg0 - arg1 {name: "SUBL", argLength: 2, reg: gp21, asm: "SUBL", resultInArg0: true}, // arg0 - arg1 diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go index db3c43d3a3..2aec4a324b 100644 --- a/src/cmd/compile/internal/ssa/gen/main.go +++ b/src/cmd/compile/internal/ssa/gen/main.go @@ -39,7 +39,7 @@ type opData struct { rematerializeable bool argLength int32 // number of arguments, if -1, then this operation has a variable number of arguments commutative bool // this operation is commutative (e.g. addition) - resultInArg0 bool // prefer v and v.Args[0] to be allocated to the same register + resultInArg0 bool // v and v.Args[0] must be allocated to the same register } type blockData struct { @@ -155,6 +155,12 @@ func genOp() { } if v.resultInArg0 { fmt.Fprintln(w, "resultInArg0: true,") + if v.reg.inputs[0] != v.reg.outputs[0] { + log.Fatalf("input[0] and output registers must be equal for %s", v.name) + } + if v.commutative && v.reg.inputs[1] != v.reg.outputs[0] { + log.Fatalf("input[1] and output registers must be equal for %s", v.name) + } } if a.name == "generic" { fmt.Fprintln(w, "generic:true,") diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index d10ea230ff..64807ec106 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -26,7 +26,7 @@ type opInfo struct { generic bool // this is a generic (arch-independent) opcode rematerializeable bool // this op is rematerializeable commutative bool // this operation is commutative (e.g. addition) - resultInArg0 bool // prefer v and v.Args[0] to be allocated to the same register + resultInArg0 bool // v and v.Args[0] must be allocated to the same register } type inputInfo struct { diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 5465d7f5ed..381422adfd 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -971,15 +971,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDQ", - argLen: 2, - commutative: true, - resultInArg0: true, - asm: x86.AADDQ, + name: "ADDQ", + argLen: 2, + commutative: true, + asm: x86.AADDQ, reg: regInfo{ inputs: []inputInfo{ + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -988,15 +987,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDL", - argLen: 2, - commutative: true, - resultInArg0: true, - asm: x86.AADDL, + name: "ADDL", + argLen: 2, + commutative: true, + asm: x86.AADDL, reg: regInfo{ inputs: []inputInfo{ + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1005,15 +1003,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDW", - argLen: 2, - commutative: true, - resultInArg0: true, - asm: x86.AADDL, + name: "ADDW", + argLen: 2, + commutative: true, + asm: x86.AADDL, reg: regInfo{ inputs: []inputInfo{ + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1022,15 +1019,14 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDB", - argLen: 2, - commutative: true, - resultInArg0: true, - asm: x86.AADDL, + name: "ADDB", + argLen: 2, + commutative: true, + asm: x86.AADDL, reg: regInfo{ inputs: []inputInfo{ + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1039,11 +1035,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDQconst", - auxType: auxInt64, - argLen: 1, - resultInArg0: true, - asm: x86.AADDQ, + name: "ADDQconst", + auxType: auxInt64, + argLen: 1, + asm: x86.AADDQ, reg: regInfo{ inputs: []inputInfo{ {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 @@ -1055,11 +1050,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDLconst", - auxType: auxInt32, - argLen: 1, - resultInArg0: true, - asm: x86.AADDL, + name: "ADDLconst", + auxType: auxInt32, + argLen: 1, + asm: x86.AADDL, reg: regInfo{ inputs: []inputInfo{ {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 @@ -1071,11 +1065,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDWconst", - auxType: auxInt16, - argLen: 1, - resultInArg0: true, - asm: x86.AADDL, + name: "ADDWconst", + auxType: auxInt16, + argLen: 1, + asm: x86.AADDL, reg: regInfo{ inputs: []inputInfo{ {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 @@ -1087,11 +1080,10 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "ADDBconst", - auxType: auxInt8, - argLen: 1, - resultInArg0: true, - asm: x86.AADDL, + name: "ADDBconst", + auxType: auxInt8, + argLen: 1, + asm: x86.AADDL, reg: regInfo{ inputs: []inputInfo{ {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 @@ -1109,8 +1101,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1125,8 +1117,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1141,8 +1133,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1157,8 +1149,8 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1174,7 +1166,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1190,7 +1182,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1206,7 +1198,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1222,7 +1214,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASUBL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1238,8 +1230,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1255,8 +1247,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1272,8 +1264,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1289,8 +1281,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1306,7 +1298,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1322,7 +1314,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1338,7 +1330,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1354,7 +1346,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AIMULW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1489,8 +1481,8 @@ var opcodeTable = [...]opInfo{ resultInArg0: true, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1686,8 +1678,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1703,8 +1695,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1720,8 +1712,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1737,8 +1729,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1754,7 +1746,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1770,7 +1762,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1786,7 +1778,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1802,7 +1794,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AANDL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1818,8 +1810,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AORQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1835,8 +1827,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1852,8 +1844,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1869,8 +1861,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1886,7 +1878,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AORQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1902,7 +1894,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1918,7 +1910,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1934,7 +1926,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1950,8 +1942,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1967,8 +1959,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -1984,8 +1976,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2001,8 +1993,8 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 - {1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2018,7 +2010,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2034,7 +2026,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2050,7 +2042,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2066,7 +2058,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AXORL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2334,11 +2326,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2350,11 +2342,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2366,11 +2358,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2382,11 +2374,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2398,7 +2390,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHLQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2414,7 +2406,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHLL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2430,7 +2422,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHLL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2446,7 +2438,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHLL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2462,11 +2454,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2478,11 +2470,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2494,11 +2486,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2510,11 +2502,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2526,7 +2518,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHRQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2542,7 +2534,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHRL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2558,7 +2550,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHRW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2574,7 +2566,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASHRB, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2590,11 +2582,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2606,11 +2598,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2622,11 +2614,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2638,11 +2630,11 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 2}, // CX - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ - 65517, // AX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + 65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, }, }, @@ -2654,7 +2646,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASARQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2670,7 +2662,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASARL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2686,7 +2678,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASARW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2702,7 +2694,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ASARB, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2718,7 +2710,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AROLQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2734,7 +2726,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AROLL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2750,7 +2742,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AROLW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2766,7 +2758,7 @@ var opcodeTable = [...]opInfo{ asm: x86.AROLB, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2781,7 +2773,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANEGQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2796,7 +2788,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANEGL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2811,7 +2803,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANEGL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2826,7 +2818,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANEGL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2841,7 +2833,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANOTQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2856,7 +2848,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANOTL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2871,7 +2863,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANOTL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2886,7 +2878,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ANOTL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2900,7 +2892,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSFQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2914,7 +2906,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSFL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2928,7 +2920,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSFW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2942,7 +2934,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSRQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2956,7 +2948,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSRL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2970,7 +2962,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSRW, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -2987,7 +2979,7 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 8589934592}, // FLAGS - {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934593, // AX FLAGS outputs: []regMask{ @@ -3004,7 +2996,7 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 8589934592}, // FLAGS - {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934593, // AX FLAGS outputs: []regMask{ @@ -3021,7 +3013,7 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 8589934592}, // FLAGS - {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934593, // AX FLAGS outputs: []regMask{ @@ -3038,7 +3030,7 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 8589934592}, // FLAGS - {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934593, // AX FLAGS outputs: []regMask{ @@ -3055,7 +3047,7 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 8589934592}, // FLAGS - {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934593, // AX FLAGS outputs: []regMask{ @@ -3072,7 +3064,7 @@ var opcodeTable = [...]opInfo{ reg: regInfo{ inputs: []inputInfo{ {1, 8589934592}, // FLAGS - {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934593, // AX FLAGS outputs: []regMask{ @@ -3087,7 +3079,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSWAPQ, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ @@ -3102,7 +3094,7 @@ var opcodeTable = [...]opInfo{ asm: x86.ABSWAPL, reg: regInfo{ inputs: []inputInfo{ - {0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 + {0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 }, clobbers: 8589934592, // FLAGS outputs: []regMask{ diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index aec23a1368..dfae8612d6 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -527,6 +527,18 @@ func (s *regAllocState) advanceUses(v *Value) { } } +// liveAfterCurrentInstruction reports whether v is live after +// the current instruction is completed. v must be used by the +// current instruction. +func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool { + u := s.values[v.ID].uses + d := u.dist + for u != nil && u.dist == d { + u = u.next + } + return u != nil && u.dist > d +} + // Sets the state of the registers to that encoded in regs. func (s *regAllocState) setState(regs []endReg) { s.freeRegs(s.used) @@ -891,6 +903,27 @@ func (s *regAllocState) regalloc(f *Func) { args[i.idx] = s.allocValToReg(v.Args[i.idx], i.regs, true, v.Line) } + // If the output clobbers the input register, and the input register is + // live beyond the instruction, make another copy of the input register so + // we don't have to reload the value from the spill location. + if opcodeTable[v.Op].resultInArg0 && + s.liveAfterCurrentInstruction(v.Args[0]) && + countRegs(s.values[v.Args[0].ID].regs) == 1 { + + if opcodeTable[v.Op].commutative && + (!s.liveAfterCurrentInstruction(v.Args[1]) || + countRegs(s.values[v.Args[1].ID].regs) > 1) { + // Input #1 is dead after the instruction, or we have + // more than one copy of it in a register. Either way, + // use that input as the one that is clobbered. + args[0], args[1] = args[1], args[0] + } else { + m := s.compatRegs(v.Args[0].Type) + m &^= s.values[v.Args[0].ID].regs // a register not already holding v.Args[0] + s.allocValToReg(v.Args[0], m, true, v.Line) + } + } + // Now that all args are in regs, we're ready to issue the value itself. // Before we pick a register for the output value, allow input registers // to be deallocated. We do this here so that the output can use the @@ -908,19 +941,9 @@ func (s *regAllocState) regalloc(f *Func) { s.f.Fatalf("bad mask %s\n", v.LongString()) } if opcodeTable[v.Op].resultInArg0 { + // Output must use the same register as input 0. r := register(s.f.getHome(args[0].ID).(*Register).Num) - if (mask&^s.used)>>r&1 != 0 { - mask = regMask(1) << r - } - if opcodeTable[v.Op].commutative { - r := register(s.f.getHome(args[1].ID).(*Register).Num) - if (mask&^s.used)>>r&1 != 0 { - mask = regMask(1) << r - } - } - // TODO: enforce resultInArg0 always, instead of treating it - // as a hint. Then we don't need the special cases adding - // moves all throughout ssa.go:genValue. + mask = regMask(1) << r } r := s.allocReg(v, mask) s.assignReg(r, v, v) diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 34a393bbc5..a6600513fa 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -16653,6 +16653,17 @@ func rewriteValueAMD64_OpAMD64SUBBconst(v *Value, config *Config) bool { v.AddArg(x) return true } + // match: (SUBBconst [c] x) + // cond: + // result: (ADDBconst [int64(int8(-c))] x) + for { + c := v.AuxInt + x := v.Args[0] + v.reset(OpAMD64ADDBconst) + v.AuxInt = int64(int8(-c)) + v.AddArg(x) + return true + } // match: (SUBBconst (MOVBconst [d]) [c]) // cond: // result: (MOVBconst [int64(int8(d-c))]) @@ -16751,6 +16762,17 @@ func rewriteValueAMD64_OpAMD64SUBLconst(v *Value, config *Config) bool { v.AddArg(x) return true } + // match: (SUBLconst [c] x) + // cond: + // result: (ADDLconst [int64(int32(-c))] x) + for { + c := v.AuxInt + x := v.Args[0] + v.reset(OpAMD64ADDLconst) + v.AuxInt = int64(int32(-c)) + v.AddArg(x) + return true + } // match: (SUBLconst (MOVLconst [d]) [c]) // cond: // result: (MOVLconst [int64(int32(d-c))]) @@ -16854,6 +16876,20 @@ func rewriteValueAMD64_OpAMD64SUBQconst(v *Value, config *Config) bool { v.AddArg(x) return true } + // match: (SUBQconst [c] x) + // cond: c != -(1<<31) + // result: (ADDQconst [-c] x) + for { + c := v.AuxInt + x := v.Args[0] + if !(c != -(1 << 31)) { + break + } + v.reset(OpAMD64ADDQconst) + v.AuxInt = -c + v.AddArg(x) + return true + } // match: (SUBQconst (MOVQconst [d]) [c]) // cond: // result: (MOVQconst [d-c]) @@ -16955,6 +16991,17 @@ func rewriteValueAMD64_OpAMD64SUBWconst(v *Value, config *Config) bool { v.AddArg(x) return true } + // match: (SUBWconst [c] x) + // cond: + // result: (ADDWconst [int64(int16(-c))] x) + for { + c := v.AuxInt + x := v.Args[0] + v.reset(OpAMD64ADDWconst) + v.AuxInt = int64(int16(-c)) + v.AddArg(x) + return true + } // match: (SUBWconst (MOVWconst [d]) [c]) // cond: // result: (MOVWconst [int64(int16(d-c))]) -- cgit v1.3 From 0fd270ab7abec08c050f29a3bbeb83d7740d0a47 Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Fri, 8 Apr 2016 15:39:32 +1000 Subject: text/template: emit field error over nil pointer error where appropriate When evaluating "{{.MissingField}}" on a nil *T, Exec returns "can't evaluate field MissingField in type *T" instead of "nil pointer evaluating *T.MissingField". Fixes golang/go#15125 Change-Id: I6e73f61b8a72c694179c1f8cdc808766c90b6f57 Reviewed-on: https://go-review.googlesource.com/21705 Reviewed-by: Rob Pike --- src/text/template/exec.go | 12 +++++++----- src/text/template/exec_test.go | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/text/template/exec.go b/src/text/template/exec.go index a169e62ab0..22881c6852 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -538,14 +538,14 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, return s.evalCall(dot, method, node, fieldName, args, final) } hasArgs := len(args) > 1 || final.IsValid() - // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil. - if isNil { - s.errorf("nil pointer evaluating %s.%s", typ, fieldName) - } + // It's not a method; must be a field of a struct or an element of a map. switch receiver.Kind() { case reflect.Struct: tField, ok := receiver.Type().FieldByName(fieldName) if ok { + if isNil { + s.errorf("nil pointer evaluating %s.%s", typ, fieldName) + } field := receiver.FieldByIndex(tField.Index) if tField.PkgPath != "" { // field is unexported s.errorf("%s is an unexported field of struct type %s", fieldName, typ) @@ -556,8 +556,10 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, } return field } - s.errorf("%s is not a field of struct type %s", fieldName, typ) case reflect.Map: + if isNil { + s.errorf("nil pointer evaluating %s.%s", typ, fieldName) + } // If it's a map, attempt to use the field name as a key. nameVal := reflect.ValueOf(fieldName) if nameVal.Type().AssignableTo(receiver.Type().Key()) { diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index e507e917fe..bc2aa683ec 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -1280,3 +1280,20 @@ func TestBlock(t *testing.T) { t.Errorf("got %q, want %q", got, want2) } } + +// Check that calling an invalid field on nil pointer prints +// a field error instead of a distracting nil pointer error. +// https://golang.org/issue/15125 +func TestMissingFieldOnNil(t *testing.T) { + tmpl := Must(New("tmpl").Parse("{{.MissingField}}")) + var d *T + err := tmpl.Execute(ioutil.Discard, d) + got := "" + if err != nil { + got = err.Error() + } + want := "can't evaluate field MissingField in type *template.T" + if !strings.HasSuffix(got, want) { + t.Errorf("got error %q, want %q", got, want) + } +} -- cgit v1.3 From 5b3e5766bcc5e1090d0512a5916886ffc24ab246 Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Fri, 8 Apr 2016 14:43:35 +1000 Subject: cmd/go: remove special case that puts godoc in $GOROOT/bin Updates golang/go#15106 Change-Id: I4214b841d63bb7e9c3c5ede2abe21a8a68f06c41 Reviewed-on: https://go-review.googlesource.com/21701 Reviewed-by: Brad Fitzpatrick --- src/cmd/go/go_test.go | 22 ---------------------- src/cmd/go/pkg.go | 9 +-------- 2 files changed, 1 insertion(+), 30 deletions(-) (limited to 'src') diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 8a0416089c..411fd1e322 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -1377,28 +1377,6 @@ func TestInstallToGOBINCommandLinePackage(t *testing.T) { tg.wantExecutable("testdata/bin1/helloworld"+exeSuffix, "go install testdata/src/go-cmd-test/helloworld.go did not write testdata/bin1/helloworld") } -func TestGodocInstalls(t *testing.T) { - testenv.MustHaveExternalNetwork(t) - - // godoc installs into GOBIN - tg := testgo(t) - defer tg.cleanup() - tg.parallel() - tg.tempDir("gobin") - tg.setenv("GOPATH", tg.path(".")) - tg.setenv("GOBIN", tg.path("gobin")) - tg.run("get", "golang.org/x/tools/cmd/godoc") - tg.wantExecutable(tg.path("gobin/godoc"), "did not install godoc to $GOBIN") - tg.unsetenv("GOBIN") - - // godoc installs into GOROOT - goroot := runtime.GOROOT() - tg.setenv("GOROOT", goroot) - tg.check(os.RemoveAll(filepath.Join(goroot, "bin", "godoc"))) - tg.run("install", "golang.org/x/tools/cmd/godoc") - tg.wantExecutable(filepath.Join(goroot, "bin", "godoc"), "did not install godoc to $GOROOT/bin") -} - func TestGoGetNonPkg(t *testing.T) { testenv.MustHaveExternalNetwork(t) diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index f330b4db43..ef7fd124a9 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -682,7 +682,6 @@ type targetDir int const ( toRoot targetDir = iota // to bin dir inside package root (default) toTool // GOROOT/pkg/tool - toBin // GOROOT/bin stalePath // the old import path; fail to build ) @@ -706,7 +705,6 @@ var goTools = map[string]targetDir{ "cmd/trace": toTool, "cmd/vet": toTool, "cmd/yacc": toTool, - "golang.org/x/tools/cmd/godoc": toBin, "code.google.com/p/go.tools/cmd/cover": stalePath, "code.google.com/p/go.tools/cmd/godoc": stalePath, "code.google.com/p/go.tools/cmd/vet": stalePath, @@ -793,12 +791,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Install cross-compiled binaries to subdirectories of bin. elem = full } - if p.build.BinDir != gobin && goTools[p.ImportPath] == toBin { - // Override BinDir. - // This is from a subrepo but installs to $GOROOT/bin - // by default anyway (like godoc). - p.target = filepath.Join(gorootBin, elem) - } else if p.build.BinDir != "" { + if p.build.BinDir != "" { // Install to GOBIN or bin of GOPATH entry. p.target = filepath.Join(p.build.BinDir, elem) if !p.Goroot && strings.Contains(elem, "/") && gobin != "" { -- cgit v1.3 From ad7448fe982d83de15deec9c55c56d0cd9261c6c Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sun, 10 Apr 2016 17:32:35 +0200 Subject: runtime: speed up makeslice by avoiding divisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only compute the number of maximum allowed elements per slice once. name old time/op new time/op delta MakeSlice-2 55.5ns ± 1% 45.6ns ± 2% -17.88% (p=0.000 n=99+100) Change-Id: I951feffda5d11910a75e55d7e978d306d14da2c5 Reviewed-on: https://go-review.googlesource.com/21801 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/runtime/append_test.go | 8 ++++++++ src/runtime/slice.go | 14 +++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/runtime/append_test.go b/src/runtime/append_test.go index 3170870b0e..6d7836a351 100644 --- a/src/runtime/append_test.go +++ b/src/runtime/append_test.go @@ -7,6 +7,14 @@ import "testing" const N = 20 +func BenchmarkMakeSlice(b *testing.B) { + var x []byte + for i := 0; i < b.N; i++ { + x = make([]byte, 32) + _ = x + } +} + func BenchmarkGrowSliceBytes(b *testing.B) { b.StopTimer() var x = make([]byte, 9) diff --git a/src/runtime/slice.go b/src/runtime/slice.go index 4ab221056c..f36ec0b466 100644 --- a/src/runtime/slice.go +++ b/src/runtime/slice.go @@ -16,19 +16,27 @@ type slice struct { // TODO: take uintptrs instead of int64s? func makeslice(t *slicetype, len64, cap64 int64) slice { - // NOTE: The len > MaxMem/elemsize check here is not strictly necessary, + // NOTE: The len > maxElements check here is not strictly necessary, // but it produces a 'len out of range' error instead of a 'cap out of range' error // when someone does make([]T, bignumber). 'cap out of range' is true too, // but since the cap is only being supplied implicitly, saying len is clearer. // See issue 4085. + + maxElements := ^uintptr(0) + if t.elem.size > 0 { + maxElements = _MaxMem / t.elem.size + } + len := int(len64) - if len64 < 0 || int64(len) != len64 || t.elem.size > 0 && uintptr(len) > _MaxMem/t.elem.size { + if len64 < 0 || int64(len) != len64 || uintptr(len) > maxElements { panic(errorString("makeslice: len out of range")) } + cap := int(cap64) - if cap < len || int64(cap) != cap64 || t.elem.size > 0 && uintptr(cap) > _MaxMem/t.elem.size { + if cap < len || int64(cap) != cap64 || uintptr(cap) > maxElements { panic(errorString("makeslice: cap out of range")) } + p := newarray(t.elem, uintptr(cap)) return slice{p, len, cap} } -- cgit v1.3 From e6a8daceb0b0df77f5d2ca34a73561375bb89c63 Mon Sep 17 00:00:00 2001 From: David Symonds Date: Mon, 11 Apr 2016 10:26:38 +1000 Subject: cmd/vet: refresh command for updating whitelist data. This excludes internal and testdata packages, as well as func types. No new whitelist entries were found. Change-Id: Ie7d42ce0a235394e4bcabf09e155726a35cd2d3d Reviewed-on: https://go-review.googlesource.com/21822 Reviewed-by: Rob Pike --- src/cmd/vet/internal/whitelist/whitelist.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/vet/internal/whitelist/whitelist.go b/src/cmd/vet/internal/whitelist/whitelist.go index b6c85850f3..696f7a533d 100644 --- a/src/cmd/vet/internal/whitelist/whitelist.go +++ b/src/cmd/vet/internal/whitelist/whitelist.go @@ -11,7 +11,8 @@ package whitelist // library's exported slice types. var UnkeyedLiteral = map[string]bool{ /* - find $GOROOT/src -type f | grep -v _test.go | xargs grep '^type.*\[\]' | \ + find $GOROOT/src -type f | grep -v _test.go | grep -v /internal/ | grep -v /testdata/ | \ + xargs grep '^type.*\[\]' | grep -v ' func(' | \ grep -v ' map\[' | sed 's,/[^/]*go.type,,' | sed 's,.*src/,,' | \ sed 's, ,.,' | sed 's, .*,,' | grep -v '\.[a-z]' | \ sort | awk '{ print "\"" $0 "\": true," }' -- cgit v1.3 From bd7249766617fda12d112c3ad3ae2857ff97c71e Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 10 Apr 2016 15:45:34 +0000 Subject: context: document that WithValue's key must be comparable Also, check it and explode earlier, rather than cryptic failures later. Change-Id: I319a425f60e2bc9d005a187fbdbd153faa96411c Reviewed-on: https://go-review.googlesource.com/21799 Reviewed-by: Andrew Gerrand Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Minux Ma --- src/context/context.go | 8 +++++++- src/context/context_test.go | 13 +++++++++++++ src/go/build/deps_test.go | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/context/context.go b/src/context/context.go index 21dc8676bf..c332e1f443 100644 --- a/src/context/context.go +++ b/src/context/context.go @@ -39,6 +39,7 @@ package context import ( "errors" "fmt" + "reflect" "sync" "time" ) @@ -424,7 +425,12 @@ func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { // // Use context Values only for request-scoped data that transits processes and // APIs, not for passing optional parameters to functions. -func WithValue(parent Context, key interface{}, val interface{}) Context { +// +// The provided key must be comparable. +func WithValue(parent Context, key, val interface{}) Context { + if !reflect.TypeOf(key).Comparable() { + panic("key is not comparable") + } return &valueCtx{parent, key, val} } diff --git a/src/context/context_test.go b/src/context/context_test.go index 573470e084..0616704dd8 100644 --- a/src/context/context_test.go +++ b/src/context/context_test.go @@ -586,3 +586,16 @@ func TestCancelRemoves(t *testing.T) { cancel() checkChildren("after cancelling WithTimeout child", ctx, 0) } + +func TestWithValueChecksKey(t *testing.T) { + panicVal := recoveredValue(func() { WithValue(Background(), []byte("foo"), "bar") }) + if panicVal == nil { + t.Error("expected panic") + } +} + +func recoveredValue(fn func()) (v interface{}) { + defer func() { v = recover() }() + fn() + return +} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 8e2fd6e584..f1d19bb50c 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -215,7 +215,7 @@ var pkgDeps = map[string][]string{ "compress/gzip": {"L4", "compress/flate"}, "compress/lzw": {"L4"}, "compress/zlib": {"L4", "compress/flate"}, - "context": {"errors", "fmt", "sync", "time"}, + "context": {"errors", "fmt", "reflect", "sync", "time"}, "database/sql": {"L4", "container/list", "database/sql/driver"}, "database/sql/driver": {"L4", "time"}, "debug/dwarf": {"L4"}, -- cgit v1.3 From 1faa8869c6c72f055cdaa2b547964830909c96c6 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Apr 2016 12:31:55 -0700 Subject: net/http: set the Request context for incoming server requests Updates #13021 Updates #15224 Change-Id: Ia3cd608bb887fcfd8d81b035fa57bd5eb8edf09b Reviewed-on: https://go-review.googlesource.com/21810 Reviewed-by: Andrew Gerrand Run-TryBot: Brad Fitzpatrick Reviewed-by: Emmanuel Odeke TryBot-Result: Gobot Gobot --- src/net/http/request.go | 8 ++++-- src/net/http/serve_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++ src/net/http/server.go | 28 ++++++++++++++----- 3 files changed, 95 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/net/http/request.go b/src/net/http/request.go index 5510691912..5bca888845 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -266,9 +266,13 @@ type Request struct { // // The returned context is always non-nil; it defaults to the // background context. +// +// For outgoing client requests, the context controls cancelation. +// +// For incoming server requests, the context is canceled when either +// the client's connection closes, or when the ServeHTTP method +// returns. func (r *Request) Context() context.Context { - // TODO(bradfitz): document above what Context means for server and client - // requests, once implemented. if r.ctx != nil { return r.ctx } diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 638ba5f48f..4cd6ed077f 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -9,6 +9,7 @@ package http_test import ( "bufio" "bytes" + "context" "crypto/tls" "errors" "fmt" @@ -3989,6 +3990,72 @@ func TestServerValidatesHeaders(t *testing.T) { } } +func TestServerRequestContextCancel_ServeHTTPDone(t *testing.T) { + defer afterTest(t) + ctxc := make(chan context.Context, 1) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + ctx := r.Context() + select { + case <-ctx.Done(): + t.Error("should not be Done in ServeHTTP") + default: + } + ctxc <- ctx + })) + defer ts.Close() + res, err := Get(ts.URL) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + ctx := <-ctxc + select { + case <-ctx.Done(): + default: + t.Error("context should be done after ServeHTTP completes") + } +} + +func TestServerRequestContextCancel_ConnClose(t *testing.T) { + // Currently the context is not canceled when the connection + // is closed because we're not reading from the connection + // until after ServeHTTP for the previous handler is done. + // Until the server code is modified to always be in a read + // (Issue 15224), this test doesn't work yet. + t.Skip("TODO(bradfitz): this test doesn't yet work; golang.org/issue/15224") + defer afterTest(t) + inHandler := make(chan struct{}) + handlerDone := make(chan struct{}) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + close(inHandler) + select { + case <-r.Context().Done(): + case <-time.After(3 * time.Second): + t.Errorf("timeout waiting for context to be done") + } + close(handlerDone) + })) + defer ts.Close() + c, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + io.WriteString(c, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n") + select { + case <-inHandler: + case <-time.After(3 * time.Second): + t.Fatalf("timeout waiting to see ServeHTTP get called") + } + c.Close() // this should trigger the context being done + + select { + case <-handlerDone: + case <-time.After(3 * time.Second): + t.Fatalf("timeout waiting to see ServeHTTP exit") + } +} + func BenchmarkClientServer(b *testing.B) { b.ReportAllocs() b.StopTimer() diff --git a/src/net/http/server.go b/src/net/http/server.go index f4e697169d..e37df99deb 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -9,6 +9,7 @@ package http import ( "bufio" "bytes" + "context" "crypto/tls" "errors" "fmt" @@ -312,10 +313,11 @@ type response struct { conn *conn req *Request // request for this response reqBody io.ReadCloser - wroteHeader bool // reply header has been (logically) written - wroteContinue bool // 100 Continue response was written - wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" - wantsClose bool // HTTP request has Connection "close" + cancelCtx context.CancelFunc // when ServeHTTP exits + wroteHeader bool // reply header has been (logically) written + wroteContinue bool // 100 Continue response was written + wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" + wantsClose bool // HTTP request has Connection "close" w *bufio.Writer // buffers output in chunks to chunkWriter cw chunkWriter @@ -686,7 +688,7 @@ func appendTime(b []byte, t time.Time) []byte { var errTooLarge = errors.New("http: request too large") // Read next request from connection. -func (c *conn) readRequest() (w *response, err error) { +func (c *conn) readRequest(ctx context.Context) (w *response, err error) { if c.hijacked() { return nil, ErrHijacked } @@ -715,6 +717,10 @@ func (c *conn) readRequest() (w *response, err error) { } return nil, err } + + ctx, cancelCtx := context.WithCancel(ctx) + req.ctx = ctx + c.lastMethod = req.Method c.r.setInfiniteReadLimit() @@ -749,6 +755,7 @@ func (c *conn) readRequest() (w *response, err error) { w = &response{ conn: c, + cancelCtx: cancelCtx, req: req, reqBody: req.Body, handlerHeader: make(Header), @@ -1432,12 +1439,20 @@ func (c *conn) serve() { } } + // HTTP/1.x from here on. + c.r = &connReader{r: c.rwc} c.bufr = newBufioReader(c.r) c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10) + // TODO: allow changing base context? can't imagine concrete + // use cases yet. + baseCtx := context.Background() + ctx, cancelCtx := context.WithCancel(baseCtx) + defer cancelCtx() + for { - w, err := c.readRequest() + w, err := c.readRequest(ctx) if c.r.remain != c.server.initialReadLimitSize() { // If we read any bytes off the wire, we're active. c.setState(c.rwc, StateActive) @@ -1485,6 +1500,7 @@ func (c *conn) serve() { // [*] Not strictly true: HTTP pipelining. We could let them all process // in parallel even if their responses need to be serialized. serverHandler{c.server}.ServeHTTP(w, w.req) + w.cancelCtx() if c.hijacked() { return } -- cgit v1.3 From ba09d06e166a06b4405b2ffd92df6acf222d281f Mon Sep 17 00:00:00 2001 From: Jeremy Jackins Date: Thu, 7 Apr 2016 15:42:35 +0900 Subject: runtime: remove remaining references to TheChar After mdempsky's recent changes, these are the only references to "TheChar" left in the Go tree. Without the context, and without knowing the history, this is confusing. Also rename sys.TheGoos and sys.TheGoarch to sys.GOOS and sys.GOARCH. Also change the heap dump format to include sys.GOARCH rather than TheChar, which is no longer a concept. Updates #15169 (changes heapdump format) Change-Id: I3e99eeeae00ed55d7d01e6ed503d958c6e931dca Reviewed-on: https://go-review.googlesource.com/21647 Reviewed-by: Matthew Dempsky --- src/runtime/extern.go | 4 ++-- src/runtime/heapdump.go | 2 +- src/runtime/internal/sys/arch.go | 17 +++++++++++++++++ src/runtime/internal/sys/arch_386.go | 2 +- src/runtime/internal/sys/arch_amd64.go | 2 +- src/runtime/internal/sys/arch_amd64p32.go | 2 +- src/runtime/internal/sys/arch_arm.go | 2 +- src/runtime/internal/sys/arch_arm64.go | 2 +- src/runtime/internal/sys/arch_mips64.go | 2 +- src/runtime/internal/sys/arch_mips64le.go | 2 +- src/runtime/internal/sys/arch_ppc64.go | 2 +- src/runtime/internal/sys/arch_ppc64le.go | 2 +- src/runtime/internal/sys/arch_s390x.go | 2 +- src/runtime/internal/sys/gengoos.go | 4 ++-- src/runtime/internal/sys/zgoarch_386.go | 2 +- src/runtime/internal/sys/zgoarch_amd64.go | 2 +- src/runtime/internal/sys/zgoarch_amd64p32.go | 2 +- src/runtime/internal/sys/zgoarch_arm.go | 2 +- src/runtime/internal/sys/zgoarch_arm64.go | 2 +- src/runtime/internal/sys/zgoarch_mips64.go | 2 +- src/runtime/internal/sys/zgoarch_mips64le.go | 2 +- src/runtime/internal/sys/zgoarch_ppc64.go | 2 +- src/runtime/internal/sys/zgoarch_ppc64le.go | 2 +- src/runtime/internal/sys/zgoarch_s390x.go | 2 +- src/runtime/internal/sys/zgoos_android.go | 2 +- src/runtime/internal/sys/zgoos_darwin.go | 2 +- src/runtime/internal/sys/zgoos_dragonfly.go | 2 +- src/runtime/internal/sys/zgoos_freebsd.go | 2 +- src/runtime/internal/sys/zgoos_linux.go | 2 +- src/runtime/internal/sys/zgoos_nacl.go | 2 +- src/runtime/internal/sys/zgoos_netbsd.go | 2 +- src/runtime/internal/sys/zgoos_openbsd.go | 2 +- src/runtime/internal/sys/zgoos_plan9.go | 2 +- src/runtime/internal/sys/zgoos_solaris.go | 2 +- src/runtime/internal/sys/zgoos_windows.go | 2 +- src/runtime/mgcmark.go | 4 ++-- src/runtime/stack.go | 8 ++++---- 37 files changed, 59 insertions(+), 42 deletions(-) create mode 100644 src/runtime/internal/sys/arch.go (limited to 'src') diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 1d8304f4fc..1df8691cfc 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -224,8 +224,8 @@ func Version() string { // GOOS is the running program's operating system target: // one of darwin, freebsd, linux, and so on. -const GOOS string = sys.TheGoos +const GOOS string = sys.GOOS // GOARCH is the running program's architecture target: // 386, amd64, arm, or s390x. -const GOARCH string = sys.TheGoarch +const GOARCH string = sys.GOARCH diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index e6a41f7f97..2410b1954a 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -496,7 +496,7 @@ func dumpparams() { dumpint(sys.PtrSize) dumpint(uint64(mheap_.arena_start)) dumpint(uint64(mheap_.arena_used)) - dumpint(sys.TheChar) + dumpstr(sys.GOARCH) dumpstr(sys.Goexperiment) dumpint(uint64(ncpu)) } diff --git a/src/runtime/internal/sys/arch.go b/src/runtime/internal/sys/arch.go new file mode 100644 index 0000000000..c1757041d8 --- /dev/null +++ b/src/runtime/internal/sys/arch.go @@ -0,0 +1,17 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sys + +type ArchFamilyType int + +const ( + AMD64 ArchFamilyType = iota + ARM + ARM64 + I386 + MIPS64 + PPC64 + S390X +) diff --git a/src/runtime/internal/sys/arch_386.go b/src/runtime/internal/sys/arch_386.go index 1f1c704f9a..48c42f7584 100644 --- a/src/runtime/internal/sys/arch_386.go +++ b/src/runtime/internal/sys/arch_386.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '8' + ArchFamily = I386 BigEndian = 0 CacheLineSize = 64 PhysPageSize = GoosNacl*65536 + (1-GoosNacl)*4096 // 4k normally; 64k on NaCl diff --git a/src/runtime/internal/sys/arch_amd64.go b/src/runtime/internal/sys/arch_amd64.go index 80fff557f2..1bbdb99e07 100644 --- a/src/runtime/internal/sys/arch_amd64.go +++ b/src/runtime/internal/sys/arch_amd64.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '6' + ArchFamily = AMD64 BigEndian = 0 CacheLineSize = 64 PhysPageSize = 4096 diff --git a/src/runtime/internal/sys/arch_amd64p32.go b/src/runtime/internal/sys/arch_amd64p32.go index ca29f698a2..b7011a4ff2 100644 --- a/src/runtime/internal/sys/arch_amd64p32.go +++ b/src/runtime/internal/sys/arch_amd64p32.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '6' + ArchFamily = AMD64 BigEndian = 0 CacheLineSize = 64 PhysPageSize = 65536*GoosNacl + 4096*(1-GoosNacl) diff --git a/src/runtime/internal/sys/arch_arm.go b/src/runtime/internal/sys/arch_arm.go index b185e8fb69..f90f52da7f 100644 --- a/src/runtime/internal/sys/arch_arm.go +++ b/src/runtime/internal/sys/arch_arm.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '5' + ArchFamily = ARM BigEndian = 0 CacheLineSize = 32 PhysPageSize = 65536*GoosNacl + 4096*(1-GoosNacl) diff --git a/src/runtime/internal/sys/arch_arm64.go b/src/runtime/internal/sys/arch_arm64.go index b63a7a6f9a..aaaa4b0947 100644 --- a/src/runtime/internal/sys/arch_arm64.go +++ b/src/runtime/internal/sys/arch_arm64.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '7' + ArchFamily = ARM64 BigEndian = 0 CacheLineSize = 32 PhysPageSize = 65536 diff --git a/src/runtime/internal/sys/arch_mips64.go b/src/runtime/internal/sys/arch_mips64.go index 5b933d4e1a..d5672599d2 100644 --- a/src/runtime/internal/sys/arch_mips64.go +++ b/src/runtime/internal/sys/arch_mips64.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '0' + ArchFamily = MIPS64 BigEndian = 1 CacheLineSize = 32 PhysPageSize = 16384 diff --git a/src/runtime/internal/sys/arch_mips64le.go b/src/runtime/internal/sys/arch_mips64le.go index ce2e98b19f..f8cdf2b2d2 100644 --- a/src/runtime/internal/sys/arch_mips64le.go +++ b/src/runtime/internal/sys/arch_mips64le.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '0' + ArchFamily = MIPS64 BigEndian = 0 CacheLineSize = 32 PhysPageSize = 16384 diff --git a/src/runtime/internal/sys/arch_ppc64.go b/src/runtime/internal/sys/arch_ppc64.go index 3aa07e1f56..cdec63ff71 100644 --- a/src/runtime/internal/sys/arch_ppc64.go +++ b/src/runtime/internal/sys/arch_ppc64.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '9' + ArchFamily = PPC64 BigEndian = 1 CacheLineSize = 64 PhysPageSize = 65536 diff --git a/src/runtime/internal/sys/arch_ppc64le.go b/src/runtime/internal/sys/arch_ppc64le.go index 0f02f0bf3c..4fd68f9ce3 100644 --- a/src/runtime/internal/sys/arch_ppc64le.go +++ b/src/runtime/internal/sys/arch_ppc64le.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = '9' + ArchFamily = PPC64 BigEndian = 0 CacheLineSize = 64 PhysPageSize = 65536 diff --git a/src/runtime/internal/sys/arch_s390x.go b/src/runtime/internal/sys/arch_s390x.go index 8690571c81..ca1cb8646e 100644 --- a/src/runtime/internal/sys/arch_s390x.go +++ b/src/runtime/internal/sys/arch_s390x.go @@ -5,7 +5,7 @@ package sys const ( - TheChar = 'z' + ArchFamily = S390X BigEndian = 1 CacheLineSize = 256 PhysPageSize = 4096 diff --git a/src/runtime/internal/sys/gengoos.go b/src/runtime/internal/sys/gengoos.go index e2bd87de4e..4c45c0af02 100644 --- a/src/runtime/internal/sys/gengoos.go +++ b/src/runtime/internal/sys/gengoos.go @@ -50,7 +50,7 @@ func main() { fmt.Fprintf(&buf, "// +build !android\n\n") // must explicitly exclude android for linux } fmt.Fprintf(&buf, "package sys\n\n") - fmt.Fprintf(&buf, "const TheGoos = `%s`\n\n", target) + fmt.Fprintf(&buf, "const GOOS = `%s`\n\n", target) for _, goos := range gooses { value := 0 if goos == target { @@ -68,7 +68,7 @@ func main() { var buf bytes.Buffer fmt.Fprintf(&buf, "// generated by gengoos.go using 'go generate'\n\n") fmt.Fprintf(&buf, "package sys\n\n") - fmt.Fprintf(&buf, "const TheGoarch = `%s`\n\n", target) + fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target) for _, goarch := range goarches { value := 0 if goarch == target { diff --git a/src/runtime/internal/sys/zgoarch_386.go b/src/runtime/internal/sys/zgoarch_386.go index 3ad244509d..3bcf83b8e3 100644 --- a/src/runtime/internal/sys/zgoarch_386.go +++ b/src/runtime/internal/sys/zgoarch_386.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `386` +const GOARCH = `386` const Goarch386 = 1 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_amd64.go b/src/runtime/internal/sys/zgoarch_amd64.go index 7c858e3f5d..699f191fba 100644 --- a/src/runtime/internal/sys/zgoarch_amd64.go +++ b/src/runtime/internal/sys/zgoarch_amd64.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `amd64` +const GOARCH = `amd64` const Goarch386 = 0 const GoarchAmd64 = 1 diff --git a/src/runtime/internal/sys/zgoarch_amd64p32.go b/src/runtime/internal/sys/zgoarch_amd64p32.go index 772031c090..cc2d658406 100644 --- a/src/runtime/internal/sys/zgoarch_amd64p32.go +++ b/src/runtime/internal/sys/zgoarch_amd64p32.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `amd64p32` +const GOARCH = `amd64p32` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_arm.go b/src/runtime/internal/sys/zgoarch_arm.go index 276e8a869b..a5fd789f13 100644 --- a/src/runtime/internal/sys/zgoarch_arm.go +++ b/src/runtime/internal/sys/zgoarch_arm.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `arm` +const GOARCH = `arm` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_arm64.go b/src/runtime/internal/sys/zgoarch_arm64.go index d124ec0343..084d2c7330 100644 --- a/src/runtime/internal/sys/zgoarch_arm64.go +++ b/src/runtime/internal/sys/zgoarch_arm64.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `arm64` +const GOARCH = `arm64` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_mips64.go b/src/runtime/internal/sys/zgoarch_mips64.go index b4a97d6da9..2ad62bd68c 100644 --- a/src/runtime/internal/sys/zgoarch_mips64.go +++ b/src/runtime/internal/sys/zgoarch_mips64.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `mips64` +const GOARCH = `mips64` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_mips64le.go b/src/runtime/internal/sys/zgoarch_mips64le.go index 3328a35bd2..047c8b425a 100644 --- a/src/runtime/internal/sys/zgoarch_mips64le.go +++ b/src/runtime/internal/sys/zgoarch_mips64le.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `mips64le` +const GOARCH = `mips64le` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_ppc64.go b/src/runtime/internal/sys/zgoarch_ppc64.go index 06f78b2023..748b5b562c 100644 --- a/src/runtime/internal/sys/zgoarch_ppc64.go +++ b/src/runtime/internal/sys/zgoarch_ppc64.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `ppc64` +const GOARCH = `ppc64` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_ppc64le.go b/src/runtime/internal/sys/zgoarch_ppc64le.go index 50b56dbe3f..d3dcba467d 100644 --- a/src/runtime/internal/sys/zgoarch_ppc64le.go +++ b/src/runtime/internal/sys/zgoarch_ppc64le.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `ppc64le` +const GOARCH = `ppc64le` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoarch_s390x.go b/src/runtime/internal/sys/zgoarch_s390x.go index ce85f20e0a..1ead5d573c 100644 --- a/src/runtime/internal/sys/zgoarch_s390x.go +++ b/src/runtime/internal/sys/zgoarch_s390x.go @@ -2,7 +2,7 @@ package sys -const TheGoarch = `s390x` +const GOARCH = `s390x` const Goarch386 = 0 const GoarchAmd64 = 0 diff --git a/src/runtime/internal/sys/zgoos_android.go b/src/runtime/internal/sys/zgoos_android.go index 03d91760ed..6503b15246 100644 --- a/src/runtime/internal/sys/zgoos_android.go +++ b/src/runtime/internal/sys/zgoos_android.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `android` +const GOOS = `android` const GoosAndroid = 1 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_darwin.go b/src/runtime/internal/sys/zgoos_darwin.go index eb2efeb7af..6a285984bd 100644 --- a/src/runtime/internal/sys/zgoos_darwin.go +++ b/src/runtime/internal/sys/zgoos_darwin.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `darwin` +const GOOS = `darwin` const GoosAndroid = 0 const GoosDarwin = 1 diff --git a/src/runtime/internal/sys/zgoos_dragonfly.go b/src/runtime/internal/sys/zgoos_dragonfly.go index 403cf65311..886ac2698f 100644 --- a/src/runtime/internal/sys/zgoos_dragonfly.go +++ b/src/runtime/internal/sys/zgoos_dragonfly.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `dragonfly` +const GOOS = `dragonfly` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_freebsd.go b/src/runtime/internal/sys/zgoos_freebsd.go index 632d5db9db..0bf2403eab 100644 --- a/src/runtime/internal/sys/zgoos_freebsd.go +++ b/src/runtime/internal/sys/zgoos_freebsd.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `freebsd` +const GOOS = `freebsd` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_linux.go b/src/runtime/internal/sys/zgoos_linux.go index 2d43869a84..c8664db15d 100644 --- a/src/runtime/internal/sys/zgoos_linux.go +++ b/src/runtime/internal/sys/zgoos_linux.go @@ -4,7 +4,7 @@ package sys -const TheGoos = `linux` +const GOOS = `linux` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_nacl.go b/src/runtime/internal/sys/zgoos_nacl.go index a56b6ef3c9..054122638a 100644 --- a/src/runtime/internal/sys/zgoos_nacl.go +++ b/src/runtime/internal/sys/zgoos_nacl.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `nacl` +const GOOS = `nacl` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_netbsd.go b/src/runtime/internal/sys/zgoos_netbsd.go index 46fd0a7cd5..5c509a1250 100644 --- a/src/runtime/internal/sys/zgoos_netbsd.go +++ b/src/runtime/internal/sys/zgoos_netbsd.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `netbsd` +const GOOS = `netbsd` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_openbsd.go b/src/runtime/internal/sys/zgoos_openbsd.go index 7ee650afbb..dc43157d49 100644 --- a/src/runtime/internal/sys/zgoos_openbsd.go +++ b/src/runtime/internal/sys/zgoos_openbsd.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `openbsd` +const GOOS = `openbsd` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_plan9.go b/src/runtime/internal/sys/zgoos_plan9.go index 162e7f6260..4b0934f77a 100644 --- a/src/runtime/internal/sys/zgoos_plan9.go +++ b/src/runtime/internal/sys/zgoos_plan9.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `plan9` +const GOOS = `plan9` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_solaris.go b/src/runtime/internal/sys/zgoos_solaris.go index b2a8f98504..42511a36ad 100644 --- a/src/runtime/internal/sys/zgoos_solaris.go +++ b/src/runtime/internal/sys/zgoos_solaris.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `solaris` +const GOOS = `solaris` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/internal/sys/zgoos_windows.go b/src/runtime/internal/sys/zgoos_windows.go index 817ec79e4c..d77f62c396 100644 --- a/src/runtime/internal/sys/zgoos_windows.go +++ b/src/runtime/internal/sys/zgoos_windows.go @@ -2,7 +2,7 @@ package sys -const TheGoos = `windows` +const GOOS = `windows` const GoosAndroid = 0 const GoosDarwin = 0 diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 66d61bae1e..1ab8315a29 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -719,8 +719,8 @@ func scanframeworker(frame *stkframe, cache *pcvalueCache, gcw *gcWork) { // Scan local variables if stack frame has been allocated. size := frame.varp - frame.sp var minsize uintptr - switch sys.TheChar { - case '7': + switch sys.ArchFamily { + case sys.ARM64: minsize = sys.SpAlign default: minsize = sys.MinFrameSize diff --git a/src/runtime/stack.go b/src/runtime/stack.go index fdd6710bad..dcb1b06dbd 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -634,8 +634,8 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { // Adjust local variables if stack frame has been allocated. size := frame.varp - frame.sp var minsize uintptr - switch sys.TheChar { - case '7': + switch sys.ArchFamily { + case sys.ARM64: minsize = sys.SpAlign default: minsize = sys.MinFrameSize @@ -662,7 +662,7 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { } // Adjust saved base pointer if there is one. - if sys.TheChar == '6' && frame.argp-frame.varp == 2*sys.RegSize { + if sys.ArchFamily == sys.AMD64 && frame.argp-frame.varp == 2*sys.RegSize { if !framepointer_enabled { print("runtime: found space for saved base pointer, but no framepointer experiment\n") print("argp=", hex(frame.argp), " varp=", hex(frame.varp), "\n") @@ -969,7 +969,7 @@ func newstack() { throw("missing stack in newstack") } sp := gp.sched.sp - if sys.TheChar == '6' || sys.TheChar == '8' { + if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 { // The call to morestack cost a word. sp -= sys.PtrSize } -- cgit v1.3 From 2a4158207edb499f8b210aaa7a9af103b93b5ac7 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Sun, 10 Apr 2016 21:58:37 -0400 Subject: cmd/compile/internal/gc: refactor cgen_div This commit adds two new functions to cgen.go: hasHMUL64 and hasRROTC64. These are used to determine whether or not an architecture supports the instructions needed to perform an optimization in cgen_div. This commit should not affect existing architectures (although it does add s390x to the new functions). However, since most architectures support HMUL the hasHMUL64 function could be modified to enable most of the optimizations in cgen_div on those platforms. Change-Id: I33bf329ddeb6cf2954bd17b7c161012de352fb62 Reviewed-on: https://go-review.googlesource.com/21775 Reviewed-by: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Josh Bleecher Snyder TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/cgen.go | 68 ++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go index a9cedf7cfc..eacbc30f87 100644 --- a/src/cmd/compile/internal/gc/cgen.go +++ b/src/cmd/compile/internal/gc/cgen.go @@ -2622,24 +2622,48 @@ func cgen_ret(n *Node) { } } +// hasHMUL64 reports whether the architecture supports 64-bit +// signed and unsigned high multiplication (OHMUL). +func hasHMUL64() bool { + switch Ctxt.Arch.Family { + case sys.AMD64, sys.S390X: + return true + case sys.ARM, sys.ARM64, sys.I386, sys.MIPS64, sys.PPC64: + return false + } + Fatalf("unknown architecture") + return false +} + +// hasRROTC64 reports whether the architecture supports 64-bit +// rotate through carry instructions (ORROTC). +func hasRROTC64() bool { + switch Ctxt.Arch.Family { + case sys.AMD64: + return true + case sys.ARM, sys.ARM64, sys.I386, sys.MIPS64, sys.PPC64, sys.S390X: + return false + } + Fatalf("unknown architecture") + return false +} + // generate division according to op, one of: // res = nl / nr // res = nl % nr func cgen_div(op Op, nl *Node, nr *Node, res *Node) { var w int - // TODO(rsc): arm64 needs to support the relevant instructions - // in peep and optoas in order to enable this. - // TODO(rsc): ppc64 needs to support the relevant instructions - // in peep and optoas in order to enable this. - if nr.Op != OLITERAL || Ctxt.Arch.Family == sys.MIPS64 || Ctxt.Arch.Family == sys.ARM64 || Ctxt.Arch.Family == sys.PPC64 { + // Architectures need to support 64-bit high multiplications + // (OHMUL) in order to perform divide by constant optimizations. + if nr.Op != OLITERAL || !hasHMUL64() { goto longdiv } w = int(nl.Type.Width * 8) // Front end handled 32-bit division. We only need to handle 64-bit. - // try to do division by multiply by (2^w)/d - // see hacker's delight chapter 10 + // Try to do division using multiplication: (2^w)/d. + // See Hacker's Delight, chapter 10. switch Simtype[nl.Type.Etype] { default: goto longdiv @@ -2652,6 +2676,17 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) { if m.Bad != 0 { break } + + // In order to add the numerator we need to be able to + // avoid overflow. This is done by shifting the result of the + // addition right by 1 and inserting the carry bit into + // the MSB. For now this needs the RROTC instruction. + // TODO(mundaym): Hacker's Delight 2nd ed. chapter 10 proposes + // an alternative sequence of instructions for architectures + // that do not have a shift right with carry instruction. + if m.Ua != 0 && !hasRROTC64() { + goto longdiv + } if op == OMOD { goto longmod } @@ -2665,7 +2700,7 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) { Thearch.Cgen_hmul(&n1, &n2, &n3) if m.Ua != 0 { - // need to add numerator accounting for overflow + // Need to add numerator accounting for overflow. Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) Nodconst(&n2, nl.Type, 1) @@ -2703,7 +2738,7 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) { Thearch.Cgen_hmul(&n1, &n2, &n3) if m.Sm < 0 { - // need to add numerator + // Need to add numerator (cannot overflow). Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) } @@ -2716,8 +2751,8 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) { Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n1, &n3) // added if m.Sd < 0 { - // this could probably be removed - // by factoring it into the multiplier + // This could probably be removed by factoring it into + // the multiplier. Thearch.Gins(Thearch.Optoas(OMINUS, nl.Type), nil, &n3) } @@ -2729,14 +2764,14 @@ func cgen_div(op Op, nl *Node, nr *Node, res *Node) { goto longdiv - // division and mod using (slow) hardware instruction + // Division and mod using (slow) hardware instruction. longdiv: Thearch.Dodiv(op, nl, nr, res) return - // mod using formula A%B = A-(A/B*B) but - // we know that there is a fast algorithm for A/B + // Mod using formula A%B = A-(A/B*B) but + // we know that there is a fast algorithm for A/B. longmod: var n1 Node Regalloc(&n1, nl.Type, res) @@ -2746,11 +2781,6 @@ longmod: Regalloc(&n2, nl.Type, nil) cgen_div(ODIV, &n1, nr, &n2) a := Thearch.Optoas(OMUL, nl.Type) - if w == 8 { - // use 2-operand 16-bit multiply - // because there is no 2-operand 8-bit multiply - a = Thearch.Optoas(OMUL, Types[TINT16]) // XXX was IMULW - } if !Smallintconst(nr) { var n3 Node -- cgit v1.3 From 20375f64b18e9f904302d8f873e23702117bf4f5 Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Mon, 11 Apr 2016 11:04:15 +1000 Subject: cmd/go: document that -run=^$ skips all tests Change-Id: I7bbdd9600e0d9a647aeea16f1ae9e42a4e0cf44d Reviewed-on: https://go-review.googlesource.com/21823 Reviewed-by: Rob Pike --- src/cmd/go/alldocs.go | 8 +++++--- src/cmd/go/test.go | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 1f5981055c..ac975c0ab7 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1347,8 +1347,8 @@ control the execution of any test: -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. To run all benchmarks, - use '-bench .' or '-bench=.'. + By default, no benchmarks run. + To run all benchmarks, use '-bench=.'. -benchmem Print memory allocation statistics for benchmarks. @@ -1435,7 +1435,9 @@ control the execution of any test: -run regexp Run only those tests and examples matching the regular - expression. + expression. By default, all tests run. + To skip all tests, use a pattern that matches no test names, + such as '-run=^$'. -short Tell long-running tests to shorten their run time. diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index a17bc4e982..8dbd9e22bf 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -126,8 +126,8 @@ control the execution of any test: const testFlag2 = ` -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. To run all benchmarks, - use '-bench .' or '-bench=.'. + By default, no benchmarks run. + To run all benchmarks, use '-bench=.'. -benchmem Print memory allocation statistics for benchmarks. @@ -214,7 +214,9 @@ const testFlag2 = ` -run regexp Run only those tests and examples matching the regular - expression. + expression. By default, all tests run. + To skip all tests, use a pattern that matches no test names, + such as '-run=^$'. -short Tell long-running tests to shorten their run time. -- cgit v1.3 From 720c4c016c75d37d14e0621696127819c8a73b0b Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Fri, 8 Apr 2016 17:50:40 +1000 Subject: runtime: merge lfstack_amd64.go into lfstack_64bit.go Merge the amd64 lfstack implementation into the general 64 bit implementation. Change-Id: Id9ed61b90d2e3bc3b0246294c03eb2c92803b6ca Reviewed-on: https://go-review.googlesource.com/21707 Run-TryBot: Dave Cheney Reviewed-by: Minux Ma --- src/runtime/lfstack_64bit.go | 11 ++++++++++- src/runtime/lfstack_amd64.go | 22 ---------------------- 2 files changed, 10 insertions(+), 23 deletions(-) delete mode 100644 src/runtime/lfstack_amd64.go (limited to 'src') diff --git a/src/runtime/lfstack_64bit.go b/src/runtime/lfstack_64bit.go index 8180b0a248..5367f08c56 100644 --- a/src/runtime/lfstack_64bit.go +++ b/src/runtime/lfstack_64bit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build arm64 mips64 mips64le ppc64 ppc64le s390x +// +build amd64 arm64 mips64 mips64le ppc64 ppc64le s390x package runtime @@ -22,6 +22,10 @@ const ( // s390x TASK_SIZE 0x020000000000UL (41 bit addresses) // // These values may increase over time. + // + // On AMD64, virtual addresses are 48-bit numbers sign extended to 64. + // We shift the address left 16 to eliminate the sign extended part and make + // room in the bottom for the count. addrBits = 48 // In addition to the 16 bits taken from the top, we can take 3 from the @@ -35,5 +39,10 @@ func lfstackPack(node *lfnode, cnt uintptr) uint64 { } func lfstackUnpack(val uint64) *lfnode { + if GOARCH == "amd64" { + // amd64 systems can place the stack above the VA hole, so we need to sign extend + // val before unpacking. + return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> cntBits << 3))) + } return (*lfnode)(unsafe.Pointer(uintptr(val >> cntBits << 3))) } diff --git a/src/runtime/lfstack_amd64.go b/src/runtime/lfstack_amd64.go deleted file mode 100644 index 6397e1d47f..0000000000 --- a/src/runtime/lfstack_amd64.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 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 runtime - -import "unsafe" - -// On AMD64, virtual addresses are 48-bit numbers sign extended to 64. -// We shift the address left 16 to eliminate the sign extended part and make -// room in the bottom for the count. -// In addition to the 16 bits taken from the top, we can take 3 from the -// bottom, because node must be pointer-aligned, giving a total of 19 bits -// of count. - -func lfstackPack(node *lfnode, cnt uintptr) uint64 { - return uint64(uintptr(unsafe.Pointer(node)))<<16 | uint64(cnt&(1<<19-1)) -} - -func lfstackUnpack(val uint64) *lfnode { - return (*lfnode)(unsafe.Pointer(uintptr(int64(val) >> 19 << 3))) -} -- cgit v1.3 From b0eeb8b0aaaf4997c25e3048bfc40e53d556a8eb Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sun, 3 Apr 2016 12:52:12 -0400 Subject: net/http/pprof: accept fractional seconds in trace handler For heavily loaded servers, even 1 second of trace is too large to process with the trace viewer; using a float64 here allows fetching /debug/pprof/trace?seconds=0.1. Change-Id: I286c07abf04f9c1fe594b0e26799bf37f5c734db Reviewed-on: https://go-review.googlesource.com/21455 Reviewed-by: Austin Clements --- src/net/http/pprof/pprof.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/net/http/pprof/pprof.go b/src/net/http/pprof/pprof.go index 44afa2d8d8..cb4086b963 100644 --- a/src/net/http/pprof/pprof.go +++ b/src/net/http/pprof/pprof.go @@ -120,8 +120,8 @@ func Profile(w http.ResponseWriter, r *http.Request) { // Tracing lasts for duration specified in seconds GET parameter, or for 1 second if not specified. // The package initialization registers it as /debug/pprof/trace. func Trace(w http.ResponseWriter, r *http.Request) { - sec, _ := strconv.ParseInt(r.FormValue("seconds"), 10, 64) - if sec == 0 { + sec, err := strconv.ParseFloat(r.FormValue("seconds"), 64) + if sec <= 0 || err != nil { sec = 1 } @@ -136,7 +136,7 @@ func Trace(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Could not enable tracing: %s\n", err) return } - sleep(w, time.Duration(sec)*time.Second) + sleep(w, time.Duration(sec*float64(time.Second))) trace.Stop() } -- cgit v1.3 From 4f12cc08132f3e5d2ba4b756c91d88c2e58a73b1 Mon Sep 17 00:00:00 2001 From: Shahar Kohanim Date: Thu, 7 Apr 2016 18:00:57 +0300 Subject: cmd/link: symbol generation optimizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After making dwarf generation backed by LSyms there was a performance regression of about 10%. These changes make on the fly symbol generation faster and are meant to help mitigate that. name old secs new secs delta LinkCmdGo 0.55 ± 9% 0.53 ± 8% -4.42% (p=0.000 n=100+99) name old MaxRSS new MaxRSS delta LinkCmdGo 152k ± 6% 149k ± 3% -1.99% (p=0.000 n=99+97) Change-Id: Iacca3ec924ce401aa83126bc0b10fe89bedf0ba6 Reviewed-on: https://go-review.googlesource.com/21733 Run-TryBot: Shahar Kohanim TryBot-Result: Gobot Gobot Reviewed-by: David Crawshaw --- src/cmd/link/internal/ld/data.go | 32 ++++++++++++++++++-------------- src/cmd/link/internal/ld/dwarf.go | 13 +++++++++---- src/cmd/link/internal/ld/pcln.go | 3 +-- src/cmd/link/internal/ld/symtab.go | 10 ++-------- 4 files changed, 30 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 2c8cc9ca4f..ae7c287f59 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -50,8 +50,9 @@ func Symgrow(ctxt *Link, s *LSym, siz int64) { if int64(len(s.P)) >= siz { return } - for cap(s.P) < int(siz) { - s.P = append(s.P[:len(s.P)], 0) + if cap(s.P) < int(siz) { + p := make([]byte, 2*(siz+1)) + s.P = append(p[:0], s.P...) } s.P = s.P[:siz] } @@ -90,11 +91,8 @@ func Addbytes(ctxt *Link, s *LSym, bytes []byte) int64 { s.Type = obj.SDATA } s.Attr |= AttrReachable - s.Size += int64(len(bytes)) - if int64(int(s.Size)) != s.Size { - log.Fatalf("Addbytes size %d too long", s.Size) - } s.P = append(s.P, bytes...) + s.Size = int64(len(s.P)) return s.Size } @@ -106,7 +104,15 @@ func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 { } func Adduint8(ctxt *Link, s *LSym, v uint8) int64 { - return adduintxx(ctxt, s, uint64(v), 1) + off := s.Size + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Attr |= AttrReachable + s.Size++ + s.P = append(s.P, v) + + return off } func Adduint16(ctxt *Link, s *LSym, v uint16) int64 { @@ -1006,16 +1012,14 @@ func Addstring(s *LSym, str string) int64 { s.Type = obj.SNOPTRDATA } s.Attr |= AttrReachable - r := int32(s.Size) - n := len(str) + 1 + r := s.Size if s.Name == ".shstrtab" { elfsetstring(str, int(r)) } - Symgrow(Ctxt, s, int64(r)+int64(n)) - copy(s.P[r:], str) - s.P[int(r)+len(str)] = 0 - s.Size += int64(n) - return int64(r) + s.P = append(s.P, str...) + s.P = append(s.P, 0) + s.Size = int64(len(s.P)) + return r } // addgostring adds str, as a Go string value, to s. symname is the name of the diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index de2d50a1a9..a3a931f94c 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -88,9 +88,7 @@ func uleb128put(s *LSym, v int64) { func sleb128put(s *LSym, v int64) { b := appendSleb128(encbuf[:0], v) - for _, x := range b { - Adduint8(Ctxt, s, x) - } + Addbytes(Ctxt, s, b) } /* @@ -552,8 +550,15 @@ func findchild(die *DWDie, name string) *DWDie { return nil } +// Used to avoid string allocation when looking up dwarf symbols +var prefixBuf = []byte(infoprefix) + func find(name string) *LSym { - return Linkrlookup(Ctxt, infoprefix+name, 0) + n := append(prefixBuf, name...) + // The string allocation below is optimized away because it is only used in a map lookup. + s := Linkrlookup(Ctxt, string(n), 0) + prefixBuf = n[:len(infoprefix)] + return s } func mustFind(name string) *LSym { diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index a5fea3db76..9a947c7c07 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -127,8 +127,7 @@ func addpctab(ftab *LSym, off int32, d *Pcdata) int32 { var start int32 if len(d.P) > 0 { start = int32(len(ftab.P)) - Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P))) - copy(ftab.P[start:], d.P) + Addbytes(Ctxt, ftab, d.P) } return int32(setuint32(Ctxt, ftab, int64(off), uint32(start))) } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index ecd5c741bb..c7c2733507 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -54,15 +54,9 @@ func putelfstr(s string) int { s = strings.Replace(s, "·", ".", -1) } - n := len(s) + 1 - for len(Elfstrdat)+n > cap(Elfstrdat) { - Elfstrdat = append(Elfstrdat[:cap(Elfstrdat)], 0)[:len(Elfstrdat)] - } - off := len(Elfstrdat) - Elfstrdat = Elfstrdat[:off+n] - copy(Elfstrdat[off:], s) - + Elfstrdat = append(Elfstrdat, s...) + Elfstrdat = append(Elfstrdat, 0) return off } -- cgit v1.3 From 683917a72154e3409e1ab5ef5b26030388312d0b Mon Sep 17 00:00:00 2001 From: Dominik Honnef Date: Fri, 1 Apr 2016 07:34:18 +0200 Subject: all: use bytes.Equal, bytes.Contains and strings.Contains, again The previous cleanup was done with a buggy tool, missing some potential rewrites. Change-Id: I333467036e355f999a6a493e8de87e084f374e26 Reviewed-on: https://go-review.googlesource.com/21378 Reviewed-by: Brad Fitzpatrick --- src/cmd/go/go_test.go | 4 ++-- src/html/template/url.go | 2 +- src/net/http/serve_test.go | 2 +- src/path/filepath/path_test.go | 2 +- src/runtime/gcinfo_test.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index 411fd1e322..42efa9f312 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -1466,7 +1466,7 @@ func TestGoTestWithPackageListedMultipleTimes(t *testing.T) { defer tg.cleanup() tg.parallel() tg.run("test", "errors", "errors", "errors", "errors", "errors") - if strings.Index(strings.TrimSpace(tg.getStdout()), "\n") != -1 { + if strings.Contains(strings.TrimSpace(tg.getStdout()), "\n") { t.Error("go test errors errors errors errors errors tested the same package multiple times") } } @@ -1495,7 +1495,7 @@ func TestGoListCmdOnlyShowsCommands(t *testing.T) { tg.run("list", "cmd") out := strings.TrimSpace(tg.getStdout()) for _, line := range strings.Split(out, "\n") { - if strings.Index(line, "cmd/") == -1 { + if !strings.Contains(line, "cmd/") { t.Error("go list cmd shows non-commands") break } diff --git a/src/html/template/url.go b/src/html/template/url.go index 2ca76bf389..246bfd32cd 100644 --- a/src/html/template/url.go +++ b/src/html/template/url.go @@ -17,7 +17,7 @@ func urlFilter(args ...interface{}) string { if t == contentTypeURL { return s } - if i := strings.IndexRune(s, ':'); i >= 0 && strings.IndexRune(s[:i], '/') < 0 { + if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') { protocol := strings.ToLower(s[:i]) if protocol != "http" && protocol != "https" && protocol != "mailto" { return "#" + filterFailsafe diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 4cd6ed077f..e0094234de 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -4267,7 +4267,7 @@ func BenchmarkClient(b *testing.B) { if err != nil { b.Fatalf("ReadAll: %v", err) } - if bytes.Compare(body, data) != 0 { + if !bytes.Equal(body, data) { b.Fatalf("Got body: %q", body) } } diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go index 3622f9178e..1a4a9d2a1a 100644 --- a/src/path/filepath/path_test.go +++ b/src/path/filepath/path_test.go @@ -1015,7 +1015,7 @@ func TestAbs(t *testing.T) { vol := filepath.VolumeName(root) var extra []string for _, path := range absTests { - if strings.Index(path, "$") != -1 { + if strings.Contains(path, "$") { continue } path = vol + path diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go index c1c2354bf9..9a61b4f2b2 100644 --- a/src/runtime/gcinfo_test.go +++ b/src/runtime/gcinfo_test.go @@ -59,7 +59,7 @@ func TestGCInfo(t *testing.T) { func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) { mask := runtime.GCMask(p) - if bytes.Compare(mask, mask0) != 0 { + if !bytes.Equal(mask, mask0) { t.Errorf("bad GC program for %v:\nwant %+v\ngot %+v", name, mask0, mask) return } -- cgit v1.3 From 00681eec6aec03b8b2822c9220fba27c18923c01 Mon Sep 17 00:00:00 2001 From: Dan Peterson Date: Mon, 11 Apr 2016 11:15:00 -0300 Subject: net/http: document Error does not end the request Fixes #15205 Change-Id: Ia650806756758ca8ed2272b1696e59b809b16c61 Reviewed-on: https://go-review.googlesource.com/21836 Reviewed-by: Brad Fitzpatrick --- src/net/http/server.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/net/http/server.go b/src/net/http/server.go index e37df99deb..7a6950aee4 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1652,6 +1652,8 @@ func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { // Helper handlers // Error replies to the request with the specified error message and HTTP code. +// It does not otherwise end the request; the caller should ensure no further +// writes are done to w. // The error message should be plain text. func Error(w ResponseWriter, error string, code int) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") -- cgit v1.3 From 6c6089b3fdba9eb0cff863a03074dbac47c92f63 Mon Sep 17 00:00:00 2001 From: Alexandru Moșoi Date: Fri, 1 Apr 2016 15:09:19 +0200 Subject: cmd/compile: bce when max and limit are consts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes 49 more bound checks in make.bash. For example: var a[100]int for i := 0; i < 50; i++ { use a[i+25] } Change-Id: I85e0130ee5d07f0ece9b17044bba1a2047414ce7 Reviewed-on: https://go-review.googlesource.com/21379 Reviewed-by: David Chase Run-TryBot: Alexandru Moșoi TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/ssa/loopbce.go | 41 ++++++++++++++++++ test/checkbce.go | 15 ++++++- test/loopbce.go | 77 +++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/loopbce.go b/src/cmd/compile/internal/ssa/loopbce.go index 17486ac49f..c937ead1b2 100644 --- a/src/cmd/compile/internal/ssa/loopbce.go +++ b/src/cmd/compile/internal/ssa/loopbce.go @@ -240,6 +240,37 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) { } skip2: + // Simplify + // (IsInBounds (Add64 ind) (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c]) + // (IsSliceInBounds ind (Const64 [c])) where 0 <= min <= ind < max <= (Const64 [c]) + if v.Op == OpIsInBounds || v.Op == OpIsSliceInBounds { + ind, add := dropAdd64(v.Args[0]) + if ind.Op != OpPhi { + goto skip3 + } + + // ind + add >= 0 <-> min + add >= 0 <-> min >= -add + if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) { + if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() { + goto skip3 + } + + limit := v.Args[1].AuxInt + if v.Op == OpIsSliceInBounds { + // If limit++ overflows signed integer then 0 <= max && max <= limit will be false. + limit++ + } + + if max := iv.max.AuxInt + add; 0 <= max && max <= limit { // handle overflow + if f.pass.debug > 0 { + f.Config.Warnl(b.Line, "Found redundant (%s ind %d), ind < %d", v.Op, v.Args[1].AuxInt, iv.max.AuxInt+add) + } + goto simplify + } + } + } + skip3: + continue simplify: @@ -258,3 +289,13 @@ func dropAdd64(v *Value) (*Value, int64) { } return v, 0 } + +func isGreaterOrEqualThan(v *Value, c int64) bool { + if c == 0 { + return isNonNegative(v) + } + if v.isGenericIntConst() && v.AuxInt >= c { + return true + } + return false +} diff --git a/test/checkbce.go b/test/checkbce.go index 988375fcc7..fa0ea12803 100644 --- a/test/checkbce.go +++ b/test/checkbce.go @@ -57,7 +57,7 @@ func f6(a [32]int, b [64]int, i int) { useInt(b[uint64(i*0x07C4ACDD)>>58]) useInt(a[uint(i*0x07C4ACDD)>>59]) - // The following bounds should removed as they can overflow. + // The following bounds should not be removed because they can overflow. useInt(a[uint32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$" useInt(b[uint64(i*0x106297f105d0cc86)>>57]) // ERROR "Found IsInBounds$" useInt(a[int32(i*0x106297f105d0cc86)>>26]) // ERROR "Found IsInBounds$" @@ -89,6 +89,19 @@ func g3(a []int) { } } +func g4(a [100]int) { + for i := 10; i < 50; i++ { + useInt(a[i-10]) + useInt(a[i]) + useInt(a[i+25]) + useInt(a[i+50]) + + // The following are out of bounds. + useInt(a[i-11]) // ERROR "Found IsInBounds$" + useInt(a[i+51]) // ERROR "Found IsInBounds$" + } +} + //go:noinline func useInt(a int) { } diff --git a/test/loopbce.go b/test/loopbce.go index eb44092705..ea195217e6 100644 --- a/test/loopbce.go +++ b/test/loopbce.go @@ -139,6 +139,70 @@ func h2(a []byte) { } } +func k0(a [100]int) [100]int { + for i := 10; i < 90; i++ { // ERROR "Induction variable with minimum 10 and increment 1$" + a[i-11] = i + a[i-10] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 80$" + a[i-5] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 85$" + a[i] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 90$" + a[i+5] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 95$" + a[i+10] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 100$" + a[i+11] = i + } + return a +} + +func k1(a [100]int) [100]int { + for i := 10; i < 90; i++ { // ERROR "Induction variable with minimum 10 and increment 1$" + useSlice(a[:i-11]) + useSlice(a[:i-10]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 80$" + useSlice(a[:i-5]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 85$" + useSlice(a[:i]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 90$" + useSlice(a[:i+5]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 95$" + useSlice(a[:i+10]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 100$" + useSlice(a[:i+11]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 101$" + + } + return a +} + +func k2(a [100]int) [100]int { + for i := 10; i < 90; i++ { // ERROR "Induction variable with minimum 10 and increment 1$" + useSlice(a[i-11:]) + useSlice(a[i-10:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 80$" + useSlice(a[i-5:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 85$" + useSlice(a[i:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 90$" + useSlice(a[i+5:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 95$" + useSlice(a[i+10:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 100$" + useSlice(a[i+11:]) // ERROR "Found redundant \(IsSliceInBounds ind 100\), ind < 101$" + } + return a +} + +func k3(a [100]int) [100]int { + for i := -10; i < 90; i++ { // ERROR "Induction variable with minimum -10 and increment 1$" + a[i+10] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 100$" + } + return a +} + +func k4(a [100]int) [100]int { + min := (-1) << 63 + for i := min; i < min+50; i++ { // ERROR "Induction variable with minimum -9223372036854775808 and increment 1$" + a[i-min] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 50$" + } + return a +} + +func k5(a [100]int) [100]int { + max := (1 << 63) - 1 + for i := max - 50; i < max; i++ { // ERROR "Induction variable with minimum 9223372036854775757 and increment 1$" + a[i-max+50] = i + a[i-(max-70)] = i // ERROR "Found redundant \(IsInBounds ind 100\), ind < 70$" + } + return a +} + func nobce1() { // tests overflow of max-min a := int64(9223372036854774057) @@ -168,9 +232,22 @@ func nobce2(a string) { } } +func nobce3(a [100]int64) [100]int64 { + min := int64((-1) << 63) + max := int64((1 << 63) - 1) + for i := min; i < max; i++ { // ERROR "Induction variable with minimum -9223372036854775808 and increment 1$" + a[i] = i + } + return a +} + //go:noinline func useString(a string) { } +//go:noinline +func useSlice(a []int) { +} + func main() { } -- cgit v1.3 From b04e145248d5d3721a41d4bb26704fdb43caaf38 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 31 Mar 2016 21:24:10 -0700 Subject: cmd/compile: fix naming of decomposed structs When a struct is SSAable, we will name its component parts by their field names. For example, type T struct { a, b, c int } If we ever need to spill a variable x of type T, we will spill its individual components to variables named x.a, x.b, and x.c. Change-Id: I857286ff1f2597f2c4bbd7b4c0b936386fb37131 Reviewed-on: https://go-review.googlesource.com/21389 Reviewed-by: David Chase --- src/cmd/compile/internal/gc/ssa.go | 16 ++++++++- src/cmd/compile/internal/gc/type.go | 3 ++ src/cmd/compile/internal/ssa/config.go | 1 + src/cmd/compile/internal/ssa/decompose.go | 27 ++++++++++---- src/cmd/compile/internal/ssa/export_test.go | 3 ++ src/cmd/compile/internal/ssa/type.go | 56 +++++++++++++++-------------- src/cmd/compile/internal/ssa/type_test.go | 49 ++++++++++++------------- 7 files changed, 97 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index d69559d945..5ee370395b 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4227,7 +4227,7 @@ func (e *ssaExport) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.Local func (e *ssaExport) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) { n := name.N.(*Node) - ptrType := Ptrto(n.Type.Elem()) + ptrType := Ptrto(name.Type.ElemType().(*Type)) lenType := Types[TINT] if n.Class == PAUTO && !n.Addrtaken { // Split this slice up into three separate variables. @@ -4261,6 +4261,20 @@ func (e *ssaExport) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSl return ssa.LocalSlot{n, t, name.Off}, ssa.LocalSlot{n, t, name.Off + s} } +func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot { + n := name.N.(*Node) + st := name.Type + ft := st.FieldType(i) + if n.Class == PAUTO && !n.Addrtaken { + // Note: the _ field may appear several times. But + // have no fear, identically-named but distinct Autos are + // ok, albeit maybe confusing for a debugger. + x := e.namedAuto(n.Sym.Name+"."+st.FieldName(i), ft) + return ssa.LocalSlot{x, ft, 0} + } + return ssa.LocalSlot{n, ft, name.Off + st.FieldOff(i)} +} + // namedAuto returns a new AUTO variable with the given name and type. func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode { t := typ.(*Type) diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index eee8e0384a..25c1bcc203 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -1193,6 +1193,9 @@ func (t *Type) FieldType(i int) ssa.Type { func (t *Type) FieldOff(i int) int64 { return t.Field(i).Offset } +func (t *Type) FieldName(i int) string { + return t.Field(i).Sym.Name +} func (t *Type) NumElem() int64 { t.wantEtype(TARRAY) diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go index 33357124fc..2a676e39b3 100644 --- a/src/cmd/compile/internal/ssa/config.go +++ b/src/cmd/compile/internal/ssa/config.go @@ -103,6 +103,7 @@ type Frontend interface { SplitInterface(LocalSlot) (LocalSlot, LocalSlot) SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot) SplitComplex(LocalSlot) (LocalSlot, LocalSlot) + SplitStruct(LocalSlot, int) LocalSlot // Line returns a string describing the given line number. Line(int32) string diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index eab9974106..de02885d76 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -21,6 +21,7 @@ func decomposeBuiltIn(f *Func) { // NOTE: the component values we are making are dead at this point. // We must do the opt pass before any deadcode elimination or we will // lose the name->value correspondence. + var newNames []LocalSlot for _, name := range f.Names { t := name.Type switch { @@ -32,29 +33,31 @@ func decomposeBuiltIn(f *Func) { elemType = f.Config.fe.TypeFloat32() } rName, iName := f.Config.fe.SplitComplex(name) - f.Names = append(f.Names, rName, iName) + newNames = append(newNames, rName, iName) for _, v := range f.NamedValues[name] { r := v.Block.NewValue1(v.Line, OpComplexReal, elemType, v) i := v.Block.NewValue1(v.Line, OpComplexImag, elemType, v) f.NamedValues[rName] = append(f.NamedValues[rName], r) f.NamedValues[iName] = append(f.NamedValues[iName], i) } + delete(f.NamedValues, name) case t.IsString(): ptrType := f.Config.fe.TypeBytePtr() lenType := f.Config.fe.TypeInt() ptrName, lenName := f.Config.fe.SplitString(name) - f.Names = append(f.Names, ptrName, lenName) + newNames = append(newNames, ptrName, lenName) for _, v := range f.NamedValues[name] { ptr := v.Block.NewValue1(v.Line, OpStringPtr, ptrType, v) len := v.Block.NewValue1(v.Line, OpStringLen, lenType, v) f.NamedValues[ptrName] = append(f.NamedValues[ptrName], ptr) f.NamedValues[lenName] = append(f.NamedValues[lenName], len) } + delete(f.NamedValues, name) case t.IsSlice(): ptrType := f.Config.fe.TypeBytePtr() lenType := f.Config.fe.TypeInt() ptrName, lenName, capName := f.Config.fe.SplitSlice(name) - f.Names = append(f.Names, ptrName, lenName, capName) + newNames = append(newNames, ptrName, lenName, capName) for _, v := range f.NamedValues[name] { ptr := v.Block.NewValue1(v.Line, OpSlicePtr, ptrType, v) len := v.Block.NewValue1(v.Line, OpSliceLen, lenType, v) @@ -63,20 +66,25 @@ func decomposeBuiltIn(f *Func) { f.NamedValues[lenName] = append(f.NamedValues[lenName], len) f.NamedValues[capName] = append(f.NamedValues[capName], cap) } + delete(f.NamedValues, name) case t.IsInterface(): ptrType := f.Config.fe.TypeBytePtr() typeName, dataName := f.Config.fe.SplitInterface(name) - f.Names = append(f.Names, typeName, dataName) + newNames = append(newNames, typeName, dataName) for _, v := range f.NamedValues[name] { typ := v.Block.NewValue1(v.Line, OpITab, ptrType, v) data := v.Block.NewValue1(v.Line, OpIData, ptrType, v) f.NamedValues[typeName] = append(f.NamedValues[typeName], typ) f.NamedValues[dataName] = append(f.NamedValues[dataName], data) } + delete(f.NamedValues, name) case t.Size() > f.Config.IntSize: f.Unimplementedf("undecomposed named type %s", t) + default: + newNames = append(newNames, name) } } + f.Names = newNames } func decomposeBuiltInPhi(v *Value) { @@ -181,25 +189,32 @@ func decomposeUser(f *Func) { // We must do the opt pass before any deadcode elimination or we will // lose the name->value correspondence. i := 0 + var fnames []LocalSlot + var newNames []LocalSlot for _, name := range f.Names { t := name.Type switch { case t.IsStruct(): n := t.NumFields() + fnames = fnames[:0] + for i := 0; i < n; i++ { + fnames = append(fnames, f.Config.fe.SplitStruct(name, i)) + } for _, v := range f.NamedValues[name] { for i := 0; i < n; i++ { - fname := LocalSlot{name.N, t.FieldType(i), name.Off + t.FieldOff(i)} // TODO: use actual field name? x := v.Block.NewValue1I(v.Line, OpStructSelect, t.FieldType(i), int64(i), v) - f.NamedValues[fname] = append(f.NamedValues[fname], x) + f.NamedValues[fnames[i]] = append(f.NamedValues[fnames[i]], x) } } delete(f.NamedValues, name) + newNames = append(newNames, fnames...) default: f.Names[i] = name i++ } } f.Names = f.Names[:i] + f.Names = append(f.Names, newNames...) } func decomposeUserPhi(v *Value) { diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go index ce577ef055..0a67de9f05 100644 --- a/src/cmd/compile/internal/ssa/export_test.go +++ b/src/cmd/compile/internal/ssa/export_test.go @@ -48,6 +48,9 @@ func (d DummyFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) { } return LocalSlot{s.N, d.TypeFloat32(), s.Off}, LocalSlot{s.N, d.TypeFloat32(), s.Off + 4} } +func (d DummyFrontend) SplitStruct(s LocalSlot, i int) LocalSlot { + return LocalSlot{s.N, s.Type.FieldType(i), s.Off + s.Type.FieldOff(i)} +} func (DummyFrontend) Line(line int32) string { return "unknown.go:0" } diff --git a/src/cmd/compile/internal/ssa/type.go b/src/cmd/compile/internal/ssa/type.go index 9643b07556..2a3de282cb 100644 --- a/src/cmd/compile/internal/ssa/type.go +++ b/src/cmd/compile/internal/ssa/type.go @@ -31,9 +31,10 @@ type Type interface { ElemType() Type // given []T or *T or [n]T, return T PtrTo() Type // given T, return *T - NumFields() int // # of fields of a struct - FieldType(i int) Type // type of ith field of the struct - FieldOff(i int) int64 // offset of ith field of the struct + NumFields() int // # of fields of a struct + FieldType(i int) Type // type of ith field of the struct + FieldOff(i int) int64 // offset of ith field of the struct + FieldName(i int) string // name of ith field of the struct NumElem() int64 // # of elements of an array @@ -53,30 +54,31 @@ type CompilerType struct { Int128 bool } -func (t *CompilerType) Size() int64 { return t.size } // Size in bytes -func (t *CompilerType) Alignment() int64 { return 0 } -func (t *CompilerType) IsBoolean() bool { return false } -func (t *CompilerType) IsInteger() bool { return false } -func (t *CompilerType) IsSigned() bool { return false } -func (t *CompilerType) IsFloat() bool { return false } -func (t *CompilerType) IsComplex() bool { return false } -func (t *CompilerType) IsPtrShaped() bool { return false } -func (t *CompilerType) IsString() bool { return false } -func (t *CompilerType) IsSlice() bool { return false } -func (t *CompilerType) IsArray() bool { return false } -func (t *CompilerType) IsStruct() bool { return false } -func (t *CompilerType) IsInterface() bool { return false } -func (t *CompilerType) IsMemory() bool { return t.Memory } -func (t *CompilerType) IsFlags() bool { return t.Flags } -func (t *CompilerType) IsVoid() bool { return t.Void } -func (t *CompilerType) String() string { return t.Name } -func (t *CompilerType) SimpleString() string { return t.Name } -func (t *CompilerType) ElemType() Type { panic("not implemented") } -func (t *CompilerType) PtrTo() Type { panic("not implemented") } -func (t *CompilerType) NumFields() int { panic("not implemented") } -func (t *CompilerType) FieldType(i int) Type { panic("not implemented") } -func (t *CompilerType) FieldOff(i int) int64 { panic("not implemented") } -func (t *CompilerType) NumElem() int64 { panic("not implemented") } +func (t *CompilerType) Size() int64 { return t.size } // Size in bytes +func (t *CompilerType) Alignment() int64 { return 0 } +func (t *CompilerType) IsBoolean() bool { return false } +func (t *CompilerType) IsInteger() bool { return false } +func (t *CompilerType) IsSigned() bool { return false } +func (t *CompilerType) IsFloat() bool { return false } +func (t *CompilerType) IsComplex() bool { return false } +func (t *CompilerType) IsPtrShaped() bool { return false } +func (t *CompilerType) IsString() bool { return false } +func (t *CompilerType) IsSlice() bool { return false } +func (t *CompilerType) IsArray() bool { return false } +func (t *CompilerType) IsStruct() bool { return false } +func (t *CompilerType) IsInterface() bool { return false } +func (t *CompilerType) IsMemory() bool { return t.Memory } +func (t *CompilerType) IsFlags() bool { return t.Flags } +func (t *CompilerType) IsVoid() bool { return t.Void } +func (t *CompilerType) String() string { return t.Name } +func (t *CompilerType) SimpleString() string { return t.Name } +func (t *CompilerType) ElemType() Type { panic("not implemented") } +func (t *CompilerType) PtrTo() Type { panic("not implemented") } +func (t *CompilerType) NumFields() int { panic("not implemented") } +func (t *CompilerType) FieldType(i int) Type { panic("not implemented") } +func (t *CompilerType) FieldOff(i int) int64 { panic("not implemented") } +func (t *CompilerType) FieldName(i int) string { panic("not implemented") } +func (t *CompilerType) NumElem() int64 { panic("not implemented") } // Cmp is a comparison between values a and b. // -1 if a < b diff --git a/src/cmd/compile/internal/ssa/type_test.go b/src/cmd/compile/internal/ssa/type_test.go index cd80abf03f..3b1a892083 100644 --- a/src/cmd/compile/internal/ssa/type_test.go +++ b/src/cmd/compile/internal/ssa/type_test.go @@ -24,30 +24,31 @@ type TypeImpl struct { Name string } -func (t *TypeImpl) Size() int64 { return t.Size_ } -func (t *TypeImpl) Alignment() int64 { return t.Align } -func (t *TypeImpl) IsBoolean() bool { return t.Boolean } -func (t *TypeImpl) IsInteger() bool { return t.Integer } -func (t *TypeImpl) IsSigned() bool { return t.Signed } -func (t *TypeImpl) IsFloat() bool { return t.Float } -func (t *TypeImpl) IsComplex() bool { return t.Complex } -func (t *TypeImpl) IsPtrShaped() bool { return t.Ptr } -func (t *TypeImpl) IsString() bool { return t.string } -func (t *TypeImpl) IsSlice() bool { return t.slice } -func (t *TypeImpl) IsArray() bool { return t.array } -func (t *TypeImpl) IsStruct() bool { return t.struct_ } -func (t *TypeImpl) IsInterface() bool { return t.inter } -func (t *TypeImpl) IsMemory() bool { return false } -func (t *TypeImpl) IsFlags() bool { return false } -func (t *TypeImpl) IsVoid() bool { return false } -func (t *TypeImpl) String() string { return t.Name } -func (t *TypeImpl) SimpleString() string { return t.Name } -func (t *TypeImpl) ElemType() Type { return t.Elem_ } -func (t *TypeImpl) PtrTo() Type { panic("not implemented") } -func (t *TypeImpl) NumFields() int { panic("not implemented") } -func (t *TypeImpl) FieldType(i int) Type { panic("not implemented") } -func (t *TypeImpl) FieldOff(i int) int64 { panic("not implemented") } -func (t *TypeImpl) NumElem() int64 { panic("not implemented") } +func (t *TypeImpl) Size() int64 { return t.Size_ } +func (t *TypeImpl) Alignment() int64 { return t.Align } +func (t *TypeImpl) IsBoolean() bool { return t.Boolean } +func (t *TypeImpl) IsInteger() bool { return t.Integer } +func (t *TypeImpl) IsSigned() bool { return t.Signed } +func (t *TypeImpl) IsFloat() bool { return t.Float } +func (t *TypeImpl) IsComplex() bool { return t.Complex } +func (t *TypeImpl) IsPtrShaped() bool { return t.Ptr } +func (t *TypeImpl) IsString() bool { return t.string } +func (t *TypeImpl) IsSlice() bool { return t.slice } +func (t *TypeImpl) IsArray() bool { return t.array } +func (t *TypeImpl) IsStruct() bool { return t.struct_ } +func (t *TypeImpl) IsInterface() bool { return t.inter } +func (t *TypeImpl) IsMemory() bool { return false } +func (t *TypeImpl) IsFlags() bool { return false } +func (t *TypeImpl) IsVoid() bool { return false } +func (t *TypeImpl) String() string { return t.Name } +func (t *TypeImpl) SimpleString() string { return t.Name } +func (t *TypeImpl) ElemType() Type { return t.Elem_ } +func (t *TypeImpl) PtrTo() Type { panic("not implemented") } +func (t *TypeImpl) NumFields() int { panic("not implemented") } +func (t *TypeImpl) FieldType(i int) Type { panic("not implemented") } +func (t *TypeImpl) FieldOff(i int) int64 { panic("not implemented") } +func (t *TypeImpl) FieldName(i int) string { panic("not implemented") } +func (t *TypeImpl) NumElem() int64 { panic("not implemented") } func (t *TypeImpl) Equal(u Type) bool { x, ok := u.(*TypeImpl) -- cgit v1.3 From 3fafe2e8888dadb6877fa1e7569f5bd1f688dd3a Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 11 Apr 2016 08:57:52 +0200 Subject: internal/trace: support parsing of 1.5 traces 1. Parse out version from trace header. 2. Restore handling of 1.5 traces. 3. Restore optional symbolization of traces. 4. Add some canned 1.5 traces for regression testing (http benchmark trace, runtime/trace stress traces, plus one with broken timestamps). Change-Id: Idb18a001d03ded8e13c2730eeeb37c5836e31256 Reviewed-on: https://go-review.googlesource.com/21803 Run-TryBot: Dmitry Vyukov TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- misc/nacl/testzip.proto | 4 + src/cmd/trace/main.go | 2 +- src/internal/trace/parser.go | 314 +++++++++++++++------ src/internal/trace/parser_test.go | 61 +++- src/internal/trace/testdata/http_1_5_good | Bin 0 -> 42218 bytes src/internal/trace/testdata/stress_1_5_good | Bin 0 -> 7446 bytes src/internal/trace/testdata/stress_1_5_unordered | Bin 0 -> 8194 bytes .../trace/testdata/stress_start_stop_1_5_good | Bin 0 -> 6997 bytes src/runtime/trace.go | 2 +- src/runtime/trace/trace_test.go | 4 +- 10 files changed, 295 insertions(+), 92 deletions(-) create mode 100644 src/internal/trace/testdata/http_1_5_good create mode 100644 src/internal/trace/testdata/stress_1_5_good create mode 100644 src/internal/trace/testdata/stress_1_5_unordered create mode 100644 src/internal/trace/testdata/stress_start_stop_1_5_good (limited to 'src') diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto index 42db92f327..8c14b87f0a 100644 --- a/misc/nacl/testzip.proto +++ b/misc/nacl/testzip.proto @@ -109,6 +109,10 @@ go src=.. png testdata + + internal + trace + testdata + + io + mime diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index 12bf8c3c16..cfd222e132 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -99,7 +99,7 @@ func parseEvents() ([]*trace.Event, error) { defer tracef.Close() // Parse and symbolize. - events, err := trace.Parse(bufio.NewReader(tracef)) + events, err := trace.Parse(bufio.NewReader(tracef), programBinary) if err != nil { loader.err = fmt.Errorf("failed to parse trace: %v", err) return diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go index 65530b15c3..82ddb8b6c8 100644 --- a/src/internal/trace/parser.go +++ b/src/internal/trace/parser.go @@ -5,17 +5,22 @@ package trace import ( + "bufio" "bytes" "fmt" "io" + "os" + "os/exec" "sort" + "strconv" + "strings" ) // Event describes one event in the trace. type Event struct { Off int // offset in input file (for debugging and error reporting) Type byte // one of Ev* - Seq int64 // sequence number + seq int64 // sequence number Ts int64 // timestamp in nanoseconds P int // P on which the event happened (can be one of TimerP, NetpollP, SyscallP) G uint64 // G on which the event happened @@ -53,12 +58,12 @@ const ( ) // Parse parses, post-processes and verifies the trace. -func Parse(r io.Reader) ([]*Event, error) { - rawEvents, strings, err := readTrace(r) +func Parse(r io.Reader, bin string) ([]*Event, error) { + ver, rawEvents, strings, err := readTrace(r) if err != nil { return nil, err } - events, stacks, err := parseEvents(rawEvents, strings) + events, stacks, err := parseEvents(ver, rawEvents, strings) if err != nil { return nil, err } @@ -66,7 +71,7 @@ func Parse(r io.Reader) ([]*Event, error) { if err != nil { return nil, err } - err = postProcessTrace(events) + err = postProcessTrace(ver, events) if err != nil { return nil, err } @@ -76,6 +81,11 @@ func Parse(r io.Reader) ([]*Event, error) { ev.Stk = stacks[ev.StkID] } } + if ver < 1007 && bin != "" { + if err := symbolize(events, bin); err != nil { + return nil, err + } + } return events, nil } @@ -88,61 +98,82 @@ type rawEvent struct { // readTrace does wire-format parsing and verification. // It does not care about specific event types and argument meaning. -func readTrace(r io.Reader) ([]rawEvent, map[uint64]string, error) { +func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]string, err error) { // Read and validate trace header. var buf [16]byte - off, err := r.Read(buf[:]) - if off != 16 || err != nil { - return nil, nil, fmt.Errorf("failed to read header: read %v, err %v", off, err) + off, err := io.ReadFull(r, buf[:]) + if err != nil { + err = fmt.Errorf("failed to read header: read %v, err %v", off, err) + return } - if !bytes.Equal(buf[:], []byte("go 1.5 trace\x00\x00\x00\x00")) { - return nil, nil, fmt.Errorf("not a trace file") + ver, err = parseHeader(buf[:]) + if err != nil { + return + } + switch ver { + case 1005, 1007: + break + default: + err = fmt.Errorf("unsupported trace file version %v.%v (update Go toolchain) %v", ver/1000, ver%1000, ver) + return } // Read events. - var events []rawEvent - strings := make(map[uint64]string) + strings = make(map[uint64]string) for { // Read event type and number of arguments (1 byte). off0 := off - n, err := r.Read(buf[:1]) + var n int + n, err = r.Read(buf[:1]) if err == io.EOF { + err = nil break } if err != nil || n != 1 { - return nil, nil, fmt.Errorf("failed to read trace at offset 0x%x: n=%v err=%v", off0, n, err) + err = fmt.Errorf("failed to read trace at offset 0x%x: n=%v err=%v", off0, n, err) + return } off += n typ := buf[0] << 2 >> 2 narg := buf[0] >> 6 + if typ == EvNone || typ >= EvCount || EventDescriptions[typ].minVersion > ver { + err = fmt.Errorf("unknown event type %v at offset 0x%x", typ, off0) + return + } if typ == EvString { // String dictionary entry [ID, length, string]. var id uint64 id, off, err = readVal(r, off) if err != nil { - return nil, nil, err + return } if id == 0 { - return nil, nil, fmt.Errorf("string at offset %d has invalid id 0", off) + err = fmt.Errorf("string at offset %d has invalid id 0", off) + return } if strings[id] != "" { - return nil, nil, fmt.Errorf("string at offset %d has duplicate id %v", off, id) + err = fmt.Errorf("string at offset %d has duplicate id %v", off, id) + return } var ln uint64 ln, off, err = readVal(r, off) if err != nil { - return nil, nil, err + return } if ln == 0 { - return nil, nil, fmt.Errorf("string at offset %d has invalid length 0", off) + err = fmt.Errorf("string at offset %d has invalid length 0", off) + return } if ln > 1e6 { - return nil, nil, fmt.Errorf("string at offset %d has too large length %v", off, ln) + err = fmt.Errorf("string at offset %d has too large length %v", off, ln) + return } buf := make([]byte, ln) - n, err := io.ReadFull(r, buf) + var n int + n, err = io.ReadFull(r, buf) if err != nil { - return nil, nil, fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, ln, err) + err = fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, ln, err) + return } off += n strings[id] = string(buf) @@ -154,7 +185,8 @@ func readTrace(r io.Reader) ([]rawEvent, map[uint64]string, error) { var v uint64 v, off, err = readVal(r, off) if err != nil { - return nil, nil, err + err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err) + return } ev.args = append(ev.args, v) } @@ -163,39 +195,62 @@ func readTrace(r io.Reader) ([]rawEvent, map[uint64]string, error) { var v uint64 v, off, err = readVal(r, off) if err != nil { - return nil, nil, err + err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err) + return } evLen := v off1 := off for evLen > uint64(off-off1) { v, off, err = readVal(r, off) if err != nil { - return nil, nil, err + err = fmt.Errorf("failed to read event %v argument at offset %v (%v)", typ, off, err) + return } ev.args = append(ev.args, v) } if evLen != uint64(off-off1) { - return nil, nil, fmt.Errorf("event has wrong length at offset 0x%x: want %v, got %v", off0, evLen, off-off1) + err = fmt.Errorf("event has wrong length at offset 0x%x: want %v, got %v", off0, evLen, off-off1) + return } } events = append(events, ev) } - return events, strings, nil + return +} + +// parseHeader parses trace header of the form "go 1.7 trace\x00\x00\x00\x00" +// and returns parsed version as 1007. +func parseHeader(buf []byte) (int, error) { + if len(buf) != 16 { + return 0, fmt.Errorf("bad header length") + } + if buf[0] != 'g' || buf[1] != 'o' || buf[2] != ' ' || + buf[3] < '1' || buf[3] > '9' || + buf[4] != '.' || + buf[5] < '1' || buf[5] > '9' { + return 0, fmt.Errorf("not a trace file") + } + ver := int(buf[5] - '0') + i := 0 + for ; buf[6+i] >= '0' && buf[6+i] <= '9' && i < 2; i++ { + ver = ver*10 + int(buf[6+i]-'0') + } + ver += int(buf[3]-'0') * 1000 + if !bytes.Equal(buf[6+i:], []byte(" trace\x00\x00\x00\x00")[:10-i]) { + return 0, fmt.Errorf("not a trace file") + } + return ver, nil } // Parse events transforms raw events into events. // It does analyze and verify per-event-type arguments. -func parseEvents(rawEvents []rawEvent, strings map[uint64]string) (events []*Event, stacks map[uint64][]*Frame, err error) { +func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (events []*Event, stacks map[uint64][]*Frame, err error) { var ticksPerSec, lastSeq, lastTs int64 var lastG, timerGoid uint64 var lastP int lastGs := make(map[int]uint64) // last goroutine running on P stacks = make(map[uint64][]*Frame) for _, raw := range rawEvents { - if raw.typ == EvNone || raw.typ >= EvCount { - err = fmt.Errorf("unknown event type %v at offset 0x%x", raw.typ, raw.off) - return - } desc := EventDescriptions[raw.typ] if desc.Name == "" { err = fmt.Errorf("missing description for event type %v", raw.typ) @@ -246,7 +301,11 @@ func parseEvents(rawEvents []rawEvent, strings map[uint64]string) (events []*Eve raw.off, size) return } - if want := 2 + 4*size; uint64(len(raw.args)) != want { + want := 2 + 4*size + if ver < 1007 { + want = 2 + size + } + if uint64(len(raw.args)) != want { err = fmt.Errorf("EvStack has wrong number of arguments at offset 0x%x: want %v, got %v", raw.off, want, len(raw.args)) return @@ -255,19 +314,23 @@ func parseEvents(rawEvents []rawEvent, strings map[uint64]string) (events []*Eve if id != 0 && size > 0 { stk := make([]*Frame, size) for i := 0; i < int(size); i++ { - pc := raw.args[2+i*4+0] - fn := raw.args[2+i*4+1] - file := raw.args[2+i*4+2] - line := raw.args[2+i*4+3] - stk[i] = &Frame{PC: pc, Fn: strings[fn], File: strings[file], Line: int(line)} + if ver < 1007 { + stk[i] = &Frame{PC: raw.args[2+i]} + } else { + pc := raw.args[2+i*4+0] + fn := raw.args[2+i*4+1] + file := raw.args[2+i*4+2] + line := raw.args[2+i*4+3] + stk[i] = &Frame{PC: pc, Fn: strings[fn], File: strings[file], Line: int(line)} + } } stacks[id] = stk } default: e := &Event{Off: raw.off, Type: raw.typ, P: lastP, G: lastG} - e.Seq = lastSeq + int64(raw.args[0]) + e.seq = lastSeq + int64(raw.args[0]) e.Ts = lastTs + int64(raw.args[1]) - lastSeq = e.Seq + lastSeq = e.seq lastTs = e.Ts for i := range desc.Args { e.Args[i] = raw.args[i+2] @@ -289,7 +352,7 @@ func parseEvents(rawEvents []rawEvent, strings map[uint64]string) (events []*Eve case EvGoSysExit: // EvGoSysExit emission is delayed until the thread has a P. // Give it the real sequence number and time stamp. - e.Seq = int64(e.Args[1]) + e.seq = int64(e.Args[1]) if e.Args[2] != 0 { e.Ts = int64(e.Args[2]) } @@ -387,7 +450,7 @@ var ErrTimeOrder = fmt.Errorf("time stamps out of order") // The resulting trace is guaranteed to be consistent // (for example, a P does not run two Gs at the same time, or a G is indeed // blocked before an unblock event). -func postProcessTrace(events []*Event) error { +func postProcessTrace(ver int, events []*Event) error { const ( gDead = iota gRunnable @@ -510,7 +573,12 @@ func postProcessTrace(events []*Event) error { g.evStart = ev p.g = ev.G if g.evCreate != nil { - ev.StkID = g.evCreate.Args[1] + if ver < 1007 { + // +1 because symbolizer expects return pc. + ev.Stk = []*Frame{{PC: g.evCreate.Args[1] + 1}} + } else { + ev.StkID = g.evCreate.Args[1] + } g.evCreate = nil } @@ -611,6 +679,79 @@ func postProcessTrace(events []*Event) error { return nil } +// symbolize attaches func/file/line info to stack traces. +func symbolize(events []*Event, bin string) error { + // First, collect and dedup all pcs. + pcs := make(map[uint64]*Frame) + for _, ev := range events { + for _, f := range ev.Stk { + pcs[f.PC] = nil + } + } + + // Start addr2line. + cmd := exec.Command("go", "tool", "addr2line", bin) + in, err := cmd.StdinPipe() + if err != nil { + return fmt.Errorf("failed to pipe addr2line stdin: %v", err) + } + cmd.Stderr = os.Stderr + out, err := cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("failed to pipe addr2line stdout: %v", err) + } + err = cmd.Start() + if err != nil { + return fmt.Errorf("failed to start addr2line: %v", err) + } + outb := bufio.NewReader(out) + + // Write all pcs to addr2line. + // Need to copy pcs to an array, because map iteration order is non-deterministic. + var pcArray []uint64 + for pc := range pcs { + pcArray = append(pcArray, pc) + _, err := fmt.Fprintf(in, "0x%x\n", pc-1) + if err != nil { + return fmt.Errorf("failed to write to addr2line: %v", err) + } + } + in.Close() + + // Read in answers. + for _, pc := range pcArray { + fn, err := outb.ReadString('\n') + if err != nil { + return fmt.Errorf("failed to read from addr2line: %v", err) + } + file, err := outb.ReadString('\n') + if err != nil { + return fmt.Errorf("failed to read from addr2line: %v", err) + } + f := &Frame{PC: pc} + f.Fn = fn[:len(fn)-1] + f.File = file[:len(file)-1] + if colon := strings.LastIndex(f.File, ":"); colon != -1 { + ln, err := strconv.Atoi(f.File[colon+1:]) + if err == nil { + f.File = f.File[:colon] + f.Line = ln + } + } + pcs[pc] = f + } + cmd.Wait() + + // Replace frames in events array. + for _, ev := range events { + for i, f := range ev.Stk { + ev.Stk[i] = pcs[f.PC] + } + } + + return nil +} + // readVal reads unsigned base-128 value from r. func readVal(r io.Reader, off0 int) (v uint64, off int, err error) { off = off0 @@ -637,7 +778,7 @@ func (l eventList) Len() int { } func (l eventList) Less(i, j int) bool { - return l[i].Seq < l[j].Seq + return l[i].seq < l[j].seq } func (l eventList) Swap(i, j int) { @@ -701,46 +842,47 @@ const ( ) var EventDescriptions = [EvCount]struct { - Name string - Stack bool - Args []string + Name string + minVersion int + Stack bool + Args []string }{ - EvNone: {"None", false, []string{}}, - EvBatch: {"Batch", false, []string{"p", "seq", "ticks"}}, - EvFrequency: {"Frequency", false, []string{"freq", "unused"}}, - EvStack: {"Stack", false, []string{"id", "siz"}}, - EvGomaxprocs: {"Gomaxprocs", true, []string{"procs"}}, - EvProcStart: {"ProcStart", false, []string{"thread"}}, - EvProcStop: {"ProcStop", false, []string{}}, - EvGCStart: {"GCStart", true, []string{}}, - EvGCDone: {"GCDone", false, []string{}}, - EvGCScanStart: {"GCScanStart", false, []string{}}, - EvGCScanDone: {"GCScanDone", false, []string{}}, - EvGCSweepStart: {"GCSweepStart", true, []string{}}, - EvGCSweepDone: {"GCSweepDone", false, []string{}}, - EvGoCreate: {"GoCreate", true, []string{"g", "stack"}}, - EvGoStart: {"GoStart", false, []string{"g"}}, - EvGoEnd: {"GoEnd", false, []string{}}, - EvGoStop: {"GoStop", true, []string{}}, - EvGoSched: {"GoSched", true, []string{}}, - EvGoPreempt: {"GoPreempt", true, []string{}}, - EvGoSleep: {"GoSleep", true, []string{}}, - EvGoBlock: {"GoBlock", true, []string{}}, - EvGoUnblock: {"GoUnblock", true, []string{"g"}}, - EvGoBlockSend: {"GoBlockSend", true, []string{}}, - EvGoBlockRecv: {"GoBlockRecv", true, []string{}}, - EvGoBlockSelect: {"GoBlockSelect", true, []string{}}, - EvGoBlockSync: {"GoBlockSync", true, []string{}}, - EvGoBlockCond: {"GoBlockCond", true, []string{}}, - EvGoBlockNet: {"GoBlockNet", true, []string{}}, - EvGoSysCall: {"GoSysCall", true, []string{}}, - EvGoSysExit: {"GoSysExit", false, []string{"g", "seq", "ts"}}, - EvGoSysBlock: {"GoSysBlock", false, []string{}}, - EvGoWaiting: {"GoWaiting", false, []string{"g"}}, - EvGoInSyscall: {"GoInSyscall", false, []string{"g"}}, - EvHeapAlloc: {"HeapAlloc", false, []string{"mem"}}, - EvNextGC: {"NextGC", false, []string{"mem"}}, - EvTimerGoroutine: {"TimerGoroutine", false, []string{"g", "unused"}}, - EvFutileWakeup: {"FutileWakeup", false, []string{}}, - EvString: {"String", false, []string{}}, + EvNone: {"None", 1005, false, []string{}}, + EvBatch: {"Batch", 1005, false, []string{"p", "seq", "ticks"}}, + EvFrequency: {"Frequency", 1005, false, []string{"freq", "unused"}}, + EvStack: {"Stack", 1005, false, []string{"id", "siz"}}, + EvGomaxprocs: {"Gomaxprocs", 1005, true, []string{"procs"}}, + EvProcStart: {"ProcStart", 1005, false, []string{"thread"}}, + EvProcStop: {"ProcStop", 1005, false, []string{}}, + EvGCStart: {"GCStart", 1005, true, []string{}}, + EvGCDone: {"GCDone", 1005, false, []string{}}, + EvGCScanStart: {"GCScanStart", 1005, false, []string{}}, + EvGCScanDone: {"GCScanDone", 1005, false, []string{}}, + EvGCSweepStart: {"GCSweepStart", 1005, true, []string{}}, + EvGCSweepDone: {"GCSweepDone", 1005, false, []string{}}, + EvGoCreate: {"GoCreate", 1005, true, []string{"g", "stack"}}, + EvGoStart: {"GoStart", 1005, false, []string{"g"}}, + EvGoEnd: {"GoEnd", 1005, false, []string{}}, + EvGoStop: {"GoStop", 1005, true, []string{}}, + EvGoSched: {"GoSched", 1005, true, []string{}}, + EvGoPreempt: {"GoPreempt", 1005, true, []string{}}, + EvGoSleep: {"GoSleep", 1005, true, []string{}}, + EvGoBlock: {"GoBlock", 1005, true, []string{}}, + EvGoUnblock: {"GoUnblock", 1005, true, []string{"g"}}, + EvGoBlockSend: {"GoBlockSend", 1005, true, []string{}}, + EvGoBlockRecv: {"GoBlockRecv", 1005, true, []string{}}, + EvGoBlockSelect: {"GoBlockSelect", 1005, true, []string{}}, + EvGoBlockSync: {"GoBlockSync", 1005, true, []string{}}, + EvGoBlockCond: {"GoBlockCond", 1005, true, []string{}}, + EvGoBlockNet: {"GoBlockNet", 1005, true, []string{}}, + EvGoSysCall: {"GoSysCall", 1005, true, []string{}}, + EvGoSysExit: {"GoSysExit", 1005, false, []string{"g", "seq", "ts"}}, + EvGoSysBlock: {"GoSysBlock", 1005, false, []string{}}, + EvGoWaiting: {"GoWaiting", 1005, false, []string{"g"}}, + EvGoInSyscall: {"GoInSyscall", 1005, false, []string{"g"}}, + EvHeapAlloc: {"HeapAlloc", 1005, false, []string{"mem"}}, + EvNextGC: {"NextGC", 1005, false, []string{"mem"}}, + EvTimerGoroutine: {"TimerGoroutine", 1005, false, []string{"g", "unused"}}, + EvFutileWakeup: {"FutileWakeup", 1005, false, []string{}}, + EvString: {"String", 1007, false, []string{}}, } diff --git a/src/internal/trace/parser_test.go b/src/internal/trace/parser_test.go index fecefc4053..db8d2a30ce 100644 --- a/src/internal/trace/parser_test.go +++ b/src/internal/trace/parser_test.go @@ -5,6 +5,9 @@ package trace import ( + "bytes" + "io/ioutil" + "path/filepath" "strings" "testing" ) @@ -22,9 +25,63 @@ func TestCorruptedInputs(t *testing.T) { "go 1.5 trace\x00\x00\x00\x00\xc3\x0200", } for _, data := range tests { - events, err := Parse(strings.NewReader(data)) + events, err := Parse(strings.NewReader(data), "") if err == nil || events != nil { - t.Fatalf("no error on input: %q\n", data) + t.Fatalf("no error on input: %q", data) + } + } +} + +func TestParseCanned(t *testing.T) { + files, err := ioutil.ReadDir("./testdata") + if err != nil { + t.Fatalf("failed to read ./testdata: %v", err) + } + for _, f := range files { + data, err := ioutil.ReadFile(filepath.Join("./testdata", f.Name())) + if err != nil { + t.Fatalf("failed to read input file: %v", err) + } + _, err = Parse(bytes.NewReader(data), "") + switch { + case strings.HasSuffix(f.Name(), "_good"): + if err != nil { + t.Errorf("failed to parse good trace %v: %v", f.Name(), err) + } + case strings.HasSuffix(f.Name(), "_unordered"): + if err != ErrTimeOrder { + t.Errorf("unordered trace is not detected %v: %v", f.Name(), err) + } + default: + t.Errorf("unknown input file suffix: %v", f.Name()) + } + } +} + +func TestParseVersion(t *testing.T) { + tests := map[string]int{ + "go 1.5 trace\x00\x00\x00\x00": 1005, + "go 1.7 trace\x00\x00\x00\x00": 1007, + "go 1.10 trace\x00\x00\x00": 1010, + "go 1.25 trace\x00\x00\x00": 1025, + "go 1.234 trace\x00\x00": 1234, + "go 1.2345 trace\x00": -1, + "go 0.0 trace\x00\x00\x00\x00": -1, + "go a.b trace\x00\x00\x00\x00": -1, + } + for header, ver := range tests { + ver1, err := parseHeader([]byte(header)) + if ver == -1 { + if err == nil { + t.Fatalf("no error on input: %q, version %v", header, ver1) + } + } else { + if err != nil { + t.Fatalf("failed to parse: %q (%v)", header, err) + } + if ver != ver1 { + t.Fatalf("wrong version: %v, want %v, input: %q", ver1, ver, header) + } } } } diff --git a/src/internal/trace/testdata/http_1_5_good b/src/internal/trace/testdata/http_1_5_good new file mode 100644 index 0000000000..0736cae674 Binary files /dev/null and b/src/internal/trace/testdata/http_1_5_good differ diff --git a/src/internal/trace/testdata/stress_1_5_good b/src/internal/trace/testdata/stress_1_5_good new file mode 100644 index 0000000000..c5055ebd19 Binary files /dev/null and b/src/internal/trace/testdata/stress_1_5_good differ diff --git a/src/internal/trace/testdata/stress_1_5_unordered b/src/internal/trace/testdata/stress_1_5_unordered new file mode 100644 index 0000000000..11f7d745ca Binary files /dev/null and b/src/internal/trace/testdata/stress_1_5_unordered differ diff --git a/src/internal/trace/testdata/stress_start_stop_1_5_good b/src/internal/trace/testdata/stress_start_stop_1_5_good new file mode 100644 index 0000000000..72a887b844 Binary files /dev/null and b/src/internal/trace/testdata/stress_start_stop_1_5_good differ diff --git a/src/runtime/trace.go b/src/runtime/trace.go index f54e5e0a7e..dcf534549a 100644 --- a/src/runtime/trace.go +++ b/src/runtime/trace.go @@ -356,7 +356,7 @@ func ReadTrace() []byte { trace.headerWritten = true trace.lockOwner = nil unlock(&trace.lock) - return []byte("go 1.5 trace\x00\x00\x00\x00") + return []byte("go 1.7 trace\x00\x00\x00\x00") } // Wait for new data. if trace.fullHead == 0 && !trace.shutdown { diff --git a/src/runtime/trace/trace_test.go b/src/runtime/trace/trace_test.go index b787a2fc27..d10e928a66 100644 --- a/src/runtime/trace/trace_test.go +++ b/src/runtime/trace/trace_test.go @@ -52,7 +52,7 @@ func TestTrace(t *testing.T) { t.Fatalf("failed to start tracing: %v", err) } Stop() - _, err := trace.Parse(buf) + _, err := trace.Parse(buf, "") if err == trace.ErrTimeOrder { t.Skipf("skipping trace: %v", err) } @@ -62,7 +62,7 @@ func TestTrace(t *testing.T) { } func parseTrace(t *testing.T, r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc, error) { - events, err := trace.Parse(r) + events, err := trace.Parse(r, "") if err == trace.ErrTimeOrder { t.Skipf("skipping trace: %v", err) } -- cgit v1.3 From e79fef8e55f8a893c65f41566bbec10339d45dec Mon Sep 17 00:00:00 2001 From: Shawn Walker-Salas Date: Thu, 7 Apr 2016 15:26:57 -0700 Subject: cmd/link: external linking can fail on Solaris 11.2+ Workaround external linking issues encountered on Solaris 11.2+ due to the go.o object file being created with a NULL STT_FILE symtab entry by using a placeholder name. Fixes #14957 Change-Id: I89c501b4c548469f3c878151947d35588057982b Reviewed-on: https://go-review.googlesource.com/21636 Reviewed-by: David Crawshaw --- src/cmd/link/internal/ld/symtab.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index c7c2733507..ae0b17c259 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -198,7 +198,9 @@ func Asmelfsym() { // Some linkers will add a FILE sym if one is not present. // Avoid having the working directory inserted into the symbol table. - putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0) + // It is added with a name to avoid problems with external linking + // encountered on some versions of Solaris. See issue #14957. + putelfsyment(putelfstr("go.go"), 0, 0, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0) numelfsym++ elfbind = STB_LOCAL -- cgit v1.3 From cabb1402568ae7d05d9d5adf56953a4792085a81 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 11 Apr 2016 05:12:43 +0000 Subject: net/http: add ServerContextKey to let a handler access its Server Fixes #12438 Updates #15229 (to decide context key variable naming convention) Change-Id: I3ba423e91b689e232143247d044495a12c97a7d2 Reviewed-on: https://go-review.googlesource.com/21829 Reviewed-by: Andrew Gerrand Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/net/http/http.go | 8 ++++++++ src/net/http/serve_test.go | 17 +++++++++++++++++ src/net/http/server.go | 21 +++++++++++++++------ 3 files changed, 40 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/net/http/http.go b/src/net/http/http.go index a40b23dfdb..7484348f52 100644 --- a/src/net/http/http.go +++ b/src/net/http/http.go @@ -10,3 +10,11 @@ const maxInt64 = 1<<63 - 1 // TODO(bradfitz): move common stuff here. The other files have accumulated // generic http stuff in random places. + +// contextKey is a value for use with context.WithValue. It's used as +// a pointer so it fits in an interface{} without allocation. +type contextKey struct { + name string +} + +func (k *contextKey) String() string { return "net/http context value " + k.name } diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index e0094234de..5f206b1873 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -4056,6 +4056,23 @@ func TestServerRequestContextCancel_ConnClose(t *testing.T) { } } +func TestServerContext_ServerContextKey(t *testing.T) { + defer afterTest(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + ctx := r.Context() + got := ctx.Value(ServerContextKey) + if _, ok := got.(*Server); !ok { + t.Errorf("context value = %T; want *http.Server") + } + })) + defer ts.Close() + res, err := Get(ts.URL) + if err != nil { + t.Fatal(err) + } + res.Body.Close() +} + func BenchmarkClientServer(b *testing.B) { b.ReportAllocs() b.StopTimer() diff --git a/src/net/http/server.go b/src/net/http/server.go index 7a6950aee4..deb170c334 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -147,6 +147,14 @@ type CloseNotifier interface { CloseNotify() <-chan bool } +var ( + // ServerContextKey is a context key. It can be used in HTTP + // handlers with context.WithValue to access the server that + // started the handler. The associated value will be of + // type *Server. + ServerContextKey = &contextKey{"http-server"} +) + // A conn represents the server side of an HTTP connection. type conn struct { // server is the server on which the connection arrived. @@ -1402,7 +1410,7 @@ type badRequestError string func (e badRequestError) Error() string { return "Bad Request: " + string(e) } // Serve a new connection. -func (c *conn) serve() { +func (c *conn) serve(ctx context.Context) { c.remoteAddr = c.rwc.RemoteAddr().String() defer func() { if err := recover(); err != nil { @@ -1445,10 +1453,7 @@ func (c *conn) serve() { c.bufr = newBufioReader(c.r) c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10) - // TODO: allow changing base context? can't imagine concrete - // use cases yet. - baseCtx := context.Background() - ctx, cancelCtx := context.WithCancel(baseCtx) + ctx, cancelCtx := context.WithCancel(ctx) defer cancelCtx() for { @@ -2151,6 +2156,10 @@ func (srv *Server) Serve(l net.Listener) error { if err := srv.setupHTTP2(); err != nil { return err } + // TODO: allow changing base context? can't imagine concrete + // use cases yet. + baseCtx := context.Background() + ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { rw, e := l.Accept() if e != nil { @@ -2172,7 +2181,7 @@ func (srv *Server) Serve(l net.Listener) error { tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew) // before Serve can return - go c.serve() + go c.serve(ctx) } } -- cgit v1.3 From cd6b2b7451c6feb277d38820f41f81ce4a036af2 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Sun, 10 Apr 2016 20:01:49 -0400 Subject: cmd/internal/obj/s390x: add MULHD instruction Emulate 64-bit signed high multiplication ((a*b)>>64). To do this we use the 64-bit unsigned high multiplication method and then fix the result as shown in Hacker's Delight 2nd ed., chapter 8-3. Required to enable some division optimizations. Change-Id: I9194f428e09d3d029cb1afb4715cd5424b5d922e Reviewed-on: https://go-review.googlesource.com/21774 Reviewed-by: Bill O'Farrell Run-TryBot: Brad Fitzpatrick Reviewed-by: Brad Fitzpatrick --- src/cmd/asm/internal/asm/testdata/s390x.s | 4 ++++ src/cmd/internal/obj/s390x/a.out.go | 1 + src/cmd/internal/obj/s390x/anames.go | 1 + src/cmd/internal/obj/s390x/asmz.go | 34 +++++++++++++++++++++++-------- 4 files changed, 32 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/asm/internal/asm/testdata/s390x.s b/src/cmd/asm/internal/asm/testdata/s390x.s index 148cd2eaae..f1dc9aff2d 100644 --- a/src/cmd/asm/internal/asm/testdata/s390x.s +++ b/src/cmd/asm/internal/asm/testdata/s390x.s @@ -61,6 +61,10 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0 MULLW R6, R7, R8 // b9040087b91c0086 MULLW $8192, R6 // c26000002000 MULLW $8192, R6, R7 // b9040076c27000002000 + MULHD R9, R8 // b90400b8b98600a9ebb9003f000ab98000b8b90900abebb8003f000ab98000b9b9e9b08a + MULHD R7, R2, R1 // b90400b2b98600a7ebb7003f000ab98000b2b90900abebb2003f000ab98000b7b9e9b01a + MULHDU R3, R4 // b90400b4b98600a3b904004a + MULHDU R5, R6, R7 // b90400b6b98600a5b904007a DIVD R1, R2 // b90400b2b90d00a1b904002b DIVD R1, R2, R3 // b90400b2b90d00a1b904003b DIVW R4, R5 // b90400b5b91d00a4b904005b diff --git a/src/cmd/internal/obj/s390x/a.out.go b/src/cmd/internal/obj/s390x/a.out.go index 2cb03ae603..e7256d1d41 100644 --- a/src/cmd/internal/obj/s390x/a.out.go +++ b/src/cmd/internal/obj/s390x/a.out.go @@ -218,6 +218,7 @@ const ( ADIVDU AMULLW AMULLD + AMULHD AMULHDU ASUB ASUBC diff --git a/src/cmd/internal/obj/s390x/anames.go b/src/cmd/internal/obj/s390x/anames.go index e79a147a90..62dd181eda 100644 --- a/src/cmd/internal/obj/s390x/anames.go +++ b/src/cmd/internal/obj/s390x/anames.go @@ -17,6 +17,7 @@ var Anames = []string{ "DIVDU", "MULLW", "MULLD", + "MULHD", "MULHDU", "SUB", "SUBC", diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go index bccd7c3bd8..cf3b11424b 100644 --- a/src/cmd/internal/obj/s390x/asmz.go +++ b/src/cmd/internal/obj/s390x/asmz.go @@ -150,6 +150,8 @@ var optab = []Optab{ Optab{AMULLW, C_REG, C_NONE, C_NONE, C_REG, 2, 0}, Optab{AMULLW, C_LCON, C_REG, C_NONE, C_REG, 22, 0}, Optab{AMULLW, C_LCON, C_NONE, C_NONE, C_REG, 22, 0}, + Optab{AMULHD, C_REG, C_NONE, C_NONE, C_REG, 4, 0}, + Optab{AMULHD, C_REG, C_REG, C_NONE, C_REG, 4, 0}, Optab{ASUBC, C_REG, C_REG, C_NONE, C_REG, 10, 0}, Optab{ASUBC, C_REG, C_NONE, C_NONE, C_REG, 10, 0}, Optab{ADIVW, C_REG, C_REG, C_NONE, C_REG, 2, 0}, @@ -793,10 +795,11 @@ func buildop(ctxt *obj.Link) { case ADIVW: opset(AADDE, r) opset(AMULLD, r) - opset(AMULHDU, r) opset(ADIVD, r) opset(ADIVDU, r) opset(ADIVWU, r) + case AMULHD: + opset(AMULHDU, r) case AMOVBZ: opset(AMOVH, r) opset(AMOVHZ, r) @@ -2580,8 +2583,6 @@ func asmout(ctxt *obj.Link, asm *[]byte) { opcode = op_MSGFR case AMULLD: opcode = op_MSGR - case AMULHDU: - opcode = op_MLGR case ADIVW: opcode = op_DSGFR case ADIVWU: @@ -2628,11 +2629,6 @@ func asmout(ctxt *obj.Link, asm *[]byte) { zRRE(opcode, REGTMP, uint32(p.From.Reg), asm) zRRE(op_LGR, uint32(p.To.Reg), REGTMP2, asm) - case AMULHDU: - zRRE(op_LGR, REGTMP2, uint32(r), asm) - zRRE(opcode, REGTMP, uint32(p.From.Reg), asm) - zRRE(op_LGR, uint32(p.To.Reg), REGTMP, asm) - case AFADD, AFADDS: if r == int(p.To.Reg) { zRRE(opcode, uint32(p.To.Reg), uint32(p.From.Reg), asm) @@ -2695,6 +2691,28 @@ func asmout(ctxt *obj.Link, asm *[]byte) { zRIL(_a, op_IIHF, uint32(p.To.Reg), uint32(v>>32), asm) } + case 4: // multiply high (a*b)>>64 + r := p.Reg + if r == 0 { + r = p.To.Reg + } + zRRE(op_LGR, REGTMP2, uint32(r), asm) + zRRE(op_MLGR, REGTMP, uint32(p.From.Reg), asm) + switch p.As { + case AMULHDU: + // Unsigned: move result into correct register. + zRRE(op_LGR, uint32(p.To.Reg), REGTMP, asm) + case AMULHD: + // Signed: need to convert result. + // See Hacker's Delight 8-3. + zRSY(op_SRAG, REGTMP2, uint32(p.From.Reg), 0, 63, asm) + zRRE(op_NGR, REGTMP2, uint32(r), asm) + zRRE(op_SGR, REGTMP, REGTMP2, asm) + zRSY(op_SRAG, REGTMP2, uint32(r), 0, 63, asm) + zRRE(op_NGR, REGTMP2, uint32(p.From.Reg), asm) + zRRF(op_SGRK, REGTMP2, 0, uint32(p.To.Reg), REGTMP, asm) + } + case 5: // syscall zI(op_SVC, 0, asm) -- cgit v1.3 From 7166dfe0c11bd25b962d7f691ea1be857842dfbf Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 11 Apr 2016 11:54:07 -0700 Subject: image/color: add YCbCrToRGB benchmark Change-Id: I9ba76d5b0861a901415fdceccaf2f5caa2facb7f Reviewed-on: https://go-review.googlesource.com/21837 Run-TryBot: Josh Bleecher Snyder Reviewed-by: Brad Fitzpatrick --- src/image/color/ycbcr_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src') diff --git a/src/image/color/ycbcr_test.go b/src/image/color/ycbcr_test.go index f5e7cbf335..1b110691a2 100644 --- a/src/image/color/ycbcr_test.go +++ b/src/image/color/ycbcr_test.go @@ -171,3 +171,26 @@ func TestPalette(t *testing.T) { t.Errorf("got %v, want %v", got, want) } } + +var sinkr, sinkg, sinkb uint8 + +func BenchmarkYCbCrToRGB(b *testing.B) { + // YCbCrToRGB does saturating arithmetic. + // Low, middle, and high values can take + // different paths through the generated code. + b.Run("0", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkr, sinkg, sinkb = YCbCrToRGB(0, 0, 0) + } + }) + b.Run("128", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkr, sinkg, sinkb = YCbCrToRGB(128, 128, 128) + } + }) + b.Run("255", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkr, sinkg, sinkb = YCbCrToRGB(255, 255, 255) + } + }) +} -- cgit v1.3 From 7f53391f6b7f2387a5ed00398d34b046c321966f Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 11 Apr 2016 12:22:26 -0700 Subject: cmd/compile: fix -N build The decomposer of builtin types is confused by having structs still around from the user-type decomposer. They're all dead though, so just enabling a deadcode pass fixes things. Change-Id: I2df6bc7e829be03eabfd24c8dda1bff96f3d7091 Reviewed-on: https://go-review.googlesource.com/21839 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/compile.go | 6 +++--- src/cmd/compile/internal/ssa/decompose.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index d52ae9c6da..b4215f119e 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -230,9 +230,9 @@ var passes = [...]pass{ {name: "early deadcode", fn: deadcode}, // remove generated dead code to avoid doing pointless work during opt {name: "short circuit", fn: shortcircuit}, {name: "decompose user", fn: decomposeUser, required: true}, - {name: "opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules - {name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values - {name: "opt deadcode", fn: deadcode}, // remove any blocks orphaned during opt + {name: "opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules + {name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values + {name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt {name: "generic cse", fn: cse}, {name: "phiopt", fn: phiopt}, {name: "nilcheckelim", fn: nilcheckelim}, diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index de02885d76..53116ba593 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -79,7 +79,7 @@ func decomposeBuiltIn(f *Func) { } delete(f.NamedValues, name) case t.Size() > f.Config.IntSize: - f.Unimplementedf("undecomposed named type %s", t) + f.Unimplementedf("undecomposed named type %s %s", name, t) default: newNames = append(newNames, name) } -- cgit v1.3 From 7e40627a0e595aa321efaf44f8507b678ee5eb1e Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 11 Apr 2016 13:17:52 -0700 Subject: cmd/compile: zero all three argstorage slots These changes were missed when going from 2 to 3 argstorage slots. https://go-review.googlesource.com/20296/ Change-Id: I930a307bb0b695bf1ae088030c9bbb6d14ca31d2 Reviewed-on: https://go-review.googlesource.com/21841 Reviewed-by: Brad Fitzpatrick Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/ssa/func.go | 10 ++++++++-- src/cmd/compile/internal/ssa/value.go | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 6e47b7f19c..8dd75f6093 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -284,7 +284,10 @@ func (b *Block) NewValue2I(line int32, op Op, t Type, auxint int64, arg0, arg1 * func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *Value { v := b.Func.newValue(op, t, b, line) v.AuxInt = 0 - v.Args = []*Value{arg0, arg1, arg2} + v.Args = v.argstorage[:3] + v.argstorage[0] = arg0 + v.argstorage[1] = arg1 + v.argstorage[2] = arg2 arg0.Uses++ arg1.Uses++ arg2.Uses++ @@ -295,7 +298,10 @@ func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *V func (b *Block) NewValue3I(line int32, op Op, t Type, auxint int64, arg0, arg1, arg2 *Value) *Value { v := b.Func.newValue(op, t, b, line) v.AuxInt = auxint - v.Args = []*Value{arg0, arg1, arg2} + v.Args = v.argstorage[:3] + v.argstorage[0] = arg0 + v.argstorage[1] = arg1 + v.argstorage[2] = arg2 arg0.Uses++ arg1.Uses++ arg2.Uses++ diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index fd4eb64db1..6c364ad932 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -185,6 +185,7 @@ func (v *Value) resetArgs() { } v.argstorage[0] = nil v.argstorage[1] = nil + v.argstorage[2] = nil v.Args = v.argstorage[:0] } -- cgit v1.3 From 32efa16c3d63dd630e2190a8c0f30c0a941f6fd7 Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 1 Apr 2016 14:51:29 -0400 Subject: cmd/compile: added stats printing to stackalloc This is controlled by the "regalloc" stats flag, since regalloc calls stackalloc. The plan is for this to allow comparison of cheaper stack allocation algorithms with what we have now. Change-Id: Ibf64a780344c69babfcbb328fd6d053ea2e02cfc Reviewed-on: https://go-review.googlesource.com/21393 Run-TryBot: David Chase Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/stackalloc.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go index 1de22dc96e..e3ef66ab1b 100644 --- a/src/cmd/compile/internal/ssa/stackalloc.go +++ b/src/cmd/compile/internal/ssa/stackalloc.go @@ -22,6 +22,13 @@ type stackAllocState struct { names []LocalSlot slots []int used []bool + + nArgSlot, // Number of Values sourced to arg slot + nNotNeed, // Number of Values not needing a stack slot + nNamedSlot, // Number of Values using a named stack slot + nReuse, // Number of values reusing a stack slot + nAuto, // Number of autos allocated for stack slots. + nSelfInterfere int32 // Number of self-interferences } func newStackAllocState(f *Func) *stackAllocState { @@ -54,6 +61,7 @@ func putStackAllocState(s *stackAllocState) { s.f.Config.stackAllocState = s s.f = nil s.live = nil + s.nArgSlot, s.nNotNeed, s.nNamedSlot, s.nReuse, s.nAuto, s.nSelfInterfere = 0, 0, 0, 0, 0, 0 } type stackValState struct { @@ -75,6 +83,13 @@ func stackalloc(f *Func, spillLive [][]ID) [][]ID { defer putStackAllocState(s) s.stackalloc() + if f.pass.stats > 0 { + f.logStat("stack_alloc_stats", + s.nArgSlot, "arg_slots", s.nNotNeed, "slot_not_needed", + s.nNamedSlot, "named_slots", s.nAuto, "auto_slots", + s.nReuse, "reused_slots", s.nSelfInterfere, "self_interfering") + } + return s.live } @@ -170,9 +185,11 @@ func (s *stackAllocState) stackalloc() { for _, b := range f.Blocks { for _, v := range b.Values { if !s.values[v.ID].needSlot { + s.nNotNeed++ continue } if v.Op == OpArg { + s.nArgSlot++ continue // already picked } @@ -190,12 +207,14 @@ func (s *stackAllocState) stackalloc() { if h != nil && h.(LocalSlot).N == name.N && h.(LocalSlot).Off == name.Off { // A variable can interfere with itself. // It is rare, but but it can happen. + s.nSelfInterfere++ goto noname } } if f.pass.debug > stackDebug { fmt.Printf("stackalloc %s to %s\n", v, name.Name()) } + s.nNamedSlot++ f.setHome(v, name) continue } @@ -217,11 +236,13 @@ func (s *stackAllocState) stackalloc() { var i int for i = 0; i < len(locs); i++ { if !used[i] { + s.nReuse++ break } } // If there is no unused stack slot, allocate a new one. if i == len(locs) { + s.nAuto++ locs = append(locs, LocalSlot{N: f.Config.fe.Auto(v.Type), Type: v.Type, Off: 0}) locations[v.Type] = locs } -- cgit v1.3 From a4650a2111b2bb826ca64a13bdad9c96e3095e47 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Sun, 10 Apr 2016 09:44:17 -0700 Subject: cmd/compile: avoid write barrier in append fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are writing the result of an append back to the same slice, we don’t need a write barrier on the fast path. This re-implements an optimization that was present in the old backend. Updates #14921 Fixes #14969 Sample code: var x []byte func p() { x = append(x, 1, 2, 3) } Before: "".p t=1 size=224 args=0x0 locals=0x48 0x0000 00000 (append.go:21) TEXT "".p(SB), $72-0 0x0000 00000 (append.go:21) MOVQ (TLS), CX 0x0009 00009 (append.go:21) CMPQ SP, 16(CX) 0x000d 00013 (append.go:21) JLS 199 0x0013 00019 (append.go:21) SUBQ $72, SP 0x0017 00023 (append.go:21) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0017 00023 (append.go:21) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0017 00023 (append.go:19) MOVQ "".x+16(SB), CX 0x001e 00030 (append.go:19) MOVQ "".x(SB), DX 0x0025 00037 (append.go:19) MOVQ "".x+8(SB), BX 0x002c 00044 (append.go:19) MOVQ BX, "".autotmp_0+64(SP) 0x0031 00049 (append.go:22) LEAQ 3(BX), BP 0x0035 00053 (append.go:22) CMPQ BP, CX 0x0038 00056 (append.go:22) JGT $0, 131 0x003a 00058 (append.go:22) MOVB $1, (DX)(BX*1) 0x003e 00062 (append.go:22) MOVB $2, 1(DX)(BX*1) 0x0043 00067 (append.go:22) MOVB $3, 2(DX)(BX*1) 0x0048 00072 (append.go:22) MOVQ BP, "".x+8(SB) 0x004f 00079 (append.go:22) MOVQ CX, "".x+16(SB) 0x0056 00086 (append.go:22) MOVL runtime.writeBarrier(SB), AX 0x005c 00092 (append.go:22) TESTB AL, AL 0x005e 00094 (append.go:22) JNE $0, 108 0x0060 00096 (append.go:22) MOVQ DX, "".x(SB) 0x0067 00103 (append.go:23) ADDQ $72, SP 0x006b 00107 (append.go:23) RET 0x006c 00108 (append.go:22) LEAQ "".x(SB), CX 0x0073 00115 (append.go:22) MOVQ CX, (SP) 0x0077 00119 (append.go:22) MOVQ DX, 8(SP) 0x007c 00124 (append.go:22) PCDATA $0, $0 0x007c 00124 (append.go:22) CALL runtime.writebarrierptr(SB) 0x0081 00129 (append.go:23) JMP 103 0x0083 00131 (append.go:22) LEAQ type.[]uint8(SB), AX 0x008a 00138 (append.go:22) MOVQ AX, (SP) 0x008e 00142 (append.go:22) MOVQ DX, 8(SP) 0x0093 00147 (append.go:22) MOVQ BX, 16(SP) 0x0098 00152 (append.go:22) MOVQ CX, 24(SP) 0x009d 00157 (append.go:22) MOVQ BP, 32(SP) 0x00a2 00162 (append.go:22) PCDATA $0, $0 0x00a2 00162 (append.go:22) CALL runtime.growslice(SB) 0x00a7 00167 (append.go:22) MOVQ 40(SP), DX 0x00ac 00172 (append.go:22) MOVQ 48(SP), AX 0x00b1 00177 (append.go:22) MOVQ 56(SP), CX 0x00b6 00182 (append.go:22) ADDQ $3, AX 0x00ba 00186 (append.go:19) MOVQ "".autotmp_0+64(SP), BX 0x00bf 00191 (append.go:22) MOVQ AX, BP 0x00c2 00194 (append.go:22) JMP 58 0x00c7 00199 (append.go:22) NOP 0x00c7 00199 (append.go:21) CALL runtime.morestack_noctxt(SB) 0x00cc 00204 (append.go:21) JMP 0 After: "".p t=1 size=208 args=0x0 locals=0x48 0x0000 00000 (append.go:21) TEXT "".p(SB), $72-0 0x0000 00000 (append.go:21) MOVQ (TLS), CX 0x0009 00009 (append.go:21) CMPQ SP, 16(CX) 0x000d 00013 (append.go:21) JLS 191 0x0013 00019 (append.go:21) SUBQ $72, SP 0x0017 00023 (append.go:21) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0017 00023 (append.go:21) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0017 00023 (append.go:19) MOVQ "".x+16(SB), CX 0x001e 00030 (append.go:19) MOVQ "".x+8(SB), DX 0x0025 00037 (append.go:19) MOVQ DX, "".autotmp_0+64(SP) 0x002a 00042 (append.go:19) MOVQ "".x(SB), BX 0x0031 00049 (append.go:22) LEAQ 3(DX), BP 0x0035 00053 (append.go:22) MOVQ BP, "".x+8(SB) 0x003c 00060 (append.go:22) CMPQ BP, CX 0x003f 00063 (append.go:22) JGT $0, 84 0x0041 00065 (append.go:22) MOVB $1, (BX)(DX*1) 0x0045 00069 (append.go:22) MOVB $2, 1(BX)(DX*1) 0x004a 00074 (append.go:22) MOVB $3, 2(BX)(DX*1) 0x004f 00079 (append.go:23) ADDQ $72, SP 0x0053 00083 (append.go:23) RET 0x0054 00084 (append.go:22) LEAQ type.[]uint8(SB), AX 0x005b 00091 (append.go:22) MOVQ AX, (SP) 0x005f 00095 (append.go:22) MOVQ BX, 8(SP) 0x0064 00100 (append.go:22) MOVQ DX, 16(SP) 0x0069 00105 (append.go:22) MOVQ CX, 24(SP) 0x006e 00110 (append.go:22) MOVQ BP, 32(SP) 0x0073 00115 (append.go:22) PCDATA $0, $0 0x0073 00115 (append.go:22) CALL runtime.growslice(SB) 0x0078 00120 (append.go:22) MOVQ 40(SP), CX 0x007d 00125 (append.go:22) MOVQ 56(SP), AX 0x0082 00130 (append.go:22) MOVQ AX, "".x+16(SB) 0x0089 00137 (append.go:22) MOVL runtime.writeBarrier(SB), AX 0x008f 00143 (append.go:22) TESTB AL, AL 0x0091 00145 (append.go:22) JNE $0, 168 0x0093 00147 (append.go:22) MOVQ CX, "".x(SB) 0x009a 00154 (append.go:22) MOVQ "".x(SB), BX 0x00a1 00161 (append.go:19) MOVQ "".autotmp_0+64(SP), DX 0x00a6 00166 (append.go:22) JMP 65 0x00a8 00168 (append.go:22) LEAQ "".x(SB), DX 0x00af 00175 (append.go:22) MOVQ DX, (SP) 0x00b3 00179 (append.go:22) MOVQ CX, 8(SP) 0x00b8 00184 (append.go:22) PCDATA $0, $0 0x00b8 00184 (append.go:22) CALL runtime.writebarrierptr(SB) 0x00bd 00189 (append.go:22) JMP 154 0x00bf 00191 (append.go:22) NOP 0x00bf 00191 (append.go:21) CALL runtime.morestack_noctxt(SB) 0x00c4 00196 (append.go:21) JMP 0 Change-Id: I77a41ad3a22557a4bb4654de7d6d24a029efe34a Reviewed-on: https://go-review.googlesource.com/21813 Run-TryBot: Josh Bleecher Snyder TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/ssa.go | 123 +++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 5ee370395b..beb68b0385 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -683,14 +683,27 @@ func (s *state) stmt(n *Node) { // Evaluate RHS. rhs := n.Right - if rhs != nil && (rhs.Op == OSTRUCTLIT || rhs.Op == OARRAYLIT) { - // All literals with nonzero fields have already been - // rewritten during walk. Any that remain are just T{} - // or equivalents. Use the zero value. - if !iszero(rhs) { - Fatalf("literal with nonzero value in SSA: %v", rhs) + if rhs != nil { + switch rhs.Op { + case OSTRUCTLIT, OARRAYLIT: + // All literals with nonzero fields have already been + // rewritten during walk. Any that remain are just T{} + // or equivalents. Use the zero value. + if !iszero(rhs) { + Fatalf("literal with nonzero value in SSA: %v", rhs) + } + rhs = nil + case OAPPEND: + // If we're writing the result of an append back to the same slice, + // handle it specially to avoid write barriers on the fast (non-growth) path. + // If the slice can be SSA'd, it'll be on the stack, + // so there will be no write barriers, + // so there's no need to attempt to prevent them. + if samesafeexpr(n.Left, rhs.List.First()) && !s.canSSA(n.Left) { + s.append(rhs, true) + return + } } - rhs = nil } var r *ssa.Value needwb := n.Op == OASWB && rhs != nil @@ -709,11 +722,11 @@ func (s *state) stmt(n *Node) { } } if rhs != nil && rhs.Op == OAPPEND { - // Yuck! The frontend gets rid of the write barrier, but we need it! - // At least, we need it in the case where growslice is called. - // TODO: Do the write barrier on just the growslice branch. + // The frontend gets rid of the write barrier to enable the special OAPPEND + // handling above, but since this is not a special case, we need it. // TODO: just add a ptr graying to the end of growslice? - // TODO: check whether we need to do this for ODOTTYPE and ORECV also. + // TODO: check whether we need to provide special handling and a write barrier + // for ODOTTYPE and ORECV also. // They get similar wb-removal treatment in walk.go:OAS. needwb = true } @@ -2079,7 +2092,7 @@ func (s *state) expr(n *Node) *ssa.Value { return s.newValue1(ssa.OpGetG, n.Type, s.mem()) case OAPPEND: - return s.exprAppend(n) + return s.append(n, false) default: s.Unimplementedf("unhandled expr %s", opnames[n.Op]) @@ -2087,25 +2100,57 @@ func (s *state) expr(n *Node) *ssa.Value { } } -// exprAppend converts an OAPPEND node n to an ssa.Value, adds it to s, and returns the Value. -func (s *state) exprAppend(n *Node) *ssa.Value { - // append(s, e1, e2, e3). Compile like: +// append converts an OAPPEND node to SSA. +// If inplace is false, it converts the OAPPEND expression n to an ssa.Value, +// adds it to s, and returns the Value. +// If inplace is true, it writes the result of the OAPPEND expression n +// back to the slice being appended to, and returns nil. +// inplace MUST be set to false if the slice can be SSA'd. +func (s *state) append(n *Node, inplace bool) *ssa.Value { + // If inplace is false, process as expression "append(s, e1, e2, e3)": + // // ptr, len, cap := s // newlen := len + 3 - // if newlen > s.cap { + // if newlen > cap { // ptr, len, cap = growslice(s, newlen) // newlen = len + 3 // recalculate to avoid a spill // } + // // with write barriers, if needed: + // *(ptr+len) = e1 + // *(ptr+len+1) = e2 + // *(ptr+len+2) = e3 + // return makeslice(ptr, newlen, cap) + // + // + // If inplace is true, process as statement "s = append(s, e1, e2, e3)": + // + // a := &s + // ptr, len, cap := s + // newlen := len + 3 + // *a.len = newlen // store newlen immediately to avoid a spill + // if newlen > cap { + // newptr, _, newcap = growslice(ptr, len, cap, newlen) + // *a.cap = newcap // write before ptr to avoid a spill + // *a.ptr = newptr // with write barrier + // } + // // with write barriers, if needed: // *(ptr+len) = e1 // *(ptr+len+1) = e2 // *(ptr+len+2) = e3 - // makeslice(ptr, newlen, cap) et := n.Type.Elem() pt := Ptrto(et) // Evaluate slice - slice := s.expr(n.List.First()) + sn := n.List.First() // the slice node is the first in the list + + var slice, addr *ssa.Value + if inplace { + addr = s.addr(sn, false) + slice = s.newValue2(ssa.OpLoad, n.Type, addr, s.mem()) + } else { + slice = s.expr(sn) + } // Allocate new blocks grow := s.f.NewBlock(ssa.BlockPlain) @@ -2117,10 +2162,20 @@ func (s *state) exprAppend(n *Node) *ssa.Value { l := s.newValue1(ssa.OpSliceLen, Types[TINT], slice) c := s.newValue1(ssa.OpSliceCap, Types[TINT], slice) nl := s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], l, s.constInt(Types[TINT], nargs)) + + if inplace { + lenaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(Array_nel), addr) + s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, lenaddr, nl, s.mem()) + } + cmp := s.newValue2(s.ssaOp(OGT, Types[TINT]), Types[TBOOL], nl, c) s.vars[&ptrVar] = p - s.vars[&newlenVar] = nl - s.vars[&capVar] = c + + if !inplace { + s.vars[&newlenVar] = nl + s.vars[&capVar] = c + } + b := s.endBlock() b.Kind = ssa.BlockIf b.Likely = ssa.BranchUnlikely @@ -2134,9 +2189,18 @@ func (s *state) exprAppend(n *Node) *ssa.Value { r := s.rtcall(growslice, true, []*Type{pt, Types[TINT], Types[TINT]}, taddr, p, l, c, nl) - s.vars[&ptrVar] = r[0] - s.vars[&newlenVar] = s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], r[1], s.constInt(Types[TINT], nargs)) - s.vars[&capVar] = r[2] + if inplace { + capaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(Array_cap), addr) + s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, capaddr, r[2], s.mem()) + s.insertWBstore(pt, addr, r[0], n.Lineno, 0) + // load the value we just stored to avoid having to spill it + s.vars[&ptrVar] = s.newValue2(ssa.OpLoad, pt, addr, s.mem()) + } else { + s.vars[&ptrVar] = r[0] + s.vars[&newlenVar] = s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], r[1], s.constInt(Types[TINT], nargs)) + s.vars[&capVar] = r[2] + } + b = s.endBlock() b.AddEdgeTo(assign) @@ -2156,9 +2220,11 @@ func (s *state) exprAppend(n *Node) *ssa.Value { } } - p = s.variable(&ptrVar, pt) // generates phi for ptr - nl = s.variable(&newlenVar, Types[TINT]) // generates phi for nl - c = s.variable(&capVar, Types[TINT]) // generates phi for cap + p = s.variable(&ptrVar, pt) // generates phi for ptr + if !inplace { + nl = s.variable(&newlenVar, Types[TINT]) // generates phi for nl + c = s.variable(&capVar, Types[TINT]) // generates phi for cap + } p2 := s.newValue2(ssa.OpPtrIndex, pt, p, l) // TODO: just one write barrier call for all of these writes? // TODO: maybe just one writeBarrier.enabled check? @@ -2179,10 +2245,13 @@ func (s *state) exprAppend(n *Node) *ssa.Value { } } - // make result delete(s.vars, &ptrVar) + if inplace { + return nil + } delete(s.vars, &newlenVar) delete(s.vars, &capVar) + // make result return s.newValue3(ssa.OpSliceMake, n.Type, p, nl, c) } -- cgit v1.3 From d1feddb7ae6389ee4175ee85b7168cf58a04b952 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 4 Apr 2016 13:22:34 -0700 Subject: cmd/vet: improve documentation for flags, slightly The way that -all works was unclear from the documentation and made worse by recent changes to the flag package. Improve matters by making the help message say "default true" for the tests that do default to true, and tweak some of the wording. Before: Usage of vet: vet [flags] directory... vet [flags] files... # Must be a single package For more information run go doc cmd/vet Flags: -all enable all non-experimental checks (default unset) -asmdecl check assembly against Go declarations (default unset) ... After: Usage of vet: vet [flags] directory... vet [flags] files... # Must be a single package By default, -all is set and all non-experimental checks are run. For more information run go doc cmd/vet Flags: -all enable all non-experimental checks (default true) -asmdecl check assembly against Go declarations (default true) ... Change-Id: Ie94b27381a9ad2382a10a7542a93bce1d59fa8f5 Reviewed-on: https://go-review.googlesource.com/21495 Reviewed-by: Andrew Gerrand --- src/cmd/vet/doc.go | 11 +++++------ src/cmd/vet/main.go | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go index 2b5e8fcb59..c697f3bc36 100644 --- a/src/cmd/vet/doc.go +++ b/src/cmd/vet/doc.go @@ -29,11 +29,10 @@ check every possible problem and depends on unreliable heuristics so it should be used as guidance only, not as a firm indicator of program correctness. -By default all checks are performed. If any flags are explicitly set -to true, only those tests are run. Conversely, if any flag is -explicitly set to false, only those tests are disabled. -Thus -printf=true runs the printf check, -printf=false runs all checks -except the printf check. +By default the -all flag is set so all checks are performed. +If any flags are explicitly set to true, only those tests are run. Conversely, if +any flag is explicitly set to false, only those tests are disabled. Thus -printf=true +runs the printf check, -printf=false runs all checks except the printf check. Available checks: @@ -194,4 +193,4 @@ These flags configure the behavior of vet: -shadowstrict Whether to be strict about shadowing; can be noisy. */ -package main // import "golang.org/x/tools/cmd/vet" +package main diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go index a2142dcabb..8212a14f03 100644 --- a/src/cmd/vet/main.go +++ b/src/cmd/vet/main.go @@ -100,7 +100,7 @@ func (ts *triState) Set(value string) error { func (ts *triState) String() string { switch *ts { case unset: - return "unset" + return "true" // An unset flag will be set by -all, so defaults to true. case setTrue: return "true" case setFalse: @@ -164,6 +164,7 @@ func Usage() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n") fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n") + fmt.Fprintf(os.Stderr, "By default, -all is set and all non-experimental checks are run.\n") fmt.Fprintf(os.Stderr, "For more information run\n") fmt.Fprintf(os.Stderr, "\tgo doc cmd/vet\n\n") fmt.Fprintf(os.Stderr, "Flags:\n") -- cgit v1.3 From 501ddf7189cf97ef27eb870ad134a312f80ae585 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 11 Apr 2016 18:16:55 +0000 Subject: context: attempt to deflake timing tests Passes on OpenBSD now when running it with -count=500. Presumably this will also fix the same problems seen on FreeBSD and Windows. Fixes #15158 Change-Id: I86451c901613dfa5ecff0c2ecc516527a3c011b3 Reviewed-on: https://go-review.googlesource.com/21840 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Andrew Gerrand --- src/context/context_test.go | 65 ++++++++++++++++++----------------------- src/context/withtimeout_test.go | 4 +-- 2 files changed, 30 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/context/context_test.go b/src/context/context_test.go index 0616704dd8..aa26161d2b 100644 --- a/src/context/context_test.go +++ b/src/context/context_test.go @@ -6,7 +6,6 @@ package context import ( "fmt" - "internal/testenv" "math/rand" "runtime" "strings" @@ -230,64 +229,55 @@ func TestChildFinishesFirst(t *testing.T) { } } -func testDeadline(c Context, wait time.Duration, t *testing.T) { +func testDeadline(c Context, name string, failAfter time.Duration, t *testing.T) { select { - case <-time.After(wait): - t.Fatalf("context should have timed out") + case <-time.After(failAfter): + t.Fatalf("%s: context should have timed out", name) case <-c.Done(): } if e := c.Err(); e != DeadlineExceeded { - t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) + t.Errorf("%s: c.Err() == %v; want %v", name, e, DeadlineExceeded) } } func TestDeadline(t *testing.T) { - if runtime.GOOS == "openbsd" { - testenv.SkipFlaky(t, 15158) - } - c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + c, _ := WithDeadline(Background(), time.Now().Add(50*time.Millisecond)) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) } - testDeadline(c, 200*time.Millisecond, t) + testDeadline(c, "WithDeadline", time.Second, t) - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + c, _ = WithDeadline(Background(), time.Now().Add(50*time.Millisecond)) o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) + testDeadline(o, "WithDeadline+otherContext", time.Second, t) - c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + c, _ = WithDeadline(Background(), time.Now().Add(50*time.Millisecond)) o = otherContext{c} - c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) - testDeadline(c, 200*time.Millisecond, t) + c, _ = WithDeadline(o, time.Now().Add(4*time.Second)) + testDeadline(c, "WithDeadline+otherContext+WithDeadline", 2*time.Second, t) } func TestTimeout(t *testing.T) { - if runtime.GOOS == "openbsd" { - testenv.SkipFlaky(t, 15158) - } - c, _ := WithTimeout(Background(), 100*time.Millisecond) + c, _ := WithTimeout(Background(), 50*time.Millisecond) if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { t.Errorf("c.String() = %q want prefix %q", got, prefix) } - testDeadline(c, 200*time.Millisecond, t) + testDeadline(c, "WithTimeout", time.Second, t) - c, _ = WithTimeout(Background(), 100*time.Millisecond) + c, _ = WithTimeout(Background(), 50*time.Millisecond) o := otherContext{c} - testDeadline(o, 200*time.Millisecond, t) + testDeadline(o, "WithTimeout+otherContext", time.Second, t) - c, _ = WithTimeout(Background(), 100*time.Millisecond) + c, _ = WithTimeout(Background(), 50*time.Millisecond) o = otherContext{c} - c, _ = WithTimeout(o, 300*time.Millisecond) - testDeadline(c, 200*time.Millisecond, t) + c, _ = WithTimeout(o, 3*time.Second) + testDeadline(c, "WithTimeout+otherContext+WithTimeout", 2*time.Second, t) } func TestCanceledTimeout(t *testing.T) { - if runtime.GOOS == "openbsd" { - testenv.SkipFlaky(t, 15158) - } - c, _ := WithTimeout(Background(), 200*time.Millisecond) + c, _ := WithTimeout(Background(), time.Second) o := otherContext{c} - c, cancel := WithTimeout(o, 400*time.Millisecond) + c, cancel := WithTimeout(o, 2*time.Second) cancel() time.Sleep(100 * time.Millisecond) // let cancelation propagate select { @@ -398,9 +388,9 @@ func TestAllocs(t *testing.T) { gccgoLimit: 8, }, { - desc: "WithTimeout(bg, 100*time.Millisecond)", + desc: "WithTimeout(bg, 5*time.Millisecond)", f: func() { - c, cancel := WithTimeout(bg, 100*time.Millisecond) + c, cancel := WithTimeout(bg, 5*time.Millisecond) cancel() <-c.Done() }, @@ -414,7 +404,11 @@ func TestAllocs(t *testing.T) { // TOOD(iant): Remove this when gccgo does do escape analysis. limit = test.gccgoLimit } - if n := testing.AllocsPerRun(100, test.f); n > limit { + numRuns := 100 + if testing.Short() { + numRuns = 10 + } + if n := testing.AllocsPerRun(numRuns, test.f); n > limit { t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) } } @@ -494,9 +488,6 @@ func TestLayersTimeout(t *testing.T) { } func testLayers(t *testing.T, seed int64, testTimeout bool) { - if runtime.GOOS == "openbsd" { - testenv.SkipFlaky(t, 15158) - } rand.Seed(seed) errorf := func(format string, a ...interface{}) { t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) @@ -549,7 +540,7 @@ func testLayers(t *testing.T, seed int64, testTimeout bool) { if testTimeout { select { case <-ctx.Done(): - case <-time.After(timeout + 100*time.Millisecond): + case <-time.After(timeout + time.Second): errorf("ctx should have timed out") } checkValues("after timeout") diff --git a/src/context/withtimeout_test.go b/src/context/withtimeout_test.go index 3ab6fc347f..2aea303bed 100644 --- a/src/context/withtimeout_test.go +++ b/src/context/withtimeout_test.go @@ -13,9 +13,9 @@ import ( func ExampleWithTimeout() { // Pass a context with a timeout to tell a blocking function that it // should abandon its work after the timeout elapses. - ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) + ctx, _ := context.WithTimeout(context.Background(), 50*time.Millisecond) select { - case <-time.After(200 * time.Millisecond): + case <-time.After(1 * time.Second): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) // prints "context deadline exceeded" -- cgit v1.3 From a119f88f2c52e6eb969f51c5bf610d6f105348a3 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Mon, 11 Apr 2016 18:39:46 -0400 Subject: go/importer: make For("gccgo", nil) not panic Apparently we forgot to test this. Fixes #15092 Change-Id: I33d4fef0f659dfbdfc1ebf8401e96610c8215592 Reviewed-on: https://go-review.googlesource.com/21860 Reviewed-by: Robert Griesemer --- src/go/importer/importer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/go/importer/importer.go b/src/go/importer/importer.go index 560b853c39..f655bc1e92 100644 --- a/src/go/importer/importer.go +++ b/src/go/importer/importer.go @@ -31,7 +31,7 @@ func For(compiler string, lookup Lookup) types.Importer { return make(gcimports) case "gccgo": - if lookup == nil { + if lookup != nil { panic("gccgo importer for custom import path lookup not yet implemented") } -- cgit v1.3 From 1f54410a61e2242285e366a5580943f78fbff741 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Thu, 7 Apr 2016 17:19:29 +0900 Subject: net: make IP.{String,MarshalText} return helpful information on address error This change makes String and MarshalText methods of IP return a hexadecial form of IP with no punctuation as part of error notification. It doesn't affect the existing behavior of ParseIP. Also fixes bad shadowing in ipToSockaddr and makes use of reserved IP address blocks for documnetation. Fixes #15052. Updates #15228. Change-Id: I9e9ecce308952ed5683066c3d1bb6a7b36458c65 Reviewed-on: https://go-review.googlesource.com/21642 Reviewed-by: Brad Fitzpatrick --- src/net/error_test.go | 49 +++++++++++++++++++ src/net/ip.go | 24 +++++---- src/net/ip_test.go | 126 +++++++++++++++++++++++++++++++++++++++++------- src/net/ipsock_posix.go | 36 ++++++++------ 4 files changed, 192 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/net/error_test.go b/src/net/error_test.go index c3a4d32382..40f235c924 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -206,6 +206,55 @@ func TestProtocolDialError(t *testing.T) { } } +func TestDialAddrError(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + + for _, tt := range []struct { + network string + lit string + addr *TCPAddr + }{ + {"tcp4", "::1", nil}, + {"tcp4", "", &TCPAddr{IP: IPv6loopback}}, + // We don't test the {"tcp6", "byte sequence", nil} + // case for now because there is no easy way to + // control name resolution. + {"tcp6", "", &TCPAddr{IP: IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}}}, + } { + var err error + var c Conn + if tt.lit != "" { + c, err = Dial(tt.network, JoinHostPort(tt.lit, "0")) + } else { + c, err = DialTCP(tt.network, nil, tt.addr) + } + if err == nil { + c.Close() + t.Errorf("%s %q/%v: should fail", tt.network, tt.lit, tt.addr) + continue + } + if perr := parseDialError(err); perr != nil { + t.Error(perr) + continue + } + aerr, ok := err.(*OpError).Err.(*AddrError) + if !ok { + t.Errorf("%s %q/%v: should be AddrError: %v", tt.network, tt.lit, tt.addr, err) + continue + } + want := tt.lit + if tt.lit == "" { + want = tt.addr.IP.String() + } + if aerr.Addr != want { + t.Fatalf("%s: got %q; want %q", tt.network, aerr.Addr, want) + } + } +} + var listenErrorTests = []struct { network, address string }{ diff --git a/src/net/ip.go b/src/net/ip.go index 0501f5a6a3..a2361bbdbf 100644 --- a/src/net/ip.go +++ b/src/net/ip.go @@ -252,9 +252,11 @@ func (ip IP) Mask(mask IPMask) IP { } // String returns the string form of the IP address ip. -// If the address is an IPv4 address, the string representation -// is dotted decimal ("74.125.19.99"). Otherwise the representation -// is IPv6 ("2001:4860:0:2001::68"). +// It returns one of 4 forms: +// - "", if ip has length 0 +// - dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address +// - IPv6 ("2001:db9::1"), if ip is a valid IPv6 address +// - the hexadecimal form of ip, without punctuation, if no other cases apply func (ip IP) String() string { p := ip @@ -270,7 +272,7 @@ func (ip IP) String() string { uitoa(uint(p4[3])) } if len(p) != IPv6len { - return "?" + return hexString(ip) } // Find longest run of zeros. @@ -312,6 +314,14 @@ func (ip IP) String() string { return string(b) } +func hexString(b []byte) string { + s := make([]byte, len(b)*2) + for i, tn := range b { + s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf] + } + return string(s) +} + // ipEmptyString is like ip.String except that it returns // an empty string when ip is unset. func ipEmptyString(ip IP) string { @@ -426,11 +436,7 @@ func (m IPMask) String() string { if len(m) == 0 { return "" } - buf := make([]byte, len(m)*2) - for i, b := range m { - buf[i*2], buf[i*2+1] = hexDigit[b>>4], hexDigit[b&0xf] - } - return string(buf) + return hexString(m) } func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) { diff --git a/src/net/ip_test.go b/src/net/ip_test.go index 2006085818..87c12133c3 100644 --- a/src/net/ip_test.go +++ b/src/net/ip_test.go @@ -5,6 +5,7 @@ package net import ( + "bytes" "reflect" "runtime" "testing" @@ -124,30 +125,119 @@ func TestMarshalEmptyIP(t *testing.T) { } var ipStringTests = []struct { - in IP - out string // see RFC 5952 + in IP // see RFC 791 and RFC 4291 + str string // see RFC 791, RFC 4291 and RFC 5952 + byt []byte + error }{ - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, "2001:db8::123:12:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1}, "2001:db8:0:1:0:1:0:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0}, "2001:db8:1:0:1:0:1:0"}, - {IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001::1:0:0:1"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, "2001:db8:0:0:1::"}, - {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1:0:0:1"}, - {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"}, - {IPv4(192, 168, 0, 1), "192.168.0.1"}, - {nil, ""}, + // IPv4 address + { + IP{192, 0, 2, 1}, + "192.0.2.1", + []byte("192.0.2.1"), + nil, + }, + { + IP{0, 0, 0, 0}, + "0.0.0.0", + []byte("0.0.0.0"), + nil, + }, + + // IPv4-mapped IPv6 address + { + IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 0, 2, 1}, + "192.0.2.1", + []byte("192.0.2.1"), + nil, + }, + { + IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0}, + "0.0.0.0", + []byte("0.0.0.0"), + nil, + }, + + // IPv6 address + { + IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0x1, 0x23, 0, 0x12, 0, 0x1}, + "2001:db8::123:12:1", + []byte("2001:db8::123:12:1"), + nil, + }, + { + IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1}, + "2001:db8::1", + []byte("2001:db8::1"), + nil, + }, + { + IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1}, + "2001:db8:0:1:0:1:0:1", + []byte("2001:db8:0:1:0:1:0:1"), + nil, + }, + { + IP{0x20, 0x1, 0xd, 0xb8, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0}, + "2001:db8:1:0:1:0:1:0", + []byte("2001:db8:1:0:1:0:1:0"), + nil, + }, + { + IP{0x20, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, + "2001::1:0:0:1", + []byte("2001::1:0:0:1"), + nil, + }, + { + IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, + "2001:db8:0:0:1::", + []byte("2001:db8:0:0:1::"), + nil, + }, + { + IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, + "2001:db8::1:0:0:1", + []byte("2001:db8::1:0:0:1"), + nil, + }, + { + IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0xa, 0, 0xb, 0, 0xc, 0, 0xd}, + "2001:db8::a:b:c:d", + []byte("2001:db8::a:b:c:d"), + nil, + }, + { + IPv6unspecified, + "::", + []byte("::"), + nil, + }, + + // IP wildcard equivalent address in Dial/Listen API + { + nil, + "", + nil, + nil, + }, + + // Opaque byte sequence + { + IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, + "0123456789abcdef", + nil, + &AddrError{Err: "invalid IP address", Addr: "0123456789abcdef"}, + }, } func TestIPString(t *testing.T) { for _, tt := range ipStringTests { - if tt.in != nil { - if out := tt.in.String(); out != tt.out { - t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.out) - } + if out := tt.in.String(); out != tt.str { + t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.str) } - if out, err := tt.in.MarshalText(); string(out) != tt.out || err != nil { - t.Errorf("IP.MarshalText(%v) = %q, %v, want %q, nil", tt.in, out, err, tt.out) + if out, err := tt.in.MarshalText(); !bytes.Equal(out, tt.byt) || !reflect.DeepEqual(err, tt.error) { + t.Errorf("IP.MarshalText(%v) = %v, %v, want %v, %v", tt.in, out, err, tt.byt, tt.error) } } } diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go index 28cdb210ae..644964e78d 100644 --- a/src/net/ipsock_posix.go +++ b/src/net/ipsock_posix.go @@ -4,8 +4,6 @@ // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows -// Internet protocol family sockets for POSIX - package net import ( @@ -52,7 +50,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { }{ // IPv6 communication capability {laddr: TCPAddr{IP: ParseIP("::1")}, value: 1}, - // IPv6 IPv4-mapped address communication capability + // IPv4-mapped IPv6 address communication capability {laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0}, } var supps [2]bool @@ -154,8 +152,6 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family return syscall.AF_INET6, false } -// Internet sockets (TCP, UDP, IP) - func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, cancel <-chan struct{}) (fd *netFD, err error) { family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) return socket(net, family, sotype, proto, ipv6only, laddr, raddr, deadline, cancel) @@ -167,27 +163,35 @@ func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, e if len(ip) == 0 { ip = IPv4zero } - if ip = ip.To4(); ip == nil { + ip4 := ip.To4() + if ip4 == nil { return nil, &AddrError{Err: "non-IPv4 address", Addr: ip.String()} } sa := &syscall.SockaddrInet4{Port: port} - copy(sa.Addr[:], ip) + copy(sa.Addr[:], ip4) return sa, nil case syscall.AF_INET6: - if len(ip) == 0 { - ip = IPv6zero - } - // IPv4 callers use 0.0.0.0 to mean "announce on any available address". - // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", - // which it refuses to do. Rewrite to the IPv6 unspecified address. - if ip.Equal(IPv4zero) { + // In general, an IP wildcard address, which is either + // "0.0.0.0" or "::", means the entire IP addressing + // space. For some historical reason, it is used to + // specify "any available address" on some operations + // of IP node. + // + // When the IP node supports IPv4-mapped IPv6 address, + // we allow an listener to listen to the wildcard + // address of both IP addressing spaces by specifying + // IPv6 wildcard address. + if len(ip) == 0 || ip.Equal(IPv4zero) { ip = IPv6zero } - if ip = ip.To16(); ip == nil { + // We accept any IPv6 address including IPv4-mapped + // IPv6 address. + ip6 := ip.To16() + if ip6 == nil { return nil, &AddrError{Err: "non-IPv6 address", Addr: ip.String()} } sa := &syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneToInt(zone))} - copy(sa.Addr[:], ip) + copy(sa.Addr[:], ip6) return sa, nil } return nil, &AddrError{Err: "invalid address family", Addr: ip.String()} -- cgit v1.3 From c9b66bb355ebbc6a26ee511e996cba4da3e1d644 Mon Sep 17 00:00:00 2001 From: Dan Peterson Date: Mon, 11 Apr 2016 21:47:37 -0300 Subject: io: document WriteString calls Write exactly once Fixes #13849 Change-Id: Idd7f06b547a0179fe15571807a8c48b7c3b78d7c Reviewed-on: https://go-review.googlesource.com/21852 Reviewed-by: Brad Fitzpatrick --- src/io/io.go | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/io/io.go b/src/io/io.go index 023473c79b..c36ec2afbb 100644 --- a/src/io/io.go +++ b/src/io/io.go @@ -291,6 +291,7 @@ type stringWriter interface { // WriteString writes the contents of the string s to w, which accepts a slice of bytes. // If w implements a WriteString method, it is invoked directly. +// Otherwise, w.Write is called exactly once. func WriteString(w Writer, s string) (n int, err error) { if sw, ok := w.(stringWriter); ok { return sw.WriteString(s) -- cgit v1.3 From 73a0185ad390761f21a3407858fcccc6a11c0858 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Mon, 15 Feb 2016 11:51:54 -0500 Subject: crypto/tls: Enforce that version and cipher match on resume. Per RFC 5246, 7.4.1.3: cipher_suite The single cipher suite selected by the server from the list in ClientHello.cipher_suites. For resumed sessions, this field is the value from the state of the session being resumed. The specifications are not very clearly written about resuming sessions at the wrong version (i.e. is the TLS 1.0 notion of "session" the same type as the TLS 1.1 notion of "session"?). But every other implementation enforces this check and not doing so has some odd semantics. Change-Id: I6234708bd02b636c25139d83b0d35381167e5cad Reviewed-on: https://go-review.googlesource.com/21153 Reviewed-by: Adam Langley --- src/crypto/tls/handshake_client.go | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index f71509b25a..9517320f6c 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -556,6 +556,16 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { return false, nil } + if hs.session.vers != c.vers { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: server resumed a session with a different version") + } + + if hs.session.cipherSuite != hs.suite.id { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: server resumed a session with a different cipher suite") + } + // Restore masterSecret and peerCerts from previous state hs.masterSecret = hs.session.masterSecret c.peerCertificates = hs.session.serverCertificates -- cgit v1.3 From e569b10ebaed8fbf27d0b55886b6a81d635ddbc7 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 12 Apr 2016 00:09:05 +0000 Subject: src: split nacltest.bash into naclmake.bash and keep nacltest.bash Needed by the build system to shard tests. nacl was the last unsharded builder. (I considered also adding a -make-only flag to nacltest.bash, but that wouldn't fail fast when the file didn't exist.) Updates #15242 Change-Id: I6afc1c1fe4268ab98c0724b5764c67d3784caebe Reviewed-on: https://go-review.googlesource.com/21851 Reviewed-by: Andrew Gerrand Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/naclmake.bash | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/nacltest.bash | 38 ++++---------------------------------- 2 files changed, 52 insertions(+), 34 deletions(-) create mode 100755 src/naclmake.bash (limited to 'src') diff --git a/src/naclmake.bash b/src/naclmake.bash new file mode 100755 index 0000000000..046f50aa87 --- /dev/null +++ b/src/naclmake.bash @@ -0,0 +1,48 @@ +#!/bin/bash +# Copyright 2016 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# naclmake.bash builds runs make.bash for nacl, but not does run any +# tests. This is used by the continuous build. + +# Assumes that sel_ldr binaries and go_nacl_$GOARCH_exec scripts are in $PATH; +# see ../misc/nacl/README. + +set -e +ulimit -c 0 + +# guess GOARCH if not set +naclGOARCH=$GOARCH +if [ -z "$naclGOARCH" ]; then + case "$(uname -m)" in + x86_64) + naclGOARCH=amd64p32 + ;; + armv7l) # NativeClient on ARM only supports ARMv7A. + naclGOARCH=arm + ;; + i?86) + naclGOARCH=386 + ;; + esac +fi + +unset GOOS GOARCH +if [ ! -f make.bash ]; then + echo 'nacltest.bash must be run from $GOROOT/src' 1>&2 + exit 1 +fi + +# the builder might have set GOROOT_FINAL. +export GOROOT=$(pwd)/.. + +# Build zip file embedded in package syscall. +echo "##### Building fake file system zip for nacl" +rm -f syscall/fstest_nacl.go +GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4} +gobin=$GOROOT_BOOTSTRAP/bin +GOROOT=$GOROOT_BOOTSTRAP $gobin/go run ../misc/nacl/mkzip.go -p syscall -r .. ../misc/nacl/testzip.proto syscall/fstest_nacl.go + +# Run standard build and tests. +GOOS=nacl GOARCH=$naclGOARCH ./make.bash diff --git a/src/nacltest.bash b/src/nacltest.bash index 049aad2ff2..538d6b7e9b 100755 --- a/src/nacltest.bash +++ b/src/nacltest.bash @@ -13,21 +13,7 @@ set -e ulimit -c 0 -# guess GOARCH if not set -naclGOARCH=$GOARCH -if [ -z "$naclGOARCH" ]; then - case "$(uname -m)" in - x86_64) - naclGOARCH=amd64p32 - ;; - armv7l) # NativeClient on ARM only supports ARMv7A. - naclGOARCH=arm - ;; - i?86) - naclGOARCH=386 - ;; - esac -fi +. ./naclmake.bash # Check GOARCH. case "$naclGOARCH" in @@ -59,24 +45,8 @@ if ! which go_nacl_${naclGOARCH}_exec >/dev/null; then exit 1 fi -unset GOOS GOARCH -if [ ! -f make.bash ]; then - echo 'nacltest.bash must be run from $GOROOT/src' 1>&2 - exit 1 -fi - -# the builder might have set GOROOT_FINAL. -export GOROOT=$(pwd)/.. - -# Build zip file embedded in package syscall. -echo "##### Building fake file system zip for nacl" -rm -f syscall/fstest_nacl.go -GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4} -gobin=$GOROOT_BOOTSTRAP/bin -GOROOT=$GOROOT_BOOTSTRAP $gobin/go run ../misc/nacl/mkzip.go -p syscall -r .. ../misc/nacl/testzip.proto syscall/fstest_nacl.go - -# Run standard build and tests. -export PATH=$(pwd)/../misc/nacl:$PATH -GOOS=nacl GOARCH=$naclGOARCH ./all.bash +export PATH=$(pwd)/../bin:$(pwd)/../misc/nacl:$PATH +GOROOT=$(../bin/go env GOROOT) +GOOS=nacl GOARCH=$naclGOARCH go tool dist test --no-rebuild rm -f syscall/fstest_nacl.go -- cgit v1.3 From 07669d2737aa51107a4e54b61d6704f6ad8035b5 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Thu, 7 Apr 2016 08:01:47 +0200 Subject: cmd/compile: cleanup pragcgo Removes dynimport, dynexport, dynlinker cases since they can not be reached due to prefix check for "go:cgo_" in getlinepragma. Replaces the if chains for verb distinction by a switch statement. Replaces fmt.Sprintf by fmt.Sprintln for string concatenation. Removes the more, getimpsym and getquoted functions by introducing a pragmaFields function that partitions a pragma into its components. Adds tests for cgo pragmas. Change-Id: I43c7b9550feb3ddccaff7fb02198a3f994444123 Reviewed-on: https://go-review.googlesource.com/21607 Reviewed-by: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/lex.go | 218 ++++++++++++++------------------ src/cmd/compile/internal/gc/lex_test.go | 79 ++++++++++++ 2 files changed, 173 insertions(+), 124 deletions(-) create mode 100644 src/cmd/compile/internal/gc/lex_test.go (limited to 'src') diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 2dbbd9276b..4b95bb7124 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -44,6 +44,10 @@ func isDigit(c rune) bool { return '0' <= c && c <= '9' } +func isQuoted(s string) bool { + return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' +} + func plan9quote(s string) string { if s == "" { return "''" @@ -853,15 +857,6 @@ func internString(b []byte) string { return s } -func more(pp *string) bool { - p := *pp - for p != "" && isSpace(rune(p[0])) { - p = p[1:] - } - *pp = p - return p != "" -} - // read and interpret syntax that looks like // //line parse.y:15 // as a discontinuity in sequential line numbers. @@ -887,7 +882,7 @@ func (l *lexer) getlinepragma() rune { text := strings.TrimSuffix(lexbuf.String(), "\r") if strings.HasPrefix(text, "go:cgo_") { - pragcgo(text) + pragcgobuf += pragcgo(text) } verb := text @@ -991,139 +986,114 @@ func (l *lexer) getlinepragma() rune { return c } -func getimpsym(pp *string) string { - more(pp) // skip spaces - p := *pp - if p == "" || p[0] == '"' { - return "" - } - i := 0 - for i < len(p) && !isSpace(rune(p[i])) && p[i] != '"' { - i++ - } - sym := p[:i] - *pp = p[i:] - return sym -} - -func getquoted(pp *string) (string, bool) { - more(pp) // skip spaces - p := *pp - if p == "" || p[0] != '"' { - return "", false - } - p = p[1:] - i := strings.Index(p, `"`) - if i < 0 { - return "", false - } - *pp = p[i+1:] - return p[:i], true -} - -// Copied nearly verbatim from the C compiler's #pragma parser. -// TODO: Rewrite more cleanly once the compiler is written in Go. -func pragcgo(text string) { - var q string +func pragcgo(text string) string { + f := pragmaFields(text) - if i := strings.Index(text, " "); i >= 0 { - text, q = text[:i], text[i:] - } + verb := f[0][3:] // skip "go:" + switch verb { + case "cgo_export_static", "cgo_export_dynamic": + switch { + case len(f) == 2 && !isQuoted(f[1]): + local := plan9quote(f[1]) + return fmt.Sprintln(verb, local) - verb := text[3:] // skip "go:" + case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): + local := plan9quote(f[1]) + remote := plan9quote(f[2]) + return fmt.Sprintln(verb, local, remote) - if verb == "cgo_dynamic_linker" || verb == "dynlinker" { - p, ok := getquoted(&q) - if !ok { - Yyerror("usage: //go:cgo_dynamic_linker \"path\"") - return + default: + Yyerror(`usage: //go:%s local [remote]`, verb) } - pragcgobuf += fmt.Sprintf("cgo_dynamic_linker %v\n", plan9quote(p)) - return + case "cgo_import_dynamic": + switch { + case len(f) == 2 && !isQuoted(f[1]): + local := plan9quote(f[1]) + return fmt.Sprintln(verb, local) - } + case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): + local := plan9quote(f[1]) + remote := plan9quote(f[2]) + return fmt.Sprintln(verb, local, remote) - if verb == "dynexport" { - verb = "cgo_export_dynamic" - } - if verb == "cgo_export_static" || verb == "cgo_export_dynamic" { - local := getimpsym(&q) - var remote string - if local == "" { - goto err2 - } - if !more(&q) { - pragcgobuf += fmt.Sprintf("%s %v\n", verb, plan9quote(local)) - return - } + case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]): + local := plan9quote(f[1]) + remote := plan9quote(f[2]) + library := plan9quote(strings.Trim(f[3], `"`)) + return fmt.Sprintln(verb, local, remote, library) - remote = getimpsym(&q) - if remote == "" { - goto err2 + default: + Yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`) } - pragcgobuf += fmt.Sprintf("%s %v %v\n", verb, plan9quote(local), plan9quote(remote)) - return - - err2: - Yyerror("usage: //go:%s local [remote]", verb) - return - } + case "cgo_import_static": + switch { + case len(f) == 2 && !isQuoted(f[1]): + local := plan9quote(f[1]) + return fmt.Sprintln(verb, local) - if verb == "cgo_import_dynamic" || verb == "dynimport" { - var ok bool - local := getimpsym(&q) - var p string - var remote string - if local == "" { - goto err3 - } - if !more(&q) { - pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v\n", plan9quote(local)) - return + default: + Yyerror(`usage: //go:cgo_import_static local`) } + case "cgo_dynamic_linker": + switch { + case len(f) == 2 && isQuoted(f[1]): + path := plan9quote(strings.Trim(f[1], `"`)) + return fmt.Sprintln(verb, path) - remote = getimpsym(&q) - if remote == "" { - goto err3 - } - if !more(&q) { - pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v\n", plan9quote(local), plan9quote(remote)) - return + default: + Yyerror(`usage: //go:cgo_dynamic_linker "path"`) } + case "cgo_ldflag": + switch { + case len(f) == 2 && isQuoted(f[1]): + arg := plan9quote(strings.Trim(f[1], `"`)) + return fmt.Sprintln(verb, arg) - p, ok = getquoted(&q) - if !ok { - goto err3 + default: + Yyerror(`usage: //go:cgo_ldflag "arg"`) } - pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v %v\n", plan9quote(local), plan9quote(remote), plan9quote(p)) - return - - err3: - Yyerror("usage: //go:cgo_import_dynamic local [remote [\"library\"]]") - return } + return "" +} - if verb == "cgo_import_static" { - local := getimpsym(&q) - if local == "" || more(&q) { - Yyerror("usage: //go:cgo_import_static local") - return +// pragmaFields is similar to strings.FieldsFunc(s, isSpace) +// but does not split when inside double quoted regions and always +// splits before the start and after the end of a double quoted region. +// pragmaFields does not recognize escaped quotes. If a quote in s is not +// closed the part after the opening quote will not be returned as a field. +func pragmaFields(s string) []string { + var a []string + inQuote := false + fieldStart := -1 // Set to -1 when looking for start of field. + for i, c := range s { + switch { + case c == '"': + if inQuote { + inQuote = false + a = append(a, s[fieldStart:i+1]) + fieldStart = -1 + } else { + inQuote = true + if fieldStart >= 0 { + a = append(a, s[fieldStart:i]) + } + fieldStart = i + } + case !inQuote && isSpace(c): + if fieldStart >= 0 { + a = append(a, s[fieldStart:i]) + fieldStart = -1 + } + default: + if fieldStart == -1 { + fieldStart = i + } } - pragcgobuf += fmt.Sprintf("cgo_import_static %v\n", plan9quote(local)) - return - } - - if verb == "cgo_ldflag" { - p, ok := getquoted(&q) - if !ok { - Yyerror("usage: //go:cgo_ldflag \"arg\"") - return - } - pragcgobuf += fmt.Sprintf("cgo_ldflag %v\n", plan9quote(p)) - return - + if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string. + a = append(a, s[fieldStart:]) } + return a } func (l *lexer) getr() rune { diff --git a/src/cmd/compile/internal/gc/lex_test.go b/src/cmd/compile/internal/gc/lex_test.go new file mode 100644 index 0000000000..9230b30dad --- /dev/null +++ b/src/cmd/compile/internal/gc/lex_test.go @@ -0,0 +1,79 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gc + +import "testing" + +func eq(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + return true +} + +func TestPragmaFields(t *testing.T) { + + var tests = []struct { + in string + want []string + }{ + {"", []string{}}, + {" \t ", []string{}}, + {`""""`, []string{`""`, `""`}}, + {" a'b'c ", []string{"a'b'c"}}, + {"1 2 3 4", []string{"1", "2", "3", "4"}}, + {"\n☺\t☹\n", []string{"☺", "☹"}}, + {`"1 2 " 3 " 4 5"`, []string{`"1 2 "`, `3`, `" 4 5"`}}, + {`"1""2 3""4"`, []string{`"1"`, `"2 3"`, `"4"`}}, + {`12"34"`, []string{`12`, `"34"`}}, + {`12"34 `, []string{`12`}}, + } + + for _, tt := range tests { + got := pragmaFields(tt.in) + if !eq(got, tt.want) { + t.Errorf("pragmaFields(%q) = %v; want %v", tt.in, got, tt.want) + continue + } + } +} + +func TestPragcgo(t *testing.T) { + + var tests = []struct { + in string + want string + }{ + {`go:cgo_export_dynamic local`, "cgo_export_dynamic local\n"}, + {`go:cgo_export_dynamic local remote`, "cgo_export_dynamic local remote\n"}, + {`go:cgo_export_dynamic local' remote'`, "cgo_export_dynamic 'local''' 'remote'''\n"}, + {`go:cgo_export_static local`, "cgo_export_static local\n"}, + {`go:cgo_export_static local remote`, "cgo_export_static local remote\n"}, + {`go:cgo_export_static local' remote'`, "cgo_export_static 'local''' 'remote'''\n"}, + {`go:cgo_import_dynamic local`, "cgo_import_dynamic local\n"}, + {`go:cgo_import_dynamic local remote`, "cgo_import_dynamic local remote\n"}, + {`go:cgo_import_dynamic local remote "library"`, "cgo_import_dynamic local remote library\n"}, + {`go:cgo_import_dynamic local' remote' "lib rary"`, "cgo_import_dynamic 'local''' 'remote''' 'lib rary'\n"}, + {`go:cgo_import_static local`, "cgo_import_static local\n"}, + {`go:cgo_import_static local'`, "cgo_import_static 'local'''\n"}, + {`go:cgo_dynamic_linker "/path/"`, "cgo_dynamic_linker /path/\n"}, + {`go:cgo_dynamic_linker "/p ath/"`, "cgo_dynamic_linker '/p ath/'\n"}, + {`go:cgo_ldflag "arg"`, "cgo_ldflag arg\n"}, + {`go:cgo_ldflag "a rg"`, "cgo_ldflag 'a rg'\n"}, + } + + for _, tt := range tests { + got := pragcgo(tt.in) + if got != tt.want { + t.Errorf("pragcgo(%q) = %q; want %q", tt.in, got, tt.want) + continue + } + } +} -- cgit v1.3 From b6cd6d7d3211bd9030dec4115b6202d93fe570a3 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Thu, 31 Mar 2016 02:01:48 -0700 Subject: cmd/go: fix vcsFromDir returning bad root on Windows Apply golang/tools@5804fef4c0556d3e5e7d2440740a3d7aced7d58b. In the context of cmd/go build tool, import path is a '/'-separated path. This can be inferred from `go help importpath` and `go help packages`. vcsFromDir documentation says on return, root is the import path corresponding to the root of the repository. On Windows and other OSes where os.PathSeparator is not '/', that wasn't true since root would contain characters other than '/', and therefore it wasn't a valid import path corresponding to the root of the repository. Fix that by using filepath.ToSlash. Add test coverage for vcsFromDir, it was previously not tested. It's taken from golang.org/x/tools/go/vcs tests, and modified to improve style. Additionally, remove an unneccessary statement from the documentation "(thus root is a prefix of importPath)". There is no variable importPath that is being referred to (it's possible p.ImportPath was being referred to). Without it, the description of root value matches the documentation of repoRoot.root struct field: // root is the import path corresponding to the root of the // repository root string Rename and change signature of vcsForDir(p *Package) to vcsFromDir(dir, srcRoot string). This is more in sync with the x/tools version. It's also simpler, since vcsFromDir only needs those two values from Package, and nothing more. Change "for" to "from" in name because it's more consistent and clear. Update usage of vcsFromDir to match the new signature, and respect that returned root is a '/'-separated path rather than a os.PathSeparator separated path. Fixes #15040. Updates #7723. Helps #11490. Change-Id: Idf51b9239f57248739daaa200aa1c6e633cb5f7f Reviewed-on: https://go-review.googlesource.com/21345 Reviewed-by: Alex Brainman Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/cmd/go/get.go | 8 ++++---- src/cmd/go/vcs.go | 13 ++++++------- src/cmd/go/vcs_test.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index 7e0045fb1d..b52991a5fc 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -45,7 +45,7 @@ missing packages but does not use it to look for updates to existing packages. Get also accepts build flags to control the installation. See 'go help build'. -When checking out a new package, get creates the target directory +When checking out a new package, get creates the target directory GOPATH/src/. If the GOPATH contains multiple entries, get uses the first one. See 'go help gopath'. @@ -346,7 +346,7 @@ func downloadPackage(p *Package) error { if p.build.SrcRoot != "" { // Directory exists. Look for checkout along path to src. - vcs, rootPath, err = vcsForDir(p) + vcs, rootPath, err = vcsFromDir(p.Dir, p.build.SrcRoot) if err != nil { return err } @@ -354,7 +354,7 @@ func downloadPackage(p *Package) error { // Double-check where it came from. if *getU && vcs.remoteRepo != nil { - dir := filepath.Join(p.build.SrcRoot, rootPath) + dir := filepath.Join(p.build.SrcRoot, filepath.FromSlash(rootPath)) remote, err := vcs.remoteRepo(vcs, dir) if err != nil { return err @@ -401,7 +401,7 @@ func downloadPackage(p *Package) error { p.build.SrcRoot = filepath.Join(list[0], "src") p.build.PkgRoot = filepath.Join(list[0], "pkg") } - root := filepath.Join(p.build.SrcRoot, rootPath) + root := filepath.Join(p.build.SrcRoot, filepath.FromSlash(rootPath)) // If we've considered this repository already, don't do it again. if downloadRootCache[root] { return nil diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index a9663b2185..e3342999fa 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -479,15 +479,14 @@ type vcsPath struct { regexp *regexp.Regexp // cached compiled form of re } -// vcsForDir inspects dir and its parents to determine the +// vcsFromDir inspects dir and its parents to determine the // version control system and code repository to use. // On return, root is the import path -// corresponding to the root of the repository -// (thus root is a prefix of importPath). -func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) { +// corresponding to the root of the repository. +func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) { // Clean and double-check that dir is in (a subdirectory of) srcRoot. - dir := filepath.Clean(p.Dir) - srcRoot := filepath.Clean(p.build.SrcRoot) + dir = filepath.Clean(dir) + srcRoot = filepath.Clean(srcRoot) if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator { return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot) } @@ -496,7 +495,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) { for len(dir) > len(srcRoot) { for _, vcs := range vcsList { if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() { - return vcs, dir[len(srcRoot)+1:], nil + return vcs, filepath.ToSlash(dir[len(srcRoot)+1:]), nil } } diff --git a/src/cmd/go/vcs_test.go b/src/cmd/go/vcs_test.go index 52a534a3a3..d951189459 100644 --- a/src/cmd/go/vcs_test.go +++ b/src/cmd/go/vcs_test.go @@ -6,6 +6,10 @@ package main import ( "internal/testenv" + "io/ioutil" + "os" + "path" + "path/filepath" "testing" ) @@ -128,6 +132,37 @@ func TestRepoRootForImportPath(t *testing.T) { } } +// Test that vcsFromDir correctly inspects a given directory and returns the right VCS and root. +func TestFromDir(t *testing.T) { + tempDir, err := ioutil.TempDir("", "vcstest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tempDir) + + for _, vcs := range vcsList { + dir := filepath.Join(tempDir, "example.com", vcs.name, "."+vcs.cmd) + err := os.MkdirAll(dir, 0755) + if err != nil { + t.Fatal(err) + } + + want := repoRoot{ + vcs: vcs, + root: path.Join("example.com", vcs.name), + } + var got repoRoot + got.vcs, got.root, err = vcsFromDir(dir, tempDir) + if err != nil { + t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) + continue + } + if got.vcs.name != want.vcs.name || got.root != want.root { + t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.root, want.vcs, want.root) + } + } +} + func TestIsSecure(t *testing.T) { tests := []struct { vcs *vcsCmd -- cgit v1.3 From 944a0859b9a16a1951512b82870a31f371d1c417 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 8 Apr 2016 11:53:23 +0200 Subject: internal/trace: fix int overflow in timestamps Fixes #15102 Change-Id: I7fdb6464afd0b7af9b6652051416f0fddd34dc9a Reviewed-on: https://go-review.googlesource.com/21730 Reviewed-by: Austin Clements --- src/internal/trace/parser.go | 4 ++- src/internal/trace/parser_test.go | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go index 82ddb8b6c8..3099b0ffeb 100644 --- a/src/internal/trace/parser.go +++ b/src/internal/trace/parser.go @@ -372,8 +372,10 @@ func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (even return } minTs := events[0].Ts + // Use floating point to avoid integer overflows. + freq := 1e9 / float64(ticksPerSec) for _, ev := range events { - ev.Ts = (ev.Ts - minTs) * 1e9 / ticksPerSec + ev.Ts = int64(float64(ev.Ts-minTs) * freq) // Move timers and syscalls to separate fake Ps. if timerGoid != 0 && ev.G == timerGoid && ev.Type == EvGoUnblock { ev.P = TimerP diff --git a/src/internal/trace/parser_test.go b/src/internal/trace/parser_test.go index db8d2a30ce..337d5a85d7 100644 --- a/src/internal/trace/parser_test.go +++ b/src/internal/trace/parser_test.go @@ -85,3 +85,55 @@ func TestParseVersion(t *testing.T) { } } } + +func TestTimestampOverflow(t *testing.T) { + // Test that parser correctly handles large timestamps (long tracing). + w := newWriter() + w.emit(EvBatch, 0, 0, 0) + w.emit(EvFrequency, 1e9, 0) + for ts := uint64(1); ts < 1e16; ts *= 2 { + w.emit(EvGoCreate, 1, ts, ts, 1, 0) + } + if _, err := Parse(w, ""); err != nil { + t.Fatalf("failed to parse: %v", err) + } +} + +type writer struct { + bytes.Buffer +} + +func newWriter() *writer { + w := new(writer) + w.Write([]byte("go 1.7 trace\x00\x00\x00\x00")) + return w +} + +func (w *writer) emit(typ byte, args ...uint64) { + nargs := byte(len(args)) - 2 + if nargs > 3 { + nargs = 3 + } + buf := []byte{typ | nargs<<6} + if nargs == 3 { + buf = append(buf, 0) + } + for _, a := range args { + buf = appendVarint(buf, a) + } + if nargs == 3 { + buf[1] = byte(len(buf) - 2) + } + n, err := w.Write(buf) + if n != len(buf) || err != nil { + panic("failed to write") + } +} + +func appendVarint(buf []byte, v uint64) []byte { + for ; v >= 0x80; v >>= 7 { + buf = append(buf, 0x80|byte(v)) + } + buf = append(buf, byte(v)) + return buf +} -- cgit v1.3 From a223ccae386449169774597b15a00f2d70addce7 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Mon, 11 Apr 2016 20:23:19 -0400 Subject: cmd/compile/internal/s390x: add s390x support s390x does not require duffzero/duffcopy since it has storage-to-storage instructions that can copy/clear up to 256 bytes at a time. peep contains several new passes to optimize instruction sequences that match s390x instructions such as the compare-and-branch and load/store multiple instructions. copyprop and subprop have been extended to work with moves that require sign/zero extension. This work could be ported to other architectures that do not used sized math however it does add complexity and will probably be rendered unnecessary by ssa in the near future. Change-Id: I1b64b281b452ed82a85655a0df69cb224d2a6941 Reviewed-on: https://go-review.googlesource.com/20873 Run-TryBot: Michael Munday TryBot-Result: Gobot Gobot Reviewed-by: Bill O'Farrell Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/s390x/cgen.go | 178 ++++ src/cmd/compile/internal/s390x/galign.go | 66 ++ src/cmd/compile/internal/s390x/ggen.go | 577 +++++++++++ src/cmd/compile/internal/s390x/gsubr.go | 1115 ++++++++++++++++++++ src/cmd/compile/internal/s390x/peep.go | 1664 ++++++++++++++++++++++++++++++ src/cmd/compile/internal/s390x/prog.go | 179 ++++ src/cmd/compile/internal/s390x/reg.go | 130 +++ src/cmd/compile/main.go | 3 + src/cmd/dist/buildtool.go | 1 + 9 files changed, 3913 insertions(+) create mode 100644 src/cmd/compile/internal/s390x/cgen.go create mode 100644 src/cmd/compile/internal/s390x/galign.go create mode 100644 src/cmd/compile/internal/s390x/ggen.go create mode 100644 src/cmd/compile/internal/s390x/gsubr.go create mode 100644 src/cmd/compile/internal/s390x/peep.go create mode 100644 src/cmd/compile/internal/s390x/prog.go create mode 100644 src/cmd/compile/internal/s390x/reg.go (limited to 'src') diff --git a/src/cmd/compile/internal/s390x/cgen.go b/src/cmd/compile/internal/s390x/cgen.go new file mode 100644 index 0000000000..28bb34e0ef --- /dev/null +++ b/src/cmd/compile/internal/s390x/cgen.go @@ -0,0 +1,178 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s390x + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/s390x" +) + +type direction int + +const ( + _FORWARDS direction = iota + _BACKWARDS +) + +// blockcopy copies w bytes from &n to &res +func blockcopy(n, res *gc.Node, osrc, odst, w int64) { + var dst gc.Node + var src gc.Node + if n.Ullman >= res.Ullman { + gc.Agenr(n, &dst, res) // temporarily use dst + gc.Regalloc(&src, gc.Types[gc.Tptr], nil) + gins(s390x.AMOVD, &dst, &src) + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agen(res, &dst) + } else { + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agenr(res, &dst, res) + gc.Agenr(n, &src, nil) + } + defer gc.Regfree(&src) + defer gc.Regfree(&dst) + + var tmp gc.Node + gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) + defer gc.Regfree(&tmp) + + offset := int64(0) + dir := _FORWARDS + if osrc < odst && odst < osrc+w { + // Reverse. Can't use MVC, fall back onto basic moves. + dir = _BACKWARDS + const copiesPerIter = 2 + if w >= 8*copiesPerIter { + cnt := w - (w % (8 * copiesPerIter)) + ginscon(s390x.AADD, w, &src) + ginscon(s390x.AADD, w, &dst) + + var end gc.Node + gc.Regalloc(&end, gc.Types[gc.Tptr], nil) + p := gins(s390x.ASUB, nil, &end) + p.From.Type = obj.TYPE_CONST + p.From.Offset = cnt + p.Reg = src.Reg + + var label *obj.Prog + for i := 0; i < copiesPerIter; i++ { + offset := int64(-8 * (i + 1)) + p := gins(s390x.AMOVD, &src, &tmp) + p.From.Type = obj.TYPE_MEM + p.From.Offset = offset + if i == 0 { + label = p + } + p = gins(s390x.AMOVD, &tmp, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = offset + } + + ginscon(s390x.ASUB, 8*copiesPerIter, &src) + ginscon(s390x.ASUB, 8*copiesPerIter, &dst) + gins(s390x.ACMP, &src, &end) + gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), label) + gc.Regfree(&end) + + w -= cnt + } else { + offset = w + } + } + + if dir == _FORWARDS && w > 1024 { + // Loop over MVCs + cnt := w - (w % 256) + + var end gc.Node + gc.Regalloc(&end, gc.Types[gc.Tptr], nil) + add := gins(s390x.AADD, nil, &end) + add.From.Type = obj.TYPE_CONST + add.From.Offset = cnt + add.Reg = src.Reg + + mvc := gins(s390x.AMVC, &src, &dst) + mvc.From.Type = obj.TYPE_MEM + mvc.From.Offset = 0 + mvc.To.Type = obj.TYPE_MEM + mvc.To.Offset = 0 + mvc.From3 = new(obj.Addr) + mvc.From3.Type = obj.TYPE_CONST + mvc.From3.Offset = 256 + + ginscon(s390x.AADD, 256, &src) + ginscon(s390x.AADD, 256, &dst) + gins(s390x.ACMP, &src, &end) + gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), mvc) + gc.Regfree(&end) + + w -= cnt + } + + for w > 0 { + cnt := w + // If in reverse we can only do 8, 4, 2 or 1 bytes at a time. + if dir == _BACKWARDS { + switch { + case cnt >= 8: + cnt = 8 + case cnt >= 4: + cnt = 4 + case cnt >= 2: + cnt = 2 + } + } else if cnt > 256 { + cnt = 256 + } + + switch cnt { + case 8, 4, 2, 1: + op := s390x.AMOVB + switch cnt { + case 8: + op = s390x.AMOVD + case 4: + op = s390x.AMOVW + case 2: + op = s390x.AMOVH + } + load := gins(op, &src, &tmp) + load.From.Type = obj.TYPE_MEM + load.From.Offset = offset + + store := gins(op, &tmp, &dst) + store.To.Type = obj.TYPE_MEM + store.To.Offset = offset + + if dir == _BACKWARDS { + load.From.Offset -= cnt + store.To.Offset -= cnt + } + + default: + p := gins(s390x.AMVC, &src, &dst) + p.From.Type = obj.TYPE_MEM + p.From.Offset = offset + p.To.Type = obj.TYPE_MEM + p.To.Offset = offset + p.From3 = new(obj.Addr) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = cnt + } + + switch dir { + case _FORWARDS: + offset += cnt + case _BACKWARDS: + offset -= cnt + } + w -= cnt + } +} diff --git a/src/cmd/compile/internal/s390x/galign.go b/src/cmd/compile/internal/s390x/galign.go new file mode 100644 index 0000000000..d0d621e557 --- /dev/null +++ b/src/cmd/compile/internal/s390x/galign.go @@ -0,0 +1,66 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s390x + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj/s390x" +) + +func betypeinit() { + gc.Widthptr = 8 + gc.Widthint = 8 + gc.Widthreg = 8 +} + +func Main() { + gc.Thearch.LinkArch = &s390x.Links390x + gc.Thearch.REGSP = s390x.REGSP + gc.Thearch.REGCTXT = s390x.REGCTXT + gc.Thearch.REGCALLX = s390x.REG_R3 + gc.Thearch.REGCALLX2 = s390x.REG_R4 + gc.Thearch.REGRETURN = s390x.REG_R3 + gc.Thearch.REGMIN = s390x.REG_R0 + gc.Thearch.REGMAX = s390x.REG_R15 + gc.Thearch.FREGMIN = s390x.REG_F0 + gc.Thearch.FREGMAX = s390x.REG_F15 + gc.Thearch.MAXWIDTH = 1 << 50 + gc.Thearch.ReservedRegs = resvd + + gc.Thearch.Betypeinit = betypeinit + gc.Thearch.Cgen_hmul = cgen_hmul + gc.Thearch.Cgen_shift = cgen_shift + gc.Thearch.Clearfat = clearfat + gc.Thearch.Defframe = defframe + gc.Thearch.Dodiv = dodiv + gc.Thearch.Excise = excise + gc.Thearch.Expandchecks = expandchecks + gc.Thearch.Getg = getg + gc.Thearch.Gins = gins + gc.Thearch.Ginscmp = ginscmp + gc.Thearch.Ginscon = ginscon + gc.Thearch.Ginsnop = ginsnop + gc.Thearch.Gmove = gmove + gc.Thearch.Peep = peep + gc.Thearch.Proginfo = proginfo + gc.Thearch.Regtyp = isReg + gc.Thearch.Sameaddr = sameaddr + gc.Thearch.Smallindir = smallindir + gc.Thearch.Stackaddr = stackaddr + gc.Thearch.Blockcopy = blockcopy + gc.Thearch.Sudoaddable = sudoaddable + gc.Thearch.Sudoclean = sudoclean + gc.Thearch.Excludedregs = excludedregs + gc.Thearch.RtoB = RtoB + gc.Thearch.FtoB = RtoB + gc.Thearch.BtoR = BtoR + gc.Thearch.BtoF = BtoF + gc.Thearch.Optoas = optoas + gc.Thearch.Doregbits = doregbits + gc.Thearch.Regnames = regnames + + gc.Main() + gc.Exit(0) +} diff --git a/src/cmd/compile/internal/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go new file mode 100644 index 0000000000..39885baace --- /dev/null +++ b/src/cmd/compile/internal/s390x/ggen.go @@ -0,0 +1,577 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s390x + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/s390x" + "fmt" +) + +// clearLoopCutOff is the (somewhat arbitrary) value above which it is better +// to have a loop of clear instructions (e.g. XCs) rather than just generating +// multiple instructions (i.e. loop unrolling). +// Must be between 256 and 4096. +const clearLoopCutoff = 1024 + +func defframe(ptxt *obj.Prog) { + // fill in argument size, stack size + ptxt.To.Type = obj.TYPE_TEXTSIZE + + ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.ArgWidth(), int64(gc.Widthptr))) + frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) + ptxt.To.Offset = int64(frame) + + // insert code to zero ambiguously live variables + // so that the garbage collector only sees initialized values + // when it looks for pointers. + p := ptxt + + hi := int64(0) + lo := hi + + // iterate through declarations - they are sorted in decreasing xoffset order. + for _, n := range gc.Curfn.Func.Dcl { + if !n.Name.Needzero { + continue + } + if n.Class != gc.PAUTO { + gc.Fatalf("needzero class %d", n.Class) + } + if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { + gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset)) + } + + if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { + // merge with range we already have + lo = n.Xoffset + + continue + } + + // zero old range + p = zerorange(p, int64(frame), lo, hi) + + // set new range + hi = n.Xoffset + n.Type.Width + + lo = n.Xoffset + } + + // zero final range + zerorange(p, int64(frame), lo, hi) +} + +// zerorange clears the stack in the given range. +func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { + cnt := hi - lo + if cnt == 0 { + return p + } + + // Adjust the frame to account for LR. + frame += gc.Ctxt.FixedFrameSize() + offset := frame + lo + reg := int16(s390x.REGSP) + + // If the offset cannot fit in a 12-bit unsigned displacement then we + // need to create a copy of the stack pointer that we can adjust. + // We also need to do this if we are going to loop. + if offset < 0 || offset > 4096-clearLoopCutoff || cnt > clearLoopCutoff { + p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset, obj.TYPE_REG, s390x.REGRT1, 0) + p.Reg = int16(s390x.REGSP) + reg = s390x.REGRT1 + offset = 0 + } + + // Generate a loop of large clears. + if cnt > clearLoopCutoff { + n := cnt - (cnt % 256) + end := int16(s390x.REGRT2) + p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset+n, obj.TYPE_REG, end, 0) + p.Reg = reg + p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset) + p.From3 = new(obj.Addr) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = 256 + pl := p + p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, 256, obj.TYPE_REG, reg, 0) + p = appendpp(p, s390x.ACMP, obj.TYPE_REG, reg, 0, obj.TYPE_REG, end, 0) + p = appendpp(p, s390x.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) + gc.Patch(p, pl) + + cnt -= n + } + + // Generate remaining clear instructions without a loop. + for cnt > 0 { + n := cnt + + // Can clear at most 256 bytes per instruction. + if n > 256 { + n = 256 + } + + switch n { + // Handle very small clears with move instructions. + case 8, 4, 2, 1: + ins := s390x.AMOVB + switch n { + case 8: + ins = s390x.AMOVD + case 4: + ins = s390x.AMOVW + case 2: + ins = s390x.AMOVH + } + p = appendpp(p, ins, obj.TYPE_CONST, 0, 0, obj.TYPE_MEM, reg, offset) + + // Handle clears that would require multiple move instructions with XC. + default: + p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset) + p.From3 = new(obj.Addr) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = n + } + + cnt -= n + offset += n + } + + return p +} + +func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog { + q := gc.Ctxt.NewProg() + gc.Clearp(q) + q.As = as + q.Lineno = p.Lineno + q.From.Type = ftype + q.From.Reg = freg + q.From.Offset = foffset + q.To.Type = ttype + q.To.Reg = treg + q.To.Offset = toffset + q.Link = p.Link + p.Link = q + return q +} + +func ginsnop() { + var reg gc.Node + gc.Nodreg(®, gc.Types[gc.TINT], s390x.REG_R0) + gins(s390x.AOR, ®, ®) +} + +var panicdiv *gc.Node + +/* + * generate division. + * generates one of: + * res = nl / nr + * res = nl % nr + * according to op. + */ +func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { + // Have to be careful about handling + // most negative int divided by -1 correctly. + // The hardware will generate undefined result. + // Also need to explicitly trap on division on zero, + // the hardware will silently generate undefined result. + // DIVW will leave unpredicable result in higher 32-bit, + // so always use DIVD/DIVDU. + t := nl.Type + + t0 := t + check := 0 + if t.IsSigned() { + check = 1 + if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<= nr.Ullman { + gc.Cgen(nl, &tl) + gc.Cgen(nr, &tr) + } else { + gc.Cgen(nr, &tr) + gc.Cgen(nl, &tl) + } + + if t != t0 { + // Convert + tl2 := tl + + tr2 := tr + tl.Type = t + tr.Type = t + gmove(&tl2, &tl) + gmove(&tr2, &tr) + } + + // Handle divide-by-zero panic. + p1 := gins(optoas(gc.OCMP, t), &tr, nil) + + p1.To.Type = obj.TYPE_REG + p1.To.Reg = s390x.REGZERO + p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if panicdiv == nil { + panicdiv = gc.Sysfunc("panicdivide") + } + gc.Ginscall(panicdiv, -1) + gc.Patch(p1, gc.Pc) + + var p2 *obj.Prog + if check != 0 { + var nm1 gc.Node + gc.Nodconst(&nm1, t, -1) + gins(optoas(gc.OCMP, t), &tr, &nm1) + p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if op == gc.ODIV { + // a / (-1) is -a. + gins(optoas(gc.OMINUS, t), nil, &tl) + + gmove(&tl, res) + } else { + // a % (-1) is 0. + var nz gc.Node + gc.Nodconst(&nz, t, 0) + + gmove(&nz, res) + } + + p2 = gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + } + + p1 = gins(a, &tr, &tl) + if op == gc.ODIV { + gc.Regfree(&tr) + gmove(&tl, res) + } else { + // A%B = A-(A/B*B) + var tm gc.Node + gc.Regalloc(&tm, t, nil) + + // patch div to use the 3 register form + // TODO(minux): add gins3? + p1.Reg = p1.To.Reg + + p1.To.Reg = tm.Reg + gins(optoas(gc.OMUL, t), &tr, &tm) + gc.Regfree(&tr) + gins(optoas(gc.OSUB, t), &tm, &tl) + gc.Regfree(&tm) + gmove(&tl, res) + } + + gc.Regfree(&tl) + if check != 0 { + gc.Patch(p2, gc.Pc) + } +} + +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { + // largest ullman on left. + if nl.Ullman < nr.Ullman { + nl, nr = nr, nl + } + + t := nl.Type + w := int(t.Width) * 8 + var n1 gc.Node + gc.Cgenr(nl, &n1, res) + var n2 gc.Node + gc.Cgenr(nr, &n2, nil) + switch gc.Simtype[t.Etype] { + case gc.TINT8, + gc.TINT16, + gc.TINT32: + gins(optoas(gc.OMUL, t), &n2, &n1) + p := gins(s390x.ASRAD, nil, &n1) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(w) + + case gc.TUINT8, + gc.TUINT16, + gc.TUINT32: + gins(optoas(gc.OMUL, t), &n2, &n1) + p := gins(s390x.ASRD, nil, &n1) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(w) + + case gc.TINT64: + gins(s390x.AMULHD, &n2, &n1) + + case gc.TUINT64: + gins(s390x.AMULHDU, &n2, &n1) + + default: + gc.Fatalf("cgen_hmul %v", t) + } + + gc.Cgen(&n1, res) + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +/* + * generate shift according to op, one of: + * res = nl << nr + * res = nl >> nr + */ +func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { + a := optoas(op, nl.Type) + + if nr.Op == gc.OLITERAL { + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + gc.Cgen(nl, &n1) + sc := uint64(nr.Int64()) + if sc >= uint64(nl.Type.Width*8) { + // large shift gets 2 shifts by width-1 + var n3 gc.Node + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + + gins(a, &n3, &n1) + gins(a, &n3, &n1) + } else { + gins(a, nr, &n1) + } + gmove(&n1, res) + gc.Regfree(&n1) + return + } + + if nl.Ullman >= gc.UINF { + var n4 gc.Node + gc.Tempname(&n4, nl.Type) + gc.Cgen(nl, &n4) + nl = &n4 + } + + if nr.Ullman >= gc.UINF { + var n5 gc.Node + gc.Tempname(&n5, nr.Type) + gc.Cgen(nr, &n5) + nr = &n5 + } + + // Allow either uint32 or uint64 as shift type, + // to avoid unnecessary conversion from uint32 to uint64 + // just to do the comparison. + tcount := gc.Types[gc.Simtype[nr.Type.Etype]] + + if tcount.Etype < gc.TUINT32 { + tcount = gc.Types[gc.TUINT32] + } + + var n1 gc.Node + gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX + var n3 gc.Node + gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX + + var n2 gc.Node + gc.Regalloc(&n2, nl.Type, res) + + if nl.Ullman >= nr.Ullman { + gc.Cgen(nl, &n2) + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + } else { + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + gc.Cgen(nl, &n2) + } + + gc.Regfree(&n3) + + // test and fix up large shifts + if !bounded { + gc.Nodconst(&n3, tcount, nl.Type.Width*8) + gins(optoas(gc.OCMP, tcount), &n1, &n3) + p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, 1) + if op == gc.ORSH && nl.Type.IsSigned() { + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + gins(a, &n3, &n2) + } else { + gc.Nodconst(&n3, nl.Type, 0) + gmove(&n3, &n2) + } + + gc.Patch(p1, gc.Pc) + } + + gins(a, &n1, &n2) + + gmove(&n2, res) + + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +// clearfat clears (i.e. replaces with zeros) the value pointed to by nl. +func clearfat(nl *gc.Node) { + if gc.Debug['g'] != 0 { + fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) + } + + // Avoid taking the address for simple enough types. + if gc.Componentgen(nil, nl) { + return + } + + var dst gc.Node + gc.Regalloc(&dst, gc.Types[gc.Tptr], nil) + gc.Agen(nl, &dst) + + var boff int64 + w := nl.Type.Width + if w > clearLoopCutoff { + // Generate a loop clearing 256 bytes per iteration using XCs. + var end gc.Node + gc.Regalloc(&end, gc.Types[gc.Tptr], nil) + p := gins(s390x.AMOVD, &dst, &end) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = w - (w % 256) + + p = gins(s390x.AXC, &dst, &dst) + p.From.Type = obj.TYPE_MEM + p.From.Offset = 0 + p.To.Type = obj.TYPE_MEM + p.To.Offset = 0 + p.From3 = new(obj.Addr) + p.From3.Offset = 256 + p.From3.Type = obj.TYPE_CONST + pl := p + + ginscon(s390x.AADD, 256, &dst) + gins(s390x.ACMP, &dst, &end) + gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), pl) + gc.Regfree(&end) + w = w % 256 + } + + // Generate instructions to clear the remaining memory. + for w > 0 { + n := w + + // Can clear at most 256 bytes per instruction. + if n > 256 { + n = 256 + } + + switch n { + // Handle very small clears using moves. + case 8, 4, 2, 1: + ins := s390x.AMOVB + switch n { + case 8: + ins = s390x.AMOVD + case 4: + ins = s390x.AMOVW + case 2: + ins = s390x.AMOVH + } + p := gins(ins, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 0 + p.To.Type = obj.TYPE_MEM + p.To.Offset = boff + + // Handle clears that would require multiple moves with a XC. + default: + p := gins(s390x.AXC, &dst, &dst) + p.From.Type = obj.TYPE_MEM + p.From.Offset = boff + p.To.Type = obj.TYPE_MEM + p.To.Offset = boff + p.From3 = new(obj.Addr) + p.From3.Offset = n + p.From3.Type = obj.TYPE_CONST + } + + boff += n + w -= n + } + + gc.Regfree(&dst) +} + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +func expandchecks(firstp *obj.Prog) { + for p := firstp; p != nil; p = p.Link { + if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { + fmt.Printf("expandchecks: %v\n", p) + } + if p.As != obj.ACHECKNIL { + continue + } + if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers + gc.Warnl(p.Lineno, "generated nil check") + } + if p.From.Type != obj.TYPE_REG { + gc.Fatalf("invalid nil check %v\n", p) + } + + // check is + // CMPBNE arg, $0, 2(PC) [likely] + // MOVD R0, 0(R0) + p1 := gc.Ctxt.NewProg() + + gc.Clearp(p1) + p1.Link = p.Link + p.Link = p1 + p1.Lineno = p.Lineno + p1.Pc = 9999 + p.As = s390x.ACMPBNE + p.From3 = new(obj.Addr) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = 0 + + p.To.Type = obj.TYPE_BRANCH + p.To.Val = p1.Link + + // crash by write to memory address 0. + p1.As = s390x.AMOVD + + p1.From.Type = obj.TYPE_REG + p1.From.Reg = s390x.REGZERO + p1.To.Type = obj.TYPE_MEM + p1.To.Reg = s390x.REGZERO + p1.To.Offset = 0 + } +} + +// res = runtime.getg() +func getg(res *gc.Node) { + var n1 gc.Node + gc.Nodreg(&n1, res.Type, s390x.REGG) + gmove(&n1, res) +} diff --git a/src/cmd/compile/internal/s390x/gsubr.go b/src/cmd/compile/internal/s390x/gsubr.go new file mode 100644 index 0000000000..e9cfd23e42 --- /dev/null +++ b/src/cmd/compile/internal/s390x/gsubr.go @@ -0,0 +1,1115 @@ +// Derived from Inferno utils/6c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/s390x" + "fmt" +) + +var resvd = []int{ + s390x.REGZERO, // R0 + s390x.REGTMP, // R10 + s390x.REGTMP2, // R11 + s390x.REGCTXT, // R12 + s390x.REGG, // R13 + s390x.REG_LR, // R14 + s390x.REGSP, // R15 +} + +// generate +// as $c, n +func ginscon(as obj.As, c int64, n2 *gc.Node) { + var n1 gc.Node + + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + + if as != s390x.AMOVD && (c < -s390x.BIG || c > s390x.BIG) || n2.Op != gc.OREGISTER || as == s390x.AMULLD { + // cannot have more than 16-bit of immediate in ADD, etc. + // instead, MOV into register first. + var ntmp gc.Node + gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) + + rawgins(s390x.AMOVD, &n1, &ntmp) + rawgins(as, &ntmp, n2) + gc.Regfree(&ntmp) + return + } + + rawgins(as, &n1, n2) +} + +// generate +// as n, $c (CMP/CMPU) +func ginscon2(as obj.As, n2 *gc.Node, c int64) { + var n1 gc.Node + + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + + switch as { + default: + gc.Fatalf("ginscon2") + + case s390x.ACMP: + if -s390x.BIG <= c && c <= s390x.BIG { + rawgins(as, n2, &n1) + return + } + + case s390x.ACMPU: + if 0 <= c && c <= 2*s390x.BIG { + rawgins(as, n2, &n1) + return + } + } + + // MOV n1 into register first + var ntmp gc.Node + gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) + + rawgins(s390x.AMOVD, &n1, &ntmp) + rawgins(as, n2, &ntmp) + gc.Regfree(&ntmp) +} + +func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { + if t.IsInteger() && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL { + // Reverse comparison to place constant last. + op = gc.Brrev(op) + n1, n2 = n2, n1 + } + + var r1, r2, g1, g2 gc.Node + gc.Regalloc(&r1, t, n1) + gc.Regalloc(&g1, n1.Type, &r1) + gc.Cgen(n1, &g1) + gmove(&g1, &r1) + if t.IsInteger() && gc.Isconst(n2, gc.CTINT) { + ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64()) + } else { + gc.Regalloc(&r2, t, n2) + gc.Regalloc(&g2, n1.Type, &r2) + gc.Cgen(n2, &g2) + gmove(&g2, &r2) + rawgins(optoas(gc.OCMP, t), &r1, &r2) + gc.Regfree(&g2) + gc.Regfree(&r2) + } + gc.Regfree(&g1) + gc.Regfree(&r1) + return gc.Gbranch(optoas(op, t), nil, likely) +} + +// gmvc tries to move f to t using a mvc instruction. +// If successful it returns true, otherwise it returns false. +func gmvc(f, t *gc.Node) bool { + ft := int(gc.Simsimtype(f.Type)) + tt := int(gc.Simsimtype(t.Type)) + + if ft != tt { + return false + } + + if f.Op != gc.OINDREG || t.Op != gc.OINDREG { + return false + } + + if f.Xoffset < 0 || f.Xoffset >= 4096-8 { + return false + } + + if t.Xoffset < 0 || t.Xoffset >= 4096-8 { + return false + } + + var len int64 + switch ft { + case gc.TUINT8, gc.TINT8, gc.TBOOL: + len = 1 + case gc.TUINT16, gc.TINT16: + len = 2 + case gc.TUINT32, gc.TINT32, gc.TFLOAT32: + len = 4 + case gc.TUINT64, gc.TINT64, gc.TFLOAT64, gc.TPTR64: + len = 8 + case gc.TUNSAFEPTR: + len = int64(gc.Widthptr) + default: + return false + } + + p := gc.Prog(s390x.AMVC) + gc.Naddr(&p.From, f) + gc.Naddr(&p.To, t) + p.From3 = new(obj.Addr) + p.From3.Offset = len + p.From3.Type = obj.TYPE_CONST + return true +} + +// generate move: +// t = f +// hard part is conversions. +func gmove(f *gc.Node, t *gc.Node) { + if gc.Debug['M'] != 0 { + fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, gc.FmtLong), gc.Nconv(t, gc.FmtLong)) + } + + ft := int(gc.Simsimtype(f.Type)) + tt := int(gc.Simsimtype(t.Type)) + cvt := t.Type + + if gc.Iscomplex[ft] || gc.Iscomplex[tt] { + gc.Complexmove(f, t) + return + } + + var a obj.As + + // cannot have two memory operands + if gc.Ismem(f) && gc.Ismem(t) { + if gmvc(f, t) { + return + } + goto hard + } + + // convert constant to desired type + if f.Op == gc.OLITERAL { + var con gc.Node + f.Convconst(&con, t.Type) + f = &con + ft = tt // so big switch will choose a simple mov + + // some constants can't move directly to memory. + if gc.Ismem(t) { + // float constants come from memory. + if t.Type.IsFloat() { + goto hard + } + + // all immediates are 16-bit sign-extended + // unless moving into a register. + if t.Type.IsInteger() { + if i := con.Int64(); int64(int16(i)) != i { + goto hard + } + } + + // immediate moves to memory have a 12-bit unsigned displacement + if t.Xoffset < 0 || t.Xoffset >= 4096-8 { + goto hard + } + } + } + + // a float-to-int or int-to-float conversion requires the source operand in a register + if gc.Ismem(f) && ((f.Type.IsFloat() && t.Type.IsInteger()) || (f.Type.IsInteger() && t.Type.IsFloat())) { + cvt = f.Type + goto hard + } + + // a float32-to-float64 or float64-to-float32 conversion requires the source operand in a register + if gc.Ismem(f) && f.Type.IsFloat() && t.Type.IsFloat() && (ft != tt) { + cvt = f.Type + goto hard + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + switch uint32(ft)<<16 | uint32(tt) { + default: + gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong)) + + // integer copy and truncate + case gc.TINT8<<16 | gc.TINT8, + gc.TUINT8<<16 | gc.TINT8, + gc.TINT16<<16 | gc.TINT8, + gc.TUINT16<<16 | gc.TINT8, + gc.TINT32<<16 | gc.TINT8, + gc.TUINT32<<16 | gc.TINT8, + gc.TINT64<<16 | gc.TINT8, + gc.TUINT64<<16 | gc.TINT8: + a = s390x.AMOVB + + case gc.TINT8<<16 | gc.TUINT8, + gc.TUINT8<<16 | gc.TUINT8, + gc.TINT16<<16 | gc.TUINT8, + gc.TUINT16<<16 | gc.TUINT8, + gc.TINT32<<16 | gc.TUINT8, + gc.TUINT32<<16 | gc.TUINT8, + gc.TINT64<<16 | gc.TUINT8, + gc.TUINT64<<16 | gc.TUINT8: + a = s390x.AMOVBZ + + case gc.TINT16<<16 | gc.TINT16, + gc.TUINT16<<16 | gc.TINT16, + gc.TINT32<<16 | gc.TINT16, + gc.TUINT32<<16 | gc.TINT16, + gc.TINT64<<16 | gc.TINT16, + gc.TUINT64<<16 | gc.TINT16: + a = s390x.AMOVH + + case gc.TINT16<<16 | gc.TUINT16, + gc.TUINT16<<16 | gc.TUINT16, + gc.TINT32<<16 | gc.TUINT16, + gc.TUINT32<<16 | gc.TUINT16, + gc.TINT64<<16 | gc.TUINT16, + gc.TUINT64<<16 | gc.TUINT16: + a = s390x.AMOVHZ + + case gc.TINT32<<16 | gc.TINT32, + gc.TUINT32<<16 | gc.TINT32, + gc.TINT64<<16 | gc.TINT32, + gc.TUINT64<<16 | gc.TINT32: + a = s390x.AMOVW + + case gc.TINT32<<16 | gc.TUINT32, + gc.TUINT32<<16 | gc.TUINT32, + gc.TINT64<<16 | gc.TUINT32, + gc.TUINT64<<16 | gc.TUINT32: + a = s390x.AMOVWZ + + case gc.TINT64<<16 | gc.TINT64, + gc.TINT64<<16 | gc.TUINT64, + gc.TUINT64<<16 | gc.TINT64, + gc.TUINT64<<16 | gc.TUINT64: + a = s390x.AMOVD + + // sign extend int8 + case gc.TINT8<<16 | gc.TINT16, + gc.TINT8<<16 | gc.TUINT16, + gc.TINT8<<16 | gc.TINT32, + gc.TINT8<<16 | gc.TUINT32, + gc.TINT8<<16 | gc.TINT64, + gc.TINT8<<16 | gc.TUINT64: + a = s390x.AMOVB + goto rdst + + // sign extend uint8 + case gc.TUINT8<<16 | gc.TINT16, + gc.TUINT8<<16 | gc.TUINT16, + gc.TUINT8<<16 | gc.TINT32, + gc.TUINT8<<16 | gc.TUINT32, + gc.TUINT8<<16 | gc.TINT64, + gc.TUINT8<<16 | gc.TUINT64: + a = s390x.AMOVBZ + goto rdst + + // sign extend int16 + case gc.TINT16<<16 | gc.TINT32, + gc.TINT16<<16 | gc.TUINT32, + gc.TINT16<<16 | gc.TINT64, + gc.TINT16<<16 | gc.TUINT64: + a = s390x.AMOVH + goto rdst + + // zero extend uint16 + case gc.TUINT16<<16 | gc.TINT32, + gc.TUINT16<<16 | gc.TUINT32, + gc.TUINT16<<16 | gc.TINT64, + gc.TUINT16<<16 | gc.TUINT64: + a = s390x.AMOVHZ + goto rdst + + // sign extend int32 + case gc.TINT32<<16 | gc.TINT64, + gc.TINT32<<16 | gc.TUINT64: + a = s390x.AMOVW + goto rdst + + // zero extend uint32 + case gc.TUINT32<<16 | gc.TINT64, + gc.TUINT32<<16 | gc.TUINT64: + a = s390x.AMOVWZ + goto rdst + + // float to integer + case gc.TFLOAT32<<16 | gc.TUINT8, + gc.TFLOAT32<<16 | gc.TUINT16: + cvt = gc.Types[gc.TUINT32] + goto hard + + case gc.TFLOAT32<<16 | gc.TUINT32: + a = s390x.ACLFEBR + goto rdst + + case gc.TFLOAT32<<16 | gc.TUINT64: + a = s390x.ACLGEBR + goto rdst + + case gc.TFLOAT64<<16 | gc.TUINT8, + gc.TFLOAT64<<16 | gc.TUINT16: + cvt = gc.Types[gc.TUINT32] + goto hard + + case gc.TFLOAT64<<16 | gc.TUINT32: + a = s390x.ACLFDBR + goto rdst + + case gc.TFLOAT64<<16 | gc.TUINT64: + a = s390x.ACLGDBR + goto rdst + + case gc.TFLOAT32<<16 | gc.TINT8, + gc.TFLOAT32<<16 | gc.TINT16: + cvt = gc.Types[gc.TINT32] + goto hard + + case gc.TFLOAT32<<16 | gc.TINT32: + a = s390x.ACFEBRA + goto rdst + + case gc.TFLOAT32<<16 | gc.TINT64: + a = s390x.ACGEBRA + goto rdst + + case gc.TFLOAT64<<16 | gc.TINT8, + gc.TFLOAT64<<16 | gc.TINT16: + cvt = gc.Types[gc.TINT32] + goto hard + + case gc.TFLOAT64<<16 | gc.TINT32: + a = s390x.ACFDBRA + goto rdst + + case gc.TFLOAT64<<16 | gc.TINT64: + a = s390x.ACGDBRA + goto rdst + + // integer to float + case gc.TUINT8<<16 | gc.TFLOAT32, + gc.TUINT16<<16 | gc.TFLOAT32: + cvt = gc.Types[gc.TUINT32] + goto hard + + case gc.TUINT32<<16 | gc.TFLOAT32: + a = s390x.ACELFBR + goto rdst + + case gc.TUINT64<<16 | gc.TFLOAT32: + a = s390x.ACELGBR + goto rdst + + case gc.TUINT8<<16 | gc.TFLOAT64, + gc.TUINT16<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TUINT32] + goto hard + + case gc.TUINT32<<16 | gc.TFLOAT64: + a = s390x.ACDLFBR + goto rdst + + case gc.TUINT64<<16 | gc.TFLOAT64: + a = s390x.ACDLGBR + goto rdst + + case gc.TINT8<<16 | gc.TFLOAT32, + gc.TINT16<<16 | gc.TFLOAT32: + cvt = gc.Types[gc.TINT32] + goto hard + + case gc.TINT32<<16 | gc.TFLOAT32: + a = s390x.ACEFBRA + goto rdst + + case gc.TINT64<<16 | gc.TFLOAT32: + a = s390x.ACEGBRA + goto rdst + + case gc.TINT8<<16 | gc.TFLOAT64, + gc.TINT16<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TINT32] + goto hard + + case gc.TINT32<<16 | gc.TFLOAT64: + a = s390x.ACDFBRA + goto rdst + + case gc.TINT64<<16 | gc.TFLOAT64: + a = s390x.ACDGBRA + goto rdst + + // float to float + case gc.TFLOAT32<<16 | gc.TFLOAT32: + a = s390x.AFMOVS + + case gc.TFLOAT64<<16 | gc.TFLOAT64: + a = s390x.AFMOVD + + case gc.TFLOAT32<<16 | gc.TFLOAT64: + a = s390x.ALDEBR + goto rdst + + case gc.TFLOAT64<<16 | gc.TFLOAT32: + a = s390x.ALEDBR + goto rdst + } + + gins(a, f, t) + return + + // requires register destination +rdst: + if t != nil && t.Op == gc.OREGISTER { + gins(a, f, t) + return + } else { + var r1 gc.Node + gc.Regalloc(&r1, t.Type, t) + + gins(a, f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + // requires register intermediate +hard: + var r1 gc.Node + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return +} + +func intLiteral(n *gc.Node) (x int64, ok bool) { + switch { + case n == nil: + return + case gc.Isconst(n, gc.CTINT): + return n.Int64(), true + case gc.Isconst(n, gc.CTBOOL): + return int64(obj.Bool2int(n.Bool())), true + } + return +} + +// gins is called by the front end. +// It synthesizes some multiple-instruction sequences +// so the front end can stay simpler. +func gins(as obj.As, f, t *gc.Node) *obj.Prog { + if t != nil { + if as >= obj.A_ARCHSPECIFIC { + if x, ok := intLiteral(f); ok { + ginscon(as, x, t) + return nil // caller must not use + } + } + if as == s390x.ACMP || as == s390x.ACMPU { + if x, ok := intLiteral(t); ok { + ginscon2(as, f, x) + return nil // caller must not use + } + } + } + return rawgins(as, f, t) +} + +// generate one instruction: +// as f, t +func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog { + // self move check + // TODO(mundaym): use sized math and extend to MOVB, MOVWZ etc. + switch as { + case s390x.AMOVD, s390x.AFMOVS, s390x.AFMOVD: + if f != nil && t != nil && + f.Op == gc.OREGISTER && t.Op == gc.OREGISTER && + f.Reg == t.Reg { + return nil + } + } + + p := gc.Prog(as) + gc.Naddr(&p.From, f) + gc.Naddr(&p.To, t) + + switch as { + // Bad things the front end has done to us. Crash to find call stack. + case s390x.AMULLD: + if p.From.Type == obj.TYPE_CONST { + gc.Debug['h'] = 1 + gc.Fatalf("bad inst: %v", p) + } + case s390x.ACMP, s390x.ACMPU: + if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM { + gc.Debug['h'] = 1 + gc.Fatalf("bad inst: %v", p) + } + } + + if gc.Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + + w := int32(0) + switch as { + case s390x.AMOVB, s390x.AMOVBZ: + w = 1 + + case s390x.AMOVH, s390x.AMOVHZ: + w = 2 + + case s390x.AMOVW, s390x.AMOVWZ: + w = 4 + + case s390x.AMOVD: + if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR { + break + } + w = 8 + } + + if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) { + gc.Dump("f", f) + gc.Dump("t", t) + gc.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) + } + + return p +} + +// optoas returns the Axxx equivalent of Oxxx for type t +func optoas(op gc.Op, t *gc.Type) obj.As { + if t == nil { + gc.Fatalf("optoas: t is nil") + } + + // avoid constant conversions in switches below + const ( + OMINUS_ = uint32(gc.OMINUS) << 16 + OLSH_ = uint32(gc.OLSH) << 16 + ORSH_ = uint32(gc.ORSH) << 16 + OADD_ = uint32(gc.OADD) << 16 + OSUB_ = uint32(gc.OSUB) << 16 + OMUL_ = uint32(gc.OMUL) << 16 + ODIV_ = uint32(gc.ODIV) << 16 + OOR_ = uint32(gc.OOR) << 16 + OAND_ = uint32(gc.OAND) << 16 + OXOR_ = uint32(gc.OXOR) << 16 + OEQ_ = uint32(gc.OEQ) << 16 + ONE_ = uint32(gc.ONE) << 16 + OLT_ = uint32(gc.OLT) << 16 + OLE_ = uint32(gc.OLE) << 16 + OGE_ = uint32(gc.OGE) << 16 + OGT_ = uint32(gc.OGT) << 16 + OCMP_ = uint32(gc.OCMP) << 16 + OAS_ = uint32(gc.OAS) << 16 + OHMUL_ = uint32(gc.OHMUL) << 16 + OSQRT_ = uint32(gc.OSQRT) << 16 + OLROT_ = uint32(gc.OLROT) << 16 + ) + + a := obj.AXXX + switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { + default: + gc.Fatalf("optoas: no entry for op=%v type=%v", gc.Oconv(op, 0), t) + + case OEQ_ | gc.TBOOL, + OEQ_ | gc.TINT8, + OEQ_ | gc.TUINT8, + OEQ_ | gc.TINT16, + OEQ_ | gc.TUINT16, + OEQ_ | gc.TINT32, + OEQ_ | gc.TUINT32, + OEQ_ | gc.TINT64, + OEQ_ | gc.TUINT64, + OEQ_ | gc.TPTR32, + OEQ_ | gc.TPTR64, + OEQ_ | gc.TFLOAT32, + OEQ_ | gc.TFLOAT64: + a = s390x.ABEQ + + case ONE_ | gc.TBOOL, + ONE_ | gc.TINT8, + ONE_ | gc.TUINT8, + ONE_ | gc.TINT16, + ONE_ | gc.TUINT16, + ONE_ | gc.TINT32, + ONE_ | gc.TUINT32, + ONE_ | gc.TINT64, + ONE_ | gc.TUINT64, + ONE_ | gc.TPTR32, + ONE_ | gc.TPTR64, + ONE_ | gc.TFLOAT32, + ONE_ | gc.TFLOAT64: + a = s390x.ABNE + + case OLT_ | gc.TINT8, // ACMP + OLT_ | gc.TINT16, + OLT_ | gc.TINT32, + OLT_ | gc.TINT64, + OLT_ | gc.TUINT8, + // ACMPU + OLT_ | gc.TUINT16, + OLT_ | gc.TUINT32, + OLT_ | gc.TUINT64, + OLT_ | gc.TFLOAT32, + // AFCMPU + OLT_ | gc.TFLOAT64: + a = s390x.ABLT + + case OLE_ | gc.TINT8, // ACMP + OLE_ | gc.TINT16, + OLE_ | gc.TINT32, + OLE_ | gc.TINT64, + OLE_ | gc.TUINT8, + // ACMPU + OLE_ | gc.TUINT16, + OLE_ | gc.TUINT32, + OLE_ | gc.TUINT64, + OLE_ | gc.TFLOAT32, + OLE_ | gc.TFLOAT64: + a = s390x.ABLE + + case OGT_ | gc.TINT8, + OGT_ | gc.TINT16, + OGT_ | gc.TINT32, + OGT_ | gc.TINT64, + OGT_ | gc.TUINT8, + OGT_ | gc.TUINT16, + OGT_ | gc.TUINT32, + OGT_ | gc.TUINT64, + OGT_ | gc.TFLOAT32, + OGT_ | gc.TFLOAT64: + a = s390x.ABGT + + case OGE_ | gc.TINT8, + OGE_ | gc.TINT16, + OGE_ | gc.TINT32, + OGE_ | gc.TINT64, + OGE_ | gc.TUINT8, + OGE_ | gc.TUINT16, + OGE_ | gc.TUINT32, + OGE_ | gc.TUINT64, + OGE_ | gc.TFLOAT32, + OGE_ | gc.TFLOAT64: + a = s390x.ABGE + + case OCMP_ | gc.TBOOL, + OCMP_ | gc.TINT8, + OCMP_ | gc.TINT16, + OCMP_ | gc.TINT32, + OCMP_ | gc.TPTR32, + OCMP_ | gc.TINT64: + a = s390x.ACMP + + case OCMP_ | gc.TUINT8, + OCMP_ | gc.TUINT16, + OCMP_ | gc.TUINT32, + OCMP_ | gc.TUINT64, + OCMP_ | gc.TPTR64: + a = s390x.ACMPU + + case OCMP_ | gc.TFLOAT32: + a = s390x.ACEBR + + case OCMP_ | gc.TFLOAT64: + a = s390x.AFCMPU + + case OAS_ | gc.TBOOL, + OAS_ | gc.TINT8: + a = s390x.AMOVB + + case OAS_ | gc.TUINT8: + a = s390x.AMOVBZ + + case OAS_ | gc.TINT16: + a = s390x.AMOVH + + case OAS_ | gc.TUINT16: + a = s390x.AMOVHZ + + case OAS_ | gc.TINT32: + a = s390x.AMOVW + + case OAS_ | gc.TUINT32, + OAS_ | gc.TPTR32: + a = s390x.AMOVWZ + + case OAS_ | gc.TINT64, + OAS_ | gc.TUINT64, + OAS_ | gc.TPTR64: + a = s390x.AMOVD + + case OAS_ | gc.TFLOAT32: + a = s390x.AFMOVS + + case OAS_ | gc.TFLOAT64: + a = s390x.AFMOVD + + case OADD_ | gc.TINT8, + OADD_ | gc.TUINT8, + OADD_ | gc.TINT16, + OADD_ | gc.TUINT16, + OADD_ | gc.TINT32, + OADD_ | gc.TUINT32, + OADD_ | gc.TPTR32, + OADD_ | gc.TINT64, + OADD_ | gc.TUINT64, + OADD_ | gc.TPTR64: + a = s390x.AADD + + case OADD_ | gc.TFLOAT32: + a = s390x.AFADDS + + case OADD_ | gc.TFLOAT64: + a = s390x.AFADD + + case OSUB_ | gc.TINT8, + OSUB_ | gc.TUINT8, + OSUB_ | gc.TINT16, + OSUB_ | gc.TUINT16, + OSUB_ | gc.TINT32, + OSUB_ | gc.TUINT32, + OSUB_ | gc.TPTR32, + OSUB_ | gc.TINT64, + OSUB_ | gc.TUINT64, + OSUB_ | gc.TPTR64: + a = s390x.ASUB + + case OSUB_ | gc.TFLOAT32: + a = s390x.AFSUBS + + case OSUB_ | gc.TFLOAT64: + a = s390x.AFSUB + + case OMINUS_ | gc.TINT8, + OMINUS_ | gc.TUINT8, + OMINUS_ | gc.TINT16, + OMINUS_ | gc.TUINT16, + OMINUS_ | gc.TINT32, + OMINUS_ | gc.TUINT32, + OMINUS_ | gc.TPTR32, + OMINUS_ | gc.TINT64, + OMINUS_ | gc.TUINT64, + OMINUS_ | gc.TPTR64: + a = s390x.ANEG + + case OAND_ | gc.TINT8, + OAND_ | gc.TUINT8, + OAND_ | gc.TINT16, + OAND_ | gc.TUINT16, + OAND_ | gc.TINT32, + OAND_ | gc.TUINT32, + OAND_ | gc.TPTR32, + OAND_ | gc.TINT64, + OAND_ | gc.TUINT64, + OAND_ | gc.TPTR64: + a = s390x.AAND + + case OOR_ | gc.TINT8, + OOR_ | gc.TUINT8, + OOR_ | gc.TINT16, + OOR_ | gc.TUINT16, + OOR_ | gc.TINT32, + OOR_ | gc.TUINT32, + OOR_ | gc.TPTR32, + OOR_ | gc.TINT64, + OOR_ | gc.TUINT64, + OOR_ | gc.TPTR64: + a = s390x.AOR + + case OXOR_ | gc.TINT8, + OXOR_ | gc.TUINT8, + OXOR_ | gc.TINT16, + OXOR_ | gc.TUINT16, + OXOR_ | gc.TINT32, + OXOR_ | gc.TUINT32, + OXOR_ | gc.TPTR32, + OXOR_ | gc.TINT64, + OXOR_ | gc.TUINT64, + OXOR_ | gc.TPTR64: + a = s390x.AXOR + + case OLSH_ | gc.TINT8, + OLSH_ | gc.TUINT8, + OLSH_ | gc.TINT16, + OLSH_ | gc.TUINT16, + OLSH_ | gc.TINT32, + OLSH_ | gc.TUINT32, + OLSH_ | gc.TPTR32, + OLSH_ | gc.TINT64, + OLSH_ | gc.TUINT64, + OLSH_ | gc.TPTR64: + a = s390x.ASLD + + case ORSH_ | gc.TUINT8, + ORSH_ | gc.TUINT16, + ORSH_ | gc.TUINT32, + ORSH_ | gc.TPTR32, + ORSH_ | gc.TUINT64, + ORSH_ | gc.TPTR64: + a = s390x.ASRD + + case ORSH_ | gc.TINT8, + ORSH_ | gc.TINT16, + ORSH_ | gc.TINT32, + ORSH_ | gc.TINT64: + a = s390x.ASRAD + + case OHMUL_ | gc.TINT64: + a = s390x.AMULHD + + case OHMUL_ | gc.TUINT64, + OHMUL_ | gc.TPTR64: + a = s390x.AMULHDU + + case OMUL_ | gc.TINT8, + OMUL_ | gc.TINT16, + OMUL_ | gc.TINT32, + OMUL_ | gc.TINT64: + a = s390x.AMULLD + + case OMUL_ | gc.TUINT8, + OMUL_ | gc.TUINT16, + OMUL_ | gc.TUINT32, + OMUL_ | gc.TPTR32, + // don't use word multiply, the high 32-bit are undefined. + OMUL_ | gc.TUINT64, + OMUL_ | gc.TPTR64: + // for 64-bit multiplies, signedness doesn't matter. + a = s390x.AMULLD + + case OMUL_ | gc.TFLOAT32: + a = s390x.AFMULS + + case OMUL_ | gc.TFLOAT64: + a = s390x.AFMUL + + case ODIV_ | gc.TINT8, + ODIV_ | gc.TINT16, + ODIV_ | gc.TINT32, + ODIV_ | gc.TINT64: + a = s390x.ADIVD + + case ODIV_ | gc.TUINT8, + ODIV_ | gc.TUINT16, + ODIV_ | gc.TUINT32, + ODIV_ | gc.TPTR32, + ODIV_ | gc.TUINT64, + ODIV_ | gc.TPTR64: + a = s390x.ADIVDU + + case ODIV_ | gc.TFLOAT32: + a = s390x.AFDIVS + + case ODIV_ | gc.TFLOAT64: + a = s390x.AFDIV + + case OSQRT_ | gc.TFLOAT64: + a = s390x.AFSQRT + + case OLROT_ | gc.TUINT32, + OLROT_ | gc.TPTR32, + OLROT_ | gc.TINT32: + a = s390x.ARLL + + case OLROT_ | gc.TUINT64, + OLROT_ | gc.TPTR64, + OLROT_ | gc.TINT64: + a = s390x.ARLLG + } + + return a +} + +const ( + ODynam = 1 << 0 + OAddable = 1 << 1 +) + +var clean [20]gc.Node + +var cleani int = 0 + +func sudoclean() { + if clean[cleani-1].Op != gc.OEMPTY { + gc.Regfree(&clean[cleani-1]) + } + if clean[cleani-2].Op != gc.OEMPTY { + gc.Regfree(&clean[cleani-2]) + } + cleani -= 2 +} + +/* + * generate code to compute address of n, + * a reference to a (perhaps nested) field inside + * an array or struct. + * return 0 on failure, 1 on success. + * on success, leaves usable address in a. + * + * caller is responsible for calling sudoclean + * after successful sudoaddable, + * to release the register used for a. + */ +func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { + if n.Type == nil { + return false + } + + *a = obj.Addr{} + + switch n.Op { + case gc.OLITERAL: + if !gc.Isconst(n, gc.CTINT) { + return false + } + v := n.Int64() + switch as { + default: + return false + + // operations that can cope with a 32-bit immediate + // TODO(mundaym): logical operations can work on high bits + case s390x.AADD, + s390x.AADDC, + s390x.ASUB, + s390x.AMULLW, + s390x.AAND, + s390x.AOR, + s390x.AXOR, + s390x.ASLD, + s390x.ASLW, + s390x.ASRAW, + s390x.ASRAD, + s390x.ASRW, + s390x.ASRD, + s390x.AMOVB, + s390x.AMOVBZ, + s390x.AMOVH, + s390x.AMOVHZ, + s390x.AMOVW, + s390x.AMOVWZ, + s390x.AMOVD: + if int64(int32(v)) != v { + return false + } + + // for comparisons avoid immediates unless they can + // fit into a int8/uint8 + // this favours combined compare and branch instructions + case s390x.ACMP: + if int64(int8(v)) != v { + return false + } + case s390x.ACMPU: + if int64(uint8(v)) != v { + return false + } + } + + cleani += 2 + reg := &clean[cleani-1] + reg1 := &clean[cleani-2] + reg.Op = gc.OEMPTY + reg1.Op = gc.OEMPTY + gc.Naddr(a, n) + return true + + case gc.ODOT, + gc.ODOTPTR: + cleani += 2 + reg := &clean[cleani-1] + reg1 := &clean[cleani-2] + reg.Op = gc.OEMPTY + reg1.Op = gc.OEMPTY + var nn *gc.Node + var oary [10]int64 + o := gc.Dotoffset(n, oary[:], &nn) + if nn == nil { + sudoclean() + return false + } + + if nn.Addable && o == 1 && oary[0] >= 0 { + // directly addressable set of DOTs + n1 := *nn + + n1.Type = n.Type + n1.Xoffset += oary[0] + // check that the offset fits into a 12-bit displacement + if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 { + sudoclean() + return false + } + gc.Naddr(a, &n1) + return true + } + + gc.Regalloc(reg, gc.Types[gc.Tptr], nil) + n1 := *reg + n1.Op = gc.OINDREG + if oary[0] >= 0 { + gc.Agen(nn, reg) + n1.Xoffset = oary[0] + } else { + gc.Cgen(nn, reg) + gc.Cgen_checknil(reg) + n1.Xoffset = -(oary[0] + 1) + } + + for i := 1; i < o; i++ { + if oary[i] >= 0 { + gc.Fatalf("can't happen") + } + gins(s390x.AMOVD, &n1, reg) + gc.Cgen_checknil(reg) + n1.Xoffset = -(oary[i] + 1) + } + + a.Type = obj.TYPE_NONE + a.Index = 0 + // check that the offset fits into a 12-bit displacement + if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 { + tmp := n1 + tmp.Op = gc.OREGISTER + tmp.Type = gc.Types[gc.Tptr] + tmp.Xoffset = 0 + gc.Cgen_checknil(&tmp) + ginscon(s390x.AADD, n1.Xoffset, &tmp) + n1.Xoffset = 0 + } + gc.Naddr(a, &n1) + return true + } + + return false +} diff --git a/src/cmd/compile/internal/s390x/peep.go b/src/cmd/compile/internal/s390x/peep.go new file mode 100644 index 0000000000..86258d67da --- /dev/null +++ b/src/cmd/compile/internal/s390x/peep.go @@ -0,0 +1,1664 @@ +// Derived from Inferno utils/6c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/s390x" + "fmt" +) + +type usage int + +const ( + _None usage = iota // no usage found + _Read // only read from + _ReadWriteSame // both read from and written to in a single operand + _Write // only written to + _ReadWriteDiff // both read from and written to in different operands +) + +var gactive uint32 + +func peep(firstp *obj.Prog) { + g := gc.Flowstart(firstp, nil) + if g == nil { + return + } + gactive = 0 + + run := func(name string, pass func(r *gc.Flow) int) int { + n := pass(g.Start) + if gc.Debug['P'] != 0 { + fmt.Println(name, ":", n) + } + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + gc.Dumpit(name, g.Start, 0) + } + return n + } + + for { + n := 0 + n += run("constant propagation", constantPropagation) + n += run("copy propagation", copyPropagation) + n += run("cast propagation", castPropagation) + n += run("remove load-hit-stores", removeLoadHitStores) + n += run("dead code elimination", deadCodeElimination) + if n == 0 { + break + } + } + run("fuse op moves", fuseOpMoves) + run("fuse clears", fuseClear) + run("load pipelining", loadPipelining) + run("fuse compare branch", fuseCompareBranch) + run("simplify ops", simplifyOps) + run("dead code elimination", deadCodeElimination) + + // TODO(mundaym): load/store multiple aren't currently handled by copyu + // so this pass must be last. + run("fuse multiple", fuseMultiple) + + gc.Flowend(g) +} + +func pushback(r0 *gc.Flow) { + var r *gc.Flow + + var b *gc.Flow + p0 := r0.Prog + for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { + p := r.Prog + if p.As != obj.ANOP { + if !(isReg(&p.From) || isConst(&p.From)) || !isReg(&p.To) { + break + } + if copyu(p, &p0.To, nil) != _None || copyu(p0, &p.To, nil) != _None { + break + } + } + + if p.As == obj.ACALL { + break + } + b = r + } + + if b == nil { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("no pushback: %v\n", r0.Prog) + if r != nil { + fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) + } + } + + return + } + + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("pushback\n") + for r := b; ; r = r.Link { + fmt.Printf("\t%v\n", r.Prog) + if r == r0 { + break + } + } + } + + t := obj.Prog(*r0.Prog) + for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) { + p0 = r.Link.Prog + p := r.Prog + p0.As = p.As + p0.Lineno = p.Lineno + p0.From = p.From + p0.To = p.To + p0.From3 = p.From3 + p0.Reg = p.Reg + p0.RegTo2 = p.RegTo2 + if r == b { + break + } + } + + p0 = r.Prog + p0.As = t.As + p0.Lineno = t.Lineno + p0.From = t.From + p0.To = t.To + p0.From3 = t.From3 + p0.Reg = t.Reg + p0.RegTo2 = t.RegTo2 + + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tafter\n") + for r := (*gc.Flow)(b); ; r = r.Link { + fmt.Printf("\t%v\n", r.Prog) + if r == r0 { + break + } + } + } +} + +// excise replaces the given instruction with a NOP and clears +// its operands. +func excise(r *gc.Flow) { + p := r.Prog + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("%v ===delete===\n", p) + } + obj.Nopout(p) + gc.Ostats.Ndelmov++ +} + +// isZero returns true if a is either the constant 0 or the register +// REGZERO. +func isZero(a *obj.Addr) bool { + if a.Type == obj.TYPE_CONST && a.Offset == 0 { + return true + } + if a.Type == obj.TYPE_REG && a.Reg == s390x.REGZERO { + return true + } + return false +} + +// isReg returns true if a is a general purpose or floating point +// register (GPR or FPR). +// +// TODO(mundaym): currently this excludes REGZER0, but not other +// special registers. +func isReg(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && + s390x.REG_R0 <= a.Reg && + a.Reg <= s390x.REG_F15 && + a.Reg != s390x.REGZERO +} + +// isGPR returns true if a is a general purpose register (GPR). +// REGZERO is treated as a GPR. +func isGPR(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && + s390x.REG_R0 <= a.Reg && + a.Reg <= s390x.REG_R15 +} + +// isFPR returns true if a is a floating point register (FPR). +func isFPR(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && + s390x.REG_F0 <= a.Reg && + a.Reg <= s390x.REG_F15 +} + +// isConst returns true if a refers to a constant (integer or +// floating point, not string currently). +func isConst(a *obj.Addr) bool { + return a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_FCONST +} + +// isBDMem returns true if a refers to a memory location addressable by a +// base register (B) and a displacement (D), such as: +// x+8(R1) +// and +// 0(R10) +// It returns false if the address contains an index register (X) such as: +// 16(R1)(R2*1) +// or if a relocation is required. +func isBDMem(a *obj.Addr) bool { + return a.Type == obj.TYPE_MEM && + a.Index == 0 && + (a.Name == obj.NAME_NONE || a.Name == obj.NAME_AUTO || a.Name == obj.NAME_PARAM) +} + +// the idea is to substitute +// one register for another +// from one MOV to another +// MOV a, R1 +// ADD b, R1 / no use of R2 +// MOV R1, R2 +// would be converted to +// MOV a, R2 +// ADD b, R2 +// MOV R2, R1 +// hopefully, then the former or latter MOV +// will be eliminated by copy propagation. +// +// r0 (the argument, not the register) is the MOV at the end of the +// above sequences. subprop returns true if it modified any instructions. +func subprop(r0 *gc.Flow) bool { + p := r0.Prog + v1 := &p.From + if !isReg(v1) { + return false + } + v2 := &p.To + if !isReg(v2) { + return false + } + cast := false + switch p.As { + case s390x.AMOVW, s390x.AMOVWZ, + s390x.AMOVH, s390x.AMOVHZ, + s390x.AMOVB, s390x.AMOVBZ: + cast = true + } + for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { + if gc.Uniqs(r) == nil { + break + } + p = r.Prog + switch copyu(p, v1, nil) { + case _Write, _ReadWriteDiff: + if p.As == obj.ACALL { + return false + } + if (!cast || p.As == r0.Prog.As) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { + copysub(&p.To, v1, v2) + for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { + p = r.Prog + copysub(&p.From, v1, v2) + copysub1(p, v1, v2) + copysub(&p.To, v1, v2) + } + v1.Reg, v2.Reg = v2.Reg, v1.Reg + return true + } + if cast { + return false + } + case _ReadWriteSame: + if cast { + return false + } + } + if copyu(p, v2, nil) != _None { + return false + } + } + return false +} + +// The idea is to remove redundant copies. +// v1->v2 F=0 +// (use v2 s/v2/v1/)* +// set v1 F=1 +// use v2 return fail (v1->v2 move must remain) +// ----------------- +// v1->v2 F=0 +// (use v2 s/v2/v1/)* +// set v1 F=1 +// set v2 return success (caller can remove v1->v2 move) +func copyprop(r *gc.Flow) bool { + p := r.Prog + + canSub := false + switch p.As { + case s390x.AFMOVS, s390x.AFMOVD, s390x.AMOVD: + canSub = true + default: + for rr := gc.Uniqp(r); rr != nil; rr = gc.Uniqp(rr) { + if gc.Uniqs(rr) == nil { + break + } + switch copyu(rr.Prog, &p.From, nil) { + case _Read, _None: + continue + } + // write + if rr.Prog.As == p.As { + canSub = true + } + break + } + } + if !canSub { + return false + } + if copyas(&p.From, &p.To) { + return true + } + + gactive++ + return copy1(&p.From, &p.To, r.S1, 0) +} + +// copy1 replaces uses of v2 with v1 starting at r and returns true if +// all uses were rewritten. +func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { + if uint32(r.Active) == gactive { + return true + } + r.Active = int32(gactive) + for ; r != nil; r = r.S1 { + p := r.Prog + if f == 0 && gc.Uniqp(r) == nil { + // Multiple predecessors; conservatively + // assume v1 was set on other path + f = 1 + } + t := copyu(p, v2, nil) + switch t { + case _ReadWriteSame: + return false + case _Write: + return true + case _Read, _ReadWriteDiff: + if f != 0 { + return false + } + if copyu(p, v2, v1) != 0 { + return false + } + if t == _ReadWriteDiff { + return true + } + } + if f == 0 { + switch copyu(p, v1, nil) { + case _ReadWriteSame, _ReadWriteDiff, _Write: + f = 1 + } + } + if r.S2 != nil { + if !copy1(v1, v2, r.S2, f) { + return false + } + } + } + return true +} + +// If s==nil, copyu returns the set/use of v in p; otherwise, it +// modifies p to replace reads of v with reads of s and returns 0 for +// success or non-zero for failure. +// +// If s==nil, copy returns one of the following values: +// _Read if v only used +// _ReadWriteSame if v is set and used in one address (read-alter-rewrite; +// can't substitute) +// _Write if v is only set +// _ReadWriteDiff if v is set in one address and used in another (so addresses +// can be rewritten independently) +// _None otherwise (not touched) +func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) usage { + if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST { + // Currently we never generate a From3 with anything other than a constant in it. + fmt.Printf("copyu: From3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) + } + + switch p.As { + default: + fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As)) + return _ReadWriteSame + + case // read p.From, write p.To + s390x.AMOVH, + s390x.AMOVHZ, + s390x.AMOVB, + s390x.AMOVBZ, + s390x.AMOVW, + s390x.AMOVWZ, + s390x.AMOVD, + s390x.ANEG, + s390x.AADDME, + s390x.AADDZE, + s390x.ASUBME, + s390x.ASUBZE, + s390x.AFMOVS, + s390x.AFMOVD, + s390x.ALEDBR, + s390x.AFNEG, + s390x.ALDEBR, + s390x.ACLFEBR, + s390x.ACLGEBR, + s390x.ACLFDBR, + s390x.ACLGDBR, + s390x.ACFEBRA, + s390x.ACGEBRA, + s390x.ACFDBRA, + s390x.ACGDBRA, + s390x.ACELFBR, + s390x.ACELGBR, + s390x.ACDLFBR, + s390x.ACDLGBR, + s390x.ACEFBRA, + s390x.ACEGBRA, + s390x.ACDFBRA, + s390x.ACDGBRA, + s390x.AFSQRT: + + if s != nil { + copysub(&p.From, v, s) + + // Update only indirect uses of v in p.To + if !copyas(&p.To, v) { + copysub(&p.To, v, s) + } + return _None + } + + if copyas(&p.To, v) { + // Fix up implicit from + if p.From.Type == obj.TYPE_NONE { + p.From = p.To + } + if copyau(&p.From, v) { + return _ReadWriteDiff + } + return _Write + } + + if copyau(&p.From, v) { + return _Read + } + if copyau(&p.To, v) { + // p.To only indirectly uses v + return _Read + } + + return _None + + // read p.From, read p.Reg, write p.To + case s390x.AADD, + s390x.AADDC, + s390x.AADDE, + s390x.ASUB, + s390x.ASLW, + s390x.ASRW, + s390x.ASRAW, + s390x.ASLD, + s390x.ASRD, + s390x.ASRAD, + s390x.ARLL, + s390x.ARLLG, + s390x.AOR, + s390x.AORN, + s390x.AAND, + s390x.AANDN, + s390x.ANAND, + s390x.ANOR, + s390x.AXOR, + s390x.AMULLW, + s390x.AMULLD, + s390x.AMULHD, + s390x.AMULHDU, + s390x.ADIVW, + s390x.ADIVD, + s390x.ADIVWU, + s390x.ADIVDU, + s390x.AFADDS, + s390x.AFADD, + s390x.AFSUBS, + s390x.AFSUB, + s390x.AFMULS, + s390x.AFMUL, + s390x.AFDIVS, + s390x.AFDIV: + if s != nil { + copysub(&p.From, v, s) + copysub1(p, v, s) + + // Update only indirect uses of v in p.To + if !copyas(&p.To, v) { + copysub(&p.To, v, s) + } + } + + if copyas(&p.To, v) { + if p.Reg == 0 { + p.Reg = p.To.Reg + } + if copyau(&p.From, v) || copyau1(p, v) { + return _ReadWriteDiff + } + return _Write + } + + if copyau(&p.From, v) { + return _Read + } + if copyau1(p, v) { + return _Read + } + if copyau(&p.To, v) { + return _Read + } + return _None + + case s390x.ABEQ, + s390x.ABGT, + s390x.ABGE, + s390x.ABLT, + s390x.ABLE, + s390x.ABNE, + s390x.ABVC, + s390x.ABVS: + return _None + + case obj.ACHECKNIL, // read p.From + s390x.ACMP, // read p.From, read p.To + s390x.ACMPU, + s390x.ACMPW, + s390x.ACMPWU, + s390x.AFCMPO, + s390x.AFCMPU, + s390x.ACEBR, + s390x.AMVC, + s390x.ACLC, + s390x.AXC, + s390x.AOC, + s390x.ANC: + if s != nil { + copysub(&p.From, v, s) + copysub(&p.To, v, s) + return _None + } + + if copyau(&p.From, v) { + return _Read + } + if copyau(&p.To, v) { + return _Read + } + return _None + + case s390x.ACMPBNE, s390x.ACMPBEQ, + s390x.ACMPBLT, s390x.ACMPBLE, + s390x.ACMPBGT, s390x.ACMPBGE, + s390x.ACMPUBNE, s390x.ACMPUBEQ, + s390x.ACMPUBLT, s390x.ACMPUBLE, + s390x.ACMPUBGT, s390x.ACMPUBGE: + if s != nil { + copysub(&p.From, v, s) + copysub1(p, v, s) + return _None + } + if copyau(&p.From, v) { + return _Read + } + if copyau1(p, v) { + return _Read + } + return _None + + case s390x.ACLEAR: + if s != nil { + copysub(&p.To, v, s) + return _None + } + if copyau(&p.To, v) { + return _Read + } + return _None + + // go never generates a branch to a GPR + // read p.To + case s390x.ABR: + if s != nil { + copysub(&p.To, v, s) + return _None + } + + if copyau(&p.To, v) { + return _Read + } + return _None + + case obj.ARET, obj.AUNDEF: + if s != nil { + return _None + } + + // All registers die at this point, so claim + // everything is set (and not used). + return _Write + + case s390x.ABL: + if v.Type == obj.TYPE_REG { + if s390x.REGARG != -1 && v.Reg == s390x.REGARG { + return _ReadWriteSame + } + if p.From.Type == obj.TYPE_REG && p.From.Reg == v.Reg { + return _ReadWriteSame + } + if v.Reg == s390x.REGZERO { + // Deliberately inserted nops set R0. + return _ReadWriteSame + } + if v.Reg == s390x.REGCTXT { + // Context register for closures. + // TODO(mundaym): not sure if we need to exclude this. + return _ReadWriteSame + } + } + if s != nil { + copysub(&p.To, v, s) + return _None + } + if copyau(&p.To, v) { + return _ReadWriteDiff + } + return _Write + + case obj.ATEXT: + if v.Type == obj.TYPE_REG { + if v.Reg == s390x.REGARG { + return _Write + } + } + return _None + + case obj.APCDATA, + obj.AFUNCDATA, + obj.AVARDEF, + obj.AVARKILL, + obj.AVARLIVE, + obj.AUSEFIELD, + obj.ANOP: + return _None + } +} + +// copyas returns 1 if a and v address the same register. +// +// If a is the from operand, this means this operation reads the +// register in v. If a is the to operand, this means this operation +// writes the register in v. +func copyas(a *obj.Addr, v *obj.Addr) bool { + if isReg(v) { + if a.Type == v.Type { + if a.Reg == v.Reg { + return true + } + } + } + return false +} + +// copyau returns 1 if a either directly or indirectly addresses the +// same register as v. +// +// If a is the from operand, this means this operation reads the +// register in v. If a is the to operand, this means the operation +// either reads or writes the register in v (if !copyas(a, v), then +// the operation reads the register in v). +func copyau(a *obj.Addr, v *obj.Addr) bool { + if copyas(a, v) { + return true + } + if v.Type == obj.TYPE_REG { + if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { + if v.Reg == a.Reg { + return true + } + } + } + return false +} + +// copyau1 returns 1 if p.Reg references the same register as v and v +// is a direct reference. +func copyau1(p *obj.Prog, v *obj.Addr) bool { + if isReg(v) && v.Reg != 0 { + if p.Reg == v.Reg { + return true + } + } + return false +} + +// copysub replaces v.Reg with s.Reg if a.Reg and v.Reg are direct +// references to the same register. +func copysub(a, v, s *obj.Addr) { + if copyau(a, v) { + a.Reg = s.Reg + } +} + +// copysub1 replaces p.Reg with s.Reg if p.Reg and v.Reg are direct +// references to the same register. +func copysub1(p *obj.Prog, v, s *obj.Addr) { + if copyau1(p, v) { + p.Reg = s.Reg + } +} + +func sameaddr(a *obj.Addr, v *obj.Addr) bool { + if a.Type != v.Type { + return false + } + if isReg(v) && a.Reg == v.Reg { + return true + } + if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { + // TODO(mundaym): is the offset enough here? Node? + if v.Offset == a.Offset { + return true + } + } + return false +} + +func smallindir(a *obj.Addr, reg *obj.Addr) bool { + return reg.Type == obj.TYPE_REG && + a.Type == obj.TYPE_MEM && + a.Reg == reg.Reg && + 0 <= a.Offset && a.Offset < 4096 +} + +func stackaddr(a *obj.Addr) bool { + // TODO(mundaym): the name implies this should check + // for TYPE_ADDR with a base register REGSP. + return a.Type == obj.TYPE_REG && a.Reg == s390x.REGSP +} + +// isMove returns true if p is a move. Moves may imply +// sign/zero extension. +func isMove(p *obj.Prog) bool { + switch p.As { + case s390x.AMOVD, + s390x.AMOVW, s390x.AMOVWZ, + s390x.AMOVH, s390x.AMOVHZ, + s390x.AMOVB, s390x.AMOVBZ, + s390x.AFMOVD, s390x.AFMOVS: + return true + } + return false +} + +// isLoad returns true if p is a move from memory to a register. +func isLoad(p *obj.Prog) bool { + if !isMove(p) { + return false + } + if !(isGPR(&p.To) || isFPR(&p.To)) { + return false + } + if p.From.Type != obj.TYPE_MEM { + return false + } + return true +} + +// isStore returns true if p is a move from a register to memory. +func isStore(p *obj.Prog) bool { + if !isMove(p) { + return false + } + if !(isGPR(&p.From) || isFPR(&p.From) || isConst(&p.From)) { + return false + } + if p.To.Type != obj.TYPE_MEM { + return false + } + return true +} + +// sameStackMem returns true if a and b are both memory operands +// and address the same location which must reside on the stack. +func sameStackMem(a, b *obj.Addr) bool { + if a.Type != obj.TYPE_MEM || + b.Type != obj.TYPE_MEM || + a.Name != b.Name || + a.Sym != b.Sym || + a.Node != b.Node || + a.Reg != b.Reg || + a.Index != b.Index || + a.Offset != b.Offset { + return false + } + switch a.Name { + case obj.NAME_NONE: + return a.Reg == s390x.REGSP + case obj.NAME_PARAM, obj.NAME_AUTO: + // params and autos are always on the stack + return true + } + return false +} + +// removeLoadHitStores trys to remove loads that take place +// immediately after a store to the same location. Returns +// true if load-hit-stores were removed. +// +// For example: +// MOVD R1, 0(R15) +// MOVD 0(R15), R2 +// Would become: +// MOVD R1, 0(R15) +// MOVD R1, R2 +func removeLoadHitStores(r *gc.Flow) int { + n := 0 + for ; r != nil; r = r.Link { + p := r.Prog + if !isStore(p) { + continue + } + for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { + pp := rr.Prog + if gc.Uniqp(rr) == nil { + break + } + if pp.As == obj.ANOP { + continue + } + if isLoad(pp) && sameStackMem(&p.To, &pp.From) { + if size(p.As) >= size(pp.As) && isGPR(&p.From) == isGPR(&pp.To) { + pp.From = p.From + } + } + if !isMove(pp) || isStore(pp) { + break + } + if copyau(&p.From, &pp.To) { + break + } + } + } + return n +} + +// size returns the width of the given move. +func size(as obj.As) int { + switch as { + case s390x.AMOVD, s390x.AFMOVD: + return 8 + case s390x.AMOVW, s390x.AMOVWZ, s390x.AFMOVS: + return 4 + case s390x.AMOVH, s390x.AMOVHZ: + return 2 + case s390x.AMOVB, s390x.AMOVBZ: + return 1 + } + return -1 +} + +// castPropagation tries to eliminate unecessary casts. +// +// For example: +// MOVHZ R1, R2 // uint16 +// MOVB R2, 0(R15) // int8 +// Can be simplified to: +// MOVB R1, 0(R15) +func castPropagation(r *gc.Flow) int { + n := 0 + for ; r != nil; r = r.Link { + p := r.Prog + if !isMove(p) || !isGPR(&p.To) { + continue + } + + // r is a move with a destination register + var move *gc.Flow + for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { + if gc.Uniqp(rr) == nil { + // branch target: leave alone + break + } + pp := rr.Prog + if isMove(pp) && copyas(&pp.From, &p.To) { + if pp.To.Type == obj.TYPE_MEM { + if p.From.Type == obj.TYPE_MEM || + p.From.Type == obj.TYPE_ADDR { + break + } + if p.From.Type == obj.TYPE_CONST && + int64(int16(p.From.Offset)) != p.From.Offset { + break + } + } + move = rr + break + } + if pp.As == obj.ANOP { + continue + } + break + } + if move == nil { + continue + } + + // we have a move that reads from our destination reg, check if any future + // instructions also read from the reg + mp := move.Prog + if !copyas(&mp.From, &mp.To) { + safe := false + for rr := gc.Uniqs(move); rr != nil; rr = gc.Uniqs(rr) { + if gc.Uniqp(rr) == nil { + break + } + switch copyu(rr.Prog, &p.To, nil) { + case _None: + continue + case _Write: + safe = true + } + break + } + if !safe { + continue + } + } + + // at this point we have something like: + // MOV* const/mem/reg, reg + // MOV* reg, reg/mem + // now check if this is a cast that cannot be forward propagated + execute := false + if p.As == mp.As || isZero(&p.From) || size(p.As) == size(mp.As) { + execute = true + } else if isGPR(&p.From) && size(p.As) >= size(mp.As) { + execute = true + } + + if execute { + mp.From = p.From + excise(r) + n++ + } + } + return n +} + +// fuseClear merges memory clear operations. +// +// Looks for this pattern (sequence of clears): +// MOVD R0, n(R15) +// MOVD R0, n+8(R15) +// MOVD R0, n+16(R15) +// Replaces with: +// CLEAR $24, n(R15) +func fuseClear(r *gc.Flow) int { + n := 0 + var align int64 + var clear *obj.Prog + for ; r != nil; r = r.Link { + // If there is a branch into the instruction stream then + // we can't fuse into previous instructions. + if gc.Uniqp(r) == nil { + clear = nil + } + + p := r.Prog + if p.As == obj.ANOP { + continue + } + if p.As == s390x.AXC { + if p.From.Reg == p.To.Reg && p.From.Offset == p.To.Offset { + // TODO(mundaym): merge clears? + p.As = s390x.ACLEAR + p.From.Offset = p.From3.Offset + p.From3 = nil + p.From.Type = obj.TYPE_CONST + p.From.Reg = 0 + clear = p + } else { + clear = nil + } + continue + } + + // Is our source a constant zero? + if !isZero(&p.From) { + clear = nil + continue + } + + // Are we moving to memory? + if p.To.Type != obj.TYPE_MEM || + p.To.Index != 0 || + p.To.Offset >= 4096 || + !(p.To.Name == obj.NAME_NONE || p.To.Name == obj.NAME_AUTO || p.To.Name == obj.NAME_PARAM) { + clear = nil + continue + } + + size := int64(0) + switch p.As { + default: + clear = nil + continue + case s390x.AMOVB, s390x.AMOVBZ: + size = 1 + case s390x.AMOVH, s390x.AMOVHZ: + size = 2 + case s390x.AMOVW, s390x.AMOVWZ: + size = 4 + case s390x.AMOVD: + size = 8 + } + + // doubleword aligned clears should be kept doubleword + // aligned + if (size == 8 && align != 8) || (size != 8 && align == 8) { + clear = nil + } + + if clear != nil && + clear.To.Reg == p.To.Reg && + clear.To.Name == p.To.Name && + clear.To.Node == p.To.Node && + clear.To.Sym == p.To.Sym { + + min := clear.To.Offset + max := clear.To.Offset + clear.From.Offset + + // previous clear is already clearing this region + if min <= p.To.Offset && max >= p.To.Offset+size { + excise(r) + n++ + continue + } + + // merge forwards + if max == p.To.Offset { + clear.From.Offset += size + excise(r) + n++ + continue + } + + // merge backwards + if min-size == p.To.Offset { + clear.From.Offset += size + clear.To.Offset -= size + excise(r) + n++ + continue + } + } + + // transform into clear + p.From.Type = obj.TYPE_CONST + p.From.Offset = size + p.From.Reg = 0 + p.As = s390x.ACLEAR + clear = p + align = size + } + return n +} + +// fuseMultiple merges memory loads and stores into load multiple and +// store multiple operations. +// +// Looks for this pattern (sequence of loads or stores): +// MOVD R1, 0(R15) +// MOVD R2, 8(R15) +// MOVD R3, 16(R15) +// Replaces with: +// STMG R1, R3, 0(R15) +func fuseMultiple(r *gc.Flow) int { + n := 0 + var fused *obj.Prog + for ; r != nil; r = r.Link { + // If there is a branch into the instruction stream then + // we can't fuse into previous instructions. + if gc.Uniqp(r) == nil { + fused = nil + } + + p := r.Prog + + isStore := isGPR(&p.From) && isBDMem(&p.To) + isLoad := isGPR(&p.To) && isBDMem(&p.From) + + // are we a candidate? + size := int64(0) + switch p.As { + default: + fused = nil + continue + case obj.ANOP: + // skip over nops + continue + case s390x.AMOVW, s390x.AMOVWZ: + size = 4 + // TODO(mundaym): 32-bit load multiple is currently not supported + // as it requires sign/zero extension. + if !isStore { + fused = nil + continue + } + case s390x.AMOVD: + size = 8 + if !isLoad && !isStore { + fused = nil + continue + } + } + + // If we merge two loads/stores with different source/destination Nodes + // then we will lose a reference the second Node which means that the + // compiler might mark the Node as unused and free its slot on the stack. + // TODO(mundaym): allow this by adding a dummy reference to the Node. + if fused == nil || + fused.From.Node != p.From.Node || + fused.From.Type != p.From.Type || + fused.To.Node != p.To.Node || + fused.To.Type != p.To.Type { + fused = p + continue + } + + // check two addresses + ca := func(a, b *obj.Addr, offset int64) bool { + return a.Reg == b.Reg && a.Offset+offset == b.Offset && + a.Sym == b.Sym && a.Name == b.Name + } + + switch fused.As { + default: + fused = p + case s390x.AMOVW, s390x.AMOVWZ: + if size == 4 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 4) { + fused.As = s390x.ASTMY + fused.Reg = p.From.Reg + excise(r) + n++ + } else { + fused = p + } + case s390x.AMOVD: + if size == 8 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 8) { + fused.As = s390x.ASTMG + fused.Reg = p.From.Reg + excise(r) + n++ + } else if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, 8) { + fused.As = s390x.ALMG + fused.Reg = fused.To.Reg + fused.To.Reg = p.To.Reg + excise(r) + n++ + } else { + fused = p + } + case s390x.ASTMG, s390x.ASTMY: + if (fused.As == s390x.ASTMY && size != 4) || + (fused.As == s390x.ASTMG && size != 8) { + fused = p + continue + } + offset := size * int64(fused.Reg-fused.From.Reg+1) + if fused.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, offset) { + fused.Reg = p.From.Reg + excise(r) + n++ + } else { + fused = p + } + case s390x.ALMG: + offset := 8 * int64(fused.To.Reg-fused.Reg+1) + if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, offset) { + fused.To.Reg = p.To.Reg + excise(r) + n++ + } else { + fused = p + } + } + } + return n +} + +// simplifyOps looks for side-effect free ops that can be removed or +// replaced with moves. +// +// For example: +// XOR $0, R1 => NOP +// ADD $0, R1, R2 => MOVD R1, R2 +func simplifyOps(r *gc.Flow) int { + n := 0 + for ; r != nil; r = r.Link { + p := r.Prog + + // if the target is R0 then this is a required NOP + if isGPR(&p.To) && p.To.Reg == s390x.REGZERO { + continue + } + + switch p.As { + case s390x.AADD, s390x.ASUB, + s390x.AOR, s390x.AXOR, + s390x.ASLW, s390x.ASRW, s390x.ASRAW, + s390x.ASLD, s390x.ASRD, s390x.ASRAD, + s390x.ARLL, s390x.ARLLG: + if isZero(&p.From) && isGPR(&p.To) { + if p.Reg == 0 || p.Reg == p.To.Reg { + excise(r) + n++ + } else { + p.As = s390x.AMOVD + p.From.Type = obj.TYPE_REG + p.From.Reg = p.Reg + p.Reg = 0 + } + } + case s390x.AMULLW, s390x.AAND: + if isZero(&p.From) && isGPR(&p.To) { + p.As = s390x.AMOVD + p.From.Type = obj.TYPE_REG + p.From.Reg = s390x.REGZERO + p.Reg = 0 + } + } + } + return n +} + +// fuseOpMoves looks for moves following 2-operand operations and trys to merge them into +// a 3-operand operation. +// +// For example: +// ADD R1, R2 +// MOVD R2, R3 +// might become +// ADD R1, R2, R3 +func fuseOpMoves(r *gc.Flow) int { + n := 0 + for ; r != nil; r = r.Link { + p := r.Prog + switch p.As { + case s390x.AADD: + case s390x.ASUB: + if isConst(&p.From) && int64(int16(p.From.Offset)) != p.From.Offset { + continue + } + case s390x.ASLW, + s390x.ASRW, + s390x.ASRAW, + s390x.ASLD, + s390x.ASRD, + s390x.ASRAD, + s390x.ARLL, + s390x.ARLLG: + // ok - p.From will be a reg or a constant + case s390x.AOR, + s390x.AORN, + s390x.AAND, + s390x.AANDN, + s390x.ANAND, + s390x.ANOR, + s390x.AXOR, + s390x.AMULLW, + s390x.AMULLD: + if isConst(&p.From) { + // these instructions can either use 3 register form + // or have an immediate but not both + continue + } + default: + continue + } + + if p.Reg != 0 && p.Reg != p.To.Reg { + continue + } + + var move *gc.Flow + rr := gc.Uniqs(r) + for { + if rr == nil || gc.Uniqp(rr) == nil || rr == r { + break + } + pp := rr.Prog + switch copyu(pp, &p.To, nil) { + case _None: + rr = gc.Uniqs(rr) + continue + case _Read: + if move == nil && pp.As == s390x.AMOVD && isGPR(&pp.From) && isGPR(&pp.To) { + move = rr + rr = gc.Uniqs(rr) + continue + } + case _Write: + if move == nil { + // dead code + excise(r) + n++ + } else { + for prev := gc.Uniqp(move); prev != r; prev = gc.Uniqp(prev) { + if copyu(prev.Prog, &move.Prog.To, nil) != 0 { + move = nil + break + } + } + if move == nil { + break + } + p.Reg, p.To.Reg = p.To.Reg, move.Prog.To.Reg + excise(move) + n++ + + // clean up + if p.From.Reg == p.To.Reg && isCommutative(p.As) { + p.From.Reg, p.Reg = p.Reg, 0 + } + if p.To.Reg == p.Reg { + p.Reg = 0 + } + // we could try again if p has become a 2-operand op + // but in testing nothing extra was extracted + } + } + break + } + } + return n +} + +// isCommutative returns true if the order of input operands +// does not affect the result. For example: +// x + y == y + x so ADD is commutative +// x ^ y == y ^ x so XOR is commutative +func isCommutative(as obj.As) bool { + switch as { + case s390x.AADD, + s390x.AOR, + s390x.AAND, + s390x.AXOR, + s390x.AMULLW, + s390x.AMULLD: + return true + } + return false +} + +// applyCast applies the cast implied by the given move +// instruction to v and returns the result. +func applyCast(cast obj.As, v int64) int64 { + switch cast { + case s390x.AMOVWZ: + return int64(uint32(v)) + case s390x.AMOVHZ: + return int64(uint16(v)) + case s390x.AMOVBZ: + return int64(uint8(v)) + case s390x.AMOVW: + return int64(int32(v)) + case s390x.AMOVH: + return int64(int16(v)) + case s390x.AMOVB: + return int64(int8(v)) + } + return v +} + +// constantPropagation removes redundant constant copies. +func constantPropagation(r *gc.Flow) int { + n := 0 + // find MOV $con,R followed by + // another MOV $con,R without + // setting R in the interim + for ; r != nil; r = r.Link { + p := r.Prog + if isMove(p) { + if !isReg(&p.To) { + continue + } + if !isConst(&p.From) { + continue + } + } else { + continue + } + + rr := r + for { + rr = gc.Uniqs(rr) + if rr == nil || rr == r { + break + } + if gc.Uniqp(rr) == nil { + break + } + + pp := rr.Prog + t := copyu(pp, &p.To, nil) + switch t { + case _None: + continue + case _Read: + if !isGPR(&pp.From) || !isMove(pp) { + continue + } + if p.From.Type == obj.TYPE_CONST { + v := applyCast(p.As, p.From.Offset) + if isGPR(&pp.To) { + if int64(int32(v)) == v || ((v>>32)<<32) == v { + pp.From.Reg = 0 + pp.From.Offset = v + pp.From.Type = obj.TYPE_CONST + n++ + } + } else if int64(int16(v)) == v { + pp.From.Reg = 0 + pp.From.Offset = v + pp.From.Type = obj.TYPE_CONST + n++ + } + } + continue + case _Write: + if p.As != pp.As || p.From.Type != pp.From.Type { + break + } + if p.From.Type == obj.TYPE_CONST && p.From.Offset == pp.From.Offset { + excise(rr) + n++ + continue + } else if p.From.Type == obj.TYPE_FCONST { + if p.From.Val.(float64) == pp.From.Val.(float64) { + excise(rr) + n++ + continue + } + } + } + break + } + } + return n +} + +// copyPropagation tries to eliminate register-to-register moves. +func copyPropagation(r *gc.Flow) int { + n := 0 + for ; r != nil; r = r.Link { + p := r.Prog + if isMove(p) && isReg(&p.To) { + // Convert uses to $0 to uses of R0 and + // propagate R0 + if isGPR(&p.To) && isZero(&p.From) { + p.From.Type = obj.TYPE_REG + p.From.Reg = s390x.REGZERO + } + + // Try to eliminate reg->reg moves + if isGPR(&p.From) || isFPR(&p.From) { + if copyprop(r) || (subprop(r) && copyprop(r)) { + excise(r) + n++ + } + } + } + } + return n +} + +// loadPipelining pushes any load from memory as early as possible. +func loadPipelining(r *gc.Flow) int { + for ; r != nil; r = r.Link { + p := r.Prog + if isLoad(p) { + pushback(r) + } + } + return 0 +} + +// fuseCompareBranch finds comparisons followed by a branch and converts +// them into a compare-and-branch instruction (which avoid setting the +// condition code). +func fuseCompareBranch(r *gc.Flow) int { + n := 0 + for ; r != nil; r = r.Link { + p := r.Prog + r1 := gc.Uniqs(r) + if r1 == nil { + continue + } + p1 := r1.Prog + + var ins obj.As + switch p.As { + case s390x.ACMP: + switch p1.As { + case s390x.ABCL, s390x.ABC: + continue + case s390x.ABEQ: + ins = s390x.ACMPBEQ + case s390x.ABGE: + ins = s390x.ACMPBGE + case s390x.ABGT: + ins = s390x.ACMPBGT + case s390x.ABLE: + ins = s390x.ACMPBLE + case s390x.ABLT: + ins = s390x.ACMPBLT + case s390x.ABNE: + ins = s390x.ACMPBNE + default: + continue + } + + case s390x.ACMPU: + switch p1.As { + case s390x.ABCL, s390x.ABC: + continue + case s390x.ABEQ: + ins = s390x.ACMPUBEQ + case s390x.ABGE: + ins = s390x.ACMPUBGE + case s390x.ABGT: + ins = s390x.ACMPUBGT + case s390x.ABLE: + ins = s390x.ACMPUBLE + case s390x.ABLT: + ins = s390x.ACMPUBLT + case s390x.ABNE: + ins = s390x.ACMPUBNE + default: + continue + } + + case s390x.ACMPW, s390x.ACMPWU: + continue + + default: + continue + } + + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("cnb %v; %v ", p, p1) + } + + if p1.To.Sym != nil { + continue + } + + if p.To.Type == obj.TYPE_REG { + p1.As = ins + p1.From = p.From + p1.Reg = p.To.Reg + p1.From3 = nil + } else if p.To.Type == obj.TYPE_CONST { + switch p.As { + case s390x.ACMP, s390x.ACMPW: + if (p.To.Offset < -(1 << 7)) || (p.To.Offset >= ((1 << 7) - 1)) { + continue + } + case s390x.ACMPU, s390x.ACMPWU: + if p.To.Offset >= (1 << 8) { + continue + } + default: + } + p1.As = ins + p1.From = p.From + p1.Reg = 0 + p1.From3 = new(obj.Addr) + *(p1.From3) = p.To + } else { + continue + } + + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("%v\n", p1) + } + excise(r) + n++ + } + return n +} + +// deadCodeElimination removes writes to registers which are written +// to again before they are next read. +func deadCodeElimination(r *gc.Flow) int { + n := 0 + for ; r != nil; r = r.Link { + p := r.Prog + // Currently there are no instructions which write to multiple + // registers in copyu. This check will need to change if there + // ever are. + if !(isGPR(&p.To) || isFPR(&p.To)) || copyu(p, &p.To, nil) != _Write { + continue + } + for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { + t := copyu(rr.Prog, &p.To, nil) + if t == _None { + continue + } + if t == _Write { + excise(r) + n++ + } + break + } + } + return n +} diff --git a/src/cmd/compile/internal/s390x/prog.go b/src/cmd/compile/internal/s390x/prog.go new file mode 100644 index 0000000000..306adf85c3 --- /dev/null +++ b/src/cmd/compile/internal/s390x/prog.go @@ -0,0 +1,179 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package s390x + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/s390x" +) + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{ + obj.ATYPE & obj.AMask: {Flags: gc.Pseudo | gc.Skip}, + obj.ATEXT & obj.AMask: {Flags: gc.Pseudo}, + obj.AFUNCDATA & obj.AMask: {Flags: gc.Pseudo}, + obj.APCDATA & obj.AMask: {Flags: gc.Pseudo}, + obj.AUNDEF & obj.AMask: {Flags: gc.Break}, + obj.AUSEFIELD & obj.AMask: {Flags: gc.OK}, + obj.ACHECKNIL & obj.AMask: {Flags: gc.LeftRead}, + obj.AVARDEF & obj.AMask: {Flags: gc.Pseudo | gc.RightWrite}, + obj.AVARKILL & obj.AMask: {Flags: gc.Pseudo | gc.RightWrite}, + obj.AVARLIVE & obj.AMask: {Flags: gc.Pseudo | gc.LeftRead}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations. + obj.ANOP & obj.AMask: {Flags: gc.LeftRead | gc.RightWrite}, + + // Integer + s390x.AADD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ASUB & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ANEG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AAND & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AOR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AXOR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AMULLD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AMULLW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AMULHD & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AMULHDU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ADIVD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ADIVDU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ASLD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ASRD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ASRAD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ARLL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ARLLG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.ACMP & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead}, + s390x.ACMPU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead}, + + // Floating point. + s390x.AFADD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFADDS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFSUB & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFSUBS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFMUL & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFMULS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFDIV & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFDIVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite}, + s390x.AFCMPU & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightRead}, + s390x.ACEBR & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightRead}, + s390x.ALEDBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ALDEBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.AFSQRT & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite}, + + // Conversions + s390x.ACEFBRA & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACDFBRA & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACEGBRA & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACDGBRA & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACFEBRA & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACFDBRA & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACGEBRA & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACGDBRA & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACELFBR & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACDLFBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACELGBR & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACDLGBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACLFEBR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACLFDBR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACLGEBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv}, + s390x.ACLGDBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv}, + + // Moves + s390x.AMOVB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv}, + s390x.AMOVBZ & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv}, + s390x.AMOVH & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv}, + s390x.AMOVHZ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv}, + s390x.AMOVW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv}, + s390x.AMOVWZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv}, + s390x.AMOVD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move}, + s390x.AFMOVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv}, + s390x.AFMOVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move}, + + // Storage operations + s390x.AMVC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr}, + s390x.ACLC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightRead | gc.RightAddr}, + s390x.AXC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr}, + s390x.AOC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr}, + s390x.ANC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr}, + + // Jumps + s390x.ABR & obj.AMask: {Flags: gc.Jump | gc.Break}, + s390x.ABL & obj.AMask: {Flags: gc.Call}, + s390x.ABEQ & obj.AMask: {Flags: gc.Cjmp}, + s390x.ABNE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ABGE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ABLT & obj.AMask: {Flags: gc.Cjmp}, + s390x.ABGT & obj.AMask: {Flags: gc.Cjmp}, + s390x.ABLE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPBEQ & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPBNE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPBGE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPBLT & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPBGT & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPBLE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPUBEQ & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPUBNE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPUBGE & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPUBLT & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPUBGT & obj.AMask: {Flags: gc.Cjmp}, + s390x.ACMPUBLE & obj.AMask: {Flags: gc.Cjmp}, + + // Macros + s390x.ACLEAR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightAddr | gc.RightWrite}, + + // Load/store multiple + s390x.ASTMG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightAddr | gc.RightWrite}, + s390x.ASTMY & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightAddr | gc.RightWrite}, + s390x.ALMG & obj.AMask: {Flags: gc.SizeQ | gc.LeftAddr | gc.LeftRead | gc.RightWrite}, + s390x.ALMY & obj.AMask: {Flags: gc.SizeL | gc.LeftAddr | gc.LeftRead | gc.RightWrite}, + + obj.ARET & obj.AMask: {Flags: gc.Break}, +} + +func proginfo(p *obj.Prog) { + info := &p.Info + *info = progtable[p.As&obj.AMask] + if info.Flags == 0 { + gc.Fatalf("proginfo: unknown instruction %v", p) + } + + if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { + info.Flags &^= gc.RegRead + info.Flags |= gc.RightRead /*CanRegRead |*/ + } + + if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 { + info.Regindex |= RtoB(int(p.From.Reg)) + } + + if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 { + info.Regindex |= RtoB(int(p.To.Reg)) + } + + if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { + info.Flags &^= gc.LeftRead + info.Flags |= gc.LeftAddr + } + + switch p.As { + // load multiple sets a range of registers + case s390x.ALMG, s390x.ALMY: + for r := p.Reg; r <= p.To.Reg; r++ { + info.Regset |= RtoB(int(r)) + } + // store multiple reads a range of registers + case s390x.ASTMG, s390x.ASTMY: + for r := p.From.Reg; r <= p.Reg; r++ { + info.Reguse |= RtoB(int(r)) + } + } +} diff --git a/src/cmd/compile/internal/s390x/reg.go b/src/cmd/compile/internal/s390x/reg.go new file mode 100644 index 0000000000..4cb8a9da05 --- /dev/null +++ b/src/cmd/compile/internal/s390x/reg.go @@ -0,0 +1,130 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package s390x + +import "cmd/internal/obj/s390x" +import "cmd/compile/internal/gc" + +const ( + NREGVAR = 32 /* 16 general + 16 floating */ +) + +var regname = []string{ + ".R0", + ".R1", + ".R2", + ".R3", + ".R4", + ".R5", + ".R6", + ".R7", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".F0", + ".F1", + ".F2", + ".F3", + ".F4", + ".F5", + ".F6", + ".F7", + ".F8", + ".F9", + ".F10", + ".F11", + ".F12", + ".F13", + ".F14", + ".F15", +} + +func regnames(n *int) []string { + *n = NREGVAR + return regname +} + +func excludedregs() uint64 { + // Exclude registers with fixed functions + return RtoB(s390x.REG_R0) | + RtoB(s390x.REGSP) | + RtoB(s390x.REGG) | + RtoB(s390x.REGTMP) | + RtoB(s390x.REGTMP2) | + RtoB(s390x.REG_LR) +} + +func doregbits(r int) uint64 { + return 0 +} + +/* + * track register variables including external registers: + * bit reg + * 0 R0 + * ... ... + * 15 R15 + * 16+0 F0 + * 16+1 F1 + * ... ... + * 16+15 F15 + */ +func RtoB(r int) uint64 { + if r >= s390x.REG_R0 && r <= s390x.REG_R15 { + return 1 << uint(r-s390x.REG_R0) + } + if r >= s390x.REG_F0 && r <= s390x.REG_F15 { + return 1 << uint(16+r-s390x.REG_F0) + } + return 0 +} + +func BtoR(b uint64) int { + b &= 0xffff + if b == 0 { + return 0 + } + return gc.Bitno(b) + s390x.REG_R0 +} + +func BtoF(b uint64) int { + b >>= 16 + b &= 0xffff + if b == 0 { + return 0 + } + return gc.Bitno(b) + s390x.REG_F0 +} diff --git a/src/cmd/compile/main.go b/src/cmd/compile/main.go index 49c2fb6263..8b8161efd1 100644 --- a/src/cmd/compile/main.go +++ b/src/cmd/compile/main.go @@ -10,6 +10,7 @@ import ( "cmd/compile/internal/arm64" "cmd/compile/internal/mips64" "cmd/compile/internal/ppc64" + "cmd/compile/internal/s390x" "cmd/compile/internal/x86" "cmd/internal/obj" "fmt" @@ -38,5 +39,7 @@ func main() { mips64.Main() case "ppc64", "ppc64le": ppc64.Main() + case "s390x": + s390x.Main() } } diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 777c92c726..a535316ca0 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -38,6 +38,7 @@ var bootstrapDirs = []string{ "compile/internal/ppc64", "compile/internal/ssa", "compile/internal/x86", + "compile/internal/s390x", "internal/bio", "internal/gcprog", "internal/obj", -- cgit v1.3 From 9743e4b0311c37ebacc2c9063a1cd778510eae09 Mon Sep 17 00:00:00 2001 From: Alexandru Moșoi Date: Mon, 11 Apr 2016 21:51:29 +0200 Subject: cmd/compile: share dominator tree among many passes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These passes do not modify the dominator tree too much. % benchstat old.txt new.txt name old time/op new time/op delta Template 335ms ± 3% 325ms ± 8% ~ (p=0.074 n=8+9) GoTypes 1.05s ± 1% 1.05s ± 3% ~ (p=0.095 n=9+10) Compiler 5.37s ± 4% 5.29s ± 1% -1.42% (p=0.022 n=9+10) MakeBash 34.9s ± 3% 34.4s ± 2% ~ (p=0.095 n=9+10) name old alloc/op new alloc/op delta Template 55.4MB ± 0% 54.9MB ± 0% -0.81% (p=0.000 n=10+10) GoTypes 179MB ± 0% 178MB ± 0% -0.89% (p=0.000 n=10+10) Compiler 807MB ± 0% 798MB ± 0% -1.10% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Template 498k ± 0% 496k ± 0% -0.29% (p=0.000 n=9+9) GoTypes 1.42M ± 0% 1.41M ± 0% -0.24% (p=0.000 n=10+10) Compiler 5.61M ± 0% 5.60M ± 0% -0.12% (p=0.000 n=10+10) Change-Id: I4cd20cfba3f132ebf371e16046ab14d7e42799ec Reviewed-on: https://go-review.googlesource.com/21806 Run-TryBot: Alexandru Moșoi TryBot-Result: Gobot Gobot Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/compile.go | 5 +++++ src/cmd/compile/internal/ssa/dom.go | 6 ++++++ src/cmd/compile/internal/ssa/func.go | 3 +++ src/cmd/compile/internal/ssa/loopbce.go | 18 ++++++++---------- src/cmd/compile/internal/ssa/nilcheck.go | 2 +- src/cmd/compile/internal/ssa/nilcheck_test.go | 10 ++++++++++ src/cmd/compile/internal/ssa/prove.go | 9 +++------ 7 files changed, 36 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index b4215f119e..f4f0d8cab2 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -234,6 +234,7 @@ var passes = [...]pass{ {name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values {name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt {name: "generic cse", fn: cse}, + {name: "generic domtree", fn: domTree}, {name: "phiopt", fn: phiopt}, {name: "nilcheckelim", fn: nilcheckelim}, {name: "prove", fn: prove}, @@ -288,6 +289,10 @@ var passOrder = [...]constraint{ {"opt", "nilcheckelim"}, // tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET {"tighten", "lower"}, + // nilcheckelim, prove and loopbce share idom. + {"generic domtree", "nilcheckelim"}, + {"generic domtree", "prove"}, + {"generic domtree", "loopbce"}, // tighten will be most effective when as many values have been removed as possible {"generic deadcode", "tighten"}, {"generic cse", "tighten"}, diff --git a/src/cmd/compile/internal/ssa/dom.go b/src/cmd/compile/internal/ssa/dom.go index 0fffcdc2af..fedaf602e4 100644 --- a/src/cmd/compile/internal/ssa/dom.go +++ b/src/cmd/compile/internal/ssa/dom.go @@ -364,3 +364,9 @@ func intersect(b, c *Block, postnum []int, idom []*Block) *Block { } return b } + +// build immediate dominators. +func domTree(f *Func) { + f.idom = dominators(f) + f.sdom = newSparseTree(f, f.idom) +} diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index 8dd75f6093..da44f26106 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -36,6 +36,9 @@ type Func struct { freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil. freeBlocks *Block // free Blocks linked by succstorage[0]. All other fields except ID are 0/nil. + idom []*Block // precomputed immediate dominators + sdom sparseTree // precomputed dominator tree + constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type } diff --git a/src/cmd/compile/internal/ssa/loopbce.go b/src/cmd/compile/internal/ssa/loopbce.go index c937ead1b2..9bd2d3f0de 100644 --- a/src/cmd/compile/internal/ssa/loopbce.go +++ b/src/cmd/compile/internal/ssa/loopbce.go @@ -31,7 +31,7 @@ type indVar struct { // // // TODO: handle 32 bit operations -func findIndVar(f *Func, sdom sparseTree) []indVar { +func findIndVar(f *Func) []indVar { var iv []indVar nextb: @@ -110,7 +110,7 @@ nextb: // Second condition: b.Succs[entry] dominates nxt so that // nxt is computed when inc < max, meaning nxt <= max. - if !sdom.isAncestorEq(b.Succs[entry], nxt.Block) { + if !f.sdom.isAncestorEq(b.Succs[entry], nxt.Block) { // inc+ind can only be reached through the branch that enters the loop. continue } @@ -160,20 +160,18 @@ nextb: // loopbce performs loop based bounds check elimination. func loopbce(f *Func) { - idom := dominators(f) - sdom := newSparseTree(f, idom) - ivList := findIndVar(f, sdom) + ivList := findIndVar(f) m := make(map[*Value]indVar) for _, iv := range ivList { m[iv.ind] = iv } - removeBoundsChecks(f, sdom, m) + removeBoundsChecks(f, m) } // removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables. -func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) { +func removeBoundsChecks(f *Func, m map[*Value]indVar) { for _, b := range f.Blocks { if b.Kind != BlockIf { continue @@ -202,7 +200,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) { goto skip1 } - if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { + if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { if v.Args[1] == iv.max { if f.pass.debug > 0 { f.Config.Warnl(b.Line, "Found redundant %s", v.Op) @@ -229,7 +227,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) { goto skip2 } - if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { + if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { if v.Args[1].Op == OpSliceCap && iv.max.Op == OpSliceLen && v.Args[1].Args[0] == iv.max.Args[0] { if f.pass.debug > 0 { f.Config.Warnl(b.Line, "Found redundant %s (len promoted to cap)", v.Op) @@ -250,7 +248,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) { } // ind + add >= 0 <-> min + add >= 0 <-> min >= -add - if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) { + if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) { if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() { goto skip3 } diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go index 881e3b2eff..753e48aad5 100644 --- a/src/cmd/compile/internal/ssa/nilcheck.go +++ b/src/cmd/compile/internal/ssa/nilcheck.go @@ -11,7 +11,7 @@ func nilcheckelim(f *Func) { // A nil check is redundant if the same nil check was successful in a // dominating block. The efficacy of this pass depends heavily on the // efficacy of the cse pass. - idom := dominators(f) + idom := f.idom domTree := make([][]*Block, f.NumBlocks()) // Create a block ID -> [dominees] mapping diff --git a/src/cmd/compile/internal/ssa/nilcheck_test.go b/src/cmd/compile/internal/ssa/nilcheck_test.go index d1f38b6951..c1c8f94767 100644 --- a/src/cmd/compile/internal/ssa/nilcheck_test.go +++ b/src/cmd/compile/internal/ssa/nilcheck_test.go @@ -49,6 +49,7 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) { b.ReportAllocs() for i := 0; i < b.N; i++ { + domTree(fun.f) nilcheckelim(fun.f) } } @@ -83,6 +84,7 @@ func TestNilcheckSimple(t *testing.T) { Exit("mem"))) CheckFunc(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -120,6 +122,7 @@ func TestNilcheckDomOrder(t *testing.T) { Goto("exit"))) CheckFunc(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -153,6 +156,7 @@ func TestNilcheckAddr(t *testing.T) { Exit("mem"))) CheckFunc(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -187,6 +191,7 @@ func TestNilcheckAddPtr(t *testing.T) { Exit("mem"))) CheckFunc(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -231,6 +236,7 @@ func TestNilcheckPhi(t *testing.T) { Exit("mem"))) CheckFunc(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -272,6 +278,7 @@ func TestNilcheckKeepRemove(t *testing.T) { Exit("mem"))) CheckFunc(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -319,6 +326,7 @@ func TestNilcheckInFalseBranch(t *testing.T) { Exit("mem"))) CheckFunc(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -370,6 +378,7 @@ func TestNilcheckUser(t *testing.T) { CheckFunc(fun.f) // we need the opt here to rewrite the user nilcheck opt(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check @@ -414,6 +423,7 @@ func TestNilcheckBug(t *testing.T) { CheckFunc(fun.f) // we need the opt here to rewrite the user nilcheck opt(fun.f) + domTree(fun.f) nilcheckelim(fun.f) // clean up the removed nil check diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index a12a996263..f4a10b508a 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -445,9 +445,6 @@ var ( // else branch of the first comparison is executed, we already know that i < len(a). // The code for the second panic can be removed. func prove(f *Func) { - idom := dominators(f) - sdom := newSparseTree(f, idom) - // current node state type walkState int const ( @@ -471,8 +468,8 @@ func prove(f *Func) { for len(work) > 0 { node := work[len(work)-1] work = work[:len(work)-1] - parent := idom[node.block.ID] - branch := getBranch(sdom, parent, node.block) + parent := f.idom[node.block.ID] + branch := getBranch(f.sdom, parent, node.block) switch node.state { case descend: @@ -491,7 +488,7 @@ func prove(f *Func) { block: node.block, state: simplify, }) - for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) { + for s := f.sdom.Child(node.block); s != nil; s = f.sdom.Sibling(s) { work = append(work, bp{ block: s, state: descend, -- cgit v1.3 From 204b6f48c5107d3132033324fd492ca0253568dc Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 12 Apr 2016 09:41:11 +0200 Subject: cmd/pprof/internal: move to cmd/internal/pprof Make internal pprof packages available to cmd/trace. cmd/trace needs access to them to generate symbolized svg profiles (create and serialize Profile struct). And potentially generate svg programmatically instead of invoking go tool pprof. Change-Id: Iafd0c87ffdd4ddc081093be0b39761f19507907a Reviewed-on: https://go-review.googlesource.com/21870 Run-TryBot: Dmitry Vyukov TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/cmd/internal/pprof/commands/commands.go | 243 ++++ src/cmd/internal/pprof/driver/driver.go | 1041 +++++++++++++ src/cmd/internal/pprof/driver/interactive.go | 492 +++++++ src/cmd/internal/pprof/fetch/fetch.go | 82 ++ src/cmd/internal/pprof/plugin/plugin.go | 213 +++ src/cmd/internal/pprof/profile/encode.go | 470 ++++++ src/cmd/internal/pprof/profile/filter.go | 158 ++ src/cmd/internal/pprof/profile/legacy_profile.go | 1251 ++++++++++++++++ src/cmd/internal/pprof/profile/profile.go | 572 ++++++++ src/cmd/internal/pprof/profile/profile_test.go | 24 + src/cmd/internal/pprof/profile/proto.go | 360 +++++ src/cmd/internal/pprof/profile/proto_test.go | 67 + src/cmd/internal/pprof/profile/prune.go | 97 ++ src/cmd/internal/pprof/report/report.go | 1682 ++++++++++++++++++++++ src/cmd/internal/pprof/report/source.go | 454 ++++++ src/cmd/internal/pprof/report/source_html.go | 77 + src/cmd/internal/pprof/svg/svg.go | 71 + src/cmd/internal/pprof/svg/svgpan.go | 291 ++++ src/cmd/internal/pprof/symbolizer/symbolizer.go | 195 +++ src/cmd/internal/pprof/symbolz/symbolz.go | 111 ++ src/cmd/internal/pprof/tempfile/tempfile.go | 45 + src/cmd/pprof/internal/commands/commands.go | 243 ---- src/cmd/pprof/internal/driver/driver.go | 1041 ------------- src/cmd/pprof/internal/driver/interactive.go | 492 ------- src/cmd/pprof/internal/fetch/fetch.go | 82 -- src/cmd/pprof/internal/plugin/plugin.go | 213 --- src/cmd/pprof/internal/profile/encode.go | 470 ------ src/cmd/pprof/internal/profile/filter.go | 158 -- src/cmd/pprof/internal/profile/legacy_profile.go | 1251 ---------------- src/cmd/pprof/internal/profile/profile.go | 572 -------- src/cmd/pprof/internal/profile/profile_test.go | 24 - src/cmd/pprof/internal/profile/proto.go | 360 ----- src/cmd/pprof/internal/profile/proto_test.go | 67 - src/cmd/pprof/internal/profile/prune.go | 97 -- src/cmd/pprof/internal/report/report.go | 1682 ---------------------- src/cmd/pprof/internal/report/source.go | 454 ------ src/cmd/pprof/internal/report/source_html.go | 77 - src/cmd/pprof/internal/svg/svg.go | 71 - src/cmd/pprof/internal/svg/svgpan.go | 291 ---- src/cmd/pprof/internal/symbolizer/symbolizer.go | 195 --- src/cmd/pprof/internal/symbolz/symbolz.go | 111 -- src/cmd/pprof/internal/tempfile/tempfile.go | 45 - src/cmd/pprof/pprof.go | 14 +- 43 files changed, 8003 insertions(+), 8003 deletions(-) create mode 100644 src/cmd/internal/pprof/commands/commands.go create mode 100644 src/cmd/internal/pprof/driver/driver.go create mode 100644 src/cmd/internal/pprof/driver/interactive.go create mode 100644 src/cmd/internal/pprof/fetch/fetch.go create mode 100644 src/cmd/internal/pprof/plugin/plugin.go create mode 100644 src/cmd/internal/pprof/profile/encode.go create mode 100644 src/cmd/internal/pprof/profile/filter.go create mode 100644 src/cmd/internal/pprof/profile/legacy_profile.go create mode 100644 src/cmd/internal/pprof/profile/profile.go create mode 100644 src/cmd/internal/pprof/profile/profile_test.go create mode 100644 src/cmd/internal/pprof/profile/proto.go create mode 100644 src/cmd/internal/pprof/profile/proto_test.go create mode 100644 src/cmd/internal/pprof/profile/prune.go create mode 100644 src/cmd/internal/pprof/report/report.go create mode 100644 src/cmd/internal/pprof/report/source.go create mode 100644 src/cmd/internal/pprof/report/source_html.go create mode 100644 src/cmd/internal/pprof/svg/svg.go create mode 100644 src/cmd/internal/pprof/svg/svgpan.go create mode 100644 src/cmd/internal/pprof/symbolizer/symbolizer.go create mode 100644 src/cmd/internal/pprof/symbolz/symbolz.go create mode 100644 src/cmd/internal/pprof/tempfile/tempfile.go delete mode 100644 src/cmd/pprof/internal/commands/commands.go delete mode 100644 src/cmd/pprof/internal/driver/driver.go delete mode 100644 src/cmd/pprof/internal/driver/interactive.go delete mode 100644 src/cmd/pprof/internal/fetch/fetch.go delete mode 100644 src/cmd/pprof/internal/plugin/plugin.go delete mode 100644 src/cmd/pprof/internal/profile/encode.go delete mode 100644 src/cmd/pprof/internal/profile/filter.go delete mode 100644 src/cmd/pprof/internal/profile/legacy_profile.go delete mode 100644 src/cmd/pprof/internal/profile/profile.go delete mode 100644 src/cmd/pprof/internal/profile/profile_test.go delete mode 100644 src/cmd/pprof/internal/profile/proto.go delete mode 100644 src/cmd/pprof/internal/profile/proto_test.go delete mode 100644 src/cmd/pprof/internal/profile/prune.go delete mode 100644 src/cmd/pprof/internal/report/report.go delete mode 100644 src/cmd/pprof/internal/report/source.go delete mode 100644 src/cmd/pprof/internal/report/source_html.go delete mode 100644 src/cmd/pprof/internal/svg/svg.go delete mode 100644 src/cmd/pprof/internal/svg/svgpan.go delete mode 100644 src/cmd/pprof/internal/symbolizer/symbolizer.go delete mode 100644 src/cmd/pprof/internal/symbolz/symbolz.go delete mode 100644 src/cmd/pprof/internal/tempfile/tempfile.go (limited to 'src') diff --git a/src/cmd/internal/pprof/commands/commands.go b/src/cmd/internal/pprof/commands/commands.go new file mode 100644 index 0000000000..5018c02af1 --- /dev/null +++ b/src/cmd/internal/pprof/commands/commands.go @@ -0,0 +1,243 @@ +// Copyright 2014 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 commands defines and manages the basic pprof commands +package commands + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strings" + "time" + + "cmd/internal/pprof/plugin" + "cmd/internal/pprof/report" + "cmd/internal/pprof/svg" + "cmd/internal/pprof/tempfile" +) + +// Commands describes the commands accepted by pprof. +type Commands map[string]*Command + +// Command describes the actions for a pprof command. Includes a +// function for command-line completion, the report format to use +// during report generation, any postprocessing functions, and whether +// the command expects a regexp parameter (typically a function name). +type Command struct { + Complete Completer // autocomplete for interactive mode + Format int // report format to generate + PostProcess PostProcessor // postprocessing to run on report + HasParam bool // Collect a parameter from the CLI + Usage string // Help text +} + +// Completer is a function for command-line autocompletion +type Completer func(prefix string) string + +// PostProcessor is a function that applies post-processing to the report output +type PostProcessor func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error + +// PProf returns the basic pprof report-generation commands +func PProf(c Completer, interactive **bool) Commands { + return Commands{ + // Commands that require no post-processing. + "tags": {nil, report.Tags, nil, false, "Outputs all tags in the profile"}, + "raw": {c, report.Raw, nil, false, "Outputs a text representation of the raw profile"}, + "dot": {c, report.Dot, nil, false, "Outputs a graph in DOT format"}, + "top": {c, report.Text, nil, false, "Outputs top entries in text form"}, + "tree": {c, report.Tree, nil, false, "Outputs a text rendering of call graph"}, + "text": {c, report.Text, nil, false, "Outputs top entries in text form"}, + "disasm": {c, report.Dis, nil, true, "Output annotated assembly for functions matching regexp or address"}, + "list": {c, report.List, nil, true, "Output annotated source for functions matching regexp"}, + "peek": {c, report.Tree, nil, true, "Output callers/callees of functions matching regexp"}, + + // Save binary formats to a file + "callgrind": {c, report.Callgrind, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format"}, + "proto": {c, report.Proto, awayFromTTY("pb.gz"), false, "Outputs the profile in compressed protobuf format"}, + + // Generate report in DOT format and postprocess with dot + "gif": {c, report.Dot, invokeDot("gif"), false, "Outputs a graph image in GIF format"}, + "pdf": {c, report.Dot, invokeDot("pdf"), false, "Outputs a graph in PDF format"}, + "png": {c, report.Dot, invokeDot("png"), false, "Outputs a graph image in PNG format"}, + "ps": {c, report.Dot, invokeDot("ps"), false, "Outputs a graph in PS format"}, + + // Save SVG output into a file after including svgpan library + "svg": {c, report.Dot, saveSVGToFile(), false, "Outputs a graph in SVG format"}, + + // Visualize postprocessed dot output + "eog": {c, report.Dot, invokeVisualizer(interactive, invokeDot("svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"}, + "evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot("pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"}, + "gv": {c, report.Dot, invokeVisualizer(interactive, invokeDot("ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"}, + "web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(), "svg", browsers()), false, "Visualize graph through web browser"}, + + // Visualize HTML directly generated by report. + "weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"}, + } +} + +// browsers returns a list of commands to attempt for web visualization +// on the current platform +func browsers() []string { + var cmds []string + if exe := os.Getenv("BROWSER"); exe != "" { + cmds = append(cmds, exe) + } + switch runtime.GOOS { + case "darwin": + cmds = append(cmds, "/usr/bin/open") + case "windows": + cmds = append(cmds, "cmd /c start") + default: + cmds = append(cmds, "xdg-open") + } + cmds = append(cmds, "chrome", "google-chrome", "firefox") + return cmds +} + +// NewCompleter creates an autocompletion function for a set of commands. +func NewCompleter(cs Commands) Completer { + return func(line string) string { + switch tokens := strings.Fields(line); len(tokens) { + case 0: + // Nothing to complete + case 1: + // Single token -- complete command name + found := "" + for c := range cs { + if strings.HasPrefix(c, tokens[0]) { + if found != "" { + return line + } + found = c + } + } + if found != "" { + return found + } + default: + // Multiple tokens -- complete using command completer + if c, ok := cs[tokens[0]]; ok { + if c.Complete != nil { + lastTokenIdx := len(tokens) - 1 + lastToken := tokens[lastTokenIdx] + if strings.HasPrefix(lastToken, "-") { + lastToken = "-" + c.Complete(lastToken[1:]) + } else { + lastToken = c.Complete(lastToken) + } + return strings.Join(append(tokens[:lastTokenIdx], lastToken), " ") + } + } + } + return line + } +} + +// awayFromTTY saves the output in a file if it would otherwise go to +// the terminal screen. This is used to avoid dumping binary data on +// the screen. +func awayFromTTY(format string) PostProcessor { + return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { + if output == os.Stdout && ui.IsTerminal() { + tempFile, err := tempfile.New("", "profile", "."+format) + if err != nil { + return err + } + ui.PrintErr("Generating report in ", tempFile.Name()) + _, err = fmt.Fprint(tempFile, input) + return err + } + _, err := fmt.Fprint(output, input) + return err + } +} + +func invokeDot(format string) PostProcessor { + divert := awayFromTTY(format) + return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { + if _, err := exec.LookPath("dot"); err != nil { + ui.PrintErr("Cannot find dot, have you installed Graphviz?") + return err + } + cmd := exec.Command("dot", "-T"+format) + var buf bytes.Buffer + cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr + if err := cmd.Run(); err != nil { + return err + } + return divert(&buf, output, ui) + } +} + +func saveSVGToFile() PostProcessor { + generateSVG := invokeDot("svg") + divert := awayFromTTY("svg") + return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { + baseSVG := &bytes.Buffer{} + generateSVG(input, baseSVG, ui) + massaged := &bytes.Buffer{} + fmt.Fprint(massaged, svg.Massage(*baseSVG)) + return divert(massaged, output, ui) + } +} + +var vizTmpDir string + +func makeVizTmpDir() error { + if vizTmpDir != "" { + return nil + } + name, err := ioutil.TempDir("", "pprof-") + if err != nil { + return err + } + vizTmpDir = name + return nil +} + +func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, visualizers []string) PostProcessor { + return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { + if err := makeVizTmpDir(); err != nil { + return err + } + tempFile, err := tempfile.New(vizTmpDir, "pprof", "."+suffix) + if err != nil { + return err + } + tempfile.DeferDelete(tempFile.Name()) + if err = format(input, tempFile, ui); err != nil { + return err + } + tempFile.Close() // on windows, if the file is Open, start cannot access it. + // Try visualizers until one is successful + for _, v := range visualizers { + // Separate command and arguments for exec.Command. + args := strings.Split(v, " ") + if len(args) == 0 { + continue + } + viewer := exec.Command(args[0], append(args[1:], tempFile.Name())...) + viewer.Stderr = os.Stderr + if err = viewer.Start(); err == nil { + // The viewer might just send a message to another program + // to open the file. Give that program a little time to open the + // file before we remove it. + time.Sleep(1 * time.Second) + + if !**interactive { + // In command-line mode, wait for the viewer to be closed + // before proceeding + return viewer.Wait() + } + return nil + } + } + return err + } +} diff --git a/src/cmd/internal/pprof/driver/driver.go b/src/cmd/internal/pprof/driver/driver.go new file mode 100644 index 0000000000..782acfdf32 --- /dev/null +++ b/src/cmd/internal/pprof/driver/driver.go @@ -0,0 +1,1041 @@ +// Copyright 2014 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 driver implements the core pprof functionality. It can be +// parameterized with a flag implementation, fetch and symbolize +// mechanisms. +package driver + +import ( + "bytes" + "fmt" + "io" + "net/url" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "time" + + "cmd/internal/pprof/commands" + "cmd/internal/pprof/plugin" + "cmd/internal/pprof/profile" + "cmd/internal/pprof/report" + "cmd/internal/pprof/tempfile" +) + +// PProf acquires a profile, and symbolizes it using a profile +// manager. Then it generates a report formatted according to the +// options selected through the flags package. +func PProf(flagset plugin.FlagSet, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, overrides commands.Commands) error { + // Remove any temporary files created during pprof processing. + defer tempfile.Cleanup() + + f, err := getFlags(flagset, overrides, ui) + if err != nil { + return err + } + + obj.SetConfig(*f.flagTools) + + sources := f.profileSource + if len(sources) > 1 { + source := sources[0] + // If the first argument is a supported object file, treat as executable. + if file, err := obj.Open(source, 0); err == nil { + file.Close() + f.profileExecName = source + sources = sources[1:] + } else if *f.flagBuildID == "" && isBuildID(source) { + f.flagBuildID = &source + sources = sources[1:] + } + } + + // errMu protects concurrent accesses to errset and err. errset is set if an + // error is encountered by one of the goroutines grabbing a profile. + errMu, errset := sync.Mutex{}, false + + // Fetch profiles. + wg := sync.WaitGroup{} + profs := make([]*profile.Profile, len(sources)) + for i, source := range sources { + wg.Add(1) + go func(i int, src string) { + defer wg.Done() + p, grabErr := grabProfile(src, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f) + if grabErr != nil { + errMu.Lock() + defer errMu.Unlock() + errset, err = true, grabErr + return + } + profs[i] = p + }(i, source) + } + wg.Wait() + if errset { + return err + } + + // Merge profiles. + prof := profs[0] + for _, p := range profs[1:] { + if err = prof.Merge(p, 1); err != nil { + return err + } + } + + if *f.flagBase != "" { + // Fetch base profile and subtract from current profile. + base, err := grabProfile(*f.flagBase, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f) + if err != nil { + return err + } + + if err = prof.Merge(base, -1); err != nil { + return err + } + } + + if err := processFlags(prof, ui, f); err != nil { + return err + } + + if !*f.flagRuntime { + prof.RemoveUninteresting() + } + + if *f.flagInteractive { + return interactive(prof, obj, ui, f) + } + + return generate(false, prof, obj, ui, f) +} + +// isBuildID determines if the profile may contain a build ID, by +// checking that it is a string of hex digits. +func isBuildID(id string) bool { + return strings.Trim(id, "0123456789abcdefABCDEF") == "" +} + +// adjustURL updates the profile source URL based on heuristics. It +// will append ?seconds=sec for CPU profiles if not already +// specified. Returns the hostname if the profile is remote. +func adjustURL(source string, sec int, ui plugin.UI) (adjusted, host string, duration time.Duration) { + // If there is a local file with this name, just use it. + if _, err := os.Stat(source); err == nil { + return source, "", 0 + } + + url, err := url.Parse(source) + + // Automatically add http:// to URLs of the form hostname:port/path. + // url.Parse treats "hostname" as the Scheme. + if err != nil || (url.Host == "" && url.Scheme != "" && url.Scheme != "file") { + url, err = url.Parse("http://" + source) + if err != nil { + return source, "", 0 + } + } + if scheme := strings.ToLower(url.Scheme); scheme == "" || scheme == "file" { + url.Scheme = "" + return url.String(), "", 0 + } + + values := url.Query() + if urlSeconds := values.Get("seconds"); urlSeconds != "" { + if us, err := strconv.ParseInt(urlSeconds, 10, 32); err == nil { + if sec >= 0 { + ui.PrintErr("Overriding -seconds for URL ", source) + } + sec = int(us) + } + } + + switch strings.ToLower(url.Path) { + case "", "/": + // Apply default /profilez. + url.Path = "/profilez" + case "/protoz": + // Rewrite to /profilez?type=proto + url.Path = "/profilez" + values.Set("type", "proto") + } + + if hasDuration(url.Path) { + if sec > 0 { + duration = time.Duration(sec) * time.Second + values.Set("seconds", fmt.Sprintf("%d", sec)) + } else { + // Assume default duration: 30 seconds + duration = 30 * time.Second + } + } + url.RawQuery = values.Encode() + return url.String(), url.Host, duration +} + +func hasDuration(path string) bool { + for _, trigger := range []string{"profilez", "wallz", "/profile"} { + if strings.Contains(path, trigger) { + return true + } + } + return false +} + +// preprocess does filtering and aggregation of a profile based on the +// requested options. +func preprocess(prof *profile.Profile, ui plugin.UI, f *flags) error { + if *f.flagFocus != "" || *f.flagIgnore != "" || *f.flagHide != "" { + focus, ignore, hide, err := compileFocusIgnore(*f.flagFocus, *f.flagIgnore, *f.flagHide) + if err != nil { + return err + } + fm, im, hm := prof.FilterSamplesByName(focus, ignore, hide) + + warnNoMatches(fm, *f.flagFocus, "Focus", ui) + warnNoMatches(im, *f.flagIgnore, "Ignore", ui) + warnNoMatches(hm, *f.flagHide, "Hide", ui) + } + + if *f.flagTagFocus != "" || *f.flagTagIgnore != "" { + focus, err := compileTagFilter(*f.flagTagFocus, ui) + if err != nil { + return err + } + ignore, err := compileTagFilter(*f.flagTagIgnore, ui) + if err != nil { + return err + } + fm, im := prof.FilterSamplesByTag(focus, ignore) + + warnNoMatches(fm, *f.flagTagFocus, "TagFocus", ui) + warnNoMatches(im, *f.flagTagIgnore, "TagIgnore", ui) + } + + return aggregate(prof, f) +} + +func compileFocusIgnore(focus, ignore, hide string) (f, i, h *regexp.Regexp, err error) { + if focus != "" { + if f, err = regexp.Compile(focus); err != nil { + return nil, nil, nil, fmt.Errorf("parsing focus regexp: %v", err) + } + } + + if ignore != "" { + if i, err = regexp.Compile(ignore); err != nil { + return nil, nil, nil, fmt.Errorf("parsing ignore regexp: %v", err) + } + } + + if hide != "" { + if h, err = regexp.Compile(hide); err != nil { + return nil, nil, nil, fmt.Errorf("parsing hide regexp: %v", err) + } + } + return +} + +func compileTagFilter(filter string, ui plugin.UI) (f func(string, string, int64) bool, err error) { + if filter == "" { + return nil, nil + } + if numFilter := parseTagFilterRange(filter); numFilter != nil { + ui.PrintErr("Interpreted '", filter, "' as range, not regexp") + return func(key, val string, num int64) bool { + if val != "" { + return false + } + return numFilter(num, key) + }, nil + } + fx, err := regexp.Compile(filter) + if err != nil { + return nil, err + } + + return func(key, val string, num int64) bool { + if val == "" { + return false + } + return fx.MatchString(key + ":" + val) + }, nil +} + +var tagFilterRangeRx = regexp.MustCompile("([[:digit:]]+)([[:alpha:]]+)") + +// parseTagFilterRange returns a function to checks if a value is +// contained on the range described by a string. It can recognize +// strings of the form: +// "32kb" -- matches values == 32kb +// ":64kb" -- matches values <= 64kb +// "4mb:" -- matches values >= 4mb +// "12kb:64mb" -- matches values between 12kb and 64mb (both included). +func parseTagFilterRange(filter string) func(int64, string) bool { + ranges := tagFilterRangeRx.FindAllStringSubmatch(filter, 2) + if len(ranges) == 0 { + return nil // No ranges were identified + } + v, err := strconv.ParseInt(ranges[0][1], 10, 64) + if err != nil { + panic(fmt.Errorf("Failed to parse int %s: %v", ranges[0][1], err)) + } + value, unit := report.ScaleValue(v, ranges[0][2], ranges[0][2]) + if len(ranges) == 1 { + switch match := ranges[0][0]; filter { + case match: + return func(v int64, u string) bool { + sv, su := report.ScaleValue(v, u, unit) + return su == unit && sv == value + } + case match + ":": + return func(v int64, u string) bool { + sv, su := report.ScaleValue(v, u, unit) + return su == unit && sv >= value + } + case ":" + match: + return func(v int64, u string) bool { + sv, su := report.ScaleValue(v, u, unit) + return su == unit && sv <= value + } + } + return nil + } + if filter != ranges[0][0]+":"+ranges[1][0] { + return nil + } + if v, err = strconv.ParseInt(ranges[1][1], 10, 64); err != nil { + panic(fmt.Errorf("Failed to parse int %s: %v", ranges[1][1], err)) + } + value2, unit2 := report.ScaleValue(v, ranges[1][2], unit) + if unit != unit2 { + return nil + } + return func(v int64, u string) bool { + sv, su := report.ScaleValue(v, u, unit) + return su == unit && sv >= value && sv <= value2 + } +} + +func warnNoMatches(match bool, rx, option string, ui plugin.UI) { + if !match && rx != "" && rx != "." { + ui.PrintErr(option + " expression matched no samples: " + rx) + } +} + +// grabProfile fetches and symbolizes a profile. +func grabProfile(source, exec, buildid string, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, f *flags) (*profile.Profile, error) { + source, host, duration := adjustURL(source, *f.flagSeconds, ui) + remote := host != "" + + if remote { + ui.Print("Fetching profile from ", source) + if duration != 0 { + ui.Print("Please wait... (" + duration.String() + ")") + } + } + + now := time.Now() + // Fetch profile from source. + // Give 50% slack on the timeout. + p, err := fetch(source, duration+duration/2, ui) + if err != nil { + return nil, err + } + + // Update the time/duration if the profile source doesn't include it. + // TODO(rsilvera): Remove this when we remove support for legacy profiles. + if remote { + if p.TimeNanos == 0 { + p.TimeNanos = now.UnixNano() + } + if duration != 0 && p.DurationNanos == 0 { + p.DurationNanos = int64(duration) + } + } + + // Replace executable/buildID with the options provided in the + // command line. Assume the executable is the first Mapping entry. + if exec != "" || buildid != "" { + if len(p.Mapping) == 0 { + // Create a fake mapping to hold the user option, and associate + // all samples to it. + m := &profile.Mapping{ + ID: 1, + } + for _, l := range p.Location { + l.Mapping = m + } + p.Mapping = []*profile.Mapping{m} + } + if exec != "" { + p.Mapping[0].File = exec + } + if buildid != "" { + p.Mapping[0].BuildID = buildid + } + } + + if err := sym(*f.flagSymbolize, source, p, obj, ui); err != nil { + return nil, err + } + + // Save a copy of any remote profiles, unless the user is explicitly + // saving it. + if remote && !f.isFormat("proto") { + prefix := "pprof." + if len(p.Mapping) > 0 && p.Mapping[0].File != "" { + prefix = prefix + filepath.Base(p.Mapping[0].File) + "." + } + if !strings.ContainsRune(host, os.PathSeparator) { + prefix = prefix + host + "." + } + for _, s := range p.SampleType { + prefix = prefix + s.Type + "." + } + + dir := os.Getenv("PPROF_TMPDIR") + tempFile, err := tempfile.New(dir, prefix, ".pb.gz") + if err == nil { + if err = p.Write(tempFile); err == nil { + ui.PrintErr("Saved profile in ", tempFile.Name()) + } + } + if err != nil { + ui.PrintErr("Could not save profile: ", err) + } + } + + if err := p.Demangle(obj.Demangle); err != nil { + ui.PrintErr("Failed to demangle profile: ", err) + } + + if err := p.CheckValid(); err != nil { + return nil, fmt.Errorf("Grab %s: %v", source, err) + } + + return p, nil +} + +type flags struct { + flagInteractive *bool // Accept commands interactively + flagCommands map[string]*bool // pprof commands without parameters + flagParamCommands map[string]*string // pprof commands with parameters + + flagOutput *string // Output file name + + flagCum *bool // Sort by cumulative data + flagCallTree *bool // generate a context-sensitive call tree + + flagAddresses *bool // Report at address level + flagLines *bool // Report at source line level + flagFiles *bool // Report at file level + flagFunctions *bool // Report at function level [default] + + flagSymbolize *string // Symbolization options (=none to disable) + flagBuildID *string // Override build if for first mapping + + flagNodeCount *int // Max number of nodes to show + flagNodeFraction *float64 // Hide nodes below *total + flagEdgeFraction *float64 // Hide edges below *total + flagTrim *bool // Set to false to ignore NodeCount/*Fraction + flagRuntime *bool // Show runtime call frames in memory profiles + flagFocus *string // Restricts to paths going through a node matching regexp + flagIgnore *string // Skips paths going through any nodes matching regexp + flagHide *string // Skips sample locations matching regexp + flagTagFocus *string // Restrict to samples tagged with key:value matching regexp + flagTagIgnore *string // Discard samples tagged with key:value matching regexp + flagDropNegative *bool // Skip negative values + + flagBase *string // Source for base profile to user for comparison + + flagSeconds *int // Length of time for dynamic profiles + + flagTotalDelay *bool // Display total delay at each region + flagContentions *bool // Display number of delays at each region + flagMeanDelay *bool // Display mean delay at each region + + flagInUseSpace *bool // Display in-use memory size + flagInUseObjects *bool // Display in-use object counts + flagAllocSpace *bool // Display allocated memory size + flagAllocObjects *bool // Display allocated object counts + flagDisplayUnit *string // Measurement unit to use on reports + flagDivideBy *float64 // Ratio to divide sample values + + flagSampleIndex *int // Sample value to use in reports. + flagMean *bool // Use mean of sample_index over count + + flagTools *string + profileSource []string + profileExecName string + + extraUsage string + commands commands.Commands +} + +func (f *flags) isFormat(format string) bool { + if fl := f.flagCommands[format]; fl != nil { + return *fl + } + if fl := f.flagParamCommands[format]; fl != nil { + return *fl != "" + } + return false +} + +// String provides a printable representation for the current set of flags. +func (f *flags) String(p *profile.Profile) string { + var ret string + + if ix := *f.flagSampleIndex; ix != -1 { + ret += fmt.Sprintf(" %-25s : %d (%s)\n", "sample_index", ix, p.SampleType[ix].Type) + } + if ix := *f.flagMean; ix { + ret += boolFlagString("mean") + } + if *f.flagDisplayUnit != "minimum" { + ret += stringFlagString("unit", *f.flagDisplayUnit) + } + + switch { + case *f.flagInteractive: + ret += boolFlagString("interactive") + } + for name, fl := range f.flagCommands { + if *fl { + ret += boolFlagString(name) + } + } + + if *f.flagCum { + ret += boolFlagString("cum") + } + if *f.flagCallTree { + ret += boolFlagString("call_tree") + } + + switch { + case *f.flagAddresses: + ret += boolFlagString("addresses") + case *f.flagLines: + ret += boolFlagString("lines") + case *f.flagFiles: + ret += boolFlagString("files") + case *f.flagFunctions: + ret += boolFlagString("functions") + } + + if *f.flagNodeCount != -1 { + ret += intFlagString("nodecount", *f.flagNodeCount) + } + + ret += floatFlagString("nodefraction", *f.flagNodeFraction) + ret += floatFlagString("edgefraction", *f.flagEdgeFraction) + + if *f.flagFocus != "" { + ret += stringFlagString("focus", *f.flagFocus) + } + if *f.flagIgnore != "" { + ret += stringFlagString("ignore", *f.flagIgnore) + } + if *f.flagHide != "" { + ret += stringFlagString("hide", *f.flagHide) + } + + if *f.flagTagFocus != "" { + ret += stringFlagString("tagfocus", *f.flagTagFocus) + } + if *f.flagTagIgnore != "" { + ret += stringFlagString("tagignore", *f.flagTagIgnore) + } + + return ret +} + +func boolFlagString(label string) string { + return fmt.Sprintf(" %-25s : true\n", label) +} + +func stringFlagString(label, value string) string { + return fmt.Sprintf(" %-25s : %s\n", label, value) +} + +func intFlagString(label string, value int) string { + return fmt.Sprintf(" %-25s : %d\n", label, value) +} + +func floatFlagString(label string, value float64) string { + return fmt.Sprintf(" %-25s : %f\n", label, value) +} + +// Utility routines to set flag values. +func newBool(b bool) *bool { + return &b +} + +func newString(s string) *string { + return &s +} + +func newFloat64(fl float64) *float64 { + return &fl +} + +func newInt(i int) *int { + return &i +} + +func (f *flags) usage(ui plugin.UI) { + var commandMsg []string + for name, cmd := range f.commands { + if cmd.HasParam { + name = name + "=p" + } + commandMsg = append(commandMsg, + fmt.Sprintf(" -%-16s %s", name, cmd.Usage)) + } + + sort.Strings(commandMsg) + + text := usageMsgHdr + strings.Join(commandMsg, "\n") + "\n" + usageMsg + "\n" + if f.extraUsage != "" { + text += f.extraUsage + "\n" + } + text += usageMsgVars + ui.Print(text) +} + +func getFlags(flag plugin.FlagSet, overrides commands.Commands, ui plugin.UI) (*flags, error) { + f := &flags{ + flagInteractive: flag.Bool("interactive", false, "Accepts commands interactively"), + flagCommands: make(map[string]*bool), + flagParamCommands: make(map[string]*string), + + // Filename for file-based output formats, stdout by default. + flagOutput: flag.String("output", "", "Output filename for file-based outputs "), + // Comparisons. + flagBase: flag.String("base", "", "Source for base profile for comparison"), + flagDropNegative: flag.Bool("drop_negative", false, "Ignore negative differences"), + + // Data sorting criteria. + flagCum: flag.Bool("cum", false, "Sort by cumulative data"), + // Graph handling options. + flagCallTree: flag.Bool("call_tree", false, "Create a context-sensitive call tree"), + // Granularity of output resolution. + flagAddresses: flag.Bool("addresses", false, "Report at address level"), + flagLines: flag.Bool("lines", false, "Report at source line level"), + flagFiles: flag.Bool("files", false, "Report at source file level"), + flagFunctions: flag.Bool("functions", false, "Report at function level [default]"), + // Internal options. + flagSymbolize: flag.String("symbolize", "", "Options for profile symbolization"), + flagBuildID: flag.String("buildid", "", "Override build id for first mapping"), + // Filtering options + flagNodeCount: flag.Int("nodecount", -1, "Max number of nodes to show"), + flagNodeFraction: flag.Float64("nodefraction", 0.005, "Hide nodes below *total"), + flagEdgeFraction: flag.Float64("edgefraction", 0.001, "Hide edges below *total"), + flagTrim: flag.Bool("trim", true, "Honor nodefraction/edgefraction/nodecount defaults"), + flagRuntime: flag.Bool("runtime", false, "Show runtime call frames in memory profiles"), + flagFocus: flag.String("focus", "", "Restricts to paths going through a node matching regexp"), + flagIgnore: flag.String("ignore", "", "Skips paths going through any nodes matching regexp"), + flagHide: flag.String("hide", "", "Skips nodes matching regexp"), + flagTagFocus: flag.String("tagfocus", "", "Restrict to samples with tags in range or matched by regexp"), + flagTagIgnore: flag.String("tagignore", "", "Discard samples with tags in range or matched by regexp"), + // CPU profile options + flagSeconds: flag.Int("seconds", -1, "Length of time for dynamic profiles"), + // Heap profile options + flagInUseSpace: flag.Bool("inuse_space", false, "Display in-use memory size"), + flagInUseObjects: flag.Bool("inuse_objects", false, "Display in-use object counts"), + flagAllocSpace: flag.Bool("alloc_space", false, "Display allocated memory size"), + flagAllocObjects: flag.Bool("alloc_objects", false, "Display allocated object counts"), + flagDisplayUnit: flag.String("unit", "minimum", "Measurement units to display"), + flagDivideBy: flag.Float64("divide_by", 1.0, "Ratio to divide all samples before visualization"), + flagSampleIndex: flag.Int("sample_index", -1, "Index of sample value to report"), + flagMean: flag.Bool("mean", false, "Average sample value over first value (count)"), + // Contention profile options + flagTotalDelay: flag.Bool("total_delay", false, "Display total delay at each region"), + flagContentions: flag.Bool("contentions", false, "Display number of delays at each region"), + flagMeanDelay: flag.Bool("mean_delay", false, "Display mean delay at each region"), + flagTools: flag.String("tools", os.Getenv("PPROF_TOOLS"), "Path for object tool pathnames"), + extraUsage: flag.ExtraUsage(), + } + + // Flags used during command processing + interactive := &f.flagInteractive + f.commands = commands.PProf(functionCompleter, interactive) + + // Override commands + for name, cmd := range overrides { + f.commands[name] = cmd + } + + for name, cmd := range f.commands { + if cmd.HasParam { + f.flagParamCommands[name] = flag.String(name, "", "Generate a report in "+name+" format, matching regexp") + } else { + f.flagCommands[name] = flag.Bool(name, false, "Generate a report in "+name+" format") + } + } + + args := flag.Parse(func() { f.usage(ui) }) + if len(args) == 0 { + return nil, fmt.Errorf("no profile source specified") + } + + f.profileSource = args + + // Instruct legacy heapz parsers to grab historical allocation data, + // instead of the default in-use data. Not available with tcmalloc. + if *f.flagAllocSpace || *f.flagAllocObjects { + profile.LegacyHeapAllocated = true + } + + if profileDir := os.Getenv("PPROF_TMPDIR"); profileDir == "" { + profileDir = os.Getenv("HOME") + "/pprof" + os.Setenv("PPROF_TMPDIR", profileDir) + if err := os.MkdirAll(profileDir, 0755); err != nil { + return nil, fmt.Errorf("failed to access temp dir %s: %v", profileDir, err) + } + } + + return f, nil +} + +func processFlags(p *profile.Profile, ui plugin.UI, f *flags) error { + flagDis := f.isFormat("disasm") + flagPeek := f.isFormat("peek") + flagWebList := f.isFormat("weblist") + flagList := f.isFormat("list") + + if flagDis || flagWebList { + // Collect all samples at address granularity for assembly + // listing. + f.flagNodeCount = newInt(0) + f.flagAddresses = newBool(true) + f.flagLines = newBool(false) + f.flagFiles = newBool(false) + f.flagFunctions = newBool(false) + } + + if flagPeek { + // Collect all samples at function granularity for peek command + f.flagNodeCount = newInt(0) + f.flagAddresses = newBool(false) + f.flagLines = newBool(false) + f.flagFiles = newBool(false) + f.flagFunctions = newBool(true) + } + + if flagList { + // Collect all samples at fileline granularity for source + // listing. + f.flagNodeCount = newInt(0) + f.flagAddresses = newBool(false) + f.flagLines = newBool(true) + f.flagFiles = newBool(false) + f.flagFunctions = newBool(false) + } + + if !*f.flagTrim { + f.flagNodeCount = newInt(0) + f.flagNodeFraction = newFloat64(0) + f.flagEdgeFraction = newFloat64(0) + } + + if oc := countFlagMap(f.flagCommands, f.flagParamCommands); oc == 0 { + f.flagInteractive = newBool(true) + } else if oc > 1 { + f.usage(ui) + return fmt.Errorf("must set at most one output format") + } + + // Apply nodecount defaults for non-interactive mode. The + // interactive shell will apply defaults for the interactive mode. + if *f.flagNodeCount < 0 && !*f.flagInteractive { + switch { + default: + f.flagNodeCount = newInt(80) + case f.isFormat("text"): + f.flagNodeCount = newInt(0) + } + } + + // Apply legacy options and diagnose conflicts. + if rc := countFlags([]*bool{f.flagAddresses, f.flagLines, f.flagFiles, f.flagFunctions}); rc == 0 { + f.flagFunctions = newBool(true) + } else if rc > 1 { + f.usage(ui) + return fmt.Errorf("must set at most one granularity option") + } + + var err error + si, sm := *f.flagSampleIndex, *f.flagMean || *f.flagMeanDelay + si, err = sampleIndex(p, &f.flagTotalDelay, si, 1, "delay", "-total_delay", err) + si, err = sampleIndex(p, &f.flagMeanDelay, si, 1, "delay", "-mean_delay", err) + si, err = sampleIndex(p, &f.flagContentions, si, 0, "contentions", "-contentions", err) + + si, err = sampleIndex(p, &f.flagInUseSpace, si, 1, "inuse_space", "-inuse_space", err) + si, err = sampleIndex(p, &f.flagInUseObjects, si, 0, "inuse_objects", "-inuse_objects", err) + si, err = sampleIndex(p, &f.flagAllocSpace, si, 1, "alloc_space", "-alloc_space", err) + si, err = sampleIndex(p, &f.flagAllocObjects, si, 0, "alloc_objects", "-alloc_objects", err) + + if si == -1 { + // Use last value if none is requested. + si = len(p.SampleType) - 1 + } else if si < 0 || si >= len(p.SampleType) { + err = fmt.Errorf("sample_index value %d out of range [0..%d]", si, len(p.SampleType)-1) + } + + if err != nil { + f.usage(ui) + return err + } + f.flagSampleIndex, f.flagMean = newInt(si), newBool(sm) + return nil +} + +func sampleIndex(p *profile.Profile, flag **bool, + sampleIndex int, + newSampleIndex int, + sampleType, option string, + err error) (int, error) { + if err != nil || !**flag { + return sampleIndex, err + } + *flag = newBool(false) + if sampleIndex != -1 { + return 0, fmt.Errorf("set at most one sample value selection option") + } + if newSampleIndex >= len(p.SampleType) || + p.SampleType[newSampleIndex].Type != sampleType { + return 0, fmt.Errorf("option %s not valid for this profile", option) + } + return newSampleIndex, nil +} + +func countFlags(bs []*bool) int { + var c int + for _, b := range bs { + if *b { + c++ + } + } + return c +} + +func countFlagMap(bms map[string]*bool, bmrxs map[string]*string) int { + var c int + for _, b := range bms { + if *b { + c++ + } + } + for _, s := range bmrxs { + if *s != "" { + c++ + } + } + return c +} + +var usageMsgHdr = "usage: pprof [options] [binary] ...\n" + + "Output format (only set one):\n" + +var usageMsg = "Output file parameters (for file-based output formats):\n" + + " -output=f Generate output on file f (stdout by default)\n" + + "Output granularity (only set one):\n" + + " -functions Report at function level [default]\n" + + " -files Report at source file level\n" + + " -lines Report at source line level\n" + + " -addresses Report at address level\n" + + "Comparison options:\n" + + " -base Show delta from this profile\n" + + " -drop_negative Ignore negative differences\n" + + "Sorting options:\n" + + " -cum Sort by cumulative data\n\n" + + "Dynamic profile options:\n" + + " -seconds=N Length of time for dynamic profiles\n" + + "Profile trimming options:\n" + + " -nodecount=N Max number of nodes to show\n" + + " -nodefraction=f Hide nodes below *total\n" + + " -edgefraction=f Hide edges below *total\n" + + "Sample value selection option (by index):\n" + + " -sample_index Index of sample value to display\n" + + " -mean Average sample value over first value\n" + + "Sample value selection option (for heap profiles):\n" + + " -inuse_space Display in-use memory size\n" + + " -inuse_objects Display in-use object counts\n" + + " -alloc_space Display allocated memory size\n" + + " -alloc_objects Display allocated object counts\n" + + "Sample value selection option (for contention profiles):\n" + + " -total_delay Display total delay at each region\n" + + " -contentions Display number of delays at each region\n" + + " -mean_delay Display mean delay at each region\n" + + "Filtering options:\n" + + " -runtime Show runtime call frames in memory profiles\n" + + " -focus=r Restricts to paths going through a node matching regexp\n" + + " -ignore=r Skips paths going through any nodes matching regexp\n" + + " -tagfocus=r Restrict to samples tagged with key:value matching regexp\n" + + " Restrict to samples with numeric tags in range (eg \"32kb:1mb\")\n" + + " -tagignore=r Discard samples tagged with key:value matching regexp\n" + + " Avoid samples with numeric tags in range (eg \"1mb:\")\n" + + "Miscellaneous:\n" + + " -call_tree Generate a context-sensitive call tree\n" + + " -unit=u Convert all samples to unit u for display\n" + + " -divide_by=f Scale all samples by dividing them by f\n" + + " -buildid=id Override build id for main binary in profile\n" + + " -tools=path Search path for object-level tools\n" + + " -help This message" + +var usageMsgVars = "Environment Variables:\n" + + " PPROF_TMPDIR Location for saved profiles (default $HOME/pprof)\n" + + " PPROF_TOOLS Search path for object-level tools\n" + + " PPROF_BINARY_PATH Search path for local binary files\n" + + " default: $HOME/pprof/binaries\n" + + " finds binaries by $name and $buildid/$name" + +func aggregate(prof *profile.Profile, f *flags) error { + switch { + case f.isFormat("proto"), f.isFormat("raw"): + // No aggregation for raw profiles. + case f.isFormat("callgrind"): + // Aggregate to file/line for callgrind. + fallthrough + case *f.flagLines: + return prof.Aggregate(true, true, true, true, false) + case *f.flagFiles: + return prof.Aggregate(true, false, true, false, false) + case *f.flagFunctions: + return prof.Aggregate(true, true, false, false, false) + case f.isFormat("weblist"), f.isFormat("disasm"): + return prof.Aggregate(false, true, true, true, true) + } + return nil +} + +// parseOptions parses the options into report.Options +// Returns a function to postprocess the report after generation. +func parseOptions(f *flags) (o *report.Options, p commands.PostProcessor, err error) { + + if *f.flagDivideBy == 0 { + return nil, nil, fmt.Errorf("zero divisor specified") + } + + o = &report.Options{ + CumSort: *f.flagCum, + CallTree: *f.flagCallTree, + PrintAddresses: *f.flagAddresses, + DropNegative: *f.flagDropNegative, + Ratio: 1 / *f.flagDivideBy, + + NodeCount: *f.flagNodeCount, + NodeFraction: *f.flagNodeFraction, + EdgeFraction: *f.flagEdgeFraction, + OutputUnit: *f.flagDisplayUnit, + } + + for cmd, b := range f.flagCommands { + if *b { + pcmd := f.commands[cmd] + o.OutputFormat = pcmd.Format + return o, pcmd.PostProcess, nil + } + } + + for cmd, rx := range f.flagParamCommands { + if *rx != "" { + pcmd := f.commands[cmd] + if o.Symbol, err = regexp.Compile(*rx); err != nil { + return nil, nil, fmt.Errorf("parsing -%s regexp: %v", cmd, err) + } + o.OutputFormat = pcmd.Format + return o, pcmd.PostProcess, nil + } + } + + return nil, nil, fmt.Errorf("no output format selected") +} + +type sampleValueFunc func(*profile.Sample) int64 + +// sampleFormat returns a function to extract values out of a profile.Sample, +// and the type/units of those values. +func sampleFormat(p *profile.Profile, f *flags) (sampleValueFunc, string, string) { + valueIndex := *f.flagSampleIndex + + if *f.flagMean { + return meanExtractor(valueIndex), "mean_" + p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit + } + + return valueExtractor(valueIndex), p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit +} + +func valueExtractor(ix int) sampleValueFunc { + return func(s *profile.Sample) int64 { + return s.Value[ix] + } +} + +func meanExtractor(ix int) sampleValueFunc { + return func(s *profile.Sample) int64 { + if s.Value[0] == 0 { + return 0 + } + return s.Value[ix] / s.Value[0] + } +} + +func generate(interactive bool, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error { + o, postProcess, err := parseOptions(f) + if err != nil { + return err + } + + var w io.Writer + if *f.flagOutput == "" { + w = os.Stdout + } else { + ui.PrintErr("Generating report in ", *f.flagOutput) + outputFile, err := os.Create(*f.flagOutput) + if err != nil { + return err + } + defer outputFile.Close() + w = outputFile + } + + if prof.Empty() { + return fmt.Errorf("profile is empty") + } + + value, stype, unit := sampleFormat(prof, f) + o.SampleType = stype + rpt := report.New(prof, *o, value, unit) + + // Do not apply filters if we're just generating a proto, so we + // still have all the data. + if o.OutputFormat != report.Proto { + // Delay applying focus/ignore until after creating the report so + // the report reflects the total number of samples. + if err := preprocess(prof, ui, f); err != nil { + return err + } + } + + if postProcess == nil { + return report.Generate(w, rpt, obj) + } + + var dot bytes.Buffer + if err = report.Generate(&dot, rpt, obj); err != nil { + return err + } + + return postProcess(&dot, w, ui) +} diff --git a/src/cmd/internal/pprof/driver/interactive.go b/src/cmd/internal/pprof/driver/interactive.go new file mode 100644 index 0000000000..1b08226527 --- /dev/null +++ b/src/cmd/internal/pprof/driver/interactive.go @@ -0,0 +1,492 @@ +// Copyright 2014 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 driver + +import ( + "fmt" + "io" + "regexp" + "sort" + "strconv" + "strings" + + "cmd/internal/pprof/commands" + "cmd/internal/pprof/plugin" + "cmd/internal/pprof/profile" +) + +var profileFunctionNames = []string{} + +// functionCompleter replaces provided substring with a function +// name retrieved from a profile if a single match exists. Otherwise, +// it returns unchanged substring. It defaults to no-op if the profile +// is not specified. +func functionCompleter(substring string) string { + found := "" + for _, fName := range profileFunctionNames { + if strings.Contains(fName, substring) { + if found != "" { + return substring + } + found = fName + } + } + if found != "" { + return found + } + return substring +} + +// updateAutoComplete enhances autocompletion with information that can be +// retrieved from the profile +func updateAutoComplete(p *profile.Profile) { + profileFunctionNames = nil // remove function names retrieved previously + for _, fn := range p.Function { + profileFunctionNames = append(profileFunctionNames, fn.Name) + } +} + +// splitCommand splits the command line input into tokens separated by +// spaces. Takes care to separate commands of the form 'top10' into +// two tokens: 'top' and '10' +func splitCommand(input string) []string { + fields := strings.Fields(input) + if num := strings.IndexAny(fields[0], "0123456789"); num != -1 { + inputNumber := fields[0][num:] + fields[0] = fields[0][:num] + fields = append([]string{fields[0], inputNumber}, fields[1:]...) + } + return fields +} + +// interactive displays a prompt and reads commands for profile +// manipulation/visualization. +func interactive(p *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error { + updateAutoComplete(p) + + // Enter command processing loop. + ui.Print("Entering interactive mode (type \"help\" for commands)") + ui.SetAutoComplete(commands.NewCompleter(f.commands)) + + for { + input, err := readCommand(p, ui, f) + if err != nil { + if err != io.EOF { + return err + } + if input == "" { + return nil + } + } + // Process simple commands. + switch input { + case "": + continue + case ":": + f.flagFocus = newString("") + f.flagIgnore = newString("") + f.flagTagFocus = newString("") + f.flagTagIgnore = newString("") + f.flagHide = newString("") + continue + } + + fields := splitCommand(input) + // Process report generation commands. + if _, ok := f.commands[fields[0]]; ok { + if err := generateReport(p, fields, obj, ui, f); err != nil { + if err == io.EOF { + return nil + } + ui.PrintErr(err) + } + continue + } + + switch cmd := fields[0]; cmd { + case "help": + commandHelp(fields, ui, f) + continue + case "exit", "quit": + return nil + } + + // Process option settings. + if of, err := optFlags(p, input, f); err == nil { + f = of + } else { + ui.PrintErr("Error: ", err.Error()) + } + } +} + +func generateReport(p *profile.Profile, cmd []string, obj plugin.ObjTool, ui plugin.UI, f *flags) error { + prof := p.Copy() + + cf, err := cmdFlags(prof, cmd, ui, f) + if err != nil { + return err + } + + return generate(true, prof, obj, ui, cf) +} + +// validateRegex checks if a string is a valid regular expression. +func validateRegex(v string) error { + _, err := regexp.Compile(v) + return err +} + +// readCommand prompts for and reads the next command. +func readCommand(p *profile.Profile, ui plugin.UI, f *flags) (string, error) { + //ui.Print("Options:\n", f.String(p)) + s, err := ui.ReadLine() + return strings.TrimSpace(s), err +} + +func commandHelp(_ []string, ui plugin.UI, f *flags) error { + help := ` + Commands: + cmd [n] [--cum] [focus_regex]* [-ignore_regex]* + Produce a text report with the top n entries. + Include samples matching focus_regex, and exclude ignore_regex. + Add --cum to sort using cumulative data. + Available commands: +` + var commands []string + for name, cmd := range f.commands { + commands = append(commands, fmt.Sprintf(" %-12s %s", name, cmd.Usage)) + } + sort.Strings(commands) + + help = help + strings.Join(commands, "\n") + ` + peek func_regex + Display callers and callees of functions matching func_regex. + + dot [n] [focus_regex]* [-ignore_regex]* [>file] + Produce an annotated callgraph with the top n entries. + Include samples matching focus_regex, and exclude ignore_regex. + For other outputs, replace dot with: + - Graphic formats: dot, svg, pdf, ps, gif, png (use > to name output file) + - Graph viewer: gv, web, evince, eog + + callgrind [n] [focus_regex]* [-ignore_regex]* [>file] + Produce a file in callgrind-compatible format. + Include samples matching focus_regex, and exclude ignore_regex. + + weblist func_regex [-ignore_regex]* + Show annotated source with interspersed assembly in a web browser. + + list func_regex [-ignore_regex]* + Print source for routines matching func_regex, and exclude ignore_regex. + + disasm func_regex [-ignore_regex]* + Disassemble routines matching func_regex, and exclude ignore_regex. + + tags tag_regex [-ignore_regex]* + List tags with key:value matching tag_regex and exclude ignore_regex. + + quit/exit/^D + Exit pprof. + + option=value + The following options can be set individually: + cum/flat: Sort entries based on cumulative or flat data + call_tree: Build context-sensitive call trees + nodecount: Max number of entries to display + nodefraction: Min frequency ratio of nodes to display + edgefraction: Min frequency ratio of edges to display + focus/ignore: Regexp to include/exclude samples by name/file + tagfocus/tagignore: Regexp or value range to filter samples by tag + eg "1mb", "1mb:2mb", ":64kb" + + functions: Level of aggregation for sample data + files: + lines: + addresses: + + unit: Measurement unit to use on reports + + Sample value selection by index: + sample_index: Index of sample value to display + mean: Average sample value over first value + + Sample value selection by name: + alloc_space for heap profiles + alloc_objects + inuse_space + inuse_objects + + total_delay for contention profiles + mean_delay + contentions + + : Clear focus/ignore/hide/tagfocus/tagignore` + + ui.Print(help) + return nil +} + +// cmdFlags parses the options of an interactive command and returns +// an updated flags object. +func cmdFlags(prof *profile.Profile, input []string, ui plugin.UI, f *flags) (*flags, error) { + cf := *f + + var focus, ignore string + output := *cf.flagOutput + nodeCount := *cf.flagNodeCount + cmd := input[0] + + // Update output flags based on parameters. + tokens := input[1:] + for p := 0; p < len(tokens); p++ { + t := tokens[p] + if t == "" { + continue + } + if c, err := strconv.ParseInt(t, 10, 32); err == nil { + nodeCount = int(c) + continue + } + switch t[0] { + case '>': + if len(t) > 1 { + output = t[1:] + continue + } + // find next token + for p++; p < len(tokens); p++ { + if tokens[p] != "" { + output = tokens[p] + break + } + } + case '-': + if t == "--cum" || t == "-cum" { + cf.flagCum = newBool(true) + continue + } + ignore = catRegex(ignore, t[1:]) + default: + focus = catRegex(focus, t) + } + } + + pcmd, ok := f.commands[cmd] + if !ok { + return nil, fmt.Errorf("Unexpected parse failure: %v", input) + } + // Reset flags + cf.flagCommands = make(map[string]*bool) + cf.flagParamCommands = make(map[string]*string) + + if !pcmd.HasParam { + cf.flagCommands[cmd] = newBool(true) + + switch cmd { + case "tags": + cf.flagTagFocus = newString(focus) + cf.flagTagIgnore = newString(ignore) + default: + cf.flagFocus = newString(catRegex(*cf.flagFocus, focus)) + cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore)) + } + } else { + if focus == "" { + focus = "." + } + cf.flagParamCommands[cmd] = newString(focus) + cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore)) + } + + if nodeCount < 0 { + switch cmd { + case "text", "top": + // Default text/top to 10 nodes on interactive mode + nodeCount = 10 + default: + nodeCount = 80 + } + } + + cf.flagNodeCount = newInt(nodeCount) + cf.flagOutput = newString(output) + + // Do regular flags processing + if err := processFlags(prof, ui, &cf); err != nil { + cf.usage(ui) + return nil, err + } + + return &cf, nil +} + +func catRegex(a, b string) string { + if a == "" { + return b + } + if b == "" { + return a + } + return a + "|" + b +} + +// optFlags parses an interactive option setting and returns +// an updated flags object. +func optFlags(p *profile.Profile, input string, f *flags) (*flags, error) { + inputs := strings.SplitN(input, "=", 2) + option := strings.ToLower(strings.TrimSpace(inputs[0])) + var value string + if len(inputs) == 2 { + value = strings.TrimSpace(inputs[1]) + } + + of := *f + + var err error + var bv bool + var uv uint64 + var fv float64 + + switch option { + case "cum": + if bv, err = parseBool(value); err != nil { + return nil, err + } + of.flagCum = newBool(bv) + case "flat": + if bv, err = parseBool(value); err != nil { + return nil, err + } + of.flagCum = newBool(!bv) + case "call_tree": + if bv, err = parseBool(value); err != nil { + return nil, err + } + of.flagCallTree = newBool(bv) + case "unit": + of.flagDisplayUnit = newString(value) + case "sample_index": + if uv, err = strconv.ParseUint(value, 10, 32); err != nil { + return nil, err + } + if ix := int(uv); ix < 0 || ix >= len(p.SampleType) { + return nil, fmt.Errorf("sample_index out of range [0..%d]", len(p.SampleType)-1) + } + of.flagSampleIndex = newInt(int(uv)) + case "mean": + if bv, err = parseBool(value); err != nil { + return nil, err + } + of.flagMean = newBool(bv) + case "nodecount": + if uv, err = strconv.ParseUint(value, 10, 32); err != nil { + return nil, err + } + of.flagNodeCount = newInt(int(uv)) + case "nodefraction": + if fv, err = strconv.ParseFloat(value, 64); err != nil { + return nil, err + } + of.flagNodeFraction = newFloat64(fv) + case "edgefraction": + if fv, err = strconv.ParseFloat(value, 64); err != nil { + return nil, err + } + of.flagEdgeFraction = newFloat64(fv) + case "focus": + if err = validateRegex(value); err != nil { + return nil, err + } + of.flagFocus = newString(value) + case "ignore": + if err = validateRegex(value); err != nil { + return nil, err + } + of.flagIgnore = newString(value) + case "tagfocus": + if err = validateRegex(value); err != nil { + return nil, err + } + of.flagTagFocus = newString(value) + case "tagignore": + if err = validateRegex(value); err != nil { + return nil, err + } + of.flagTagIgnore = newString(value) + case "hide": + if err = validateRegex(value); err != nil { + return nil, err + } + of.flagHide = newString(value) + case "addresses", "files", "lines", "functions": + if bv, err = parseBool(value); err != nil { + return nil, err + } + if !bv { + return nil, fmt.Errorf("select one of addresses/files/lines/functions") + } + setGranularityToggle(option, &of) + default: + if ix := findSampleIndex(p, "", option); ix >= 0 { + of.flagSampleIndex = newInt(ix) + } else if ix := findSampleIndex(p, "total_", option); ix >= 0 { + of.flagSampleIndex = newInt(ix) + of.flagMean = newBool(false) + } else if ix := findSampleIndex(p, "mean_", option); ix >= 1 { + of.flagSampleIndex = newInt(ix) + of.flagMean = newBool(true) + } else { + return nil, fmt.Errorf("unrecognized command: %s", input) + } + } + return &of, nil +} + +// parseBool parses a string as a boolean value. +func parseBool(v string) (bool, error) { + switch strings.ToLower(v) { + case "true", "t", "yes", "y", "1", "": + return true, nil + case "false", "f", "no", "n", "0": + return false, nil + } + return false, fmt.Errorf(`illegal input "%s" for bool value`, v) +} + +func findSampleIndex(p *profile.Profile, prefix, sampleType string) int { + if !strings.HasPrefix(sampleType, prefix) { + return -1 + } + sampleType = strings.TrimPrefix(sampleType, prefix) + for i, r := range p.SampleType { + if r.Type == sampleType { + return i + } + } + return -1 +} + +// setGranularityToggle manages the set of granularity options. These +// operate as a toggle; turning one on turns the others off. +func setGranularityToggle(o string, fl *flags) { + t, f := newBool(true), newBool(false) + fl.flagFunctions = f + fl.flagFiles = f + fl.flagLines = f + fl.flagAddresses = f + switch o { + case "functions": + fl.flagFunctions = t + case "files": + fl.flagFiles = t + case "lines": + fl.flagLines = t + case "addresses": + fl.flagAddresses = t + default: + panic(fmt.Errorf("unexpected option %s", o)) + } +} diff --git a/src/cmd/internal/pprof/fetch/fetch.go b/src/cmd/internal/pprof/fetch/fetch.go new file mode 100644 index 0000000000..ffd282e74d --- /dev/null +++ b/src/cmd/internal/pprof/fetch/fetch.go @@ -0,0 +1,82 @@ +// Copyright 2014 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 fetch provides an extensible mechanism to fetch a profile +// from a data source. +package fetch + +import ( + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "strings" + "time" + + "cmd/internal/pprof/plugin" + "cmd/internal/pprof/profile" +) + +// FetchProfile reads from a data source (network, file) and generates a +// profile. +func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) { + return Fetcher(source, timeout, plugin.StandardUI()) +} + +// Fetcher is the plugin.Fetcher version of FetchProfile. +func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) { + var f io.ReadCloser + var err error + + url, err := url.Parse(source) + if err == nil && url.Host != "" { + f, err = FetchURL(source, timeout) + } else { + f, err = os.Open(source) + } + if err != nil { + return nil, err + } + defer f.Close() + return profile.Parse(f) +} + +// FetchURL fetches a profile from a URL using HTTP. +func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) { + resp, err := httpGet(source, timeout) + if err != nil { + return nil, fmt.Errorf("http fetch %s: %v", source, err) + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("server response: %s", resp.Status) + } + + return resp.Body, nil +} + +// PostURL issues a POST to a URL over HTTP. +func PostURL(source, post string) ([]byte, error) { + resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post)) + if err != nil { + return nil, fmt.Errorf("http post %s: %v", source, err) + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("server response: %s", resp.Status) + } + defer resp.Body.Close() + return ioutil.ReadAll(resp.Body) +} + +// httpGet is a wrapper around http.Get; it is defined as a variable +// so it can be redefined during for testing. +var httpGet = func(url string, timeout time.Duration) (*http.Response, error) { + client := &http.Client{ + Transport: &http.Transport{ + ResponseHeaderTimeout: timeout + 5*time.Second, + }, + } + return client.Get(url) +} diff --git a/src/cmd/internal/pprof/plugin/plugin.go b/src/cmd/internal/pprof/plugin/plugin.go new file mode 100644 index 0000000000..d5025d5517 --- /dev/null +++ b/src/cmd/internal/pprof/plugin/plugin.go @@ -0,0 +1,213 @@ +// Copyright 2014 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 plugin defines the plugin implementations that the main pprof driver requires. +package plugin + +import ( + "bufio" + "fmt" + "os" + "regexp" + "strings" + "time" + + "cmd/internal/pprof/profile" +) + +// A FlagSet creates and parses command-line flags. +// It is similar to the standard flag.FlagSet. +type FlagSet interface { + // Bool, Int, Float64, and String define new flags, + // like the functions of the same name in package flag. + Bool(name string, def bool, usage string) *bool + Int(name string, def int, usage string) *int + Float64(name string, def float64, usage string) *float64 + String(name string, def string, usage string) *string + + // ExtraUsage returns any additional text that should be + // printed after the standard usage message. + // The typical use of ExtraUsage is to show any custom flags + // defined by the specific pprof plugins being used. + ExtraUsage() string + + // Parse initializes the flags with their values for this run + // and returns the non-flag command line arguments. + // If an unknown flag is encountered or there are no arguments, + // Parse should call usage and return nil. + Parse(usage func()) []string +} + +// An ObjTool inspects shared libraries and executable files. +type ObjTool interface { + // Open opens the named object file. + // If the object is a shared library, start is the address where + // it is mapped into memory in the address space being inspected. + Open(file string, start uint64) (ObjFile, error) + + // Demangle translates a batch of symbol names from mangled + // form to human-readable form. + Demangle(names []string) (map[string]string, error) + + // Disasm disassembles the named object file, starting at + // the start address and stopping at (before) the end address. + Disasm(file string, start, end uint64) ([]Inst, error) + + // SetConfig configures the tool. + // The implementation defines the meaning of the string + // and can ignore it entirely. + SetConfig(config string) +} + +// NoObjTool returns a trivial implementation of the ObjTool interface. +// Open returns an error indicating that the requested file does not exist. +// Demangle returns an empty map and a nil error. +// Disasm returns an error. +// SetConfig is a no-op. +func NoObjTool() ObjTool { + return noObjTool{} +} + +type noObjTool struct{} + +func (noObjTool) Open(file string, start uint64) (ObjFile, error) { + return nil, &os.PathError{Op: "open", Path: file, Err: os.ErrNotExist} +} + +func (noObjTool) Demangle(name []string) (map[string]string, error) { + return make(map[string]string), nil +} + +func (noObjTool) Disasm(file string, start, end uint64) ([]Inst, error) { + return nil, fmt.Errorf("disassembly not supported") +} + +func (noObjTool) SetConfig(config string) { +} + +// An ObjFile is a single object file: a shared library or executable. +type ObjFile interface { + // Name returns the underlyinf file name, if available + Name() string + + // Base returns the base address to use when looking up symbols in the file. + Base() uint64 + + // BuildID returns the GNU build ID of the file, or an empty string. + BuildID() string + + // SourceLine reports the source line information for a given + // address in the file. Due to inlining, the source line information + // is in general a list of positions representing a call stack, + // with the leaf function first. + SourceLine(addr uint64) ([]Frame, error) + + // Symbols returns a list of symbols in the object file. + // If r is not nil, Symbols restricts the list to symbols + // with names matching the regular expression. + // If addr is not zero, Symbols restricts the list to symbols + // containing that address. + Symbols(r *regexp.Regexp, addr uint64) ([]*Sym, error) + + // Close closes the file, releasing associated resources. + Close() error +} + +// A Frame describes a single line in a source file. +type Frame struct { + Func string // name of function + File string // source file name + Line int // line in file +} + +// A Sym describes a single symbol in an object file. +type Sym struct { + Name []string // names of symbol (many if symbol was dedup'ed) + File string // object file containing symbol + Start uint64 // start virtual address + End uint64 // virtual address of last byte in sym (Start+size-1) +} + +// An Inst is a single instruction in an assembly listing. +type Inst struct { + Addr uint64 // virtual address of instruction + Text string // instruction text + File string // source file + Line int // source line +} + +// A UI manages user interactions. +type UI interface { + // Read returns a line of text (a command) read from the user. + ReadLine() (string, error) + + // Print shows a message to the user. + // It formats the text as fmt.Print would and adds a final \n if not already present. + // For line-based UI, Print writes to standard error. + // (Standard output is reserved for report data.) + Print(...interface{}) + + // PrintErr shows an error message to the user. + // It formats the text as fmt.Print would and adds a final \n if not already present. + // For line-based UI, PrintErr writes to standard error. + PrintErr(...interface{}) + + // IsTerminal returns whether the UI is known to be tied to an + // interactive terminal (as opposed to being redirected to a file). + IsTerminal() bool + + // SetAutoComplete instructs the UI to call complete(cmd) to obtain + // the auto-completion of cmd, if the UI supports auto-completion at all. + SetAutoComplete(complete func(string) string) +} + +// StandardUI returns a UI that reads from standard input, +// prints messages to standard output, +// prints errors to standard error, and doesn't use auto-completion. +func StandardUI() UI { + return &stdUI{r: bufio.NewReader(os.Stdin)} +} + +type stdUI struct { + r *bufio.Reader +} + +func (ui *stdUI) ReadLine() (string, error) { + os.Stdout.WriteString("(pprof) ") + return ui.r.ReadString('\n') +} + +func (ui *stdUI) Print(args ...interface{}) { + ui.fprint(os.Stderr, args) +} + +func (ui *stdUI) PrintErr(args ...interface{}) { + ui.fprint(os.Stderr, args) +} + +func (ui *stdUI) IsTerminal() bool { + return false +} + +func (ui *stdUI) SetAutoComplete(func(string) string) { +} + +func (ui *stdUI) fprint(f *os.File, args []interface{}) { + text := fmt.Sprint(args...) + if !strings.HasSuffix(text, "\n") { + text += "\n" + } + f.WriteString(text) +} + +// A Fetcher reads and returns the profile named by src. +// It gives up after the given timeout, unless src contains a timeout override +// (as defined by the implementation). +// It can print messages to ui. +type Fetcher func(src string, timeout time.Duration, ui UI) (*profile.Profile, error) + +// A Symbolizer annotates a profile with symbol information. +// The profile was fetch from src. +// The meaning of mode is defined by the implementation. +type Symbolizer func(mode, src string, prof *profile.Profile, obj ObjTool, ui UI) error diff --git a/src/cmd/internal/pprof/profile/encode.go b/src/cmd/internal/pprof/profile/encode.go new file mode 100644 index 0000000000..6b879a84ac --- /dev/null +++ b/src/cmd/internal/pprof/profile/encode.go @@ -0,0 +1,470 @@ +// Copyright 2014 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 profile + +import ( + "errors" + "fmt" + "sort" +) + +func (p *Profile) decoder() []decoder { + return profileDecoder +} + +// preEncode populates the unexported fields to be used by encode +// (with suffix X) from the corresponding exported fields. The +// exported fields are cleared up to facilitate testing. +func (p *Profile) preEncode() { + strings := make(map[string]int) + addString(strings, "") + + for _, st := range p.SampleType { + st.typeX = addString(strings, st.Type) + st.unitX = addString(strings, st.Unit) + } + + for _, s := range p.Sample { + s.labelX = nil + var keys []string + for k := range s.Label { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := s.Label[k] + for _, v := range vs { + s.labelX = append(s.labelX, + Label{ + keyX: addString(strings, k), + strX: addString(strings, v), + }, + ) + } + } + var numKeys []string + for k := range s.NumLabel { + numKeys = append(numKeys, k) + } + sort.Strings(numKeys) + for _, k := range numKeys { + vs := s.NumLabel[k] + for _, v := range vs { + s.labelX = append(s.labelX, + Label{ + keyX: addString(strings, k), + numX: v, + }, + ) + } + } + s.locationIDX = nil + for _, l := range s.Location { + s.locationIDX = append(s.locationIDX, l.ID) + } + } + + for _, m := range p.Mapping { + m.fileX = addString(strings, m.File) + m.buildIDX = addString(strings, m.BuildID) + } + + for _, l := range p.Location { + for i, ln := range l.Line { + if ln.Function != nil { + l.Line[i].functionIDX = ln.Function.ID + } else { + l.Line[i].functionIDX = 0 + } + } + if l.Mapping != nil { + l.mappingIDX = l.Mapping.ID + } else { + l.mappingIDX = 0 + } + } + for _, f := range p.Function { + f.nameX = addString(strings, f.Name) + f.systemNameX = addString(strings, f.SystemName) + f.filenameX = addString(strings, f.Filename) + } + + p.dropFramesX = addString(strings, p.DropFrames) + p.keepFramesX = addString(strings, p.KeepFrames) + + if pt := p.PeriodType; pt != nil { + pt.typeX = addString(strings, pt.Type) + pt.unitX = addString(strings, pt.Unit) + } + + p.stringTable = make([]string, len(strings)) + for s, i := range strings { + p.stringTable[i] = s + } +} + +func (p *Profile) encode(b *buffer) { + for _, x := range p.SampleType { + encodeMessage(b, 1, x) + } + for _, x := range p.Sample { + encodeMessage(b, 2, x) + } + for _, x := range p.Mapping { + encodeMessage(b, 3, x) + } + for _, x := range p.Location { + encodeMessage(b, 4, x) + } + for _, x := range p.Function { + encodeMessage(b, 5, x) + } + encodeStrings(b, 6, p.stringTable) + encodeInt64Opt(b, 7, p.dropFramesX) + encodeInt64Opt(b, 8, p.keepFramesX) + encodeInt64Opt(b, 9, p.TimeNanos) + encodeInt64Opt(b, 10, p.DurationNanos) + if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) { + encodeMessage(b, 11, p.PeriodType) + } + encodeInt64Opt(b, 12, p.Period) +} + +var profileDecoder = []decoder{ + nil, // 0 + // repeated ValueType sample_type = 1 + func(b *buffer, m message) error { + x := new(ValueType) + pp := m.(*Profile) + pp.SampleType = append(pp.SampleType, x) + return decodeMessage(b, x) + }, + // repeated Sample sample = 2 + func(b *buffer, m message) error { + x := new(Sample) + pp := m.(*Profile) + pp.Sample = append(pp.Sample, x) + return decodeMessage(b, x) + }, + // repeated Mapping mapping = 3 + func(b *buffer, m message) error { + x := new(Mapping) + pp := m.(*Profile) + pp.Mapping = append(pp.Mapping, x) + return decodeMessage(b, x) + }, + // repeated Location location = 4 + func(b *buffer, m message) error { + x := new(Location) + pp := m.(*Profile) + pp.Location = append(pp.Location, x) + return decodeMessage(b, x) + }, + // repeated Function function = 5 + func(b *buffer, m message) error { + x := new(Function) + pp := m.(*Profile) + pp.Function = append(pp.Function, x) + return decodeMessage(b, x) + }, + // repeated string string_table = 6 + func(b *buffer, m message) error { + err := decodeStrings(b, &m.(*Profile).stringTable) + if err != nil { + return err + } + if *&m.(*Profile).stringTable[0] != "" { + return errors.New("string_table[0] must be ''") + } + return nil + }, + // repeated int64 drop_frames = 7 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) }, + // repeated int64 keep_frames = 8 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) }, + // repeated int64 time_nanos = 9 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).TimeNanos) }, + // repeated int64 duration_nanos = 10 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) }, + // optional string period_type = 11 + func(b *buffer, m message) error { + x := new(ValueType) + pp := m.(*Profile) + pp.PeriodType = x + return decodeMessage(b, x) + }, + // repeated int64 period = 12 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) }, +} + +// postDecode takes the unexported fields populated by decode (with +// suffix X) and populates the corresponding exported fields. +// The unexported fields are cleared up to facilitate testing. +func (p *Profile) postDecode() error { + var err error + + mappings := make(map[uint64]*Mapping) + for _, m := range p.Mapping { + m.File, err = getString(p.stringTable, &m.fileX, err) + m.BuildID, err = getString(p.stringTable, &m.buildIDX, err) + mappings[m.ID] = m + } + + functions := make(map[uint64]*Function) + for _, f := range p.Function { + f.Name, err = getString(p.stringTable, &f.nameX, err) + f.SystemName, err = getString(p.stringTable, &f.systemNameX, err) + f.Filename, err = getString(p.stringTable, &f.filenameX, err) + functions[f.ID] = f + } + + locations := make(map[uint64]*Location) + for _, l := range p.Location { + l.Mapping = mappings[l.mappingIDX] + l.mappingIDX = 0 + for i, ln := range l.Line { + if id := ln.functionIDX; id != 0 { + l.Line[i].Function = functions[id] + if l.Line[i].Function == nil { + return fmt.Errorf("Function ID %d not found", id) + } + l.Line[i].functionIDX = 0 + } + } + locations[l.ID] = l + } + + for _, st := range p.SampleType { + st.Type, err = getString(p.stringTable, &st.typeX, err) + st.Unit, err = getString(p.stringTable, &st.unitX, err) + } + + for _, s := range p.Sample { + labels := make(map[string][]string) + numLabels := make(map[string][]int64) + for _, l := range s.labelX { + var key, value string + key, err = getString(p.stringTable, &l.keyX, err) + if l.strX != 0 { + value, err = getString(p.stringTable, &l.strX, err) + labels[key] = append(labels[key], value) + } else { + numLabels[key] = append(numLabels[key], l.numX) + } + } + if len(labels) > 0 { + s.Label = labels + } + if len(numLabels) > 0 { + s.NumLabel = numLabels + } + s.Location = nil + for _, lid := range s.locationIDX { + s.Location = append(s.Location, locations[lid]) + } + s.locationIDX = nil + } + + p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err) + p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err) + + if pt := p.PeriodType; pt == nil { + p.PeriodType = &ValueType{} + } + + if pt := p.PeriodType; pt != nil { + pt.Type, err = getString(p.stringTable, &pt.typeX, err) + pt.Unit, err = getString(p.stringTable, &pt.unitX, err) + } + p.stringTable = nil + return nil +} + +func (p *ValueType) decoder() []decoder { + return valueTypeDecoder +} + +func (p *ValueType) encode(b *buffer) { + encodeInt64Opt(b, 1, p.typeX) + encodeInt64Opt(b, 2, p.unitX) +} + +var valueTypeDecoder = []decoder{ + nil, // 0 + // optional int64 type = 1 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) }, + // optional int64 unit = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) }, +} + +func (p *Sample) decoder() []decoder { + return sampleDecoder +} + +func (p *Sample) encode(b *buffer) { + encodeUint64s(b, 1, p.locationIDX) + for _, x := range p.Value { + encodeInt64(b, 2, x) + } + for _, x := range p.labelX { + encodeMessage(b, 3, x) + } +} + +var sampleDecoder = []decoder{ + nil, // 0 + // repeated uint64 location = 1 + func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) }, + // repeated int64 value = 2 + func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) }, + // repeated Label label = 3 + func(b *buffer, m message) error { + s := m.(*Sample) + n := len(s.labelX) + s.labelX = append(s.labelX, Label{}) + return decodeMessage(b, &s.labelX[n]) + }, +} + +func (p Label) decoder() []decoder { + return labelDecoder +} + +func (p Label) encode(b *buffer) { + encodeInt64Opt(b, 1, p.keyX) + encodeInt64Opt(b, 2, p.strX) + encodeInt64Opt(b, 3, p.numX) +} + +var labelDecoder = []decoder{ + nil, // 0 + // optional int64 key = 1 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).keyX) }, + // optional int64 str = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).strX) }, + // optional int64 num = 3 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).numX) }, +} + +func (p *Mapping) decoder() []decoder { + return mappingDecoder +} + +func (p *Mapping) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeUint64Opt(b, 2, p.Start) + encodeUint64Opt(b, 3, p.Limit) + encodeUint64Opt(b, 4, p.Offset) + encodeInt64Opt(b, 5, p.fileX) + encodeInt64Opt(b, 6, p.buildIDX) + encodeBoolOpt(b, 7, p.HasFunctions) + encodeBoolOpt(b, 8, p.HasFilenames) + encodeBoolOpt(b, 9, p.HasLineNumbers) + encodeBoolOpt(b, 10, p.HasInlineFrames) +} + +var mappingDecoder = []decoder{ + nil, // 0 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9 + func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10 +} + +func (p *Location) decoder() []decoder { + return locationDecoder +} + +func (p *Location) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeUint64Opt(b, 2, p.mappingIDX) + encodeUint64Opt(b, 3, p.Address) + for i := range p.Line { + encodeMessage(b, 4, &p.Line[i]) + } +} + +var locationDecoder = []decoder{ + nil, // 0 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1; + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2; + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3; + func(b *buffer, m message) error { // repeated Line line = 4 + pp := m.(*Location) + n := len(pp.Line) + pp.Line = append(pp.Line, Line{}) + return decodeMessage(b, &pp.Line[n]) + }, +} + +func (p *Line) decoder() []decoder { + return lineDecoder +} + +func (p *Line) encode(b *buffer) { + encodeUint64Opt(b, 1, p.functionIDX) + encodeInt64Opt(b, 2, p.Line) +} + +var lineDecoder = []decoder{ + nil, // 0 + // optional uint64 function_id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) }, + // optional int64 line = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) }, +} + +func (p *Function) decoder() []decoder { + return functionDecoder +} + +func (p *Function) encode(b *buffer) { + encodeUint64Opt(b, 1, p.ID) + encodeInt64Opt(b, 2, p.nameX) + encodeInt64Opt(b, 3, p.systemNameX) + encodeInt64Opt(b, 4, p.filenameX) + encodeInt64Opt(b, 5, p.StartLine) +} + +var functionDecoder = []decoder{ + nil, // 0 + // optional uint64 id = 1 + func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) }, + // optional int64 function_name = 2 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) }, + // optional int64 function_system_name = 3 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) }, + // repeated int64 filename = 4 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) }, + // optional int64 start_line = 5 + func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) }, +} + +func addString(strings map[string]int, s string) int64 { + i, ok := strings[s] + if !ok { + i = len(strings) + strings[s] = i + } + return int64(i) +} + +func getString(strings []string, strng *int64, err error) (string, error) { + if err != nil { + return "", err + } + s := int(*strng) + if s < 0 || s >= len(strings) { + return "", errMalformed + } + *strng = 0 + return strings[s], nil +} diff --git a/src/cmd/internal/pprof/profile/filter.go b/src/cmd/internal/pprof/profile/filter.go new file mode 100644 index 0000000000..1baa096a49 --- /dev/null +++ b/src/cmd/internal/pprof/profile/filter.go @@ -0,0 +1,158 @@ +// Copyright 2014 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. + +// Implements methods to filter samples from profiles. + +package profile + +import "regexp" + +// FilterSamplesByName filters the samples in a profile and only keeps +// samples where at least one frame matches focus but none match ignore. +// Returns true is the corresponding regexp matched at least one sample. +func (p *Profile) FilterSamplesByName(focus, ignore, hide *regexp.Regexp) (fm, im, hm bool) { + focusOrIgnore := make(map[uint64]bool) + hidden := make(map[uint64]bool) + for _, l := range p.Location { + if ignore != nil && l.matchesName(ignore) { + im = true + focusOrIgnore[l.ID] = false + } else if focus == nil || l.matchesName(focus) { + fm = true + focusOrIgnore[l.ID] = true + } + if hide != nil && l.matchesName(hide) { + hm = true + l.Line = l.unmatchedLines(hide) + if len(l.Line) == 0 { + hidden[l.ID] = true + } + } + } + + s := make([]*Sample, 0, len(p.Sample)) + for _, sample := range p.Sample { + if focusedAndNotIgnored(sample.Location, focusOrIgnore) { + if len(hidden) > 0 { + var locs []*Location + for _, loc := range sample.Location { + if !hidden[loc.ID] { + locs = append(locs, loc) + } + } + if len(locs) == 0 { + // Remove sample with no locations (by not adding it to s). + continue + } + sample.Location = locs + } + s = append(s, sample) + } + } + p.Sample = s + + return +} + +// matchesName returns whether the function name or file in the +// location matches the regular expression. +func (loc *Location) matchesName(re *regexp.Regexp) bool { + for _, ln := range loc.Line { + if fn := ln.Function; fn != nil { + if re.MatchString(fn.Name) { + return true + } + if re.MatchString(fn.Filename) { + return true + } + } + } + return false +} + +// unmatchedLines returns the lines in the location that do not match +// the regular expression. +func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line { + var lines []Line + for _, ln := range loc.Line { + if fn := ln.Function; fn != nil { + if re.MatchString(fn.Name) { + continue + } + if re.MatchString(fn.Filename) { + continue + } + } + lines = append(lines, ln) + } + return lines +} + +// focusedAndNotIgnored looks up a slice of ids against a map of +// focused/ignored locations. The map only contains locations that are +// explicitly focused or ignored. Returns whether there is at least +// one focused location but no ignored locations. +func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool { + var f bool + for _, loc := range locs { + if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore { + if focus { + // Found focused location. Must keep searching in case there + // is an ignored one as well. + f = true + } else { + // Found ignored location. Can return false right away. + return false + } + } + } + return f +} + +// TagMatch selects tags for filtering +type TagMatch func(key, val string, nval int64) bool + +// FilterSamplesByTag removes all samples from the profile, except +// those that match focus and do not match the ignore regular +// expression. +func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) { + samples := make([]*Sample, 0, len(p.Sample)) + for _, s := range p.Sample { + focused, ignored := focusedSample(s, focus, ignore) + fm = fm || focused + im = im || ignored + if focused && !ignored { + samples = append(samples, s) + } + } + p.Sample = samples + return +} + +// focusedTag checks a sample against focus and ignore regexps. +// Returns whether the focus/ignore regexps match any tags +func focusedSample(s *Sample, focus, ignore TagMatch) (fm, im bool) { + fm = focus == nil + for key, vals := range s.Label { + for _, val := range vals { + if ignore != nil && ignore(key, val, 0) { + im = true + } + if !fm && focus(key, val, 0) { + fm = true + } + } + } + for key, vals := range s.NumLabel { + for _, val := range vals { + if ignore != nil && ignore(key, "", val) { + im = true + } + if !fm && focus(key, "", val) { + fm = true + } + } + } + return fm, im +} diff --git a/src/cmd/internal/pprof/profile/legacy_profile.go b/src/cmd/internal/pprof/profile/legacy_profile.go new file mode 100644 index 0000000000..e1f24c4c6d --- /dev/null +++ b/src/cmd/internal/pprof/profile/legacy_profile.go @@ -0,0 +1,1251 @@ +// Copyright 2014 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. + +// This file implements parsers to convert legacy profiles into the +// profile.proto format. + +package profile + +import ( + "bufio" + "bytes" + "fmt" + "io" + "math" + "regexp" + "strconv" + "strings" +) + +var ( + countStartRE = regexp.MustCompile(`\A(\w+) profile: total \d+\n\z`) + countRE = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\n\z`) + + heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`) + heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`) + + contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`) + + hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`) + + growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz`) + + fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz`) + + threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`) + threadStartRE = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`) + + procMapsRE = regexp.MustCompile(`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?`) + + briefMapsRE = regexp.MustCompile(`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?`) + + // LegacyHeapAllocated instructs the heapz parsers to use the + // allocated memory stats instead of the default in-use memory. Note + // that tcmalloc doesn't provide all allocated memory, only in-use + // stats. + LegacyHeapAllocated bool +) + +func isSpaceOrComment(line string) bool { + trimmed := strings.TrimSpace(line) + return len(trimmed) == 0 || trimmed[0] == '#' +} + +// parseGoCount parses a Go count profile (e.g., threadcreate or +// goroutine) and returns a new Profile. +func parseGoCount(b []byte) (*Profile, error) { + r := bytes.NewBuffer(b) + + var line string + var err error + for { + // Skip past comments and empty lines seeking a real header. + line, err = r.ReadString('\n') + if err != nil { + return nil, err + } + if !isSpaceOrComment(line) { + break + } + } + + m := countStartRE.FindStringSubmatch(line) + if m == nil { + return nil, errUnrecognized + } + profileType := string(m[1]) + p := &Profile{ + PeriodType: &ValueType{Type: profileType, Unit: "count"}, + Period: 1, + SampleType: []*ValueType{{Type: profileType, Unit: "count"}}, + } + locations := make(map[uint64]*Location) + for { + line, err = r.ReadString('\n') + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + if isSpaceOrComment(line) { + continue + } + if strings.HasPrefix(line, "---") { + break + } + m := countRE.FindStringSubmatch(line) + if m == nil { + return nil, errMalformed + } + n, err := strconv.ParseInt(string(m[1]), 0, 64) + if err != nil { + return nil, errMalformed + } + fields := strings.Fields(string(m[2])) + locs := make([]*Location, 0, len(fields)) + for _, stk := range fields { + addr, err := strconv.ParseUint(stk, 0, 64) + if err != nil { + return nil, errMalformed + } + // Adjust all frames by -1 (except the leaf) to land on top of + // the call instruction. + if len(locs) > 0 { + addr-- + } + loc := locations[addr] + if loc == nil { + loc = &Location{ + Address: addr, + } + locations[addr] = loc + p.Location = append(p.Location, loc) + } + locs = append(locs, loc) + } + p.Sample = append(p.Sample, &Sample{ + Location: locs, + Value: []int64{n}, + }) + } + + if err = parseAdditionalSections(strings.TrimSpace(line), r, p); err != nil { + return nil, err + } + return p, nil +} + +// remapLocationIDs ensures there is a location for each address +// referenced by a sample, and remaps the samples to point to the new +// location ids. +func (p *Profile) remapLocationIDs() { + seen := make(map[*Location]bool, len(p.Location)) + var locs []*Location + + for _, s := range p.Sample { + for _, l := range s.Location { + if seen[l] { + continue + } + l.ID = uint64(len(locs) + 1) + locs = append(locs, l) + seen[l] = true + } + } + p.Location = locs +} + +func (p *Profile) remapFunctionIDs() { + seen := make(map[*Function]bool, len(p.Function)) + var fns []*Function + + for _, l := range p.Location { + for _, ln := range l.Line { + fn := ln.Function + if fn == nil || seen[fn] { + continue + } + fn.ID = uint64(len(fns) + 1) + fns = append(fns, fn) + seen[fn] = true + } + } + p.Function = fns +} + +// remapMappingIDs matches location addresses with existing mappings +// and updates them appropriately. This is O(N*M), if this ever shows +// up as a bottleneck, evaluate sorting the mappings and doing a +// binary search, which would make it O(N*log(M)). +func (p *Profile) remapMappingIDs() { + if len(p.Mapping) == 0 { + return + } + + // Some profile handlers will incorrectly set regions for the main + // executable if its section is remapped. Fix them through heuristics. + + // Remove the initial mapping if named '/anon_hugepage' and has a + // consecutive adjacent mapping. + if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") { + if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start { + p.Mapping = p.Mapping[1:] + } + } + + // Subtract the offset from the start of the main mapping if it + // ends up at a recognizable start address. + const expectedStart = 0x400000 + if m := p.Mapping[0]; m.Start-m.Offset == expectedStart { + m.Start = expectedStart + m.Offset = 0 + } + + for _, l := range p.Location { + if a := l.Address; a != 0 { + for _, m := range p.Mapping { + if m.Start <= a && a < m.Limit { + l.Mapping = m + break + } + } + } + } + + // Reset all mapping IDs. + for i, m := range p.Mapping { + m.ID = uint64(i + 1) + } +} + +var cpuInts = []func([]byte) (uint64, []byte){ + get32l, + get32b, + get64l, + get64b, +} + +func get32l(b []byte) (uint64, []byte) { + if len(b) < 4 { + return 0, nil + } + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:] +} + +func get32b(b []byte) (uint64, []byte) { + if len(b) < 4 { + return 0, nil + } + return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:] +} + +func get64l(b []byte) (uint64, []byte) { + if len(b) < 8 { + return 0, nil + } + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:] +} + +func get64b(b []byte) (uint64, []byte) { + if len(b) < 8 { + return 0, nil + } + return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:] +} + +// ParseTracebacks parses a set of tracebacks and returns a newly +// populated profile. It will accept any text file and generate a +// Profile out of it with any hex addresses it can identify, including +// a process map if it can recognize one. Each sample will include a +// tag "source" with the addresses recognized in string format. +func ParseTracebacks(b []byte) (*Profile, error) { + r := bytes.NewBuffer(b) + + p := &Profile{ + PeriodType: &ValueType{Type: "trace", Unit: "count"}, + Period: 1, + SampleType: []*ValueType{ + {Type: "trace", Unit: "count"}, + }, + } + + var sources []string + var sloc []*Location + + locs := make(map[uint64]*Location) + for { + l, err := r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + if l == "" { + break + } + } + if sectionTrigger(l) == memoryMapSection { + break + } + if s, addrs := extractHexAddresses(l); len(s) > 0 { + for _, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call + // (except for the leaf, which is not a call). + if len(sloc) > 0 { + addr-- + } + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + sources = append(sources, s...) + } else { + if len(sources) > 0 || len(sloc) > 0 { + addTracebackSample(sloc, sources, p) + sloc, sources = nil, nil + } + } + } + + // Add final sample to save any leftover data. + if len(sources) > 0 || len(sloc) > 0 { + addTracebackSample(sloc, sources, p) + } + + if err := p.ParseMemoryMap(r); err != nil { + return nil, err + } + return p, nil +} + +func addTracebackSample(l []*Location, s []string, p *Profile) { + p.Sample = append(p.Sample, + &Sample{ + Value: []int64{1}, + Location: l, + Label: map[string][]string{"source": s}, + }) +} + +// parseCPU parses a profilez legacy profile and returns a newly +// populated Profile. +// +// The general format for profilez samples is a sequence of words in +// binary format. The first words are a header with the following data: +// 1st word -- 0 +// 2nd word -- 3 +// 3rd word -- 0 if a c++ application, 1 if a java application. +// 4th word -- Sampling period (in microseconds). +// 5th word -- Padding. +func parseCPU(b []byte) (*Profile, error) { + var parse func([]byte) (uint64, []byte) + var n1, n2, n3, n4, n5 uint64 + for _, parse = range cpuInts { + var tmp []byte + n1, tmp = parse(b) + n2, tmp = parse(tmp) + n3, tmp = parse(tmp) + n4, tmp = parse(tmp) + n5, tmp = parse(tmp) + + if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 { + b = tmp + return cpuProfile(b, int64(n4), parse) + } + } + return nil, errUnrecognized +} + +// cpuProfile returns a new Profile from C++ profilez data. +// b is the profile bytes after the header, period is the profiling +// period, and parse is a function to parse 8-byte chunks from the +// profile in its native endianness. +func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { + p := &Profile{ + Period: period * 1000, + PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, + SampleType: []*ValueType{ + {Type: "samples", Unit: "count"}, + {Type: "cpu", Unit: "nanoseconds"}, + }, + } + var err error + if b, _, err = parseCPUSamples(b, parse, true, p); err != nil { + return nil, err + } + + // If all samples have the same second-to-the-bottom frame, it + // strongly suggests that it is an uninteresting artifact of + // measurement -- a stack frame pushed by the signal handler. The + // bottom frame is always correct as it is picked up from the signal + // structure, not the stack. Check if this is the case and if so, + // remove. + if len(p.Sample) > 1 && len(p.Sample[0].Location) > 1 { + allSame := true + id1 := p.Sample[0].Location[1].Address + for _, s := range p.Sample { + if len(s.Location) < 2 || id1 != s.Location[1].Address { + allSame = false + break + } + } + if allSame { + for _, s := range p.Sample { + s.Location = append(s.Location[:1], s.Location[2:]...) + } + } + } + + if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil { + return nil, err + } + return p, nil +} + +// parseCPUSamples parses a collection of profilez samples from a +// profile. +// +// profilez samples are a repeated sequence of stack frames of the +// form: +// 1st word -- The number of times this stack was encountered. +// 2nd word -- The size of the stack (StackSize). +// 3rd word -- The first address on the stack. +// ... +// StackSize + 2 -- The last address on the stack +// The last stack trace is of the form: +// 1st word -- 0 +// 2nd word -- 1 +// 3rd word -- 0 +// +// Addresses from stack traces may point to the next instruction after +// each call. Optionally adjust by -1 to land somewhere on the actual +// call (except for the leaf, which is not a call). +func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) { + locs := make(map[uint64]*Location) + for len(b) > 0 { + var count, nstk uint64 + count, b = parse(b) + nstk, b = parse(b) + if b == nil || nstk > uint64(len(b)/4) { + return nil, nil, errUnrecognized + } + var sloc []*Location + addrs := make([]uint64, nstk) + for i := 0; i < int(nstk); i++ { + addrs[i], b = parse(b) + } + + if count == 0 && nstk == 1 && addrs[0] == 0 { + // End of data marker + break + } + for i, addr := range addrs { + if adjust && i > 0 { + addr-- + } + loc := locs[addr] + if loc == nil { + loc = &Location{ + Address: addr, + } + locs[addr] = loc + p.Location = append(p.Location, loc) + } + sloc = append(sloc, loc) + } + p.Sample = append(p.Sample, + &Sample{ + Value: []int64{int64(count), int64(count) * int64(p.Period)}, + Location: sloc, + }) + } + // Reached the end without finding the EOD marker. + return b, locs, nil +} + +// parseHeap parses a heapz legacy or a growthz profile and +// returns a newly populated Profile. +func parseHeap(b []byte) (p *Profile, err error) { + r := bytes.NewBuffer(b) + l, err := r.ReadString('\n') + if err != nil { + return nil, errUnrecognized + } + + sampling := "" + + if header := heapHeaderRE.FindStringSubmatch(l); header != nil { + p = &Profile{ + SampleType: []*ValueType{ + {Type: "objects", Unit: "count"}, + {Type: "space", Unit: "bytes"}, + }, + PeriodType: &ValueType{Type: "objects", Unit: "bytes"}, + } + + var period int64 + if len(header[6]) > 0 { + if period, err = strconv.ParseInt(string(header[6]), 10, 64); err != nil { + return nil, errUnrecognized + } + } + + switch header[5] { + case "heapz_v2", "heap_v2": + sampling, p.Period = "v2", period + case "heapprofile": + sampling, p.Period = "", 1 + case "heap": + sampling, p.Period = "v2", period/2 + default: + return nil, errUnrecognized + } + } else if header = growthHeaderRE.FindStringSubmatch(l); header != nil { + p = &Profile{ + SampleType: []*ValueType{ + {Type: "objects", Unit: "count"}, + {Type: "space", Unit: "bytes"}, + }, + PeriodType: &ValueType{Type: "heapgrowth", Unit: "count"}, + Period: 1, + } + } else if header = fragmentationHeaderRE.FindStringSubmatch(l); header != nil { + p = &Profile{ + SampleType: []*ValueType{ + {Type: "objects", Unit: "count"}, + {Type: "space", Unit: "bytes"}, + }, + PeriodType: &ValueType{Type: "allocations", Unit: "count"}, + Period: 1, + } + } else { + return nil, errUnrecognized + } + + if LegacyHeapAllocated { + for _, st := range p.SampleType { + st.Type = "alloc_" + st.Type + } + } else { + for _, st := range p.SampleType { + st.Type = "inuse_" + st.Type + } + } + + locs := make(map[uint64]*Location) + for { + l, err = r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + + if l == "" { + break + } + } + + if isSpaceOrComment(l) { + continue + } + l = strings.TrimSpace(l) + + if sectionTrigger(l) != unrecognizedSection { + break + } + + value, blocksize, addrs, err := parseHeapSample(l, p.Period, sampling) + if err != nil { + return nil, err + } + var sloc []*Location + for i, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call + // (except for the leaf, which is not a call). + if i > 0 { + addr-- + } + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + p.Sample = append(p.Sample, &Sample{ + Value: value, + Location: sloc, + NumLabel: map[string][]int64{"bytes": {blocksize}}, + }) + } + + if err = parseAdditionalSections(l, r, p); err != nil { + return nil, err + } + return p, nil +} + +// parseHeapSample parses a single row from a heap profile into a new Sample. +func parseHeapSample(line string, rate int64, sampling string) (value []int64, blocksize int64, addrs []uint64, err error) { + sampleData := heapSampleRE.FindStringSubmatch(line) + if len(sampleData) != 6 { + return value, blocksize, addrs, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData)) + } + + // Use first two values by default; tcmalloc sampling generates the + // same value for both, only the older heap-profile collect separate + // stats for in-use and allocated objects. + valueIndex := 1 + if LegacyHeapAllocated { + valueIndex = 3 + } + + var v1, v2 int64 + if v1, err = strconv.ParseInt(sampleData[valueIndex], 10, 64); err != nil { + return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + if v2, err = strconv.ParseInt(sampleData[valueIndex+1], 10, 64); err != nil { + return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + if v1 == 0 { + if v2 != 0 { + return value, blocksize, addrs, fmt.Errorf("allocation count was 0 but allocation bytes was %d", v2) + } + } else { + blocksize = v2 / v1 + if sampling == "v2" { + v1, v2 = scaleHeapSample(v1, v2, rate) + } + } + + value = []int64{v1, v2} + addrs = parseHexAddresses(sampleData[5]) + + return value, blocksize, addrs, nil +} + +// extractHexAddresses extracts hex numbers from a string and returns +// them, together with their numeric value, in a slice. +func extractHexAddresses(s string) ([]string, []uint64) { + hexStrings := hexNumberRE.FindAllString(s, -1) + var ids []uint64 + for _, s := range hexStrings { + if id, err := strconv.ParseUint(s, 0, 64); err == nil { + ids = append(ids, id) + } else { + // Do not expect any parsing failures due to the regexp matching. + panic("failed to parse hex value:" + s) + } + } + return hexStrings, ids +} + +// parseHexAddresses parses hex numbers from a string and returns them +// in a slice. +func parseHexAddresses(s string) []uint64 { + _, ids := extractHexAddresses(s) + return ids +} + +// scaleHeapSample adjusts the data from a heapz Sample to +// account for its probability of appearing in the collected +// data. heapz profiles are a sampling of the memory allocations +// requests in a program. We estimate the unsampled value by dividing +// each collected sample by its probability of appearing in the +// profile. heapz v2 profiles rely on a poisson process to determine +// which samples to collect, based on the desired average collection +// rate R. The probability of a sample of size S to appear in that +// profile is 1-exp(-S/R). +func scaleHeapSample(count, size, rate int64) (int64, int64) { + if count == 0 || size == 0 { + return 0, 0 + } + + if rate <= 1 { + // if rate==1 all samples were collected so no adjustment is needed. + // if rate<1 treat as unknown and skip scaling. + return count, size + } + + avgSize := float64(size) / float64(count) + scale := 1 / (1 - math.Exp(-avgSize/float64(rate))) + + return int64(float64(count) * scale), int64(float64(size) * scale) +} + +// parseContention parses a contentionz profile and returns a newly +// populated Profile. +func parseContention(b []byte) (p *Profile, err error) { + r := bytes.NewBuffer(b) + l, err := r.ReadString('\n') + if err != nil { + return nil, errUnrecognized + } + + if !strings.HasPrefix(l, "--- contention") { + return nil, errUnrecognized + } + + p = &Profile{ + PeriodType: &ValueType{Type: "contentions", Unit: "count"}, + Period: 1, + SampleType: []*ValueType{ + {Type: "contentions", Unit: "count"}, + {Type: "delay", Unit: "nanoseconds"}, + }, + } + + var cpuHz int64 + // Parse text of the form "attribute = value" before the samples. + const delimiter = "=" + for { + l, err = r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + + if l == "" { + break + } + } + + if l = strings.TrimSpace(l); l == "" { + continue + } + + if strings.HasPrefix(l, "---") { + break + } + + attr := strings.SplitN(l, delimiter, 2) + if len(attr) != 2 { + break + } + key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]) + var err error + switch key { + case "cycles/second": + if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil { + return nil, errUnrecognized + } + case "sampling period": + if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil { + return nil, errUnrecognized + } + case "ms since reset": + ms, err := strconv.ParseInt(val, 0, 64) + if err != nil { + return nil, errUnrecognized + } + p.DurationNanos = ms * 1000 * 1000 + case "format": + // CPP contentionz profiles don't have format. + return nil, errUnrecognized + case "resolution": + // CPP contentionz profiles don't have resolution. + return nil, errUnrecognized + case "discarded samples": + default: + return nil, errUnrecognized + } + } + + locs := make(map[uint64]*Location) + for { + if l = strings.TrimSpace(l); strings.HasPrefix(l, "---") { + break + } + value, addrs, err := parseContentionSample(l, p.Period, cpuHz) + if err != nil { + return nil, err + } + var sloc []*Location + for i, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call + // (except for the leaf, which is not a call). + if i > 0 { + addr-- + } + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + p.Sample = append(p.Sample, &Sample{ + Value: value, + Location: sloc, + }) + + if l, err = r.ReadString('\n'); err != nil { + if err != io.EOF { + return nil, err + } + if l == "" { + break + } + } + } + + if err = parseAdditionalSections(l, r, p); err != nil { + return nil, err + } + + return p, nil +} + +// parseContentionSample parses a single row from a contention profile +// into a new Sample. +func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) { + sampleData := contentionSampleRE.FindStringSubmatch(line) + if sampleData == nil { + return value, addrs, errUnrecognized + } + + v1, err := strconv.ParseInt(sampleData[1], 10, 64) + if err != nil { + return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + v2, err := strconv.ParseInt(sampleData[2], 10, 64) + if err != nil { + return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) + } + + // Unsample values if period and cpuHz are available. + // - Delays are scaled to cycles and then to nanoseconds. + // - Contentions are scaled to cycles. + if period > 0 { + if cpuHz > 0 { + cpuGHz := float64(cpuHz) / 1e9 + v1 = int64(float64(v1) * float64(period) / cpuGHz) + } + v2 = v2 * period + } + + value = []int64{v2, v1} + addrs = parseHexAddresses(sampleData[3]) + + return value, addrs, nil +} + +// parseThread parses a Threadz profile and returns a new Profile. +func parseThread(b []byte) (*Profile, error) { + r := bytes.NewBuffer(b) + + var line string + var err error + for { + // Skip past comments and empty lines seeking a real header. + line, err = r.ReadString('\n') + if err != nil { + return nil, err + } + if !isSpaceOrComment(line) { + break + } + } + + if m := threadzStartRE.FindStringSubmatch(line); m != nil { + // Advance over initial comments until first stack trace. + for { + line, err = r.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, err + } + + if line == "" { + break + } + } + if sectionTrigger(line) != unrecognizedSection || line[0] == '-' { + break + } + } + } else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { + return nil, errUnrecognized + } + + p := &Profile{ + SampleType: []*ValueType{{Type: "thread", Unit: "count"}}, + PeriodType: &ValueType{Type: "thread", Unit: "count"}, + Period: 1, + } + + locs := make(map[uint64]*Location) + // Recognize each thread and populate profile samples. + for sectionTrigger(line) == unrecognizedSection { + if strings.HasPrefix(line, "---- no stack trace for") { + line = "" + break + } + if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { + return nil, errUnrecognized + } + + var addrs []uint64 + line, addrs, err = parseThreadSample(r) + if err != nil { + return nil, errUnrecognized + } + if len(addrs) == 0 { + // We got a --same as previous threads--. Bump counters. + if len(p.Sample) > 0 { + s := p.Sample[len(p.Sample)-1] + s.Value[0]++ + } + continue + } + + var sloc []*Location + for i, addr := range addrs { + // Addresses from stack traces point to the next instruction after + // each call. Adjust by -1 to land somewhere on the actual call + // (except for the leaf, which is not a call). + if i > 0 { + addr-- + } + loc := locs[addr] + if locs[addr] == nil { + loc = &Location{ + Address: addr, + } + p.Location = append(p.Location, loc) + locs[addr] = loc + } + sloc = append(sloc, loc) + } + + p.Sample = append(p.Sample, &Sample{ + Value: []int64{1}, + Location: sloc, + }) + } + + if err = parseAdditionalSections(line, r, p); err != nil { + return nil, err + } + + return p, nil +} + +// parseThreadSample parses a symbolized or unsymbolized stack trace. +// Returns the first line after the traceback, the sample (or nil if +// it hits a 'same-as-previous' marker) and an error. +func parseThreadSample(b *bytes.Buffer) (nextl string, addrs []uint64, err error) { + var l string + sameAsPrevious := false + for { + if l, err = b.ReadString('\n'); err != nil { + if err != io.EOF { + return "", nil, err + } + if l == "" { + break + } + } + if l = strings.TrimSpace(l); l == "" { + continue + } + + if strings.HasPrefix(l, "---") { + break + } + if strings.Contains(l, "same as previous thread") { + sameAsPrevious = true + continue + } + + addrs = append(addrs, parseHexAddresses(l)...) + } + + if sameAsPrevious { + return l, nil, nil + } + return l, addrs, nil +} + +// parseAdditionalSections parses any additional sections in the +// profile, ignoring any unrecognized sections. +func parseAdditionalSections(l string, b *bytes.Buffer, p *Profile) (err error) { + for { + if sectionTrigger(l) == memoryMapSection { + break + } + // Ignore any unrecognized sections. + if l, err := b.ReadString('\n'); err != nil { + if err != io.EOF { + return err + } + if l == "" { + break + } + } + } + return p.ParseMemoryMap(b) +} + +// ParseMemoryMap parses a memory map in the format of +// /proc/self/maps, and overrides the mappings in the current profile. +// It renumbers the samples and locations in the profile correspondingly. +func (p *Profile) ParseMemoryMap(rd io.Reader) error { + b := bufio.NewReader(rd) + + var attrs []string + var r *strings.Replacer + const delimiter = "=" + for { + l, err := b.ReadString('\n') + if err != nil { + if err != io.EOF { + return err + } + if l == "" { + break + } + } + if l = strings.TrimSpace(l); l == "" { + continue + } + + if r != nil { + l = r.Replace(l) + } + m, err := parseMappingEntry(l) + if err != nil { + if err == errUnrecognized { + // Recognize assignments of the form: attr=value, and replace + // $attr with value on subsequent mappings. + if attr := strings.SplitN(l, delimiter, 2); len(attr) == 2 { + attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])) + r = strings.NewReplacer(attrs...) + } + // Ignore any unrecognized entries + continue + } + return err + } + if m == nil || (m.File == "" && len(p.Mapping) != 0) { + // In some cases the first entry may include the address range + // but not the name of the file. It should be followed by + // another entry with the name. + continue + } + if len(p.Mapping) == 1 && p.Mapping[0].File == "" { + // Update the name if this is the entry following that empty one. + p.Mapping[0].File = m.File + continue + } + p.Mapping = append(p.Mapping, m) + } + p.remapLocationIDs() + p.remapFunctionIDs() + p.remapMappingIDs() + return nil +} + +func parseMappingEntry(l string) (*Mapping, error) { + mapping := &Mapping{} + var err error + if me := procMapsRE.FindStringSubmatch(l); len(me) == 9 { + if !strings.Contains(me[3], "x") { + // Skip non-executable entries. + return nil, nil + } + if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil { + return nil, errUnrecognized + } + if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil { + return nil, errUnrecognized + } + if me[4] != "" { + if mapping.Offset, err = strconv.ParseUint(me[4], 16, 64); err != nil { + return nil, errUnrecognized + } + } + mapping.File = me[8] + return mapping, nil + } + + if me := briefMapsRE.FindStringSubmatch(l); len(me) == 6 { + if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil { + return nil, errUnrecognized + } + if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil { + return nil, errUnrecognized + } + mapping.File = me[3] + if me[5] != "" { + if mapping.Offset, err = strconv.ParseUint(me[5], 16, 64); err != nil { + return nil, errUnrecognized + } + } + return mapping, nil + } + + return nil, errUnrecognized +} + +type sectionType int + +const ( + unrecognizedSection sectionType = iota + memoryMapSection +) + +var memoryMapTriggers = []string{ + "--- Memory map: ---", + "MAPPED_LIBRARIES:", +} + +func sectionTrigger(line string) sectionType { + for _, trigger := range memoryMapTriggers { + if strings.Contains(line, trigger) { + return memoryMapSection + } + } + return unrecognizedSection +} + +func (p *Profile) addLegacyFrameInfo() { + switch { + case isProfileType(p, heapzSampleTypes) || + isProfileType(p, heapzInUseSampleTypes) || + isProfileType(p, heapzAllocSampleTypes): + p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr + case isProfileType(p, contentionzSampleTypes): + p.DropFrames, p.KeepFrames = lockRxStr, "" + default: + p.DropFrames, p.KeepFrames = cpuProfilerRxStr, "" + } +} + +var heapzSampleTypes = []string{"allocations", "size"} // early Go pprof profiles +var heapzInUseSampleTypes = []string{"inuse_objects", "inuse_space"} +var heapzAllocSampleTypes = []string{"alloc_objects", "alloc_space"} +var contentionzSampleTypes = []string{"contentions", "delay"} + +func isProfileType(p *Profile, t []string) bool { + st := p.SampleType + if len(st) != len(t) { + return false + } + + for i := range st { + if st[i].Type != t[i] { + return false + } + } + return true +} + +var allocRxStr = strings.Join([]string{ + // POSIX entry points. + `calloc`, + `cfree`, + `malloc`, + `free`, + `memalign`, + `do_memalign`, + `(__)?posix_memalign`, + `pvalloc`, + `valloc`, + `realloc`, + + // TC malloc. + `tcmalloc::.*`, + `tc_calloc`, + `tc_cfree`, + `tc_malloc`, + `tc_free`, + `tc_memalign`, + `tc_posix_memalign`, + `tc_pvalloc`, + `tc_valloc`, + `tc_realloc`, + `tc_new`, + `tc_delete`, + `tc_newarray`, + `tc_deletearray`, + `tc_new_nothrow`, + `tc_newarray_nothrow`, + + // Memory-allocation routines on OS X. + `malloc_zone_malloc`, + `malloc_zone_calloc`, + `malloc_zone_valloc`, + `malloc_zone_realloc`, + `malloc_zone_memalign`, + `malloc_zone_free`, + + // Go runtime + `runtime\..*`, + + // Other misc. memory allocation routines + `BaseArena::.*`, + `(::)?do_malloc_no_errno`, + `(::)?do_malloc_pages`, + `(::)?do_malloc`, + `DoSampledAllocation`, + `MallocedMemBlock::MallocedMemBlock`, + `_M_allocate`, + `__builtin_(vec_)?delete`, + `__builtin_(vec_)?new`, + `__gnu_cxx::new_allocator::allocate`, + `__libc_malloc`, + `__malloc_alloc_template::allocate`, + `allocate`, + `cpp_alloc`, + `operator new(\[\])?`, + `simple_alloc::allocate`, +}, `|`) + +var allocSkipRxStr = strings.Join([]string{ + // Preserve Go runtime frames that appear in the middle/bottom of + // the stack. + `runtime\.panic`, +}, `|`) + +var cpuProfilerRxStr = strings.Join([]string{ + `ProfileData::Add`, + `ProfileData::prof_handler`, + `CpuProfiler::prof_handler`, + `__pthread_sighandler`, + `__restore`, +}, `|`) + +var lockRxStr = strings.Join([]string{ + `RecordLockProfileData`, + `(base::)?RecordLockProfileData.*`, + `(base::)?SubmitMutexProfileData.*`, + `(base::)?SubmitSpinLockProfileData.*`, + `(Mutex::)?AwaitCommon.*`, + `(Mutex::)?Unlock.*`, + `(Mutex::)?UnlockSlow.*`, + `(Mutex::)?ReaderUnlock.*`, + `(MutexLock::)?~MutexLock.*`, + `(SpinLock::)?Unlock.*`, + `(SpinLock::)?SlowUnlock.*`, + `(SpinLockHolder::)?~SpinLockHolder.*`, +}, `|`) diff --git a/src/cmd/internal/pprof/profile/profile.go b/src/cmd/internal/pprof/profile/profile.go new file mode 100644 index 0000000000..28e713d7be --- /dev/null +++ b/src/cmd/internal/pprof/profile/profile.go @@ -0,0 +1,572 @@ +// Copyright 2014 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 profile provides a representation of profile.proto and +// methods to encode/decode profiles in this format. +package profile + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "regexp" + "strings" + "time" +) + +// Profile is an in-memory representation of profile.proto. +type Profile struct { + SampleType []*ValueType + Sample []*Sample + Mapping []*Mapping + Location []*Location + Function []*Function + + DropFrames string + KeepFrames string + + TimeNanos int64 + DurationNanos int64 + PeriodType *ValueType + Period int64 + + dropFramesX int64 + keepFramesX int64 + stringTable []string +} + +// ValueType corresponds to Profile.ValueType +type ValueType struct { + Type string // cpu, wall, inuse_space, etc + Unit string // seconds, nanoseconds, bytes, etc + + typeX int64 + unitX int64 +} + +// Sample corresponds to Profile.Sample +type Sample struct { + Location []*Location + Value []int64 + Label map[string][]string + NumLabel map[string][]int64 + + locationIDX []uint64 + labelX []Label +} + +// Label corresponds to Profile.Label +type Label struct { + keyX int64 + // Exactly one of the two following values must be set + strX int64 + numX int64 // Integer value for this label +} + +// Mapping corresponds to Profile.Mapping +type Mapping struct { + ID uint64 + Start uint64 + Limit uint64 + Offset uint64 + File string + BuildID string + HasFunctions bool + HasFilenames bool + HasLineNumbers bool + HasInlineFrames bool + + fileX int64 + buildIDX int64 +} + +// Location corresponds to Profile.Location +type Location struct { + ID uint64 + Mapping *Mapping + Address uint64 + Line []Line + + mappingIDX uint64 +} + +// Line corresponds to Profile.Line +type Line struct { + Function *Function + Line int64 + + functionIDX uint64 +} + +// Function corresponds to Profile.Function +type Function struct { + ID uint64 + Name string + SystemName string + Filename string + StartLine int64 + + nameX int64 + systemNameX int64 + filenameX int64 +} + +// Parse parses a profile and checks for its validity. The input +// may be a gzip-compressed encoded protobuf or one of many legacy +// profile formats which may be unsupported in the future. +func Parse(r io.Reader) (*Profile, error) { + orig, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + var p *Profile + if len(orig) >= 2 && orig[0] == 0x1f && orig[1] == 0x8b { + gz, err := gzip.NewReader(bytes.NewBuffer(orig)) + if err != nil { + return nil, fmt.Errorf("decompressing profile: %v", err) + } + data, err := ioutil.ReadAll(gz) + if err != nil { + return nil, fmt.Errorf("decompressing profile: %v", err) + } + orig = data + } + if p, err = parseUncompressed(orig); err != nil { + if p, err = parseLegacy(orig); err != nil { + return nil, fmt.Errorf("parsing profile: %v", err) + } + } + + if err := p.CheckValid(); err != nil { + return nil, fmt.Errorf("malformed profile: %v", err) + } + return p, nil +} + +var errUnrecognized = fmt.Errorf("unrecognized profile format") +var errMalformed = fmt.Errorf("malformed profile format") + +func parseLegacy(data []byte) (*Profile, error) { + parsers := []func([]byte) (*Profile, error){ + parseCPU, + parseHeap, + parseGoCount, // goroutine, threadcreate + parseThread, + parseContention, + } + + for _, parser := range parsers { + p, err := parser(data) + if err == nil { + p.setMain() + p.addLegacyFrameInfo() + return p, nil + } + if err != errUnrecognized { + return nil, err + } + } + return nil, errUnrecognized +} + +func parseUncompressed(data []byte) (*Profile, error) { + p := &Profile{} + if err := unmarshal(data, p); err != nil { + return nil, err + } + + if err := p.postDecode(); err != nil { + return nil, err + } + + return p, nil +} + +var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`) + +// setMain scans Mapping entries and guesses which entry is main +// because legacy profiles don't obey the convention of putting main +// first. +func (p *Profile) setMain() { + for i := 0; i < len(p.Mapping); i++ { + file := strings.TrimSpace(strings.Replace(p.Mapping[i].File, "(deleted)", "", -1)) + if len(file) == 0 { + continue + } + if len(libRx.FindStringSubmatch(file)) > 0 { + continue + } + if strings.HasPrefix(file, "[") { + continue + } + // Swap what we guess is main to position 0. + tmp := p.Mapping[i] + p.Mapping[i] = p.Mapping[0] + p.Mapping[0] = tmp + break + } +} + +// Write writes the profile as a gzip-compressed marshaled protobuf. +func (p *Profile) Write(w io.Writer) error { + p.preEncode() + b := marshal(p) + zw := gzip.NewWriter(w) + defer zw.Close() + _, err := zw.Write(b) + return err +} + +// CheckValid tests whether the profile is valid. Checks include, but are +// not limited to: +// - len(Profile.Sample[n].value) == len(Profile.value_unit) +// - Sample.id has a corresponding Profile.Location +func (p *Profile) CheckValid() error { + // Check that sample values are consistent + sampleLen := len(p.SampleType) + if sampleLen == 0 && len(p.Sample) != 0 { + return fmt.Errorf("missing sample type information") + } + for _, s := range p.Sample { + if len(s.Value) != sampleLen { + return fmt.Errorf("mismatch: sample has: %d values vs. %d types", len(s.Value), len(p.SampleType)) + } + } + + // Check that all mappings/locations/functions are in the tables + // Check that there are no duplicate ids + mappings := make(map[uint64]*Mapping, len(p.Mapping)) + for _, m := range p.Mapping { + if m.ID == 0 { + return fmt.Errorf("found mapping with reserved ID=0") + } + if mappings[m.ID] != nil { + return fmt.Errorf("multiple mappings with same id: %d", m.ID) + } + mappings[m.ID] = m + } + functions := make(map[uint64]*Function, len(p.Function)) + for _, f := range p.Function { + if f.ID == 0 { + return fmt.Errorf("found function with reserved ID=0") + } + if functions[f.ID] != nil { + return fmt.Errorf("multiple functions with same id: %d", f.ID) + } + functions[f.ID] = f + } + locations := make(map[uint64]*Location, len(p.Location)) + for _, l := range p.Location { + if l.ID == 0 { + return fmt.Errorf("found location with reserved id=0") + } + if locations[l.ID] != nil { + return fmt.Errorf("multiple locations with same id: %d", l.ID) + } + locations[l.ID] = l + if m := l.Mapping; m != nil { + if m.ID == 0 || mappings[m.ID] != m { + return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID) + } + } + for _, ln := range l.Line { + if f := ln.Function; f != nil { + if f.ID == 0 || functions[f.ID] != f { + return fmt.Errorf("inconsistent function %p: %d", f, f.ID) + } + } + } + } + return nil +} + +// Aggregate merges the locations in the profile into equivalence +// classes preserving the request attributes. It also updates the +// samples to point to the merged locations. +func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error { + for _, m := range p.Mapping { + m.HasInlineFrames = m.HasInlineFrames && inlineFrame + m.HasFunctions = m.HasFunctions && function + m.HasFilenames = m.HasFilenames && filename + m.HasLineNumbers = m.HasLineNumbers && linenumber + } + + // Aggregate functions + if !function || !filename { + for _, f := range p.Function { + if !function { + f.Name = "" + f.SystemName = "" + } + if !filename { + f.Filename = "" + } + } + } + + // Aggregate locations + if !inlineFrame || !address || !linenumber { + for _, l := range p.Location { + if !inlineFrame && len(l.Line) > 1 { + l.Line = l.Line[len(l.Line)-1:] + } + if !linenumber { + for i := range l.Line { + l.Line[i].Line = 0 + } + } + if !address { + l.Address = 0 + } + } + } + + return p.CheckValid() +} + +// Print dumps a text representation of a profile. Intended mainly +// for debugging purposes. +func (p *Profile) String() string { + + ss := make([]string, 0, len(p.Sample)+len(p.Mapping)+len(p.Location)) + if pt := p.PeriodType; pt != nil { + ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit)) + } + ss = append(ss, fmt.Sprintf("Period: %d", p.Period)) + if p.TimeNanos != 0 { + ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos))) + } + if p.DurationNanos != 0 { + ss = append(ss, fmt.Sprintf("Duration: %v", time.Duration(p.DurationNanos))) + } + + ss = append(ss, "Samples:") + var sh1 string + for _, s := range p.SampleType { + sh1 = sh1 + fmt.Sprintf("%s/%s ", s.Type, s.Unit) + } + ss = append(ss, strings.TrimSpace(sh1)) + for _, s := range p.Sample { + var sv string + for _, v := range s.Value { + sv = fmt.Sprintf("%s %10d", sv, v) + } + sv = sv + ": " + for _, l := range s.Location { + sv = sv + fmt.Sprintf("%d ", l.ID) + } + ss = append(ss, sv) + const labelHeader = " " + if len(s.Label) > 0 { + ls := labelHeader + for k, v := range s.Label { + ls = ls + fmt.Sprintf("%s:%v ", k, v) + } + ss = append(ss, ls) + } + if len(s.NumLabel) > 0 { + ls := labelHeader + for k, v := range s.NumLabel { + ls = ls + fmt.Sprintf("%s:%v ", k, v) + } + ss = append(ss, ls) + } + } + + ss = append(ss, "Locations") + for _, l := range p.Location { + locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address) + if m := l.Mapping; m != nil { + locStr = locStr + fmt.Sprintf("M=%d ", m.ID) + } + if len(l.Line) == 0 { + ss = append(ss, locStr) + } + for li := range l.Line { + lnStr := "??" + if fn := l.Line[li].Function; fn != nil { + lnStr = fmt.Sprintf("%s %s:%d s=%d", + fn.Name, + fn.Filename, + l.Line[li].Line, + fn.StartLine) + if fn.Name != fn.SystemName { + lnStr = lnStr + "(" + fn.SystemName + ")" + } + } + ss = append(ss, locStr+lnStr) + // Do not print location details past the first line + locStr = " " + } + } + + ss = append(ss, "Mappings") + for _, m := range p.Mapping { + bits := "" + if m.HasFunctions { + bits = bits + "[FN]" + } + if m.HasFilenames { + bits = bits + "[FL]" + } + if m.HasLineNumbers { + bits = bits + "[LN]" + } + if m.HasInlineFrames { + bits = bits + "[IN]" + } + ss = append(ss, fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s", + m.ID, + m.Start, m.Limit, m.Offset, + m.File, + m.BuildID, + bits)) + } + + return strings.Join(ss, "\n") + "\n" +} + +// Merge adds profile p adjusted by ratio r into profile p. Profiles +// must be compatible (same Type and SampleType). +// TODO(rsilvera): consider normalizing the profiles based on the +// total samples collected. +func (p *Profile) Merge(pb *Profile, r float64) error { + if err := p.Compatible(pb); err != nil { + return err + } + + pb = pb.Copy() + + // Keep the largest of the two periods. + if pb.Period > p.Period { + p.Period = pb.Period + } + + p.DurationNanos += pb.DurationNanos + + p.Mapping = append(p.Mapping, pb.Mapping...) + for i, m := range p.Mapping { + m.ID = uint64(i + 1) + } + p.Location = append(p.Location, pb.Location...) + for i, l := range p.Location { + l.ID = uint64(i + 1) + } + p.Function = append(p.Function, pb.Function...) + for i, f := range p.Function { + f.ID = uint64(i + 1) + } + + if r != 1.0 { + for _, s := range pb.Sample { + for i, v := range s.Value { + s.Value[i] = int64((float64(v) * r)) + } + } + } + p.Sample = append(p.Sample, pb.Sample...) + return p.CheckValid() +} + +// Compatible determines if two profiles can be compared/merged. +// returns nil if the profiles are compatible; otherwise an error with +// details on the incompatibility. +func (p *Profile) Compatible(pb *Profile) error { + if !compatibleValueTypes(p.PeriodType, pb.PeriodType) { + return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType) + } + + if len(p.SampleType) != len(pb.SampleType) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + + for i := range p.SampleType { + if !compatibleValueTypes(p.SampleType[i], pb.SampleType[i]) { + return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) + } + } + + return nil +} + +// HasFunctions determines if all locations in this profile have +// symbolized function information. +func (p *Profile) HasFunctions() bool { + for _, l := range p.Location { + if l.Mapping == nil || !l.Mapping.HasFunctions { + return false + } + } + return true +} + +// HasFileLines determines if all locations in this profile have +// symbolized file and line number information. +func (p *Profile) HasFileLines() bool { + for _, l := range p.Location { + if l.Mapping == nil || (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) { + return false + } + } + return true +} + +func compatibleValueTypes(v1, v2 *ValueType) bool { + if v1 == nil || v2 == nil { + return true // No grounds to disqualify. + } + return v1.Type == v2.Type && v1.Unit == v2.Unit +} + +// Copy makes a fully independent copy of a profile. +func (p *Profile) Copy() *Profile { + p.preEncode() + b := marshal(p) + + pp := &Profile{} + if err := unmarshal(b, pp); err != nil { + panic(err) + } + if err := pp.postDecode(); err != nil { + panic(err) + } + + return pp +} + +// Demangler maps symbol names to a human-readable form. This may +// include C++ demangling and additional simplification. Names that +// are not demangled may be missing from the resulting map. +type Demangler func(name []string) (map[string]string, error) + +// Demangle attempts to demangle and optionally simplify any function +// names referenced in the profile. It works on a best-effort basis: +// it will silently preserve the original names in case of any errors. +func (p *Profile) Demangle(d Demangler) error { + // Collect names to demangle. + var names []string + for _, fn := range p.Function { + names = append(names, fn.SystemName) + } + + // Update profile with demangled names. + demangled, err := d(names) + if err != nil { + return err + } + for _, fn := range p.Function { + if dd, ok := demangled[fn.SystemName]; ok { + fn.Name = dd + } + } + return nil +} + +// Empty returns true if the profile contains no samples. +func (p *Profile) Empty() bool { + return len(p.Sample) == 0 +} diff --git a/src/cmd/internal/pprof/profile/profile_test.go b/src/cmd/internal/pprof/profile/profile_test.go new file mode 100644 index 0000000000..09b11a456f --- /dev/null +++ b/src/cmd/internal/pprof/profile/profile_test.go @@ -0,0 +1,24 @@ +// Copyright 2015 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 profile + +import ( + "bytes" + "testing" +) + +func TestEmptyProfile(t *testing.T) { + var buf bytes.Buffer + p, err := Parse(&buf) + if err != nil { + t.Error("Want no error, got", err) + } + if p == nil { + t.Fatal("Want a valid profile, got ") + } + if !p.Empty() { + t.Errorf("Profile should be empty, got %#v", p) + } +} diff --git a/src/cmd/internal/pprof/profile/proto.go b/src/cmd/internal/pprof/profile/proto.go new file mode 100644 index 0000000000..11d7f9ff9b --- /dev/null +++ b/src/cmd/internal/pprof/profile/proto.go @@ -0,0 +1,360 @@ +// Copyright 2014 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. + +// This file is a simple protocol buffer encoder and decoder. +// +// A protocol message must implement the message interface: +// decoder() []decoder +// encode(*buffer) +// +// The decode method returns a slice indexed by field number that gives the +// function to decode that field. +// The encode method encodes its receiver into the given buffer. +// +// The two methods are simple enough to be implemented by hand rather than +// by using a protocol compiler. +// +// See profile.go for examples of messages implementing this interface. +// +// There is no support for groups, message sets, or "has" bits. + +package profile + +import "errors" + +type buffer struct { + field int + typ int + u64 uint64 + data []byte + tmp [16]byte +} + +type decoder func(*buffer, message) error + +type message interface { + decoder() []decoder + encode(*buffer) +} + +func marshal(m message) []byte { + var b buffer + m.encode(&b) + return b.data +} + +func encodeVarint(b *buffer, x uint64) { + for x >= 128 { + b.data = append(b.data, byte(x)|0x80) + x >>= 7 + } + b.data = append(b.data, byte(x)) +} + +func encodeLength(b *buffer, tag int, len int) { + encodeVarint(b, uint64(tag)<<3|2) + encodeVarint(b, uint64(len)) +} + +func encodeUint64(b *buffer, tag int, x uint64) { + // append varint to b.data + encodeVarint(b, uint64(tag)<<3|0) + encodeVarint(b, x) +} + +func encodeUint64s(b *buffer, tag int, x []uint64) { + if len(x) > 2 { + // Use packed encoding + n1 := len(b.data) + for _, u := range x { + encodeVarint(b, u) + } + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) + return + } + for _, u := range x { + encodeUint64(b, tag, u) + } +} + +func encodeUint64Opt(b *buffer, tag int, x uint64) { + if x == 0 { + return + } + encodeUint64(b, tag, x) +} + +func encodeInt64(b *buffer, tag int, x int64) { + u := uint64(x) + encodeUint64(b, tag, u) +} + +func encodeInt64Opt(b *buffer, tag int, x int64) { + if x == 0 { + return + } + encodeInt64(b, tag, x) +} + +func encodeInt64s(b *buffer, tag int, x []int64) { + if len(x) > 2 { + // Use packed encoding + n1 := len(b.data) + for _, u := range x { + encodeVarint(b, uint64(u)) + } + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) + return + } + for _, u := range x { + encodeInt64(b, tag, u) + } +} + +func encodeString(b *buffer, tag int, x string) { + encodeLength(b, tag, len(x)) + b.data = append(b.data, x...) +} + +func encodeStrings(b *buffer, tag int, x []string) { + for _, s := range x { + encodeString(b, tag, s) + } +} + +func encodeStringOpt(b *buffer, tag int, x string) { + if x == "" { + return + } + encodeString(b, tag, x) +} + +func encodeBool(b *buffer, tag int, x bool) { + if x { + encodeUint64(b, tag, 1) + } else { + encodeUint64(b, tag, 0) + } +} + +func encodeBoolOpt(b *buffer, tag int, x bool) { + if x == false { + return + } + encodeBool(b, tag, x) +} + +func encodeMessage(b *buffer, tag int, m message) { + n1 := len(b.data) + m.encode(b) + n2 := len(b.data) + encodeLength(b, tag, n2-n1) + n3 := len(b.data) + copy(b.tmp[:], b.data[n2:n3]) + copy(b.data[n1+(n3-n2):], b.data[n1:n2]) + copy(b.data[n1:], b.tmp[:n3-n2]) +} + +func unmarshal(data []byte, m message) (err error) { + b := buffer{data: data, typ: 2} + return decodeMessage(&b, m) +} + +func le64(p []byte) uint64 { + return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 +} + +func le32(p []byte) uint32 { + return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 +} + +func decodeVarint(data []byte) (uint64, []byte, error) { + var i int + var u uint64 + for i = 0; ; i++ { + if i >= 10 || i >= len(data) { + return 0, nil, errors.New("bad varint") + } + u |= uint64(data[i]&0x7F) << uint(7*i) + if data[i]&0x80 == 0 { + return u, data[i+1:], nil + } + } +} + +func decodeField(b *buffer, data []byte) ([]byte, error) { + x, data, err := decodeVarint(data) + if err != nil { + return nil, err + } + b.field = int(x >> 3) + b.typ = int(x & 7) + b.data = nil + b.u64 = 0 + switch b.typ { + case 0: + b.u64, data, err = decodeVarint(data) + if err != nil { + return nil, err + } + case 1: + if len(data) < 8 { + return nil, errors.New("not enough data") + } + b.u64 = le64(data[:8]) + data = data[8:] + case 2: + var n uint64 + n, data, err = decodeVarint(data) + if err != nil { + return nil, err + } + if n > uint64(len(data)) { + return nil, errors.New("too much data") + } + b.data = data[:n] + data = data[n:] + case 5: + if len(data) < 4 { + return nil, errors.New("not enough data") + } + b.u64 = uint64(le32(data[:4])) + data = data[4:] + default: + return nil, errors.New("unknown type: " + string(b.typ)) + } + + return data, nil +} + +func checkType(b *buffer, typ int) error { + if b.typ != typ { + return errors.New("type mismatch") + } + return nil +} + +func decodeMessage(b *buffer, m message) error { + if err := checkType(b, 2); err != nil { + return err + } + dec := m.decoder() + data := b.data + for len(data) > 0 { + // pull varint field# + type + var err error + data, err = decodeField(b, data) + if err != nil { + return err + } + if b.field >= len(dec) || dec[b.field] == nil { + continue + } + if err := dec[b.field](b, m); err != nil { + return err + } + } + return nil +} + +func decodeInt64(b *buffer, x *int64) error { + if err := checkType(b, 0); err != nil { + return err + } + *x = int64(b.u64) + return nil +} + +func decodeInt64s(b *buffer, x *[]int64) error { + if b.typ == 2 { + // Packed encoding + data := b.data + for len(data) > 0 { + var u uint64 + var err error + + if u, data, err = decodeVarint(data); err != nil { + return err + } + *x = append(*x, int64(u)) + } + return nil + } + var i int64 + if err := decodeInt64(b, &i); err != nil { + return err + } + *x = append(*x, i) + return nil +} + +func decodeUint64(b *buffer, x *uint64) error { + if err := checkType(b, 0); err != nil { + return err + } + *x = b.u64 + return nil +} + +func decodeUint64s(b *buffer, x *[]uint64) error { + if b.typ == 2 { + data := b.data + // Packed encoding + for len(data) > 0 { + var u uint64 + var err error + + if u, data, err = decodeVarint(data); err != nil { + return err + } + *x = append(*x, u) + } + return nil + } + var u uint64 + if err := decodeUint64(b, &u); err != nil { + return err + } + *x = append(*x, u) + return nil +} + +func decodeString(b *buffer, x *string) error { + if err := checkType(b, 2); err != nil { + return err + } + *x = string(b.data) + return nil +} + +func decodeStrings(b *buffer, x *[]string) error { + var s string + if err := decodeString(b, &s); err != nil { + return err + } + *x = append(*x, s) + return nil +} + +func decodeBool(b *buffer, x *bool) error { + if err := checkType(b, 0); err != nil { + return err + } + if int64(b.u64) == 0 { + *x = false + } else { + *x = true + } + return nil +} diff --git a/src/cmd/internal/pprof/profile/proto_test.go b/src/cmd/internal/pprof/profile/proto_test.go new file mode 100644 index 0000000000..c2613fc375 --- /dev/null +++ b/src/cmd/internal/pprof/profile/proto_test.go @@ -0,0 +1,67 @@ +package profile + +import ( + "reflect" + "testing" +) + +func TestPackedEncoding(t *testing.T) { + + type testcase struct { + uint64s []uint64 + int64s []int64 + encoded []byte + } + for i, tc := range []testcase{ + { + []uint64{0, 1, 10, 100, 1000, 10000}, + []int64{1000, 0, 1000}, + []byte{10, 8, 0, 1, 10, 100, 232, 7, 144, 78, 18, 5, 232, 7, 0, 232, 7}, + }, + { + []uint64{10000}, + nil, + []byte{8, 144, 78}, + }, + { + nil, + []int64{-10000}, + []byte{16, 240, 177, 255, 255, 255, 255, 255, 255, 255, 1}, + }, + } { + source := &packedInts{tc.uint64s, tc.int64s} + if got, want := marshal(source), tc.encoded; !reflect.DeepEqual(got, want) { + t.Errorf("failed encode %d, got %v, want %v", i, got, want) + } + + dest := new(packedInts) + if err := unmarshal(tc.encoded, dest); err != nil { + t.Errorf("failed decode %d: %v", i, err) + continue + } + if got, want := dest.uint64s, tc.uint64s; !reflect.DeepEqual(got, want) { + t.Errorf("failed decode uint64s %d, got %v, want %v", i, got, want) + } + if got, want := dest.int64s, tc.int64s; !reflect.DeepEqual(got, want) { + t.Errorf("failed decode int64s %d, got %v, want %v", i, got, want) + } + } +} + +type packedInts struct { + uint64s []uint64 + int64s []int64 +} + +func (u *packedInts) decoder() []decoder { + return []decoder{ + nil, + func(b *buffer, m message) error { return decodeUint64s(b, &m.(*packedInts).uint64s) }, + func(b *buffer, m message) error { return decodeInt64s(b, &m.(*packedInts).int64s) }, + } +} + +func (u *packedInts) encode(b *buffer) { + encodeUint64s(b, 1, u.uint64s) + encodeInt64s(b, 2, u.int64s) +} diff --git a/src/cmd/internal/pprof/profile/prune.go b/src/cmd/internal/pprof/profile/prune.go new file mode 100644 index 0000000000..1924fada7a --- /dev/null +++ b/src/cmd/internal/pprof/profile/prune.go @@ -0,0 +1,97 @@ +// Copyright 2014 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. + +// Implements methods to remove frames from profiles. + +package profile + +import ( + "fmt" + "regexp" +) + +// Prune removes all nodes beneath a node matching dropRx, and not +// matching keepRx. If the root node of a Sample matches, the sample +// will have an empty stack. +func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) { + prune := make(map[uint64]bool) + pruneBeneath := make(map[uint64]bool) + + for _, loc := range p.Location { + var i int + for i = len(loc.Line) - 1; i >= 0; i-- { + if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { + funcName := fn.Name + // Account for leading '.' on the PPC ELF v1 ABI. + if funcName[0] == '.' { + funcName = funcName[1:] + } + if dropRx.MatchString(funcName) { + if keepRx == nil || !keepRx.MatchString(funcName) { + break + } + } + } + } + + if i >= 0 { + // Found matching entry to prune. + pruneBeneath[loc.ID] = true + + // Remove the matching location. + if i == len(loc.Line)-1 { + // Matched the top entry: prune the whole location. + prune[loc.ID] = true + } else { + loc.Line = loc.Line[i+1:] + } + } + } + + // Prune locs from each Sample + for _, sample := range p.Sample { + // Scan from the root to the leaves to find the prune location. + // Do not prune frames before the first user frame, to avoid + // pruning everything. + foundUser := false + for i := len(sample.Location) - 1; i >= 0; i-- { + id := sample.Location[i].ID + if !prune[id] && !pruneBeneath[id] { + foundUser = true + continue + } + if !foundUser { + continue + } + if prune[id] { + sample.Location = sample.Location[i+1:] + break + } + if pruneBeneath[id] { + sample.Location = sample.Location[i:] + break + } + } + } +} + +// RemoveUninteresting prunes and elides profiles using built-in +// tables of uninteresting function names. +func (p *Profile) RemoveUninteresting() error { + var keep, drop *regexp.Regexp + var err error + + if p.DropFrames != "" { + if drop, err = regexp.Compile("^(" + p.DropFrames + ")$"); err != nil { + return fmt.Errorf("failed to compile regexp %s: %v", p.DropFrames, err) + } + if p.KeepFrames != "" { + if keep, err = regexp.Compile("^(" + p.KeepFrames + ")$"); err != nil { + return fmt.Errorf("failed to compile regexp %s: %v", p.KeepFrames, err) + } + } + p.Prune(drop, keep) + } + return nil +} diff --git a/src/cmd/internal/pprof/report/report.go b/src/cmd/internal/pprof/report/report.go new file mode 100644 index 0000000000..c492b752b9 --- /dev/null +++ b/src/cmd/internal/pprof/report/report.go @@ -0,0 +1,1682 @@ +// Copyright 2014 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 report summarizes a performance profile into a +// human-readable report. +package report + +import ( + "fmt" + "io" + "math" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "time" + + "cmd/internal/pprof/plugin" + "cmd/internal/pprof/profile" +) + +// Generate generates a report as directed by the Report. +func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error { + o := rpt.options + + switch o.OutputFormat { + case Dot: + return printDOT(w, rpt) + case Tree: + return printTree(w, rpt) + case Text: + return printText(w, rpt) + case Raw: + fmt.Fprint(w, rpt.prof.String()) + return nil + case Tags: + return printTags(w, rpt) + case Proto: + return rpt.prof.Write(w) + case Dis: + return printAssembly(w, rpt, obj) + case List: + return printSource(w, rpt) + case WebList: + return printWebSource(w, rpt, obj) + case Callgrind: + return printCallgrind(w, rpt) + } + return fmt.Errorf("unexpected output format") +} + +// printAssembly prints an annotated assembly listing. +func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error { + g, err := newGraph(rpt) + if err != nil { + return err + } + + o := rpt.options + prof := rpt.prof + + // If the regexp source can be parsed as an address, also match + // functions that land on that address. + var address *uint64 + if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil { + address = &hex + } + + fmt.Fprintln(w, "Total:", rpt.formatValue(rpt.total)) + symbols := symbolsFromBinaries(prof, g, o.Symbol, address, obj) + symNodes := nodesPerSymbol(g.ns, symbols) + // Sort function names for printing. + var syms objSymbols + for s := range symNodes { + syms = append(syms, s) + } + sort.Sort(syms) + + // Correlate the symbols from the binary with the profile samples. + for _, s := range syms { + sns := symNodes[s] + + // Gather samples for this symbol. + flatSum, cumSum := sumNodes(sns) + + // Get the function assembly. + insns, err := obj.Disasm(s.sym.File, s.sym.Start, s.sym.End) + if err != nil { + return err + } + + ns := annotateAssembly(insns, sns, s.base) + + fmt.Fprintf(w, "ROUTINE ======================== %s\n", s.sym.Name[0]) + for _, name := range s.sym.Name[1:] { + fmt.Fprintf(w, " AKA ======================== %s\n", name) + } + fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n", + rpt.formatValue(flatSum), rpt.formatValue(cumSum), + percentage(cumSum, rpt.total)) + + for _, n := range ns { + fmt.Fprintf(w, "%10s %10s %10x: %s\n", valueOrDot(n.flat, rpt), valueOrDot(n.cum, rpt), n.info.address, n.info.name) + } + } + return nil +} + +// symbolsFromBinaries examines the binaries listed on the profile +// that have associated samples, and identifies symbols matching rx. +func symbolsFromBinaries(prof *profile.Profile, g graph, rx *regexp.Regexp, address *uint64, obj plugin.ObjTool) []*objSymbol { + hasSamples := make(map[string]bool) + // Only examine mappings that have samples that match the + // regexp. This is an optimization to speed up pprof. + for _, n := range g.ns { + if name := n.info.prettyName(); rx.MatchString(name) && n.info.objfile != "" { + hasSamples[n.info.objfile] = true + } + } + + // Walk all mappings looking for matching functions with samples. + var objSyms []*objSymbol + for _, m := range prof.Mapping { + if !hasSamples[filepath.Base(m.File)] { + if address == nil || !(m.Start <= *address && *address <= m.Limit) { + continue + } + } + + f, err := obj.Open(m.File, m.Start) + if err != nil { + fmt.Printf("%v\n", err) + continue + } + + // Find symbols in this binary matching the user regexp. + var addr uint64 + if address != nil { + addr = *address + } + msyms, err := f.Symbols(rx, addr) + base := f.Base() + f.Close() + if err != nil { + continue + } + for _, ms := range msyms { + objSyms = append(objSyms, + &objSymbol{ + sym: ms, + base: base, + }, + ) + } + } + + return objSyms +} + +// objSym represents a symbol identified from a binary. It includes +// the SymbolInfo from the disasm package and the base that must be +// added to correspond to sample addresses +type objSymbol struct { + sym *plugin.Sym + base uint64 +} + +// objSymbols is a wrapper type to enable sorting of []*objSymbol. +type objSymbols []*objSymbol + +func (o objSymbols) Len() int { + return len(o) +} + +func (o objSymbols) Less(i, j int) bool { + if namei, namej := o[i].sym.Name[0], o[j].sym.Name[0]; namei != namej { + return namei < namej + } + return o[i].sym.Start < o[j].sym.Start +} + +func (o objSymbols) Swap(i, j int) { + o[i], o[j] = o[j], o[i] +} + +// nodesPerSymbol classifies nodes into a group of symbols. +func nodesPerSymbol(ns nodes, symbols []*objSymbol) map[*objSymbol]nodes { + symNodes := make(map[*objSymbol]nodes) + for _, s := range symbols { + // Gather samples for this symbol. + for _, n := range ns { + address := n.info.address - s.base + if address >= s.sym.Start && address < s.sym.End { + symNodes[s] = append(symNodes[s], n) + } + } + } + return symNodes +} + +// annotateAssembly annotates a set of assembly instructions with a +// set of samples. It returns a set of nodes to display. base is an +// offset to adjust the sample addresses. +func annotateAssembly(insns []plugin.Inst, samples nodes, base uint64) nodes { + // Add end marker to simplify printing loop. + insns = append(insns, plugin.Inst{^uint64(0), "", "", 0}) + + // Ensure samples are sorted by address. + samples.sort(addressOrder) + + var s int + var asm nodes + for ix, in := range insns[:len(insns)-1] { + n := node{ + info: nodeInfo{ + address: in.Addr, + name: in.Text, + file: trimPath(in.File), + lineno: in.Line, + }, + } + + // Sum all the samples until the next instruction (to account + // for samples attributed to the middle of an instruction). + for next := insns[ix+1].Addr; s < len(samples) && samples[s].info.address-base < next; s++ { + n.flat += samples[s].flat + n.cum += samples[s].cum + if samples[s].info.file != "" { + n.info.file = trimPath(samples[s].info.file) + n.info.lineno = samples[s].info.lineno + } + } + asm = append(asm, &n) + } + + return asm +} + +// valueOrDot formats a value according to a report, intercepting zero +// values. +func valueOrDot(value int64, rpt *Report) string { + if value == 0 { + return "." + } + return rpt.formatValue(value) +} + +// printTags collects all tags referenced in the profile and prints +// them in a sorted table. +func printTags(w io.Writer, rpt *Report) error { + p := rpt.prof + + // Hashtable to keep accumulate tags as key,value,count. + tagMap := make(map[string]map[string]int64) + for _, s := range p.Sample { + for key, vals := range s.Label { + for _, val := range vals { + if valueMap, ok := tagMap[key]; ok { + valueMap[val] = valueMap[val] + s.Value[0] + continue + } + valueMap := make(map[string]int64) + valueMap[val] = s.Value[0] + tagMap[key] = valueMap + } + } + for key, vals := range s.NumLabel { + for _, nval := range vals { + val := scaledValueLabel(nval, key, "auto") + if valueMap, ok := tagMap[key]; ok { + valueMap[val] = valueMap[val] + s.Value[0] + continue + } + valueMap := make(map[string]int64) + valueMap[val] = s.Value[0] + tagMap[key] = valueMap + } + } + } + + tagKeys := make(tags, 0, len(tagMap)) + for key := range tagMap { + tagKeys = append(tagKeys, &tag{name: key}) + } + sort.Sort(tagKeys) + + for _, tagKey := range tagKeys { + var total int64 + key := tagKey.name + tags := make(tags, 0, len(tagMap[key])) + for t, c := range tagMap[key] { + total += c + tags = append(tags, &tag{name: t, weight: c}) + } + + sort.Sort(tags) + fmt.Fprintf(w, "%s: Total %d\n", key, total) + for _, t := range tags { + if total > 0 { + fmt.Fprintf(w, " %8d (%s): %s\n", t.weight, + percentage(t.weight, total), t.name) + } else { + fmt.Fprintf(w, " %8d: %s\n", t.weight, t.name) + } + } + fmt.Fprintln(w) + } + return nil +} + +// printText prints a flat text report for a profile. +func printText(w io.Writer, rpt *Report) error { + g, err := newGraph(rpt) + if err != nil { + return err + } + + origCount, droppedNodes, _ := g.preprocess(rpt) + fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n")) + + fmt.Fprintf(w, "%10s %5s%% %5s%% %10s %5s%%\n", + "flat", "flat", "sum", "cum", "cum") + + var flatSum int64 + for _, n := range g.ns { + name, flat, cum := n.info.prettyName(), n.flat, n.cum + + flatSum += flat + fmt.Fprintf(w, "%10s %s %s %10s %s %s\n", + rpt.formatValue(flat), + percentage(flat, rpt.total), + percentage(flatSum, rpt.total), + rpt.formatValue(cum), + percentage(cum, rpt.total), + name) + } + return nil +} + +// printCallgrind prints a graph for a profile on callgrind format. +func printCallgrind(w io.Writer, rpt *Report) error { + g, err := newGraph(rpt) + if err != nil { + return err + } + + o := rpt.options + rpt.options.NodeFraction = 0 + rpt.options.EdgeFraction = 0 + rpt.options.NodeCount = 0 + + g.preprocess(rpt) + + fmt.Fprintln(w, "events:", o.SampleType+"("+o.OutputUnit+")") + + files := make(map[string]int) + names := make(map[string]int) + for _, n := range g.ns { + fmt.Fprintln(w, "fl="+callgrindName(files, n.info.file)) + fmt.Fprintln(w, "fn="+callgrindName(names, n.info.name)) + sv, _ := ScaleValue(n.flat, o.SampleUnit, o.OutputUnit) + fmt.Fprintf(w, "%d %d\n", n.info.lineno, int(sv)) + + // Print outgoing edges. + for _, out := range sortedEdges(n.out) { + c, _ := ScaleValue(out.weight, o.SampleUnit, o.OutputUnit) + count := fmt.Sprintf("%d", int(c)) + callee := out.dest + fmt.Fprintln(w, "cfl="+callgrindName(files, callee.info.file)) + fmt.Fprintln(w, "cfn="+callgrindName(names, callee.info.name)) + fmt.Fprintln(w, "calls="+count, callee.info.lineno) + fmt.Fprintln(w, n.info.lineno, count) + } + fmt.Fprintln(w) + } + + return nil +} + +// callgrindName implements the callgrind naming compression scheme. +// For names not previously seen returns "(N) name", where N is a +// unique index. For names previously seen returns "(N)" where N is +// the index returned the first time. +func callgrindName(names map[string]int, name string) string { + if name == "" { + return "" + } + if id, ok := names[name]; ok { + return fmt.Sprintf("(%d)", id) + } + id := len(names) + 1 + names[name] = id + return fmt.Sprintf("(%d) %s", id, name) +} + +// printTree prints a tree-based report in text form. +func printTree(w io.Writer, rpt *Report) error { + const separator = "----------------------------------------------------------+-------------" + const legend = " flat flat% sum% cum cum% calls calls% + context " + + g, err := newGraph(rpt) + if err != nil { + return err + } + + origCount, droppedNodes, _ := g.preprocess(rpt) + fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n")) + + fmt.Fprintln(w, separator) + fmt.Fprintln(w, legend) + var flatSum int64 + + rx := rpt.options.Symbol + for _, n := range g.ns { + name, flat, cum := n.info.prettyName(), n.flat, n.cum + + // Skip any entries that do not match the regexp (for the "peek" command). + if rx != nil && !rx.MatchString(name) { + continue + } + + fmt.Fprintln(w, separator) + // Print incoming edges. + inEdges := sortedEdges(n.in) + inSum := inEdges.sum() + for _, in := range inEdges { + fmt.Fprintf(w, "%50s %s | %s\n", rpt.formatValue(in.weight), + percentage(in.weight, inSum), in.src.info.prettyName()) + } + + // Print current node. + flatSum += flat + fmt.Fprintf(w, "%10s %s %s %10s %s | %s\n", + rpt.formatValue(flat), + percentage(flat, rpt.total), + percentage(flatSum, rpt.total), + rpt.formatValue(cum), + percentage(cum, rpt.total), + name) + + // Print outgoing edges. + outEdges := sortedEdges(n.out) + outSum := outEdges.sum() + for _, out := range outEdges { + fmt.Fprintf(w, "%50s %s | %s\n", rpt.formatValue(out.weight), + percentage(out.weight, outSum), out.dest.info.prettyName()) + } + } + if len(g.ns) > 0 { + fmt.Fprintln(w, separator) + } + return nil +} + +// printDOT prints an annotated callgraph in DOT format. +func printDOT(w io.Writer, rpt *Report) error { + g, err := newGraph(rpt) + if err != nil { + return err + } + + origCount, droppedNodes, droppedEdges := g.preprocess(rpt) + + prof := rpt.prof + graphname := "unnamed" + if len(prof.Mapping) > 0 { + graphname = filepath.Base(prof.Mapping[0].File) + } + fmt.Fprintln(w, `digraph "`+graphname+`" {`) + fmt.Fprintln(w, `node [style=filled fillcolor="#f8f8f8"]`) + fmt.Fprintln(w, dotLegend(rpt, g, origCount, droppedNodes, droppedEdges)) + + if len(g.ns) == 0 { + fmt.Fprintln(w, "}") + return nil + } + + // Make sure nodes have a unique consistent id. + nodeIndex := make(map[*node]int) + maxFlat := float64(g.ns[0].flat) + for i, n := range g.ns { + nodeIndex[n] = i + 1 + if float64(n.flat) > maxFlat { + maxFlat = float64(n.flat) + } + } + var edges edgeList + for _, n := range g.ns { + node := dotNode(rpt, maxFlat, nodeIndex[n], n) + fmt.Fprintln(w, node) + if nodelets := dotNodelets(rpt, nodeIndex[n], n); nodelets != "" { + fmt.Fprint(w, nodelets) + } + + // Collect outgoing edges. + for _, e := range n.out { + edges = append(edges, e) + } + } + // Sort edges by frequency as a hint to the graph layout engine. + sort.Sort(edges) + for _, e := range edges { + fmt.Fprintln(w, dotEdge(rpt, nodeIndex[e.src], nodeIndex[e.dest], e)) + } + fmt.Fprintln(w, "}") + return nil +} + +// percentage computes the percentage of total of a value, and encodes +// it as a string. At least two digits of precision are printed. +func percentage(value, total int64) string { + var ratio float64 + if total != 0 { + ratio = float64(value) / float64(total) * 100 + } + switch { + case ratio >= 99.95: + return " 100%" + case ratio >= 1.0: + return fmt.Sprintf("%5.2f%%", ratio) + default: + return fmt.Sprintf("%5.2g%%", ratio) + } +} + +// dotLegend generates the overall graph label for a report in DOT format. +func dotLegend(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) string { + label := legendLabels(rpt) + label = append(label, legendDetailLabels(rpt, g, origCount, droppedNodes, droppedEdges)...) + return fmt.Sprintf(`subgraph cluster_L { L [shape=box fontsize=32 label="%s\l"] }`, strings.Join(label, `\l`)) +} + +// legendLabels generates labels exclusive to graph visualization. +func legendLabels(rpt *Report) []string { + prof := rpt.prof + o := rpt.options + var label []string + if len(prof.Mapping) > 0 { + if prof.Mapping[0].File != "" { + label = append(label, "File: "+filepath.Base(prof.Mapping[0].File)) + } + if prof.Mapping[0].BuildID != "" { + label = append(label, "Build ID: "+prof.Mapping[0].BuildID) + } + } + if o.SampleType != "" { + label = append(label, "Type: "+o.SampleType) + } + if prof.TimeNanos != 0 { + const layout = "Jan 2, 2006 at 3:04pm (MST)" + label = append(label, "Time: "+time.Unix(0, prof.TimeNanos).Format(layout)) + } + if prof.DurationNanos != 0 { + label = append(label, fmt.Sprintf("Duration: %v", time.Duration(prof.DurationNanos))) + } + return label +} + +// legendDetailLabels generates labels common to graph and text visualization. +func legendDetailLabels(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) []string { + nodeFraction := rpt.options.NodeFraction + edgeFraction := rpt.options.EdgeFraction + nodeCount := rpt.options.NodeCount + + label := []string{} + + var flatSum int64 + for _, n := range g.ns { + flatSum = flatSum + n.flat + } + + label = append(label, fmt.Sprintf("%s of %s total (%s)", rpt.formatValue(flatSum), rpt.formatValue(rpt.total), percentage(flatSum, rpt.total))) + + if rpt.total > 0 { + if droppedNodes > 0 { + label = append(label, genLabel(droppedNodes, "node", "cum", + rpt.formatValue(int64(float64(rpt.total)*nodeFraction)))) + } + if droppedEdges > 0 { + label = append(label, genLabel(droppedEdges, "edge", "freq", + rpt.formatValue(int64(float64(rpt.total)*edgeFraction)))) + } + if nodeCount > 0 && nodeCount < origCount { + label = append(label, fmt.Sprintf("Showing top %d nodes out of %d (cum >= %s)", + nodeCount, origCount, + rpt.formatValue(g.ns[len(g.ns)-1].cum))) + } + } + return label +} + +func genLabel(d int, n, l, f string) string { + if d > 1 { + n = n + "s" + } + return fmt.Sprintf("Dropped %d %s (%s <= %s)", d, n, l, f) +} + +// dotNode generates a graph node in DOT format. +func dotNode(rpt *Report, maxFlat float64, rIndex int, n *node) string { + flat, cum := n.flat, n.cum + + labels := strings.Split(n.info.prettyName(), "::") + label := strings.Join(labels, `\n`) + `\n` + + flatValue := rpt.formatValue(flat) + if flat > 0 { + label = label + fmt.Sprintf(`%s(%s)`, + flatValue, + strings.TrimSpace(percentage(flat, rpt.total))) + } else { + label = label + "0" + } + cumValue := flatValue + if cum != flat { + if flat > 0 { + label = label + `\n` + } else { + label = label + " " + } + cumValue = rpt.formatValue(cum) + label = label + fmt.Sprintf(`of %s(%s)`, + cumValue, + strings.TrimSpace(percentage(cum, rpt.total))) + } + + // Scale font sizes from 8 to 24 based on percentage of flat frequency. + // Use non linear growth to emphasize the size difference. + baseFontSize, maxFontGrowth := 8, 16.0 + fontSize := baseFontSize + if maxFlat > 0 && flat > 0 && float64(flat) <= maxFlat { + fontSize += int(math.Ceil(maxFontGrowth * math.Sqrt(float64(flat)/maxFlat))) + } + return fmt.Sprintf(`N%d [label="%s" fontsize=%d shape=box tooltip="%s (%s)"]`, + rIndex, + label, + fontSize, n.info.prettyName(), cumValue) +} + +// dotEdge generates a graph edge in DOT format. +func dotEdge(rpt *Report, from, to int, e *edgeInfo) string { + w := rpt.formatValue(e.weight) + attr := fmt.Sprintf(`label=" %s"`, w) + if rpt.total > 0 { + if weight := 1 + int(e.weight*100/rpt.total); weight > 1 { + attr = fmt.Sprintf(`%s weight=%d`, attr, weight) + } + if width := 1 + int(e.weight*5/rpt.total); width > 1 { + attr = fmt.Sprintf(`%s penwidth=%d`, attr, width) + } + } + arrow := "->" + if e.residual { + arrow = "..." + } + tooltip := fmt.Sprintf(`"%s %s %s (%s)"`, + e.src.info.prettyName(), arrow, e.dest.info.prettyName(), w) + attr = fmt.Sprintf(`%s tooltip=%s labeltooltip=%s`, + attr, tooltip, tooltip) + + if e.residual { + attr = attr + ` style="dotted"` + } + + if len(e.src.tags) > 0 { + // Separate children further if source has tags. + attr = attr + " minlen=2" + } + return fmt.Sprintf("N%d -> N%d [%s]", from, to, attr) +} + +// dotNodelets generates the DOT boxes for the node tags. +func dotNodelets(rpt *Report, rIndex int, n *node) (dot string) { + const maxNodelets = 4 // Number of nodelets for alphanumeric labels + const maxNumNodelets = 4 // Number of nodelets for numeric labels + + var ts, nts tags + for _, t := range n.tags { + if t.unit == "" { + ts = append(ts, t) + } else { + nts = append(nts, t) + } + } + + // Select the top maxNodelets alphanumeric labels by weight + sort.Sort(ts) + if len(ts) > maxNodelets { + ts = ts[:maxNodelets] + } + for i, t := range ts { + weight := rpt.formatValue(t.weight) + dot += fmt.Sprintf(`N%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight) + dot += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight) + } + + // Collapse numeric labels into maxNumNodelets buckets, of the form: + // 1MB..2MB, 3MB..5MB, ... + nts = collapseTags(nts, maxNumNodelets) + sort.Sort(nts) + for i, t := range nts { + weight := rpt.formatValue(t.weight) + dot += fmt.Sprintf(`NN%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight) + dot += fmt.Sprintf(`N%d -> NN%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight) + } + + return dot +} + +// graph summarizes a performance profile into a format that is +// suitable for visualization. +type graph struct { + ns nodes +} + +// nodes is an ordered collection of graph nodes. +type nodes []*node + +// tags represent sample annotations +type tags []*tag +type tagMap map[string]*tag + +type tag struct { + name string + unit string // Describe the value, "" for non-numeric tags + value int64 + weight int64 +} + +func (t tags) Len() int { return len(t) } +func (t tags) Swap(i, j int) { t[i], t[j] = t[j], t[i] } +func (t tags) Less(i, j int) bool { + if t[i].weight == t[j].weight { + return t[i].name < t[j].name + } + return t[i].weight > t[j].weight +} + +// node is an entry on a profiling report. It represents a unique +// program location. It can include multiple names to represent +// inlined functions. +type node struct { + info nodeInfo // Information associated to this entry. + + // values associated to this node. + // flat is exclusive to this node, cum includes all descendents. + flat, cum int64 + + // in and out contains the nodes immediately reaching or reached by this nodes. + in, out edgeMap + + // tags provide additional information about subsets of a sample. + tags tagMap +} + +type nodeInfo struct { + name string + origName string + address uint64 + file string + startLine, lineno int + inline bool + lowPriority bool + objfile string + parent *node // Used only if creating a calltree +} + +func (n *node) addTags(s *profile.Sample, weight int64) { + // Add a tag with all string labels + var labels []string + for key, vals := range s.Label { + for _, v := range vals { + labels = append(labels, key+":"+v) + } + } + if len(labels) > 0 { + sort.Strings(labels) + l := n.tags.findOrAddTag(strings.Join(labels, `\n`), "", 0) + l.weight += weight + } + + for key, nvals := range s.NumLabel { + for _, v := range nvals { + label := scaledValueLabel(v, key, "auto") + l := n.tags.findOrAddTag(label, key, v) + l.weight += weight + } + } +} + +func (m tagMap) findOrAddTag(label, unit string, value int64) *tag { + if l := m[label]; l != nil { + return l + } + l := &tag{ + name: label, + unit: unit, + value: value, + } + m[label] = l + return l +} + +// collapseTags reduces the number of entries in a tagMap by merging +// adjacent nodes into ranges. It uses a greedy approach to merge +// starting with the entries with the lowest weight. +func collapseTags(ts tags, count int) tags { + if len(ts) <= count { + return ts + } + + sort.Sort(ts) + tagGroups := make([]tags, count) + for i, t := range ts[:count] { + tagGroups[i] = tags{t} + } + for _, t := range ts[count:] { + g, d := 0, tagDistance(t, tagGroups[0][0]) + for i := 1; i < count; i++ { + if nd := tagDistance(t, tagGroups[i][0]); nd < d { + g, d = i, nd + } + } + tagGroups[g] = append(tagGroups[g], t) + } + + var nts tags + for _, g := range tagGroups { + l, w := tagGroupLabel(g) + nts = append(nts, &tag{ + name: l, + weight: w, + }) + } + return nts +} + +func tagDistance(t, u *tag) float64 { + v, _ := ScaleValue(u.value, u.unit, t.unit) + if v < float64(t.value) { + return float64(t.value) - v + } + return v - float64(t.value) +} + +func tagGroupLabel(g tags) (string, int64) { + if len(g) == 1 { + t := g[0] + return scaledValueLabel(t.value, t.unit, "auto"), t.weight + } + min := g[0] + max := g[0] + w := min.weight + for _, t := range g[1:] { + if v, _ := ScaleValue(t.value, t.unit, min.unit); int64(v) < min.value { + min = t + } + if v, _ := ScaleValue(t.value, t.unit, max.unit); int64(v) > max.value { + max = t + } + w += t.weight + } + return scaledValueLabel(min.value, min.unit, "auto") + ".." + + scaledValueLabel(max.value, max.unit, "auto"), w +} + +// sumNodes adds the flat and sum values on a report. +func sumNodes(ns nodes) (flat int64, cum int64) { + for _, n := range ns { + flat += n.flat + cum += n.cum + } + return +} + +type edgeMap map[*node]*edgeInfo + +// edgeInfo contains any attributes to be represented about edges in a graph/ +type edgeInfo struct { + src, dest *node + // The summary weight of the edge + weight int64 + // residual edges connect nodes that were connected through a + // separate node, which has been removed from the report. + residual bool +} + +// bumpWeight increases the weight of an edge. If there isn't such an +// edge in the map one is created. +func bumpWeight(from, to *node, w int64, residual bool) { + if from.out[to] != to.in[from] { + panic(fmt.Errorf("asymmetric edges %v %v", *from, *to)) + } + + if n := from.out[to]; n != nil { + n.weight += w + if n.residual && !residual { + n.residual = false + } + return + } + + info := &edgeInfo{src: from, dest: to, weight: w, residual: residual} + from.out[to] = info + to.in[from] = info +} + +// Output formats. +const ( + Proto = iota + Dot + Tags + Tree + Text + Raw + Dis + List + WebList + Callgrind +) + +// Options are the formatting and filtering options used to generate a +// profile. +type Options struct { + OutputFormat int + + CumSort bool + CallTree bool + PrintAddresses bool + DropNegative bool + Ratio float64 + + NodeCount int + NodeFraction float64 + EdgeFraction float64 + + SampleType string + SampleUnit string // Unit for the sample data from the profile. + OutputUnit string // Units for data formatting in report. + + Symbol *regexp.Regexp // Symbols to include on disassembly report. +} + +// newGraph summarizes performance data from a profile into a graph. +func newGraph(rpt *Report) (g graph, err error) { + prof := rpt.prof + o := rpt.options + + // Generate a tree for graphical output if requested. + buildTree := o.CallTree && o.OutputFormat == Dot + + locations := make(map[uint64][]nodeInfo) + for _, l := range prof.Location { + locations[l.ID] = newLocInfo(l) + } + + nm := make(nodeMap) + for _, sample := range prof.Sample { + if sample.Location == nil { + continue + } + + // Construct list of node names for sample. + var stack []nodeInfo + for _, loc := range sample.Location { + id := loc.ID + stack = append(stack, locations[id]...) + } + + // Upfront pass to update the parent chains, to prevent the + // merging of nodes with different parents. + if buildTree { + var nn *node + for i := len(stack); i > 0; i-- { + n := &stack[i-1] + n.parent = nn + nn = nm.findOrInsertNode(*n) + } + } + + leaf := nm.findOrInsertNode(stack[0]) + weight := rpt.sampleValue(sample) + leaf.addTags(sample, weight) + + // Aggregate counter data. + leaf.flat += weight + seen := make(map[*node]bool) + var nn *node + for _, s := range stack { + n := nm.findOrInsertNode(s) + if !seen[n] { + seen[n] = true + n.cum += weight + + if nn != nil { + bumpWeight(n, nn, weight, false) + } + } + nn = n + } + } + + // Collect new nodes into a report. + ns := make(nodes, 0, len(nm)) + for _, n := range nm { + if rpt.options.DropNegative && n.flat < 0 { + continue + } + ns = append(ns, n) + } + + return graph{ns}, nil +} + +// Create a slice of formatted names for a location. +func newLocInfo(l *profile.Location) []nodeInfo { + var objfile string + + if m := l.Mapping; m != nil { + objfile = filepath.Base(m.File) + } + + if len(l.Line) == 0 { + return []nodeInfo{ + { + address: l.Address, + objfile: objfile, + }, + } + } + var info []nodeInfo + numInlineFrames := len(l.Line) - 1 + for li, line := range l.Line { + ni := nodeInfo{ + address: l.Address, + lineno: int(line.Line), + inline: li < numInlineFrames, + objfile: objfile, + } + + if line.Function != nil { + ni.name = line.Function.Name + ni.origName = line.Function.SystemName + ni.file = line.Function.Filename + ni.startLine = int(line.Function.StartLine) + } + + info = append(info, ni) + } + return info +} + +// nodeMap maps from a node info struct to a node. It is used to merge +// report entries with the same info. +type nodeMap map[nodeInfo]*node + +func (m nodeMap) findOrInsertNode(info nodeInfo) *node { + rr := m[info] + if rr == nil { + rr = &node{ + info: info, + in: make(edgeMap), + out: make(edgeMap), + tags: make(map[string]*tag), + } + m[info] = rr + } + return rr +} + +// preprocess does any required filtering/sorting according to the +// report options. Returns the mapping from each node to any nodes +// removed by path compression and statistics on the nodes/edges removed. +func (g *graph) preprocess(rpt *Report) (origCount, droppedNodes, droppedEdges int) { + o := rpt.options + + // Compute total weight of current set of nodes. + // This is <= rpt.total because of node filtering. + var totalValue int64 + for _, n := range g.ns { + totalValue += n.flat + } + + // Remove nodes with value <= total*nodeFraction + if nodeFraction := o.NodeFraction; nodeFraction > 0 { + var removed nodes + minValue := int64(float64(totalValue) * nodeFraction) + kept := make(nodes, 0, len(g.ns)) + for _, n := range g.ns { + if n.cum < minValue { + removed = append(removed, n) + } else { + kept = append(kept, n) + tagsKept := make(map[string]*tag) + for s, t := range n.tags { + if t.weight >= minValue { + tagsKept[s] = t + } + } + n.tags = tagsKept + } + } + droppedNodes = len(removed) + removeNodes(removed, false, false) + g.ns = kept + } + + // Remove edges below minimum frequency. + if edgeFraction := o.EdgeFraction; edgeFraction > 0 { + minEdge := int64(float64(totalValue) * edgeFraction) + for _, n := range g.ns { + for src, e := range n.in { + if e.weight < minEdge { + delete(n.in, src) + delete(src.out, n) + droppedEdges++ + } + } + } + } + + sortOrder := flatName + if o.CumSort { + // Force cum sorting for graph output, to preserve connectivity. + sortOrder = cumName + } + + // Nodes that have flat==0 and a single in/out do not provide much + // information. Give them first chance to be removed. Do not consider edges + // from/to nodes that are expected to be removed. + maxNodes := o.NodeCount + if o.OutputFormat == Dot { + if maxNodes > 0 && maxNodes < len(g.ns) { + sortOrder = cumName + g.ns.sort(cumName) + cumCutoff := g.ns[maxNodes].cum + for _, n := range g.ns { + if n.flat == 0 { + if count := countEdges(n.out, cumCutoff); count > 1 { + continue + } + if count := countEdges(n.in, cumCutoff); count != 1 { + continue + } + n.info.lowPriority = true + } + } + } + } + + g.ns.sort(sortOrder) + if maxNodes > 0 { + origCount = len(g.ns) + for index, nodes := 0, 0; index < len(g.ns); index++ { + nodes++ + // For DOT output, count the tags as nodes since we will draw + // boxes for them. + if o.OutputFormat == Dot { + nodes += len(g.ns[index].tags) + } + if nodes > maxNodes { + // Trim to the top n nodes. Create dotted edges to bridge any + // broken connections. + removeNodes(g.ns[index:], true, true) + g.ns = g.ns[:index] + break + } + } + } + removeRedundantEdges(g.ns) + + // Select best unit for profile output. + // Find the appropriate units for the smallest non-zero sample + if o.OutputUnit == "minimum" && len(g.ns) > 0 { + var maxValue, minValue int64 + + for _, n := range g.ns { + if n.flat > 0 && (minValue == 0 || n.flat < minValue) { + minValue = n.flat + } + if n.cum > maxValue { + maxValue = n.cum + } + } + if r := o.Ratio; r > 0 && r != 1 { + minValue = int64(float64(minValue) * r) + maxValue = int64(float64(maxValue) * r) + } + + _, minUnit := ScaleValue(minValue, o.SampleUnit, "minimum") + _, maxUnit := ScaleValue(maxValue, o.SampleUnit, "minimum") + + unit := minUnit + if minUnit != maxUnit && minValue*100 < maxValue && o.OutputFormat != Callgrind { + // Minimum and maximum values have different units. Scale + // minimum by 100 to use larger units, allowing minimum value to + // be scaled down to 0.01, except for callgrind reports since + // they can only represent integer values. + _, unit = ScaleValue(100*minValue, o.SampleUnit, "minimum") + } + + if unit != "" { + o.OutputUnit = unit + } else { + o.OutputUnit = o.SampleUnit + } + } + return +} + +// countEdges counts the number of edges below the specified cutoff. +func countEdges(el edgeMap, cutoff int64) int { + count := 0 + for _, e := range el { + if e.weight > cutoff { + count++ + } + } + return count +} + +// removeNodes removes nodes from a report, optionally bridging +// connections between in/out edges and spreading out their weights +// proportionally. residual marks new bridge edges as residual +// (dotted). +func removeNodes(toRemove nodes, bridge, residual bool) { + for _, n := range toRemove { + for ei := range n.in { + delete(ei.out, n) + } + if bridge { + for ei, wi := range n.in { + for eo, wo := range n.out { + var weight int64 + if n.cum != 0 { + weight = int64(float64(wo.weight) * (float64(wi.weight) / float64(n.cum))) + } + bumpWeight(ei, eo, weight, residual) + } + } + } + for eo := range n.out { + delete(eo.in, n) + } + } +} + +// removeRedundantEdges removes residual edges if the destination can +// be reached through another path. This is done to simplify the graph +// while preserving connectivity. +func removeRedundantEdges(ns nodes) { + // Walk the nodes and outgoing edges in reverse order to prefer + // removing edges with the lowest weight. + for i := len(ns); i > 0; i-- { + n := ns[i-1] + in := sortedEdges(n.in) + for j := len(in); j > 0; j-- { + if e := in[j-1]; e.residual && isRedundant(e) { + delete(e.src.out, e.dest) + delete(e.dest.in, e.src) + } + } + } +} + +// isRedundant determines if an edge can be removed without impacting +// connectivity of the whole graph. This is implemented by checking if the +// nodes have a common ancestor after removing the edge. +func isRedundant(e *edgeInfo) bool { + destPred := predecessors(e, e.dest) + if len(destPred) == 1 { + return false + } + srcPred := predecessors(e, e.src) + + for n := range srcPred { + if destPred[n] && n != e.dest { + return true + } + } + return false +} + +// predecessors collects all the predecessors to node n, excluding edge e. +func predecessors(e *edgeInfo, n *node) map[*node]bool { + seen := map[*node]bool{n: true} + queue := []*node{n} + for len(queue) > 0 { + n := queue[0] + queue = queue[1:] + for _, ie := range n.in { + if e == ie || seen[ie.src] { + continue + } + seen[ie.src] = true + queue = append(queue, ie.src) + } + } + return seen +} + +// nodeSorter is a mechanism used to allow a report to be sorted +// in different ways. +type nodeSorter struct { + rs nodes + less func(i, j int) bool +} + +func (s nodeSorter) Len() int { return len(s.rs) } +func (s nodeSorter) Swap(i, j int) { s.rs[i], s.rs[j] = s.rs[j], s.rs[i] } +func (s nodeSorter) Less(i, j int) bool { return s.less(i, j) } + +type nodeOrder int + +const ( + flatName nodeOrder = iota + flatCumName + cumName + nameOrder + fileOrder + addressOrder +) + +// sort reorders the entries in a report based on the specified +// ordering criteria. The result is sorted in decreasing order for +// numeric quantities, alphabetically for text, and increasing for +// addresses. +func (ns nodes) sort(o nodeOrder) error { + var s nodeSorter + + switch o { + case flatName: + s = nodeSorter{ns, + func(i, j int) bool { + if iv, jv := ns[i].flat, ns[j].flat; iv != jv { + return iv > jv + } + if ns[i].info.prettyName() != ns[j].info.prettyName() { + return ns[i].info.prettyName() < ns[j].info.prettyName() + } + iv, jv := ns[i].cum, ns[j].cum + return iv > jv + }, + } + case flatCumName: + s = nodeSorter{ns, + func(i, j int) bool { + if iv, jv := ns[i].flat, ns[j].flat; iv != jv { + return iv > jv + } + if iv, jv := ns[i].cum, ns[j].cum; iv != jv { + return iv > jv + } + return ns[i].info.prettyName() < ns[j].info.prettyName() + }, + } + case cumName: + s = nodeSorter{ns, + func(i, j int) bool { + if ns[i].info.lowPriority != ns[j].info.lowPriority { + return ns[j].info.lowPriority + } + if iv, jv := ns[i].cum, ns[j].cum; iv != jv { + return iv > jv + } + if ns[i].info.prettyName() != ns[j].info.prettyName() { + return ns[i].info.prettyName() < ns[j].info.prettyName() + } + iv, jv := ns[i].flat, ns[j].flat + return iv > jv + }, + } + case nameOrder: + s = nodeSorter{ns, + func(i, j int) bool { + return ns[i].info.name < ns[j].info.name + }, + } + case fileOrder: + s = nodeSorter{ns, + func(i, j int) bool { + return ns[i].info.file < ns[j].info.file + }, + } + case addressOrder: + s = nodeSorter{ns, + func(i, j int) bool { + return ns[i].info.address < ns[j].info.address + }, + } + default: + return fmt.Errorf("report: unrecognized sort ordering: %d", o) + } + sort.Sort(s) + return nil +} + +type edgeList []*edgeInfo + +// sortedEdges return a slice of the edges in the map, sorted for +// visualization. The sort order is first based on the edge weight +// (higher-to-lower) and then by the node names to avoid flakiness. +func sortedEdges(edges map[*node]*edgeInfo) edgeList { + el := make(edgeList, 0, len(edges)) + for _, w := range edges { + el = append(el, w) + } + + sort.Sort(el) + return el +} + +func (el edgeList) Len() int { + return len(el) +} + +func (el edgeList) Less(i, j int) bool { + if el[i].weight != el[j].weight { + return el[i].weight > el[j].weight + } + + from1 := el[i].src.info.prettyName() + from2 := el[j].src.info.prettyName() + if from1 != from2 { + return from1 < from2 + } + + to1 := el[i].dest.info.prettyName() + to2 := el[j].dest.info.prettyName() + + return to1 < to2 +} + +func (el edgeList) Swap(i, j int) { + el[i], el[j] = el[j], el[i] +} + +func (el edgeList) sum() int64 { + var ret int64 + for _, e := range el { + ret += e.weight + } + return ret +} + +// ScaleValue reformats a value from a unit to a different unit. +func ScaleValue(value int64, fromUnit, toUnit string) (sv float64, su string) { + // Avoid infinite recursion on overflow. + if value < 0 && -value > 0 { + v, u := ScaleValue(-value, fromUnit, toUnit) + return -v, u + } + if m, u, ok := memoryLabel(value, fromUnit, toUnit); ok { + return m, u + } + if t, u, ok := timeLabel(value, fromUnit, toUnit); ok { + return t, u + } + // Skip non-interesting units. + switch toUnit { + case "count", "sample", "unit", "minimum": + return float64(value), "" + default: + return float64(value), toUnit + } +} + +func scaledValueLabel(value int64, fromUnit, toUnit string) string { + v, u := ScaleValue(value, fromUnit, toUnit) + + sv := strings.TrimSuffix(fmt.Sprintf("%.2f", v), ".00") + if sv == "0" || sv == "-0" { + return "0" + } + return sv + u +} + +func memoryLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) { + fromUnit = strings.TrimSuffix(strings.ToLower(fromUnit), "s") + toUnit = strings.TrimSuffix(strings.ToLower(toUnit), "s") + + switch fromUnit { + case "byte", "b": + case "kilobyte", "kb": + value *= 1024 + case "megabyte", "mb": + value *= 1024 * 1024 + case "gigabyte", "gb": + value *= 1024 * 1024 * 1024 + default: + return 0, "", false + } + + if toUnit == "minimum" || toUnit == "auto" { + switch { + case value < 1024: + toUnit = "b" + case value < 1024*1024: + toUnit = "kb" + case value < 1024*1024*1024: + toUnit = "mb" + default: + toUnit = "gb" + } + } + + var output float64 + switch toUnit { + default: + output, toUnit = float64(value), "B" + case "kb", "kbyte", "kilobyte": + output, toUnit = float64(value)/1024, "kB" + case "mb", "mbyte", "megabyte": + output, toUnit = float64(value)/(1024*1024), "MB" + case "gb", "gbyte", "gigabyte": + output, toUnit = float64(value)/(1024*1024*1024), "GB" + } + return output, toUnit, true +} + +func timeLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) { + fromUnit = strings.ToLower(fromUnit) + if len(fromUnit) > 2 { + fromUnit = strings.TrimSuffix(fromUnit, "s") + } + + toUnit = strings.ToLower(toUnit) + if len(toUnit) > 2 { + toUnit = strings.TrimSuffix(toUnit, "s") + } + + var d time.Duration + switch fromUnit { + case "nanosecond", "ns": + d = time.Duration(value) * time.Nanosecond + case "microsecond": + d = time.Duration(value) * time.Microsecond + case "millisecond", "ms": + d = time.Duration(value) * time.Millisecond + case "second", "sec": + d = time.Duration(value) * time.Second + case "cycle": + return float64(value), "", true + default: + return 0, "", false + } + + if toUnit == "minimum" || toUnit == "auto" { + switch { + case d < 1*time.Microsecond: + toUnit = "ns" + case d < 1*time.Millisecond: + toUnit = "us" + case d < 1*time.Second: + toUnit = "ms" + case d < 1*time.Minute: + toUnit = "sec" + case d < 1*time.Hour: + toUnit = "min" + case d < 24*time.Hour: + toUnit = "hour" + case d < 15*24*time.Hour: + toUnit = "day" + case d < 120*24*time.Hour: + toUnit = "week" + default: + toUnit = "year" + } + } + + var output float64 + dd := float64(d) + switch toUnit { + case "ns", "nanosecond": + output, toUnit = dd/float64(time.Nanosecond), "ns" + case "us", "microsecond": + output, toUnit = dd/float64(time.Microsecond), "us" + case "ms", "millisecond": + output, toUnit = dd/float64(time.Millisecond), "ms" + case "min", "minute": + output, toUnit = dd/float64(time.Minute), "mins" + case "hour", "hr": + output, toUnit = dd/float64(time.Hour), "hrs" + case "day": + output, toUnit = dd/float64(24*time.Hour), "days" + case "week", "wk": + output, toUnit = dd/float64(7*24*time.Hour), "wks" + case "year", "yr": + output, toUnit = dd/float64(365*7*24*time.Hour), "yrs" + default: + fallthrough + case "sec", "second", "s": + output, toUnit = dd/float64(time.Second), "s" + } + return output, toUnit, true +} + +// prettyName determines the printable name to be used for a node. +func (info *nodeInfo) prettyName() string { + var name string + if info.address != 0 { + name = fmt.Sprintf("%016x", info.address) + } + + if info.name != "" { + name = name + " " + info.name + } + + if info.file != "" { + name += " " + trimPath(info.file) + if info.lineno != 0 { + name += fmt.Sprintf(":%d", info.lineno) + } + } + + if info.inline { + name = name + " (inline)" + } + + if name = strings.TrimSpace(name); name == "" && info.objfile != "" { + name = "[" + info.objfile + "]" + } + return name +} + +// New builds a new report indexing the sample values interpreting the +// samples with the provided function. +func New(prof *profile.Profile, options Options, value func(s *profile.Sample) int64, unit string) *Report { + o := &options + if o.SampleUnit == "" { + o.SampleUnit = unit + } + format := func(v int64) string { + if r := o.Ratio; r > 0 && r != 1 { + fv := float64(v) * r + v = int64(fv) + } + return scaledValueLabel(v, o.SampleUnit, o.OutputUnit) + } + return &Report{prof, computeTotal(prof, value), o, value, format} +} + +// NewDefault builds a new report indexing the sample values with the +// last value available. +func NewDefault(prof *profile.Profile, options Options) *Report { + index := len(prof.SampleType) - 1 + o := &options + if o.SampleUnit == "" { + o.SampleUnit = strings.ToLower(prof.SampleType[index].Unit) + } + value := func(s *profile.Sample) int64 { + return s.Value[index] + } + format := func(v int64) string { + if r := o.Ratio; r > 0 && r != 1 { + fv := float64(v) * r + v = int64(fv) + } + return scaledValueLabel(v, o.SampleUnit, o.OutputUnit) + } + return &Report{prof, computeTotal(prof, value), o, value, format} +} + +func computeTotal(prof *profile.Profile, value func(s *profile.Sample) int64) int64 { + var ret int64 + for _, sample := range prof.Sample { + ret += value(sample) + } + return ret +} + +// Report contains the data and associated routines to extract a +// report from a profile. +type Report struct { + prof *profile.Profile + total int64 + options *Options + sampleValue func(*profile.Sample) int64 + formatValue func(int64) string +} diff --git a/src/cmd/internal/pprof/report/source.go b/src/cmd/internal/pprof/report/source.go new file mode 100644 index 0000000000..7beea39562 --- /dev/null +++ b/src/cmd/internal/pprof/report/source.go @@ -0,0 +1,454 @@ +// Copyright 2014 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 report + +// This file contains routines related to the generation of annotated +// source listings. + +import ( + "bufio" + "fmt" + "html/template" + "io" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + + "cmd/internal/pprof/plugin" +) + +// printSource prints an annotated source listing, include all +// functions with samples that match the regexp rpt.options.symbol. +// The sources are sorted by function name and then by filename to +// eliminate potential nondeterminism. +func printSource(w io.Writer, rpt *Report) error { + o := rpt.options + g, err := newGraph(rpt) + if err != nil { + return err + } + + // Identify all the functions that match the regexp provided. + // Group nodes for each matching function. + var functions nodes + functionNodes := make(map[string]nodes) + for _, n := range g.ns { + if !o.Symbol.MatchString(n.info.name) { + continue + } + if functionNodes[n.info.name] == nil { + functions = append(functions, n) + } + functionNodes[n.info.name] = append(functionNodes[n.info.name], n) + } + functions.sort(nameOrder) + + fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total)) + for _, fn := range functions { + name := fn.info.name + + // Identify all the source files associated to this function. + // Group nodes for each source file. + var sourceFiles nodes + fileNodes := make(map[string]nodes) + for _, n := range functionNodes[name] { + if n.info.file == "" { + continue + } + if fileNodes[n.info.file] == nil { + sourceFiles = append(sourceFiles, n) + } + fileNodes[n.info.file] = append(fileNodes[n.info.file], n) + } + + if len(sourceFiles) == 0 { + fmt.Printf("No source information for %s\n", name) + continue + } + + sourceFiles.sort(fileOrder) + + // Print each file associated with this function. + for _, fl := range sourceFiles { + filename := fl.info.file + fns := fileNodes[filename] + flatSum, cumSum := sumNodes(fns) + + fnodes, path, err := getFunctionSource(name, filename, fns, 0, 0) + fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, path) + fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n", + rpt.formatValue(flatSum), rpt.formatValue(cumSum), + percentage(cumSum, rpt.total)) + + if err != nil { + fmt.Fprintf(w, " Error: %v\n", err) + continue + } + + for _, fn := range fnodes { + fmt.Fprintf(w, "%10s %10s %6d:%s\n", valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt), fn.info.lineno, fn.info.name) + } + } + } + return nil +} + +// printWebSource prints an annotated source listing, include all +// functions with samples that match the regexp rpt.options.symbol. +func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error { + o := rpt.options + g, err := newGraph(rpt) + if err != nil { + return err + } + + // If the regexp source can be parsed as an address, also match + // functions that land on that address. + var address *uint64 + if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil { + address = &hex + } + + // Extract interesting symbols from binary files in the profile and + // classify samples per symbol. + symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj) + symNodes := nodesPerSymbol(g.ns, symbols) + + // Sort symbols for printing. + var syms objSymbols + for s := range symNodes { + syms = append(syms, s) + } + sort.Sort(syms) + + if len(syms) == 0 { + return fmt.Errorf("no samples found on routines matching: %s", o.Symbol.String()) + } + + printHeader(w, rpt) + for _, s := range syms { + name := s.sym.Name[0] + // Identify sources associated to a symbol by examining + // symbol samples. Classify samples per source file. + var sourceFiles nodes + fileNodes := make(map[string]nodes) + for _, n := range symNodes[s] { + if n.info.file == "" { + continue + } + if fileNodes[n.info.file] == nil { + sourceFiles = append(sourceFiles, n) + } + fileNodes[n.info.file] = append(fileNodes[n.info.file], n) + } + + if len(sourceFiles) == 0 { + fmt.Printf("No source information for %s\n", name) + continue + } + + sourceFiles.sort(fileOrder) + + // Print each file associated with this function. + for _, fl := range sourceFiles { + filename := fl.info.file + fns := fileNodes[filename] + + asm := assemblyPerSourceLine(symbols, fns, filename, obj) + start, end := sourceCoordinates(asm) + + fnodes, path, err := getFunctionSource(name, filename, fns, start, end) + if err != nil { + fnodes, path = getMissingFunctionSource(filename, asm, start, end) + } + + flatSum, cumSum := sumNodes(fnodes) + printFunctionHeader(w, name, path, flatSum, cumSum, rpt) + for _, fn := range fnodes { + printFunctionSourceLine(w, fn, asm[fn.info.lineno], rpt) + } + printFunctionClosing(w) + } + } + printPageClosing(w) + return nil +} + +// sourceCoordinates returns the lowest and highest line numbers from +// a set of assembly statements. +func sourceCoordinates(asm map[int]nodes) (start, end int) { + for l := range asm { + if start == 0 || l < start { + start = l + } + if end == 0 || l > end { + end = l + } + } + return start, end +} + +// assemblyPerSourceLine disassembles the binary containing a symbol +// and classifies the assembly instructions according to its +// corresponding source line, annotating them with a set of samples. +func assemblyPerSourceLine(objSyms []*objSymbol, rs nodes, src string, obj plugin.ObjTool) map[int]nodes { + assembly := make(map[int]nodes) + // Identify symbol to use for this collection of samples. + o := findMatchingSymbol(objSyms, rs) + if o == nil { + return assembly + } + + // Extract assembly for matched symbol + insns, err := obj.Disasm(o.sym.File, o.sym.Start, o.sym.End) + if err != nil { + return assembly + } + + srcBase := filepath.Base(src) + anodes := annotateAssembly(insns, rs, o.base) + var lineno = 0 + for _, an := range anodes { + if filepath.Base(an.info.file) == srcBase { + lineno = an.info.lineno + } + if lineno != 0 { + assembly[lineno] = append(assembly[lineno], an) + } + } + + return assembly +} + +// findMatchingSymbol looks for the symbol that corresponds to a set +// of samples, by comparing their addresses. +func findMatchingSymbol(objSyms []*objSymbol, ns nodes) *objSymbol { + for _, n := range ns { + for _, o := range objSyms { + if filepath.Base(o.sym.File) == n.info.objfile && + o.sym.Start <= n.info.address-o.base && + n.info.address-o.base <= o.sym.End { + return o + } + } + } + return nil +} + +// printHeader prints the page header for a weblist report. +func printHeader(w io.Writer, rpt *Report) { + fmt.Fprintln(w, weblistPageHeader) + + var labels []string + for _, l := range legendLabels(rpt) { + labels = append(labels, template.HTMLEscapeString(l)) + } + + fmt.Fprintf(w, `
%s
Total: %s
`, + strings.Join(labels, "
\n"), + rpt.formatValue(rpt.total), + ) +} + +// printFunctionHeader prints a function header for a weblist report. +func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64, rpt *Report) { + fmt.Fprintf(w, `

%s

%s +
+  Total:  %10s %10s (flat, cum) %s
+`,
+		template.HTMLEscapeString(name), template.HTMLEscapeString(path),
+		rpt.formatValue(flatSum), rpt.formatValue(cumSum),
+		percentage(cumSum, rpt.total))
+}
+
+// printFunctionSourceLine prints a source line and the corresponding assembly.
+func printFunctionSourceLine(w io.Writer, fn *node, assembly nodes, rpt *Report) {
+	if len(assembly) == 0 {
+		fmt.Fprintf(w,
+			" %6d   %10s %10s %s \n",
+			fn.info.lineno,
+			valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
+			template.HTMLEscapeString(fn.info.name))
+		return
+	}
+
+	fmt.Fprintf(w,
+		" %6d   %10s %10s %s ",
+		fn.info.lineno,
+		valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
+		template.HTMLEscapeString(fn.info.name))
+	fmt.Fprint(w, "")
+	for _, an := range assembly {
+		var fileline string
+		class := "disasmloc"
+		if an.info.file != "" {
+			fileline = fmt.Sprintf("%s:%d", template.HTMLEscapeString(an.info.file), an.info.lineno)
+			if an.info.lineno != fn.info.lineno {
+				class = "unimportant"
+			}
+		}
+		fmt.Fprintf(w, " %8s %10s %10s %8x: %-48s %s\n", "",
+			valueOrDot(an.flat, rpt), valueOrDot(an.cum, rpt),
+			an.info.address,
+			template.HTMLEscapeString(an.info.name),
+			class,
+			template.HTMLEscapeString(fileline))
+	}
+	fmt.Fprintln(w, "")
+}
+
+// printFunctionClosing prints the end of a function in a weblist report.
+func printFunctionClosing(w io.Writer) {
+	fmt.Fprintln(w, "
") +} + +// printPageClosing prints the end of the page in a weblist report. +func printPageClosing(w io.Writer) { + fmt.Fprintln(w, weblistPageClosing) +} + +// getFunctionSource collects the sources of a function from a source +// file and annotates it with the samples in fns. Returns the sources +// as nodes, using the info.name field to hold the source code. +func getFunctionSource(fun, file string, fns nodes, start, end int) (nodes, string, error) { + f, file, err := adjustSourcePath(file) + if err != nil { + return nil, file, err + } + + lineNodes := make(map[int]nodes) + + // Collect source coordinates from profile. + const margin = 5 // Lines before first/after last sample. + if start == 0 { + if fns[0].info.startLine != 0 { + start = fns[0].info.startLine + } else { + start = fns[0].info.lineno - margin + } + } else { + start -= margin + } + if end == 0 { + end = fns[0].info.lineno + } + end += margin + for _, n := range fns { + lineno := n.info.lineno + nodeStart := n.info.startLine + if nodeStart == 0 { + nodeStart = lineno - margin + } + nodeEnd := lineno + margin + if nodeStart < start { + start = nodeStart + } else if nodeEnd > end { + end = nodeEnd + } + lineNodes[lineno] = append(lineNodes[lineno], n) + } + + var src nodes + buf := bufio.NewReader(f) + lineno := 1 + for { + line, err := buf.ReadString('\n') + if err != nil { + if err != io.EOF { + return nil, file, err + } + if line == "" { + // end was at or past EOF; that's okay + break + } + } + if lineno >= start { + flat, cum := sumNodes(lineNodes[lineno]) + + src = append(src, &node{ + info: nodeInfo{ + name: strings.TrimRight(line, "\n"), + lineno: lineno, + }, + flat: flat, + cum: cum, + }) + } + lineno++ + if lineno > end { + break + } + } + return src, file, nil +} + +// getMissingFunctionSource creates a dummy function body to point to +// the source file and annotates it with the samples in asm. +func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int) (nodes, string) { + var fnodes nodes + for i := start; i <= end; i++ { + lrs := asm[i] + if len(lrs) == 0 { + continue + } + flat, cum := sumNodes(lrs) + fnodes = append(fnodes, &node{ + info: nodeInfo{ + name: "???", + lineno: i, + }, + flat: flat, + cum: cum, + }) + } + return fnodes, filename +} + +// adjustSourcePath adjusts the path for a source file by trimming +// known prefixes and searching for the file on all parents of the +// current working dir. +func adjustSourcePath(path string) (*os.File, string, error) { + path = trimPath(path) + f, err := os.Open(path) + if err == nil { + return f, path, nil + } + + if dir, wderr := os.Getwd(); wderr == nil { + for { + parent := filepath.Dir(dir) + if parent == dir { + break + } + if f, err := os.Open(filepath.Join(parent, path)); err == nil { + return f, filepath.Join(parent, path), nil + } + + dir = parent + } + } + + return nil, path, err +} + +// trimPath cleans up a path by removing prefixes that are commonly +// found on profiles. +func trimPath(path string) string { + basePaths := []string{ + "/proc/self/cwd/./", + "/proc/self/cwd/", + } + + sPath := filepath.ToSlash(path) + + for _, base := range basePaths { + if strings.HasPrefix(sPath, base) { + return filepath.FromSlash(sPath[len(base):]) + } + } + return path +} diff --git a/src/cmd/internal/pprof/report/source_html.go b/src/cmd/internal/pprof/report/source_html.go new file mode 100644 index 0000000000..267fabdc4b --- /dev/null +++ b/src/cmd/internal/pprof/report/source_html.go @@ -0,0 +1,77 @@ +// Copyright 2014 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 report + +const weblistPageHeader = ` + + + +Pprof listing + + + + +` + +const weblistPageClosing = ` + + +` diff --git a/src/cmd/internal/pprof/svg/svg.go b/src/cmd/internal/pprof/svg/svg.go new file mode 100644 index 0000000000..04f6ff1870 --- /dev/null +++ b/src/cmd/internal/pprof/svg/svg.go @@ -0,0 +1,71 @@ +// Copyright 2014 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 svg provides tools related to handling of SVG files +package svg + +import ( + "bytes" + "regexp" + "strings" +) + +var ( + viewBox = regexp.MustCompile(``) +) + +// Massage enhances the SVG output from DOT to provide better +// panning inside a web browser. It uses the SVGPan library, which is +// included directly. +func Massage(in bytes.Buffer) string { + svg := string(in.Bytes()) + + // Work around for dot bug which misses quoting some ampersands, + // resulting on unparsable SVG. + svg = strings.Replace(svg, "&;", "&;", -1) + + //Dot's SVG output is + // + // + // + // ... + // + // + // + // Change it to + // + // + // + // + // + // ... + // + // + // + + if loc := viewBox.FindStringIndex(svg); loc != nil { + svg = svg[:loc[0]] + + `` + svgPanJS + `` + + `` + + svg[loc[0]:] + } + + if loc := svgClose.FindStringIndex(svg); loc != nil { + svg = svg[:loc[0]] + + `` + + svg[loc[0]:] + } + + return svg +} diff --git a/src/cmd/internal/pprof/svg/svgpan.go b/src/cmd/internal/pprof/svg/svgpan.go new file mode 100644 index 0000000000..4975b103e3 --- /dev/null +++ b/src/cmd/internal/pprof/svg/svgpan.go @@ -0,0 +1,291 @@ +// SVG pan and zoom library. +// See copyright notice in string constant below. + +package svg + +// https://www.cyberz.org/projects/SVGPan/SVGPan.js + +const svgPanJS = ` +/** + * SVGPan library 1.2.1 + * ====================== + * + * Given an unique existing element with id "viewport" (or when missing, the first g + * element), including the the library into any SVG adds the following capabilities: + * + * - Mouse panning + * - Mouse zooming (using the wheel) + * - Object dragging + * + * You can configure the behaviour of the pan/zoom/drag with the variables + * listed in the CONFIGURATION section of this file. + * + * Known issues: + * + * - Zooming (while panning) on Safari has still some issues + * + * Releases: + * + * 1.2.1, Mon Jul 4 00:33:18 CEST 2011, Andrea Leofreddi + * - Fixed a regression with mouse wheel (now working on Firefox 5) + * - Working with viewBox attribute (#4) + * - Added "use strict;" and fixed resulting warnings (#5) + * - Added configuration variables, dragging is disabled by default (#3) + * + * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui + * Fixed a bug with browser mouse handler interaction + * + * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui + * Updated the zoom code to support the mouse wheel on Safari/Chrome + * + * 1.0, Andrea Leofreddi + * First release + * + * This code is licensed under the following BSD license: + * + * Copyright 2009-2010 Andrea Leofreddi . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ` + "``AS IS''" + ` AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of Andrea Leofreddi. + */ + +"use strict"; + +/// CONFIGURATION +/// ====> + +var enablePan = 1; // 1 or 0: enable or disable panning (default enabled) +var enableZoom = 1; // 1 or 0: enable or disable zooming (default enabled) +var enableDrag = 0; // 1 or 0: enable or disable dragging (default disabled) + +/// <==== +/// END OF CONFIGURATION + +var root = document.documentElement; + +var state = 'none', svgRoot, stateTarget, stateOrigin, stateTf; + +setupHandlers(root); + +/** + * Register handlers + */ +function setupHandlers(root){ + setAttributes(root, { + "onmouseup" : "handleMouseUp(evt)", + "onmousedown" : "handleMouseDown(evt)", + "onmousemove" : "handleMouseMove(evt)", + //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element + }); + + if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0) + window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari + else + window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others +} + +/** + * Retrieves the root element for SVG manipulation. The element is then cached into the svgRoot global variable. + */ +function getRoot(root) { + if(typeof(svgRoot) == "undefined") { + var g = null; + + g = root.getElementById("viewport"); + + if(g == null) + g = root.getElementsByTagName('g')[0]; + + if(g == null) + alert('Unable to obtain SVG root element'); + + setCTM(g, g.getCTM()); + + g.removeAttribute("viewBox"); + + svgRoot = g; + } + + return svgRoot; +} + +/** + * Instance an SVGPoint object with given event coordinates. + */ +function getEventPoint(evt) { + var p = root.createSVGPoint(); + + p.x = evt.clientX; + p.y = evt.clientY; + + return p; +} + +/** + * Sets the current transform matrix of an element. + */ +function setCTM(element, matrix) { + var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; + + element.setAttribute("transform", s); +} + +/** + * Dumps a matrix to a string (useful for debug). + */ +function dumpMatrix(matrix) { + var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]"; + + return s; +} + +/** + * Sets attributes of an element. + */ +function setAttributes(element, attributes){ + for (var i in attributes) + element.setAttributeNS(null, i, attributes[i]); +} + +/** + * Handle mouse wheel event. + */ +function handleMouseWheel(evt) { + if(!enableZoom) + return; + + if(evt.preventDefault) + evt.preventDefault(); + + evt.returnValue = false; + + var svgDoc = evt.target.ownerDocument; + + var delta; + + if(evt.wheelDelta) + delta = evt.wheelDelta / 3600; // Chrome/Safari + else + delta = evt.detail / -90; // Mozilla + + var z = 1 + delta; // Zoom factor: 0.9/1.1 + + var g = getRoot(svgDoc); + + var p = getEventPoint(evt); + + p = p.matrixTransform(g.getCTM().inverse()); + + // Compute new scale matrix in current mouse position + var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y); + + setCTM(g, g.getCTM().multiply(k)); + + if(typeof(stateTf) == "undefined") + stateTf = g.getCTM().inverse(); + + stateTf = stateTf.multiply(k.inverse()); +} + +/** + * Handle mouse move event. + */ +function handleMouseMove(evt) { + if(evt.preventDefault) + evt.preventDefault(); + + evt.returnValue = false; + + var svgDoc = evt.target.ownerDocument; + + var g = getRoot(svgDoc); + + if(state == 'pan' && enablePan) { + // Pan mode + var p = getEventPoint(evt).matrixTransform(stateTf); + + setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y)); + } else if(state == 'drag' && enableDrag) { + // Drag mode + var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse()); + + setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM())); + + stateOrigin = p; + } +} + +/** + * Handle click event. + */ +function handleMouseDown(evt) { + if(evt.preventDefault) + evt.preventDefault(); + + evt.returnValue = false; + + var svgDoc = evt.target.ownerDocument; + + var g = getRoot(svgDoc); + + if( + evt.target.tagName == "svg" + || !enableDrag // Pan anyway when drag is disabled and the user clicked on an element + ) { + // Pan mode + state = 'pan'; + + stateTf = g.getCTM().inverse(); + + stateOrigin = getEventPoint(evt).matrixTransform(stateTf); + } else { + // Drag mode + state = 'drag'; + + stateTarget = evt.target; + + stateTf = g.getCTM().inverse(); + + stateOrigin = getEventPoint(evt).matrixTransform(stateTf); + } +} + +/** + * Handle mouse button release event. + */ +function handleMouseUp(evt) { + if(evt.preventDefault) + evt.preventDefault(); + + evt.returnValue = false; + + var svgDoc = evt.target.ownerDocument; + + if(state == 'pan' || state == 'drag') { + // Quit pan mode + state = ''; + } +} + +` diff --git a/src/cmd/internal/pprof/symbolizer/symbolizer.go b/src/cmd/internal/pprof/symbolizer/symbolizer.go new file mode 100644 index 0000000000..bc22800530 --- /dev/null +++ b/src/cmd/internal/pprof/symbolizer/symbolizer.go @@ -0,0 +1,195 @@ +// Copyright 2014 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 symbolizer provides a routine to populate a profile with +// symbol, file and line number information. It relies on the +// addr2liner and demangler packages to do the actual work. +package symbolizer + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "cmd/internal/pprof/plugin" + "cmd/internal/pprof/profile" +) + +// Symbolize adds symbol and line number information to all locations +// in a profile. mode enables some options to control +// symbolization. Currently only recognizes "force", which causes it +// to overwrite any existing data. +func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error { + force := false + // Disable some mechanisms based on mode string. + for _, o := range strings.Split(strings.ToLower(mode), ":") { + switch o { + case "force": + force = true + default: + } + } + + if len(prof.Mapping) == 0 { + return fmt.Errorf("no known mappings") + } + + mt, err := newMapping(prof, obj, ui, force) + if err != nil { + return err + } + defer mt.close() + + functions := make(map[profile.Function]*profile.Function) + for _, l := range mt.prof.Location { + m := l.Mapping + segment := mt.segments[m] + if segment == nil { + // Nothing to do + continue + } + + stack, err := segment.SourceLine(l.Address) + if err != nil || len(stack) == 0 { + // No answers from addr2line + continue + } + + l.Line = make([]profile.Line, len(stack)) + for i, frame := range stack { + if frame.Func != "" { + m.HasFunctions = true + } + if frame.File != "" { + m.HasFilenames = true + } + if frame.Line != 0 { + m.HasLineNumbers = true + } + f := &profile.Function{ + Name: frame.Func, + SystemName: frame.Func, + Filename: frame.File, + } + if fp := functions[*f]; fp != nil { + f = fp + } else { + functions[*f] = f + f.ID = uint64(len(mt.prof.Function)) + 1 + mt.prof.Function = append(mt.prof.Function, f) + } + l.Line[i] = profile.Line{ + Function: f, + Line: int64(frame.Line), + } + } + + if len(stack) > 0 { + m.HasInlineFrames = true + } + } + return nil +} + +// newMapping creates a mappingTable for a profile. +func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) { + mt := &mappingTable{ + prof: prof, + segments: make(map[*profile.Mapping]plugin.ObjFile), + } + + // Identify used mappings + mappings := make(map[*profile.Mapping]bool) + for _, l := range prof.Location { + mappings[l.Mapping] = true + } + + for _, m := range prof.Mapping { + if !mappings[m] { + continue + } + // Do not attempt to re-symbolize a mapping that has already been symbolized. + if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) { + continue + } + + f, err := locateFile(obj, m.File, m.BuildID, m.Start) + if err != nil { + ui.PrintErr("Local symbolization failed for ", filepath.Base(m.File), ": ", err) + // Move on to other mappings + continue + } + + if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID { + // Build ID mismatch - ignore. + f.Close() + continue + } + + mt.segments[m] = f + } + + return mt, nil +} + +// locateFile opens a local file for symbolization on the search path +// at $PPROF_BINARY_PATH. Looks inside these directories for files +// named $BUILDID/$BASENAME and $BASENAME (if build id is available). +func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) { + // Construct search path to examine + searchPath := os.Getenv("PPROF_BINARY_PATH") + if searchPath == "" { + // Use $HOME/pprof/binaries as default directory for local symbolization binaries + searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries") + } + + // Collect names to search: {buildid/basename, basename} + var fileNames []string + if baseName := filepath.Base(file); buildID != "" { + fileNames = []string{filepath.Join(buildID, baseName), baseName} + } else { + fileNames = []string{baseName} + } + for _, path := range filepath.SplitList(searchPath) { + for nameIndex, name := range fileNames { + file := filepath.Join(path, name) + if f, err := obj.Open(file, start); err == nil { + fileBuildID := f.BuildID() + if buildID == "" || buildID == fileBuildID { + return f, nil + } + f.Close() + if nameIndex == 0 { + // If this is the first name, the path includes the build id. Report inconsistency. + return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID) + } + } + } + } + // Try original file name + f, err := obj.Open(file, start) + if err == nil && buildID != "" { + if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID { + // Mismatched build IDs, ignore + f.Close() + return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID) + } + } + return f, err +} + +// mappingTable contains the mechanisms for symbolization of a +// profile. +type mappingTable struct { + prof *profile.Profile + segments map[*profile.Mapping]plugin.ObjFile +} + +// Close releases any external processes being used for the mapping. +func (mt *mappingTable) close() { + for _, segment := range mt.segments { + segment.Close() + } +} diff --git a/src/cmd/internal/pprof/symbolz/symbolz.go b/src/cmd/internal/pprof/symbolz/symbolz.go new file mode 100644 index 0000000000..2f2850afeb --- /dev/null +++ b/src/cmd/internal/pprof/symbolz/symbolz.go @@ -0,0 +1,111 @@ +// Copyright 2014 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 symbolz symbolizes a profile using the output from the symbolz +// service. +package symbolz + +import ( + "bytes" + "fmt" + "io" + "net/url" + "regexp" + "strconv" + "strings" + + "cmd/internal/pprof/profile" +) + +var ( + symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`) +) + +// Symbolize symbolizes profile p by parsing data returned by a +// symbolz handler. syms receives the symbolz query (hex addresses +// separated by '+') and returns the symbolz output in a string. It +// symbolizes all locations based on their addresses, regardless of +// mapping. +func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error { + if source = symbolz(source, p); source == "" { + // If the source is not a recognizable URL, do nothing. + return nil + } + + // Construct query of addresses to symbolize. + var a []string + for _, l := range p.Location { + if l.Address != 0 && len(l.Line) == 0 { + a = append(a, fmt.Sprintf("%#x", l.Address)) + } + } + + if len(a) == 0 { + // No addresses to symbolize. + return nil + } + lines := make(map[uint64]profile.Line) + functions := make(map[string]*profile.Function) + if b, err := syms(source, strings.Join(a, "+")); err == nil { + buf := bytes.NewBuffer(b) + for { + l, err := buf.ReadString('\n') + + if err != nil { + if err == io.EOF { + break + } + return err + } + + if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 { + addr, err := strconv.ParseUint(symbol[1], 0, 64) + if err != nil { + return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err) + } + + name := symbol[2] + fn := functions[name] + if fn == nil { + fn = &profile.Function{ + ID: uint64(len(p.Function) + 1), + Name: name, + SystemName: name, + } + functions[name] = fn + p.Function = append(p.Function, fn) + } + + lines[addr] = profile.Line{Function: fn} + } + } + } + + for _, l := range p.Location { + if line, ok := lines[l.Address]; ok { + l.Line = []profile.Line{line} + if l.Mapping != nil { + l.Mapping.HasFunctions = true + } + } + } + + return nil +} + +// symbolz returns the corresponding symbolz source for a profile URL. +func symbolz(source string, p *profile.Profile) string { + if url, err := url.Parse(source); err == nil && url.Host != "" { + if last := strings.LastIndex(url.Path, "/"); last != -1 { + if strings.HasSuffix(url.Path[:last], "pprof") { + url.Path = url.Path[:last] + "/symbol" + } else { + url.Path = url.Path[:last] + "/symbolz" + } + return url.String() + } + } + + return "" +} diff --git a/src/cmd/internal/pprof/tempfile/tempfile.go b/src/cmd/internal/pprof/tempfile/tempfile.go new file mode 100644 index 0000000000..31c117690a --- /dev/null +++ b/src/cmd/internal/pprof/tempfile/tempfile.go @@ -0,0 +1,45 @@ +// Copyright 2014 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 tempfile provides tools to create and delete temporary files +package tempfile + +import ( + "fmt" + "os" + "path/filepath" + "sync" +) + +// New returns an unused filename for output files. +func New(dir, prefix, suffix string) (*os.File, error) { + for index := 1; index < 10000; index++ { + path := filepath.Join(dir, fmt.Sprintf("%s%03d%s", prefix, index, suffix)) + if _, err := os.Stat(path); err != nil { + return os.Create(path) + } + } + // Give up + return nil, fmt.Errorf("could not create file of the form %s%03d%s", prefix, 1, suffix) +} + +var tempFiles []string +var tempFilesMu = sync.Mutex{} + +// DeferDelete marks a file to be deleted by next call to Cleanup() +func DeferDelete(path string) { + tempFilesMu.Lock() + tempFiles = append(tempFiles, path) + tempFilesMu.Unlock() +} + +// Cleanup removes any temporary files selected for deferred cleaning. +func Cleanup() { + tempFilesMu.Lock() + for _, f := range tempFiles { + os.Remove(f) + } + tempFiles = nil + tempFilesMu.Unlock() +} diff --git a/src/cmd/pprof/internal/commands/commands.go b/src/cmd/pprof/internal/commands/commands.go deleted file mode 100644 index 9aeee5762e..0000000000 --- a/src/cmd/pprof/internal/commands/commands.go +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2014 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 commands defines and manages the basic pprof commands -package commands - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "runtime" - "strings" - "time" - - "cmd/pprof/internal/plugin" - "cmd/pprof/internal/report" - "cmd/pprof/internal/svg" - "cmd/pprof/internal/tempfile" -) - -// Commands describes the commands accepted by pprof. -type Commands map[string]*Command - -// Command describes the actions for a pprof command. Includes a -// function for command-line completion, the report format to use -// during report generation, any postprocessing functions, and whether -// the command expects a regexp parameter (typically a function name). -type Command struct { - Complete Completer // autocomplete for interactive mode - Format int // report format to generate - PostProcess PostProcessor // postprocessing to run on report - HasParam bool // Collect a parameter from the CLI - Usage string // Help text -} - -// Completer is a function for command-line autocompletion -type Completer func(prefix string) string - -// PostProcessor is a function that applies post-processing to the report output -type PostProcessor func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error - -// PProf returns the basic pprof report-generation commands -func PProf(c Completer, interactive **bool) Commands { - return Commands{ - // Commands that require no post-processing. - "tags": {nil, report.Tags, nil, false, "Outputs all tags in the profile"}, - "raw": {c, report.Raw, nil, false, "Outputs a text representation of the raw profile"}, - "dot": {c, report.Dot, nil, false, "Outputs a graph in DOT format"}, - "top": {c, report.Text, nil, false, "Outputs top entries in text form"}, - "tree": {c, report.Tree, nil, false, "Outputs a text rendering of call graph"}, - "text": {c, report.Text, nil, false, "Outputs top entries in text form"}, - "disasm": {c, report.Dis, nil, true, "Output annotated assembly for functions matching regexp or address"}, - "list": {c, report.List, nil, true, "Output annotated source for functions matching regexp"}, - "peek": {c, report.Tree, nil, true, "Output callers/callees of functions matching regexp"}, - - // Save binary formats to a file - "callgrind": {c, report.Callgrind, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format"}, - "proto": {c, report.Proto, awayFromTTY("pb.gz"), false, "Outputs the profile in compressed protobuf format"}, - - // Generate report in DOT format and postprocess with dot - "gif": {c, report.Dot, invokeDot("gif"), false, "Outputs a graph image in GIF format"}, - "pdf": {c, report.Dot, invokeDot("pdf"), false, "Outputs a graph in PDF format"}, - "png": {c, report.Dot, invokeDot("png"), false, "Outputs a graph image in PNG format"}, - "ps": {c, report.Dot, invokeDot("ps"), false, "Outputs a graph in PS format"}, - - // Save SVG output into a file after including svgpan library - "svg": {c, report.Dot, saveSVGToFile(), false, "Outputs a graph in SVG format"}, - - // Visualize postprocessed dot output - "eog": {c, report.Dot, invokeVisualizer(interactive, invokeDot("svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"}, - "evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot("pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"}, - "gv": {c, report.Dot, invokeVisualizer(interactive, invokeDot("ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"}, - "web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(), "svg", browsers()), false, "Visualize graph through web browser"}, - - // Visualize HTML directly generated by report. - "weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"}, - } -} - -// browsers returns a list of commands to attempt for web visualization -// on the current platform -func browsers() []string { - var cmds []string - if exe := os.Getenv("BROWSER"); exe != "" { - cmds = append(cmds, exe) - } - switch runtime.GOOS { - case "darwin": - cmds = append(cmds, "/usr/bin/open") - case "windows": - cmds = append(cmds, "cmd /c start") - default: - cmds = append(cmds, "xdg-open") - } - cmds = append(cmds, "chrome", "google-chrome", "firefox") - return cmds -} - -// NewCompleter creates an autocompletion function for a set of commands. -func NewCompleter(cs Commands) Completer { - return func(line string) string { - switch tokens := strings.Fields(line); len(tokens) { - case 0: - // Nothing to complete - case 1: - // Single token -- complete command name - found := "" - for c := range cs { - if strings.HasPrefix(c, tokens[0]) { - if found != "" { - return line - } - found = c - } - } - if found != "" { - return found - } - default: - // Multiple tokens -- complete using command completer - if c, ok := cs[tokens[0]]; ok { - if c.Complete != nil { - lastTokenIdx := len(tokens) - 1 - lastToken := tokens[lastTokenIdx] - if strings.HasPrefix(lastToken, "-") { - lastToken = "-" + c.Complete(lastToken[1:]) - } else { - lastToken = c.Complete(lastToken) - } - return strings.Join(append(tokens[:lastTokenIdx], lastToken), " ") - } - } - } - return line - } -} - -// awayFromTTY saves the output in a file if it would otherwise go to -// the terminal screen. This is used to avoid dumping binary data on -// the screen. -func awayFromTTY(format string) PostProcessor { - return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { - if output == os.Stdout && ui.IsTerminal() { - tempFile, err := tempfile.New("", "profile", "."+format) - if err != nil { - return err - } - ui.PrintErr("Generating report in ", tempFile.Name()) - _, err = fmt.Fprint(tempFile, input) - return err - } - _, err := fmt.Fprint(output, input) - return err - } -} - -func invokeDot(format string) PostProcessor { - divert := awayFromTTY(format) - return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { - if _, err := exec.LookPath("dot"); err != nil { - ui.PrintErr("Cannot find dot, have you installed Graphviz?") - return err - } - cmd := exec.Command("dot", "-T"+format) - var buf bytes.Buffer - cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr - if err := cmd.Run(); err != nil { - return err - } - return divert(&buf, output, ui) - } -} - -func saveSVGToFile() PostProcessor { - generateSVG := invokeDot("svg") - divert := awayFromTTY("svg") - return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { - baseSVG := &bytes.Buffer{} - generateSVG(input, baseSVG, ui) - massaged := &bytes.Buffer{} - fmt.Fprint(massaged, svg.Massage(*baseSVG)) - return divert(massaged, output, ui) - } -} - -var vizTmpDir string - -func makeVizTmpDir() error { - if vizTmpDir != "" { - return nil - } - name, err := ioutil.TempDir("", "pprof-") - if err != nil { - return err - } - vizTmpDir = name - return nil -} - -func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, visualizers []string) PostProcessor { - return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { - if err := makeVizTmpDir(); err != nil { - return err - } - tempFile, err := tempfile.New(vizTmpDir, "pprof", "."+suffix) - if err != nil { - return err - } - tempfile.DeferDelete(tempFile.Name()) - if err = format(input, tempFile, ui); err != nil { - return err - } - tempFile.Close() // on windows, if the file is Open, start cannot access it. - // Try visualizers until one is successful - for _, v := range visualizers { - // Separate command and arguments for exec.Command. - args := strings.Split(v, " ") - if len(args) == 0 { - continue - } - viewer := exec.Command(args[0], append(args[1:], tempFile.Name())...) - viewer.Stderr = os.Stderr - if err = viewer.Start(); err == nil { - // The viewer might just send a message to another program - // to open the file. Give that program a little time to open the - // file before we remove it. - time.Sleep(1 * time.Second) - - if !**interactive { - // In command-line mode, wait for the viewer to be closed - // before proceeding - return viewer.Wait() - } - return nil - } - } - return err - } -} diff --git a/src/cmd/pprof/internal/driver/driver.go b/src/cmd/pprof/internal/driver/driver.go deleted file mode 100644 index 7cd1ddc928..0000000000 --- a/src/cmd/pprof/internal/driver/driver.go +++ /dev/null @@ -1,1041 +0,0 @@ -// Copyright 2014 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 driver implements the core pprof functionality. It can be -// parameterized with a flag implementation, fetch and symbolize -// mechanisms. -package driver - -import ( - "bytes" - "fmt" - "io" - "net/url" - "os" - "path/filepath" - "regexp" - "sort" - "strconv" - "strings" - "sync" - "time" - - "cmd/pprof/internal/commands" - "cmd/pprof/internal/plugin" - "cmd/pprof/internal/profile" - "cmd/pprof/internal/report" - "cmd/pprof/internal/tempfile" -) - -// PProf acquires a profile, and symbolizes it using a profile -// manager. Then it generates a report formatted according to the -// options selected through the flags package. -func PProf(flagset plugin.FlagSet, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, overrides commands.Commands) error { - // Remove any temporary files created during pprof processing. - defer tempfile.Cleanup() - - f, err := getFlags(flagset, overrides, ui) - if err != nil { - return err - } - - obj.SetConfig(*f.flagTools) - - sources := f.profileSource - if len(sources) > 1 { - source := sources[0] - // If the first argument is a supported object file, treat as executable. - if file, err := obj.Open(source, 0); err == nil { - file.Close() - f.profileExecName = source - sources = sources[1:] - } else if *f.flagBuildID == "" && isBuildID(source) { - f.flagBuildID = &source - sources = sources[1:] - } - } - - // errMu protects concurrent accesses to errset and err. errset is set if an - // error is encountered by one of the goroutines grabbing a profile. - errMu, errset := sync.Mutex{}, false - - // Fetch profiles. - wg := sync.WaitGroup{} - profs := make([]*profile.Profile, len(sources)) - for i, source := range sources { - wg.Add(1) - go func(i int, src string) { - defer wg.Done() - p, grabErr := grabProfile(src, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f) - if grabErr != nil { - errMu.Lock() - defer errMu.Unlock() - errset, err = true, grabErr - return - } - profs[i] = p - }(i, source) - } - wg.Wait() - if errset { - return err - } - - // Merge profiles. - prof := profs[0] - for _, p := range profs[1:] { - if err = prof.Merge(p, 1); err != nil { - return err - } - } - - if *f.flagBase != "" { - // Fetch base profile and subtract from current profile. - base, err := grabProfile(*f.flagBase, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f) - if err != nil { - return err - } - - if err = prof.Merge(base, -1); err != nil { - return err - } - } - - if err := processFlags(prof, ui, f); err != nil { - return err - } - - if !*f.flagRuntime { - prof.RemoveUninteresting() - } - - if *f.flagInteractive { - return interactive(prof, obj, ui, f) - } - - return generate(false, prof, obj, ui, f) -} - -// isBuildID determines if the profile may contain a build ID, by -// checking that it is a string of hex digits. -func isBuildID(id string) bool { - return strings.Trim(id, "0123456789abcdefABCDEF") == "" -} - -// adjustURL updates the profile source URL based on heuristics. It -// will append ?seconds=sec for CPU profiles if not already -// specified. Returns the hostname if the profile is remote. -func adjustURL(source string, sec int, ui plugin.UI) (adjusted, host string, duration time.Duration) { - // If there is a local file with this name, just use it. - if _, err := os.Stat(source); err == nil { - return source, "", 0 - } - - url, err := url.Parse(source) - - // Automatically add http:// to URLs of the form hostname:port/path. - // url.Parse treats "hostname" as the Scheme. - if err != nil || (url.Host == "" && url.Scheme != "" && url.Scheme != "file") { - url, err = url.Parse("http://" + source) - if err != nil { - return source, "", 0 - } - } - if scheme := strings.ToLower(url.Scheme); scheme == "" || scheme == "file" { - url.Scheme = "" - return url.String(), "", 0 - } - - values := url.Query() - if urlSeconds := values.Get("seconds"); urlSeconds != "" { - if us, err := strconv.ParseInt(urlSeconds, 10, 32); err == nil { - if sec >= 0 { - ui.PrintErr("Overriding -seconds for URL ", source) - } - sec = int(us) - } - } - - switch strings.ToLower(url.Path) { - case "", "/": - // Apply default /profilez. - url.Path = "/profilez" - case "/protoz": - // Rewrite to /profilez?type=proto - url.Path = "/profilez" - values.Set("type", "proto") - } - - if hasDuration(url.Path) { - if sec > 0 { - duration = time.Duration(sec) * time.Second - values.Set("seconds", fmt.Sprintf("%d", sec)) - } else { - // Assume default duration: 30 seconds - duration = 30 * time.Second - } - } - url.RawQuery = values.Encode() - return url.String(), url.Host, duration -} - -func hasDuration(path string) bool { - for _, trigger := range []string{"profilez", "wallz", "/profile"} { - if strings.Contains(path, trigger) { - return true - } - } - return false -} - -// preprocess does filtering and aggregation of a profile based on the -// requested options. -func preprocess(prof *profile.Profile, ui plugin.UI, f *flags) error { - if *f.flagFocus != "" || *f.flagIgnore != "" || *f.flagHide != "" { - focus, ignore, hide, err := compileFocusIgnore(*f.flagFocus, *f.flagIgnore, *f.flagHide) - if err != nil { - return err - } - fm, im, hm := prof.FilterSamplesByName(focus, ignore, hide) - - warnNoMatches(fm, *f.flagFocus, "Focus", ui) - warnNoMatches(im, *f.flagIgnore, "Ignore", ui) - warnNoMatches(hm, *f.flagHide, "Hide", ui) - } - - if *f.flagTagFocus != "" || *f.flagTagIgnore != "" { - focus, err := compileTagFilter(*f.flagTagFocus, ui) - if err != nil { - return err - } - ignore, err := compileTagFilter(*f.flagTagIgnore, ui) - if err != nil { - return err - } - fm, im := prof.FilterSamplesByTag(focus, ignore) - - warnNoMatches(fm, *f.flagTagFocus, "TagFocus", ui) - warnNoMatches(im, *f.flagTagIgnore, "TagIgnore", ui) - } - - return aggregate(prof, f) -} - -func compileFocusIgnore(focus, ignore, hide string) (f, i, h *regexp.Regexp, err error) { - if focus != "" { - if f, err = regexp.Compile(focus); err != nil { - return nil, nil, nil, fmt.Errorf("parsing focus regexp: %v", err) - } - } - - if ignore != "" { - if i, err = regexp.Compile(ignore); err != nil { - return nil, nil, nil, fmt.Errorf("parsing ignore regexp: %v", err) - } - } - - if hide != "" { - if h, err = regexp.Compile(hide); err != nil { - return nil, nil, nil, fmt.Errorf("parsing hide regexp: %v", err) - } - } - return -} - -func compileTagFilter(filter string, ui plugin.UI) (f func(string, string, int64) bool, err error) { - if filter == "" { - return nil, nil - } - if numFilter := parseTagFilterRange(filter); numFilter != nil { - ui.PrintErr("Interpreted '", filter, "' as range, not regexp") - return func(key, val string, num int64) bool { - if val != "" { - return false - } - return numFilter(num, key) - }, nil - } - fx, err := regexp.Compile(filter) - if err != nil { - return nil, err - } - - return func(key, val string, num int64) bool { - if val == "" { - return false - } - return fx.MatchString(key + ":" + val) - }, nil -} - -var tagFilterRangeRx = regexp.MustCompile("([[:digit:]]+)([[:alpha:]]+)") - -// parseTagFilterRange returns a function to checks if a value is -// contained on the range described by a string. It can recognize -// strings of the form: -// "32kb" -- matches values == 32kb -// ":64kb" -- matches values <= 64kb -// "4mb:" -- matches values >= 4mb -// "12kb:64mb" -- matches values between 12kb and 64mb (both included). -func parseTagFilterRange(filter string) func(int64, string) bool { - ranges := tagFilterRangeRx.FindAllStringSubmatch(filter, 2) - if len(ranges) == 0 { - return nil // No ranges were identified - } - v, err := strconv.ParseInt(ranges[0][1], 10, 64) - if err != nil { - panic(fmt.Errorf("Failed to parse int %s: %v", ranges[0][1], err)) - } - value, unit := report.ScaleValue(v, ranges[0][2], ranges[0][2]) - if len(ranges) == 1 { - switch match := ranges[0][0]; filter { - case match: - return func(v int64, u string) bool { - sv, su := report.ScaleValue(v, u, unit) - return su == unit && sv == value - } - case match + ":": - return func(v int64, u string) bool { - sv, su := report.ScaleValue(v, u, unit) - return su == unit && sv >= value - } - case ":" + match: - return func(v int64, u string) bool { - sv, su := report.ScaleValue(v, u, unit) - return su == unit && sv <= value - } - } - return nil - } - if filter != ranges[0][0]+":"+ranges[1][0] { - return nil - } - if v, err = strconv.ParseInt(ranges[1][1], 10, 64); err != nil { - panic(fmt.Errorf("Failed to parse int %s: %v", ranges[1][1], err)) - } - value2, unit2 := report.ScaleValue(v, ranges[1][2], unit) - if unit != unit2 { - return nil - } - return func(v int64, u string) bool { - sv, su := report.ScaleValue(v, u, unit) - return su == unit && sv >= value && sv <= value2 - } -} - -func warnNoMatches(match bool, rx, option string, ui plugin.UI) { - if !match && rx != "" && rx != "." { - ui.PrintErr(option + " expression matched no samples: " + rx) - } -} - -// grabProfile fetches and symbolizes a profile. -func grabProfile(source, exec, buildid string, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, f *flags) (*profile.Profile, error) { - source, host, duration := adjustURL(source, *f.flagSeconds, ui) - remote := host != "" - - if remote { - ui.Print("Fetching profile from ", source) - if duration != 0 { - ui.Print("Please wait... (" + duration.String() + ")") - } - } - - now := time.Now() - // Fetch profile from source. - // Give 50% slack on the timeout. - p, err := fetch(source, duration+duration/2, ui) - if err != nil { - return nil, err - } - - // Update the time/duration if the profile source doesn't include it. - // TODO(rsilvera): Remove this when we remove support for legacy profiles. - if remote { - if p.TimeNanos == 0 { - p.TimeNanos = now.UnixNano() - } - if duration != 0 && p.DurationNanos == 0 { - p.DurationNanos = int64(duration) - } - } - - // Replace executable/buildID with the options provided in the - // command line. Assume the executable is the first Mapping entry. - if exec != "" || buildid != "" { - if len(p.Mapping) == 0 { - // Create a fake mapping to hold the user option, and associate - // all samples to it. - m := &profile.Mapping{ - ID: 1, - } - for _, l := range p.Location { - l.Mapping = m - } - p.Mapping = []*profile.Mapping{m} - } - if exec != "" { - p.Mapping[0].File = exec - } - if buildid != "" { - p.Mapping[0].BuildID = buildid - } - } - - if err := sym(*f.flagSymbolize, source, p, obj, ui); err != nil { - return nil, err - } - - // Save a copy of any remote profiles, unless the user is explicitly - // saving it. - if remote && !f.isFormat("proto") { - prefix := "pprof." - if len(p.Mapping) > 0 && p.Mapping[0].File != "" { - prefix = prefix + filepath.Base(p.Mapping[0].File) + "." - } - if !strings.ContainsRune(host, os.PathSeparator) { - prefix = prefix + host + "." - } - for _, s := range p.SampleType { - prefix = prefix + s.Type + "." - } - - dir := os.Getenv("PPROF_TMPDIR") - tempFile, err := tempfile.New(dir, prefix, ".pb.gz") - if err == nil { - if err = p.Write(tempFile); err == nil { - ui.PrintErr("Saved profile in ", tempFile.Name()) - } - } - if err != nil { - ui.PrintErr("Could not save profile: ", err) - } - } - - if err := p.Demangle(obj.Demangle); err != nil { - ui.PrintErr("Failed to demangle profile: ", err) - } - - if err := p.CheckValid(); err != nil { - return nil, fmt.Errorf("Grab %s: %v", source, err) - } - - return p, nil -} - -type flags struct { - flagInteractive *bool // Accept commands interactively - flagCommands map[string]*bool // pprof commands without parameters - flagParamCommands map[string]*string // pprof commands with parameters - - flagOutput *string // Output file name - - flagCum *bool // Sort by cumulative data - flagCallTree *bool // generate a context-sensitive call tree - - flagAddresses *bool // Report at address level - flagLines *bool // Report at source line level - flagFiles *bool // Report at file level - flagFunctions *bool // Report at function level [default] - - flagSymbolize *string // Symbolization options (=none to disable) - flagBuildID *string // Override build if for first mapping - - flagNodeCount *int // Max number of nodes to show - flagNodeFraction *float64 // Hide nodes below *total - flagEdgeFraction *float64 // Hide edges below *total - flagTrim *bool // Set to false to ignore NodeCount/*Fraction - flagRuntime *bool // Show runtime call frames in memory profiles - flagFocus *string // Restricts to paths going through a node matching regexp - flagIgnore *string // Skips paths going through any nodes matching regexp - flagHide *string // Skips sample locations matching regexp - flagTagFocus *string // Restrict to samples tagged with key:value matching regexp - flagTagIgnore *string // Discard samples tagged with key:value matching regexp - flagDropNegative *bool // Skip negative values - - flagBase *string // Source for base profile to user for comparison - - flagSeconds *int // Length of time for dynamic profiles - - flagTotalDelay *bool // Display total delay at each region - flagContentions *bool // Display number of delays at each region - flagMeanDelay *bool // Display mean delay at each region - - flagInUseSpace *bool // Display in-use memory size - flagInUseObjects *bool // Display in-use object counts - flagAllocSpace *bool // Display allocated memory size - flagAllocObjects *bool // Display allocated object counts - flagDisplayUnit *string // Measurement unit to use on reports - flagDivideBy *float64 // Ratio to divide sample values - - flagSampleIndex *int // Sample value to use in reports. - flagMean *bool // Use mean of sample_index over count - - flagTools *string - profileSource []string - profileExecName string - - extraUsage string - commands commands.Commands -} - -func (f *flags) isFormat(format string) bool { - if fl := f.flagCommands[format]; fl != nil { - return *fl - } - if fl := f.flagParamCommands[format]; fl != nil { - return *fl != "" - } - return false -} - -// String provides a printable representation for the current set of flags. -func (f *flags) String(p *profile.Profile) string { - var ret string - - if ix := *f.flagSampleIndex; ix != -1 { - ret += fmt.Sprintf(" %-25s : %d (%s)\n", "sample_index", ix, p.SampleType[ix].Type) - } - if ix := *f.flagMean; ix { - ret += boolFlagString("mean") - } - if *f.flagDisplayUnit != "minimum" { - ret += stringFlagString("unit", *f.flagDisplayUnit) - } - - switch { - case *f.flagInteractive: - ret += boolFlagString("interactive") - } - for name, fl := range f.flagCommands { - if *fl { - ret += boolFlagString(name) - } - } - - if *f.flagCum { - ret += boolFlagString("cum") - } - if *f.flagCallTree { - ret += boolFlagString("call_tree") - } - - switch { - case *f.flagAddresses: - ret += boolFlagString("addresses") - case *f.flagLines: - ret += boolFlagString("lines") - case *f.flagFiles: - ret += boolFlagString("files") - case *f.flagFunctions: - ret += boolFlagString("functions") - } - - if *f.flagNodeCount != -1 { - ret += intFlagString("nodecount", *f.flagNodeCount) - } - - ret += floatFlagString("nodefraction", *f.flagNodeFraction) - ret += floatFlagString("edgefraction", *f.flagEdgeFraction) - - if *f.flagFocus != "" { - ret += stringFlagString("focus", *f.flagFocus) - } - if *f.flagIgnore != "" { - ret += stringFlagString("ignore", *f.flagIgnore) - } - if *f.flagHide != "" { - ret += stringFlagString("hide", *f.flagHide) - } - - if *f.flagTagFocus != "" { - ret += stringFlagString("tagfocus", *f.flagTagFocus) - } - if *f.flagTagIgnore != "" { - ret += stringFlagString("tagignore", *f.flagTagIgnore) - } - - return ret -} - -func boolFlagString(label string) string { - return fmt.Sprintf(" %-25s : true\n", label) -} - -func stringFlagString(label, value string) string { - return fmt.Sprintf(" %-25s : %s\n", label, value) -} - -func intFlagString(label string, value int) string { - return fmt.Sprintf(" %-25s : %d\n", label, value) -} - -func floatFlagString(label string, value float64) string { - return fmt.Sprintf(" %-25s : %f\n", label, value) -} - -// Utility routines to set flag values. -func newBool(b bool) *bool { - return &b -} - -func newString(s string) *string { - return &s -} - -func newFloat64(fl float64) *float64 { - return &fl -} - -func newInt(i int) *int { - return &i -} - -func (f *flags) usage(ui plugin.UI) { - var commandMsg []string - for name, cmd := range f.commands { - if cmd.HasParam { - name = name + "=p" - } - commandMsg = append(commandMsg, - fmt.Sprintf(" -%-16s %s", name, cmd.Usage)) - } - - sort.Strings(commandMsg) - - text := usageMsgHdr + strings.Join(commandMsg, "\n") + "\n" + usageMsg + "\n" - if f.extraUsage != "" { - text += f.extraUsage + "\n" - } - text += usageMsgVars - ui.Print(text) -} - -func getFlags(flag plugin.FlagSet, overrides commands.Commands, ui plugin.UI) (*flags, error) { - f := &flags{ - flagInteractive: flag.Bool("interactive", false, "Accepts commands interactively"), - flagCommands: make(map[string]*bool), - flagParamCommands: make(map[string]*string), - - // Filename for file-based output formats, stdout by default. - flagOutput: flag.String("output", "", "Output filename for file-based outputs "), - // Comparisons. - flagBase: flag.String("base", "", "Source for base profile for comparison"), - flagDropNegative: flag.Bool("drop_negative", false, "Ignore negative differences"), - - // Data sorting criteria. - flagCum: flag.Bool("cum", false, "Sort by cumulative data"), - // Graph handling options. - flagCallTree: flag.Bool("call_tree", false, "Create a context-sensitive call tree"), - // Granularity of output resolution. - flagAddresses: flag.Bool("addresses", false, "Report at address level"), - flagLines: flag.Bool("lines", false, "Report at source line level"), - flagFiles: flag.Bool("files", false, "Report at source file level"), - flagFunctions: flag.Bool("functions", false, "Report at function level [default]"), - // Internal options. - flagSymbolize: flag.String("symbolize", "", "Options for profile symbolization"), - flagBuildID: flag.String("buildid", "", "Override build id for first mapping"), - // Filtering options - flagNodeCount: flag.Int("nodecount", -1, "Max number of nodes to show"), - flagNodeFraction: flag.Float64("nodefraction", 0.005, "Hide nodes below *total"), - flagEdgeFraction: flag.Float64("edgefraction", 0.001, "Hide edges below *total"), - flagTrim: flag.Bool("trim", true, "Honor nodefraction/edgefraction/nodecount defaults"), - flagRuntime: flag.Bool("runtime", false, "Show runtime call frames in memory profiles"), - flagFocus: flag.String("focus", "", "Restricts to paths going through a node matching regexp"), - flagIgnore: flag.String("ignore", "", "Skips paths going through any nodes matching regexp"), - flagHide: flag.String("hide", "", "Skips nodes matching regexp"), - flagTagFocus: flag.String("tagfocus", "", "Restrict to samples with tags in range or matched by regexp"), - flagTagIgnore: flag.String("tagignore", "", "Discard samples with tags in range or matched by regexp"), - // CPU profile options - flagSeconds: flag.Int("seconds", -1, "Length of time for dynamic profiles"), - // Heap profile options - flagInUseSpace: flag.Bool("inuse_space", false, "Display in-use memory size"), - flagInUseObjects: flag.Bool("inuse_objects", false, "Display in-use object counts"), - flagAllocSpace: flag.Bool("alloc_space", false, "Display allocated memory size"), - flagAllocObjects: flag.Bool("alloc_objects", false, "Display allocated object counts"), - flagDisplayUnit: flag.String("unit", "minimum", "Measurement units to display"), - flagDivideBy: flag.Float64("divide_by", 1.0, "Ratio to divide all samples before visualization"), - flagSampleIndex: flag.Int("sample_index", -1, "Index of sample value to report"), - flagMean: flag.Bool("mean", false, "Average sample value over first value (count)"), - // Contention profile options - flagTotalDelay: flag.Bool("total_delay", false, "Display total delay at each region"), - flagContentions: flag.Bool("contentions", false, "Display number of delays at each region"), - flagMeanDelay: flag.Bool("mean_delay", false, "Display mean delay at each region"), - flagTools: flag.String("tools", os.Getenv("PPROF_TOOLS"), "Path for object tool pathnames"), - extraUsage: flag.ExtraUsage(), - } - - // Flags used during command processing - interactive := &f.flagInteractive - f.commands = commands.PProf(functionCompleter, interactive) - - // Override commands - for name, cmd := range overrides { - f.commands[name] = cmd - } - - for name, cmd := range f.commands { - if cmd.HasParam { - f.flagParamCommands[name] = flag.String(name, "", "Generate a report in "+name+" format, matching regexp") - } else { - f.flagCommands[name] = flag.Bool(name, false, "Generate a report in "+name+" format") - } - } - - args := flag.Parse(func() { f.usage(ui) }) - if len(args) == 0 { - return nil, fmt.Errorf("no profile source specified") - } - - f.profileSource = args - - // Instruct legacy heapz parsers to grab historical allocation data, - // instead of the default in-use data. Not available with tcmalloc. - if *f.flagAllocSpace || *f.flagAllocObjects { - profile.LegacyHeapAllocated = true - } - - if profileDir := os.Getenv("PPROF_TMPDIR"); profileDir == "" { - profileDir = os.Getenv("HOME") + "/pprof" - os.Setenv("PPROF_TMPDIR", profileDir) - if err := os.MkdirAll(profileDir, 0755); err != nil { - return nil, fmt.Errorf("failed to access temp dir %s: %v", profileDir, err) - } - } - - return f, nil -} - -func processFlags(p *profile.Profile, ui plugin.UI, f *flags) error { - flagDis := f.isFormat("disasm") - flagPeek := f.isFormat("peek") - flagWebList := f.isFormat("weblist") - flagList := f.isFormat("list") - - if flagDis || flagWebList { - // Collect all samples at address granularity for assembly - // listing. - f.flagNodeCount = newInt(0) - f.flagAddresses = newBool(true) - f.flagLines = newBool(false) - f.flagFiles = newBool(false) - f.flagFunctions = newBool(false) - } - - if flagPeek { - // Collect all samples at function granularity for peek command - f.flagNodeCount = newInt(0) - f.flagAddresses = newBool(false) - f.flagLines = newBool(false) - f.flagFiles = newBool(false) - f.flagFunctions = newBool(true) - } - - if flagList { - // Collect all samples at fileline granularity for source - // listing. - f.flagNodeCount = newInt(0) - f.flagAddresses = newBool(false) - f.flagLines = newBool(true) - f.flagFiles = newBool(false) - f.flagFunctions = newBool(false) - } - - if !*f.flagTrim { - f.flagNodeCount = newInt(0) - f.flagNodeFraction = newFloat64(0) - f.flagEdgeFraction = newFloat64(0) - } - - if oc := countFlagMap(f.flagCommands, f.flagParamCommands); oc == 0 { - f.flagInteractive = newBool(true) - } else if oc > 1 { - f.usage(ui) - return fmt.Errorf("must set at most one output format") - } - - // Apply nodecount defaults for non-interactive mode. The - // interactive shell will apply defaults for the interactive mode. - if *f.flagNodeCount < 0 && !*f.flagInteractive { - switch { - default: - f.flagNodeCount = newInt(80) - case f.isFormat("text"): - f.flagNodeCount = newInt(0) - } - } - - // Apply legacy options and diagnose conflicts. - if rc := countFlags([]*bool{f.flagAddresses, f.flagLines, f.flagFiles, f.flagFunctions}); rc == 0 { - f.flagFunctions = newBool(true) - } else if rc > 1 { - f.usage(ui) - return fmt.Errorf("must set at most one granularity option") - } - - var err error - si, sm := *f.flagSampleIndex, *f.flagMean || *f.flagMeanDelay - si, err = sampleIndex(p, &f.flagTotalDelay, si, 1, "delay", "-total_delay", err) - si, err = sampleIndex(p, &f.flagMeanDelay, si, 1, "delay", "-mean_delay", err) - si, err = sampleIndex(p, &f.flagContentions, si, 0, "contentions", "-contentions", err) - - si, err = sampleIndex(p, &f.flagInUseSpace, si, 1, "inuse_space", "-inuse_space", err) - si, err = sampleIndex(p, &f.flagInUseObjects, si, 0, "inuse_objects", "-inuse_objects", err) - si, err = sampleIndex(p, &f.flagAllocSpace, si, 1, "alloc_space", "-alloc_space", err) - si, err = sampleIndex(p, &f.flagAllocObjects, si, 0, "alloc_objects", "-alloc_objects", err) - - if si == -1 { - // Use last value if none is requested. - si = len(p.SampleType) - 1 - } else if si < 0 || si >= len(p.SampleType) { - err = fmt.Errorf("sample_index value %d out of range [0..%d]", si, len(p.SampleType)-1) - } - - if err != nil { - f.usage(ui) - return err - } - f.flagSampleIndex, f.flagMean = newInt(si), newBool(sm) - return nil -} - -func sampleIndex(p *profile.Profile, flag **bool, - sampleIndex int, - newSampleIndex int, - sampleType, option string, - err error) (int, error) { - if err != nil || !**flag { - return sampleIndex, err - } - *flag = newBool(false) - if sampleIndex != -1 { - return 0, fmt.Errorf("set at most one sample value selection option") - } - if newSampleIndex >= len(p.SampleType) || - p.SampleType[newSampleIndex].Type != sampleType { - return 0, fmt.Errorf("option %s not valid for this profile", option) - } - return newSampleIndex, nil -} - -func countFlags(bs []*bool) int { - var c int - for _, b := range bs { - if *b { - c++ - } - } - return c -} - -func countFlagMap(bms map[string]*bool, bmrxs map[string]*string) int { - var c int - for _, b := range bms { - if *b { - c++ - } - } - for _, s := range bmrxs { - if *s != "" { - c++ - } - } - return c -} - -var usageMsgHdr = "usage: pprof [options] [binary] ...\n" + - "Output format (only set one):\n" - -var usageMsg = "Output file parameters (for file-based output formats):\n" + - " -output=f Generate output on file f (stdout by default)\n" + - "Output granularity (only set one):\n" + - " -functions Report at function level [default]\n" + - " -files Report at source file level\n" + - " -lines Report at source line level\n" + - " -addresses Report at address level\n" + - "Comparison options:\n" + - " -base Show delta from this profile\n" + - " -drop_negative Ignore negative differences\n" + - "Sorting options:\n" + - " -cum Sort by cumulative data\n\n" + - "Dynamic profile options:\n" + - " -seconds=N Length of time for dynamic profiles\n" + - "Profile trimming options:\n" + - " -nodecount=N Max number of nodes to show\n" + - " -nodefraction=f Hide nodes below *total\n" + - " -edgefraction=f Hide edges below *total\n" + - "Sample value selection option (by index):\n" + - " -sample_index Index of sample value to display\n" + - " -mean Average sample value over first value\n" + - "Sample value selection option (for heap profiles):\n" + - " -inuse_space Display in-use memory size\n" + - " -inuse_objects Display in-use object counts\n" + - " -alloc_space Display allocated memory size\n" + - " -alloc_objects Display allocated object counts\n" + - "Sample value selection option (for contention profiles):\n" + - " -total_delay Display total delay at each region\n" + - " -contentions Display number of delays at each region\n" + - " -mean_delay Display mean delay at each region\n" + - "Filtering options:\n" + - " -runtime Show runtime call frames in memory profiles\n" + - " -focus=r Restricts to paths going through a node matching regexp\n" + - " -ignore=r Skips paths going through any nodes matching regexp\n" + - " -tagfocus=r Restrict to samples tagged with key:value matching regexp\n" + - " Restrict to samples with numeric tags in range (eg \"32kb:1mb\")\n" + - " -tagignore=r Discard samples tagged with key:value matching regexp\n" + - " Avoid samples with numeric tags in range (eg \"1mb:\")\n" + - "Miscellaneous:\n" + - " -call_tree Generate a context-sensitive call tree\n" + - " -unit=u Convert all samples to unit u for display\n" + - " -divide_by=f Scale all samples by dividing them by f\n" + - " -buildid=id Override build id for main binary in profile\n" + - " -tools=path Search path for object-level tools\n" + - " -help This message" - -var usageMsgVars = "Environment Variables:\n" + - " PPROF_TMPDIR Location for saved profiles (default $HOME/pprof)\n" + - " PPROF_TOOLS Search path for object-level tools\n" + - " PPROF_BINARY_PATH Search path for local binary files\n" + - " default: $HOME/pprof/binaries\n" + - " finds binaries by $name and $buildid/$name" - -func aggregate(prof *profile.Profile, f *flags) error { - switch { - case f.isFormat("proto"), f.isFormat("raw"): - // No aggregation for raw profiles. - case f.isFormat("callgrind"): - // Aggregate to file/line for callgrind. - fallthrough - case *f.flagLines: - return prof.Aggregate(true, true, true, true, false) - case *f.flagFiles: - return prof.Aggregate(true, false, true, false, false) - case *f.flagFunctions: - return prof.Aggregate(true, true, false, false, false) - case f.isFormat("weblist"), f.isFormat("disasm"): - return prof.Aggregate(false, true, true, true, true) - } - return nil -} - -// parseOptions parses the options into report.Options -// Returns a function to postprocess the report after generation. -func parseOptions(f *flags) (o *report.Options, p commands.PostProcessor, err error) { - - if *f.flagDivideBy == 0 { - return nil, nil, fmt.Errorf("zero divisor specified") - } - - o = &report.Options{ - CumSort: *f.flagCum, - CallTree: *f.flagCallTree, - PrintAddresses: *f.flagAddresses, - DropNegative: *f.flagDropNegative, - Ratio: 1 / *f.flagDivideBy, - - NodeCount: *f.flagNodeCount, - NodeFraction: *f.flagNodeFraction, - EdgeFraction: *f.flagEdgeFraction, - OutputUnit: *f.flagDisplayUnit, - } - - for cmd, b := range f.flagCommands { - if *b { - pcmd := f.commands[cmd] - o.OutputFormat = pcmd.Format - return o, pcmd.PostProcess, nil - } - } - - for cmd, rx := range f.flagParamCommands { - if *rx != "" { - pcmd := f.commands[cmd] - if o.Symbol, err = regexp.Compile(*rx); err != nil { - return nil, nil, fmt.Errorf("parsing -%s regexp: %v", cmd, err) - } - o.OutputFormat = pcmd.Format - return o, pcmd.PostProcess, nil - } - } - - return nil, nil, fmt.Errorf("no output format selected") -} - -type sampleValueFunc func(*profile.Sample) int64 - -// sampleFormat returns a function to extract values out of a profile.Sample, -// and the type/units of those values. -func sampleFormat(p *profile.Profile, f *flags) (sampleValueFunc, string, string) { - valueIndex := *f.flagSampleIndex - - if *f.flagMean { - return meanExtractor(valueIndex), "mean_" + p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit - } - - return valueExtractor(valueIndex), p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit -} - -func valueExtractor(ix int) sampleValueFunc { - return func(s *profile.Sample) int64 { - return s.Value[ix] - } -} - -func meanExtractor(ix int) sampleValueFunc { - return func(s *profile.Sample) int64 { - if s.Value[0] == 0 { - return 0 - } - return s.Value[ix] / s.Value[0] - } -} - -func generate(interactive bool, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error { - o, postProcess, err := parseOptions(f) - if err != nil { - return err - } - - var w io.Writer - if *f.flagOutput == "" { - w = os.Stdout - } else { - ui.PrintErr("Generating report in ", *f.flagOutput) - outputFile, err := os.Create(*f.flagOutput) - if err != nil { - return err - } - defer outputFile.Close() - w = outputFile - } - - if prof.Empty() { - return fmt.Errorf("profile is empty") - } - - value, stype, unit := sampleFormat(prof, f) - o.SampleType = stype - rpt := report.New(prof, *o, value, unit) - - // Do not apply filters if we're just generating a proto, so we - // still have all the data. - if o.OutputFormat != report.Proto { - // Delay applying focus/ignore until after creating the report so - // the report reflects the total number of samples. - if err := preprocess(prof, ui, f); err != nil { - return err - } - } - - if postProcess == nil { - return report.Generate(w, rpt, obj) - } - - var dot bytes.Buffer - if err = report.Generate(&dot, rpt, obj); err != nil { - return err - } - - return postProcess(&dot, w, ui) -} diff --git a/src/cmd/pprof/internal/driver/interactive.go b/src/cmd/pprof/internal/driver/interactive.go deleted file mode 100644 index 13009bf7e9..0000000000 --- a/src/cmd/pprof/internal/driver/interactive.go +++ /dev/null @@ -1,492 +0,0 @@ -// Copyright 2014 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 driver - -import ( - "fmt" - "io" - "regexp" - "sort" - "strconv" - "strings" - - "cmd/pprof/internal/commands" - "cmd/pprof/internal/plugin" - "cmd/pprof/internal/profile" -) - -var profileFunctionNames = []string{} - -// functionCompleter replaces provided substring with a function -// name retrieved from a profile if a single match exists. Otherwise, -// it returns unchanged substring. It defaults to no-op if the profile -// is not specified. -func functionCompleter(substring string) string { - found := "" - for _, fName := range profileFunctionNames { - if strings.Contains(fName, substring) { - if found != "" { - return substring - } - found = fName - } - } - if found != "" { - return found - } - return substring -} - -// updateAutoComplete enhances autocompletion with information that can be -// retrieved from the profile -func updateAutoComplete(p *profile.Profile) { - profileFunctionNames = nil // remove function names retrieved previously - for _, fn := range p.Function { - profileFunctionNames = append(profileFunctionNames, fn.Name) - } -} - -// splitCommand splits the command line input into tokens separated by -// spaces. Takes care to separate commands of the form 'top10' into -// two tokens: 'top' and '10' -func splitCommand(input string) []string { - fields := strings.Fields(input) - if num := strings.IndexAny(fields[0], "0123456789"); num != -1 { - inputNumber := fields[0][num:] - fields[0] = fields[0][:num] - fields = append([]string{fields[0], inputNumber}, fields[1:]...) - } - return fields -} - -// interactive displays a prompt and reads commands for profile -// manipulation/visualization. -func interactive(p *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error { - updateAutoComplete(p) - - // Enter command processing loop. - ui.Print("Entering interactive mode (type \"help\" for commands)") - ui.SetAutoComplete(commands.NewCompleter(f.commands)) - - for { - input, err := readCommand(p, ui, f) - if err != nil { - if err != io.EOF { - return err - } - if input == "" { - return nil - } - } - // Process simple commands. - switch input { - case "": - continue - case ":": - f.flagFocus = newString("") - f.flagIgnore = newString("") - f.flagTagFocus = newString("") - f.flagTagIgnore = newString("") - f.flagHide = newString("") - continue - } - - fields := splitCommand(input) - // Process report generation commands. - if _, ok := f.commands[fields[0]]; ok { - if err := generateReport(p, fields, obj, ui, f); err != nil { - if err == io.EOF { - return nil - } - ui.PrintErr(err) - } - continue - } - - switch cmd := fields[0]; cmd { - case "help": - commandHelp(fields, ui, f) - continue - case "exit", "quit": - return nil - } - - // Process option settings. - if of, err := optFlags(p, input, f); err == nil { - f = of - } else { - ui.PrintErr("Error: ", err.Error()) - } - } -} - -func generateReport(p *profile.Profile, cmd []string, obj plugin.ObjTool, ui plugin.UI, f *flags) error { - prof := p.Copy() - - cf, err := cmdFlags(prof, cmd, ui, f) - if err != nil { - return err - } - - return generate(true, prof, obj, ui, cf) -} - -// validateRegex checks if a string is a valid regular expression. -func validateRegex(v string) error { - _, err := regexp.Compile(v) - return err -} - -// readCommand prompts for and reads the next command. -func readCommand(p *profile.Profile, ui plugin.UI, f *flags) (string, error) { - //ui.Print("Options:\n", f.String(p)) - s, err := ui.ReadLine() - return strings.TrimSpace(s), err -} - -func commandHelp(_ []string, ui plugin.UI, f *flags) error { - help := ` - Commands: - cmd [n] [--cum] [focus_regex]* [-ignore_regex]* - Produce a text report with the top n entries. - Include samples matching focus_regex, and exclude ignore_regex. - Add --cum to sort using cumulative data. - Available commands: -` - var commands []string - for name, cmd := range f.commands { - commands = append(commands, fmt.Sprintf(" %-12s %s", name, cmd.Usage)) - } - sort.Strings(commands) - - help = help + strings.Join(commands, "\n") + ` - peek func_regex - Display callers and callees of functions matching func_regex. - - dot [n] [focus_regex]* [-ignore_regex]* [>file] - Produce an annotated callgraph with the top n entries. - Include samples matching focus_regex, and exclude ignore_regex. - For other outputs, replace dot with: - - Graphic formats: dot, svg, pdf, ps, gif, png (use > to name output file) - - Graph viewer: gv, web, evince, eog - - callgrind [n] [focus_regex]* [-ignore_regex]* [>file] - Produce a file in callgrind-compatible format. - Include samples matching focus_regex, and exclude ignore_regex. - - weblist func_regex [-ignore_regex]* - Show annotated source with interspersed assembly in a web browser. - - list func_regex [-ignore_regex]* - Print source for routines matching func_regex, and exclude ignore_regex. - - disasm func_regex [-ignore_regex]* - Disassemble routines matching func_regex, and exclude ignore_regex. - - tags tag_regex [-ignore_regex]* - List tags with key:value matching tag_regex and exclude ignore_regex. - - quit/exit/^D - Exit pprof. - - option=value - The following options can be set individually: - cum/flat: Sort entries based on cumulative or flat data - call_tree: Build context-sensitive call trees - nodecount: Max number of entries to display - nodefraction: Min frequency ratio of nodes to display - edgefraction: Min frequency ratio of edges to display - focus/ignore: Regexp to include/exclude samples by name/file - tagfocus/tagignore: Regexp or value range to filter samples by tag - eg "1mb", "1mb:2mb", ":64kb" - - functions: Level of aggregation for sample data - files: - lines: - addresses: - - unit: Measurement unit to use on reports - - Sample value selection by index: - sample_index: Index of sample value to display - mean: Average sample value over first value - - Sample value selection by name: - alloc_space for heap profiles - alloc_objects - inuse_space - inuse_objects - - total_delay for contention profiles - mean_delay - contentions - - : Clear focus/ignore/hide/tagfocus/tagignore` - - ui.Print(help) - return nil -} - -// cmdFlags parses the options of an interactive command and returns -// an updated flags object. -func cmdFlags(prof *profile.Profile, input []string, ui plugin.UI, f *flags) (*flags, error) { - cf := *f - - var focus, ignore string - output := *cf.flagOutput - nodeCount := *cf.flagNodeCount - cmd := input[0] - - // Update output flags based on parameters. - tokens := input[1:] - for p := 0; p < len(tokens); p++ { - t := tokens[p] - if t == "" { - continue - } - if c, err := strconv.ParseInt(t, 10, 32); err == nil { - nodeCount = int(c) - continue - } - switch t[0] { - case '>': - if len(t) > 1 { - output = t[1:] - continue - } - // find next token - for p++; p < len(tokens); p++ { - if tokens[p] != "" { - output = tokens[p] - break - } - } - case '-': - if t == "--cum" || t == "-cum" { - cf.flagCum = newBool(true) - continue - } - ignore = catRegex(ignore, t[1:]) - default: - focus = catRegex(focus, t) - } - } - - pcmd, ok := f.commands[cmd] - if !ok { - return nil, fmt.Errorf("Unexpected parse failure: %v", input) - } - // Reset flags - cf.flagCommands = make(map[string]*bool) - cf.flagParamCommands = make(map[string]*string) - - if !pcmd.HasParam { - cf.flagCommands[cmd] = newBool(true) - - switch cmd { - case "tags": - cf.flagTagFocus = newString(focus) - cf.flagTagIgnore = newString(ignore) - default: - cf.flagFocus = newString(catRegex(*cf.flagFocus, focus)) - cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore)) - } - } else { - if focus == "" { - focus = "." - } - cf.flagParamCommands[cmd] = newString(focus) - cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore)) - } - - if nodeCount < 0 { - switch cmd { - case "text", "top": - // Default text/top to 10 nodes on interactive mode - nodeCount = 10 - default: - nodeCount = 80 - } - } - - cf.flagNodeCount = newInt(nodeCount) - cf.flagOutput = newString(output) - - // Do regular flags processing - if err := processFlags(prof, ui, &cf); err != nil { - cf.usage(ui) - return nil, err - } - - return &cf, nil -} - -func catRegex(a, b string) string { - if a == "" { - return b - } - if b == "" { - return a - } - return a + "|" + b -} - -// optFlags parses an interactive option setting and returns -// an updated flags object. -func optFlags(p *profile.Profile, input string, f *flags) (*flags, error) { - inputs := strings.SplitN(input, "=", 2) - option := strings.ToLower(strings.TrimSpace(inputs[0])) - var value string - if len(inputs) == 2 { - value = strings.TrimSpace(inputs[1]) - } - - of := *f - - var err error - var bv bool - var uv uint64 - var fv float64 - - switch option { - case "cum": - if bv, err = parseBool(value); err != nil { - return nil, err - } - of.flagCum = newBool(bv) - case "flat": - if bv, err = parseBool(value); err != nil { - return nil, err - } - of.flagCum = newBool(!bv) - case "call_tree": - if bv, err = parseBool(value); err != nil { - return nil, err - } - of.flagCallTree = newBool(bv) - case "unit": - of.flagDisplayUnit = newString(value) - case "sample_index": - if uv, err = strconv.ParseUint(value, 10, 32); err != nil { - return nil, err - } - if ix := int(uv); ix < 0 || ix >= len(p.SampleType) { - return nil, fmt.Errorf("sample_index out of range [0..%d]", len(p.SampleType)-1) - } - of.flagSampleIndex = newInt(int(uv)) - case "mean": - if bv, err = parseBool(value); err != nil { - return nil, err - } - of.flagMean = newBool(bv) - case "nodecount": - if uv, err = strconv.ParseUint(value, 10, 32); err != nil { - return nil, err - } - of.flagNodeCount = newInt(int(uv)) - case "nodefraction": - if fv, err = strconv.ParseFloat(value, 64); err != nil { - return nil, err - } - of.flagNodeFraction = newFloat64(fv) - case "edgefraction": - if fv, err = strconv.ParseFloat(value, 64); err != nil { - return nil, err - } - of.flagEdgeFraction = newFloat64(fv) - case "focus": - if err = validateRegex(value); err != nil { - return nil, err - } - of.flagFocus = newString(value) - case "ignore": - if err = validateRegex(value); err != nil { - return nil, err - } - of.flagIgnore = newString(value) - case "tagfocus": - if err = validateRegex(value); err != nil { - return nil, err - } - of.flagTagFocus = newString(value) - case "tagignore": - if err = validateRegex(value); err != nil { - return nil, err - } - of.flagTagIgnore = newString(value) - case "hide": - if err = validateRegex(value); err != nil { - return nil, err - } - of.flagHide = newString(value) - case "addresses", "files", "lines", "functions": - if bv, err = parseBool(value); err != nil { - return nil, err - } - if !bv { - return nil, fmt.Errorf("select one of addresses/files/lines/functions") - } - setGranularityToggle(option, &of) - default: - if ix := findSampleIndex(p, "", option); ix >= 0 { - of.flagSampleIndex = newInt(ix) - } else if ix := findSampleIndex(p, "total_", option); ix >= 0 { - of.flagSampleIndex = newInt(ix) - of.flagMean = newBool(false) - } else if ix := findSampleIndex(p, "mean_", option); ix >= 1 { - of.flagSampleIndex = newInt(ix) - of.flagMean = newBool(true) - } else { - return nil, fmt.Errorf("unrecognized command: %s", input) - } - } - return &of, nil -} - -// parseBool parses a string as a boolean value. -func parseBool(v string) (bool, error) { - switch strings.ToLower(v) { - case "true", "t", "yes", "y", "1", "": - return true, nil - case "false", "f", "no", "n", "0": - return false, nil - } - return false, fmt.Errorf(`illegal input "%s" for bool value`, v) -} - -func findSampleIndex(p *profile.Profile, prefix, sampleType string) int { - if !strings.HasPrefix(sampleType, prefix) { - return -1 - } - sampleType = strings.TrimPrefix(sampleType, prefix) - for i, r := range p.SampleType { - if r.Type == sampleType { - return i - } - } - return -1 -} - -// setGranularityToggle manages the set of granularity options. These -// operate as a toggle; turning one on turns the others off. -func setGranularityToggle(o string, fl *flags) { - t, f := newBool(true), newBool(false) - fl.flagFunctions = f - fl.flagFiles = f - fl.flagLines = f - fl.flagAddresses = f - switch o { - case "functions": - fl.flagFunctions = t - case "files": - fl.flagFiles = t - case "lines": - fl.flagLines = t - case "addresses": - fl.flagAddresses = t - default: - panic(fmt.Errorf("unexpected option %s", o)) - } -} diff --git a/src/cmd/pprof/internal/fetch/fetch.go b/src/cmd/pprof/internal/fetch/fetch.go deleted file mode 100644 index ec4a6383c6..0000000000 --- a/src/cmd/pprof/internal/fetch/fetch.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2014 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 fetch provides an extensible mechanism to fetch a profile -// from a data source. -package fetch - -import ( - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "strings" - "time" - - "cmd/pprof/internal/plugin" - "cmd/pprof/internal/profile" -) - -// FetchProfile reads from a data source (network, file) and generates a -// profile. -func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) { - return Fetcher(source, timeout, plugin.StandardUI()) -} - -// Fetcher is the plugin.Fetcher version of FetchProfile. -func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) { - var f io.ReadCloser - var err error - - url, err := url.Parse(source) - if err == nil && url.Host != "" { - f, err = FetchURL(source, timeout) - } else { - f, err = os.Open(source) - } - if err != nil { - return nil, err - } - defer f.Close() - return profile.Parse(f) -} - -// FetchURL fetches a profile from a URL using HTTP. -func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) { - resp, err := httpGet(source, timeout) - if err != nil { - return nil, fmt.Errorf("http fetch %s: %v", source, err) - } - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("server response: %s", resp.Status) - } - - return resp.Body, nil -} - -// PostURL issues a POST to a URL over HTTP. -func PostURL(source, post string) ([]byte, error) { - resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post)) - if err != nil { - return nil, fmt.Errorf("http post %s: %v", source, err) - } - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("server response: %s", resp.Status) - } - defer resp.Body.Close() - return ioutil.ReadAll(resp.Body) -} - -// httpGet is a wrapper around http.Get; it is defined as a variable -// so it can be redefined during for testing. -var httpGet = func(url string, timeout time.Duration) (*http.Response, error) { - client := &http.Client{ - Transport: &http.Transport{ - ResponseHeaderTimeout: timeout + 5*time.Second, - }, - } - return client.Get(url) -} diff --git a/src/cmd/pprof/internal/plugin/plugin.go b/src/cmd/pprof/internal/plugin/plugin.go deleted file mode 100644 index a22ec5f3c5..0000000000 --- a/src/cmd/pprof/internal/plugin/plugin.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2014 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 plugin defines the plugin implementations that the main pprof driver requires. -package plugin - -import ( - "bufio" - "fmt" - "os" - "regexp" - "strings" - "time" - - "cmd/pprof/internal/profile" -) - -// A FlagSet creates and parses command-line flags. -// It is similar to the standard flag.FlagSet. -type FlagSet interface { - // Bool, Int, Float64, and String define new flags, - // like the functions of the same name in package flag. - Bool(name string, def bool, usage string) *bool - Int(name string, def int, usage string) *int - Float64(name string, def float64, usage string) *float64 - String(name string, def string, usage string) *string - - // ExtraUsage returns any additional text that should be - // printed after the standard usage message. - // The typical use of ExtraUsage is to show any custom flags - // defined by the specific pprof plugins being used. - ExtraUsage() string - - // Parse initializes the flags with their values for this run - // and returns the non-flag command line arguments. - // If an unknown flag is encountered or there are no arguments, - // Parse should call usage and return nil. - Parse(usage func()) []string -} - -// An ObjTool inspects shared libraries and executable files. -type ObjTool interface { - // Open opens the named object file. - // If the object is a shared library, start is the address where - // it is mapped into memory in the address space being inspected. - Open(file string, start uint64) (ObjFile, error) - - // Demangle translates a batch of symbol names from mangled - // form to human-readable form. - Demangle(names []string) (map[string]string, error) - - // Disasm disassembles the named object file, starting at - // the start address and stopping at (before) the end address. - Disasm(file string, start, end uint64) ([]Inst, error) - - // SetConfig configures the tool. - // The implementation defines the meaning of the string - // and can ignore it entirely. - SetConfig(config string) -} - -// NoObjTool returns a trivial implementation of the ObjTool interface. -// Open returns an error indicating that the requested file does not exist. -// Demangle returns an empty map and a nil error. -// Disasm returns an error. -// SetConfig is a no-op. -func NoObjTool() ObjTool { - return noObjTool{} -} - -type noObjTool struct{} - -func (noObjTool) Open(file string, start uint64) (ObjFile, error) { - return nil, &os.PathError{Op: "open", Path: file, Err: os.ErrNotExist} -} - -func (noObjTool) Demangle(name []string) (map[string]string, error) { - return make(map[string]string), nil -} - -func (noObjTool) Disasm(file string, start, end uint64) ([]Inst, error) { - return nil, fmt.Errorf("disassembly not supported") -} - -func (noObjTool) SetConfig(config string) { -} - -// An ObjFile is a single object file: a shared library or executable. -type ObjFile interface { - // Name returns the underlyinf file name, if available - Name() string - - // Base returns the base address to use when looking up symbols in the file. - Base() uint64 - - // BuildID returns the GNU build ID of the file, or an empty string. - BuildID() string - - // SourceLine reports the source line information for a given - // address in the file. Due to inlining, the source line information - // is in general a list of positions representing a call stack, - // with the leaf function first. - SourceLine(addr uint64) ([]Frame, error) - - // Symbols returns a list of symbols in the object file. - // If r is not nil, Symbols restricts the list to symbols - // with names matching the regular expression. - // If addr is not zero, Symbols restricts the list to symbols - // containing that address. - Symbols(r *regexp.Regexp, addr uint64) ([]*Sym, error) - - // Close closes the file, releasing associated resources. - Close() error -} - -// A Frame describes a single line in a source file. -type Frame struct { - Func string // name of function - File string // source file name - Line int // line in file -} - -// A Sym describes a single symbol in an object file. -type Sym struct { - Name []string // names of symbol (many if symbol was dedup'ed) - File string // object file containing symbol - Start uint64 // start virtual address - End uint64 // virtual address of last byte in sym (Start+size-1) -} - -// An Inst is a single instruction in an assembly listing. -type Inst struct { - Addr uint64 // virtual address of instruction - Text string // instruction text - File string // source file - Line int // source line -} - -// A UI manages user interactions. -type UI interface { - // Read returns a line of text (a command) read from the user. - ReadLine() (string, error) - - // Print shows a message to the user. - // It formats the text as fmt.Print would and adds a final \n if not already present. - // For line-based UI, Print writes to standard error. - // (Standard output is reserved for report data.) - Print(...interface{}) - - // PrintErr shows an error message to the user. - // It formats the text as fmt.Print would and adds a final \n if not already present. - // For line-based UI, PrintErr writes to standard error. - PrintErr(...interface{}) - - // IsTerminal returns whether the UI is known to be tied to an - // interactive terminal (as opposed to being redirected to a file). - IsTerminal() bool - - // SetAutoComplete instructs the UI to call complete(cmd) to obtain - // the auto-completion of cmd, if the UI supports auto-completion at all. - SetAutoComplete(complete func(string) string) -} - -// StandardUI returns a UI that reads from standard input, -// prints messages to standard output, -// prints errors to standard error, and doesn't use auto-completion. -func StandardUI() UI { - return &stdUI{r: bufio.NewReader(os.Stdin)} -} - -type stdUI struct { - r *bufio.Reader -} - -func (ui *stdUI) ReadLine() (string, error) { - os.Stdout.WriteString("(pprof) ") - return ui.r.ReadString('\n') -} - -func (ui *stdUI) Print(args ...interface{}) { - ui.fprint(os.Stderr, args) -} - -func (ui *stdUI) PrintErr(args ...interface{}) { - ui.fprint(os.Stderr, args) -} - -func (ui *stdUI) IsTerminal() bool { - return false -} - -func (ui *stdUI) SetAutoComplete(func(string) string) { -} - -func (ui *stdUI) fprint(f *os.File, args []interface{}) { - text := fmt.Sprint(args...) - if !strings.HasSuffix(text, "\n") { - text += "\n" - } - f.WriteString(text) -} - -// A Fetcher reads and returns the profile named by src. -// It gives up after the given timeout, unless src contains a timeout override -// (as defined by the implementation). -// It can print messages to ui. -type Fetcher func(src string, timeout time.Duration, ui UI) (*profile.Profile, error) - -// A Symbolizer annotates a profile with symbol information. -// The profile was fetch from src. -// The meaning of mode is defined by the implementation. -type Symbolizer func(mode, src string, prof *profile.Profile, obj ObjTool, ui UI) error diff --git a/src/cmd/pprof/internal/profile/encode.go b/src/cmd/pprof/internal/profile/encode.go deleted file mode 100644 index 6b879a84ac..0000000000 --- a/src/cmd/pprof/internal/profile/encode.go +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2014 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 profile - -import ( - "errors" - "fmt" - "sort" -) - -func (p *Profile) decoder() []decoder { - return profileDecoder -} - -// preEncode populates the unexported fields to be used by encode -// (with suffix X) from the corresponding exported fields. The -// exported fields are cleared up to facilitate testing. -func (p *Profile) preEncode() { - strings := make(map[string]int) - addString(strings, "") - - for _, st := range p.SampleType { - st.typeX = addString(strings, st.Type) - st.unitX = addString(strings, st.Unit) - } - - for _, s := range p.Sample { - s.labelX = nil - var keys []string - for k := range s.Label { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - vs := s.Label[k] - for _, v := range vs { - s.labelX = append(s.labelX, - Label{ - keyX: addString(strings, k), - strX: addString(strings, v), - }, - ) - } - } - var numKeys []string - for k := range s.NumLabel { - numKeys = append(numKeys, k) - } - sort.Strings(numKeys) - for _, k := range numKeys { - vs := s.NumLabel[k] - for _, v := range vs { - s.labelX = append(s.labelX, - Label{ - keyX: addString(strings, k), - numX: v, - }, - ) - } - } - s.locationIDX = nil - for _, l := range s.Location { - s.locationIDX = append(s.locationIDX, l.ID) - } - } - - for _, m := range p.Mapping { - m.fileX = addString(strings, m.File) - m.buildIDX = addString(strings, m.BuildID) - } - - for _, l := range p.Location { - for i, ln := range l.Line { - if ln.Function != nil { - l.Line[i].functionIDX = ln.Function.ID - } else { - l.Line[i].functionIDX = 0 - } - } - if l.Mapping != nil { - l.mappingIDX = l.Mapping.ID - } else { - l.mappingIDX = 0 - } - } - for _, f := range p.Function { - f.nameX = addString(strings, f.Name) - f.systemNameX = addString(strings, f.SystemName) - f.filenameX = addString(strings, f.Filename) - } - - p.dropFramesX = addString(strings, p.DropFrames) - p.keepFramesX = addString(strings, p.KeepFrames) - - if pt := p.PeriodType; pt != nil { - pt.typeX = addString(strings, pt.Type) - pt.unitX = addString(strings, pt.Unit) - } - - p.stringTable = make([]string, len(strings)) - for s, i := range strings { - p.stringTable[i] = s - } -} - -func (p *Profile) encode(b *buffer) { - for _, x := range p.SampleType { - encodeMessage(b, 1, x) - } - for _, x := range p.Sample { - encodeMessage(b, 2, x) - } - for _, x := range p.Mapping { - encodeMessage(b, 3, x) - } - for _, x := range p.Location { - encodeMessage(b, 4, x) - } - for _, x := range p.Function { - encodeMessage(b, 5, x) - } - encodeStrings(b, 6, p.stringTable) - encodeInt64Opt(b, 7, p.dropFramesX) - encodeInt64Opt(b, 8, p.keepFramesX) - encodeInt64Opt(b, 9, p.TimeNanos) - encodeInt64Opt(b, 10, p.DurationNanos) - if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) { - encodeMessage(b, 11, p.PeriodType) - } - encodeInt64Opt(b, 12, p.Period) -} - -var profileDecoder = []decoder{ - nil, // 0 - // repeated ValueType sample_type = 1 - func(b *buffer, m message) error { - x := new(ValueType) - pp := m.(*Profile) - pp.SampleType = append(pp.SampleType, x) - return decodeMessage(b, x) - }, - // repeated Sample sample = 2 - func(b *buffer, m message) error { - x := new(Sample) - pp := m.(*Profile) - pp.Sample = append(pp.Sample, x) - return decodeMessage(b, x) - }, - // repeated Mapping mapping = 3 - func(b *buffer, m message) error { - x := new(Mapping) - pp := m.(*Profile) - pp.Mapping = append(pp.Mapping, x) - return decodeMessage(b, x) - }, - // repeated Location location = 4 - func(b *buffer, m message) error { - x := new(Location) - pp := m.(*Profile) - pp.Location = append(pp.Location, x) - return decodeMessage(b, x) - }, - // repeated Function function = 5 - func(b *buffer, m message) error { - x := new(Function) - pp := m.(*Profile) - pp.Function = append(pp.Function, x) - return decodeMessage(b, x) - }, - // repeated string string_table = 6 - func(b *buffer, m message) error { - err := decodeStrings(b, &m.(*Profile).stringTable) - if err != nil { - return err - } - if *&m.(*Profile).stringTable[0] != "" { - return errors.New("string_table[0] must be ''") - } - return nil - }, - // repeated int64 drop_frames = 7 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) }, - // repeated int64 keep_frames = 8 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) }, - // repeated int64 time_nanos = 9 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).TimeNanos) }, - // repeated int64 duration_nanos = 10 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) }, - // optional string period_type = 11 - func(b *buffer, m message) error { - x := new(ValueType) - pp := m.(*Profile) - pp.PeriodType = x - return decodeMessage(b, x) - }, - // repeated int64 period = 12 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) }, -} - -// postDecode takes the unexported fields populated by decode (with -// suffix X) and populates the corresponding exported fields. -// The unexported fields are cleared up to facilitate testing. -func (p *Profile) postDecode() error { - var err error - - mappings := make(map[uint64]*Mapping) - for _, m := range p.Mapping { - m.File, err = getString(p.stringTable, &m.fileX, err) - m.BuildID, err = getString(p.stringTable, &m.buildIDX, err) - mappings[m.ID] = m - } - - functions := make(map[uint64]*Function) - for _, f := range p.Function { - f.Name, err = getString(p.stringTable, &f.nameX, err) - f.SystemName, err = getString(p.stringTable, &f.systemNameX, err) - f.Filename, err = getString(p.stringTable, &f.filenameX, err) - functions[f.ID] = f - } - - locations := make(map[uint64]*Location) - for _, l := range p.Location { - l.Mapping = mappings[l.mappingIDX] - l.mappingIDX = 0 - for i, ln := range l.Line { - if id := ln.functionIDX; id != 0 { - l.Line[i].Function = functions[id] - if l.Line[i].Function == nil { - return fmt.Errorf("Function ID %d not found", id) - } - l.Line[i].functionIDX = 0 - } - } - locations[l.ID] = l - } - - for _, st := range p.SampleType { - st.Type, err = getString(p.stringTable, &st.typeX, err) - st.Unit, err = getString(p.stringTable, &st.unitX, err) - } - - for _, s := range p.Sample { - labels := make(map[string][]string) - numLabels := make(map[string][]int64) - for _, l := range s.labelX { - var key, value string - key, err = getString(p.stringTable, &l.keyX, err) - if l.strX != 0 { - value, err = getString(p.stringTable, &l.strX, err) - labels[key] = append(labels[key], value) - } else { - numLabels[key] = append(numLabels[key], l.numX) - } - } - if len(labels) > 0 { - s.Label = labels - } - if len(numLabels) > 0 { - s.NumLabel = numLabels - } - s.Location = nil - for _, lid := range s.locationIDX { - s.Location = append(s.Location, locations[lid]) - } - s.locationIDX = nil - } - - p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err) - p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err) - - if pt := p.PeriodType; pt == nil { - p.PeriodType = &ValueType{} - } - - if pt := p.PeriodType; pt != nil { - pt.Type, err = getString(p.stringTable, &pt.typeX, err) - pt.Unit, err = getString(p.stringTable, &pt.unitX, err) - } - p.stringTable = nil - return nil -} - -func (p *ValueType) decoder() []decoder { - return valueTypeDecoder -} - -func (p *ValueType) encode(b *buffer) { - encodeInt64Opt(b, 1, p.typeX) - encodeInt64Opt(b, 2, p.unitX) -} - -var valueTypeDecoder = []decoder{ - nil, // 0 - // optional int64 type = 1 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) }, - // optional int64 unit = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) }, -} - -func (p *Sample) decoder() []decoder { - return sampleDecoder -} - -func (p *Sample) encode(b *buffer) { - encodeUint64s(b, 1, p.locationIDX) - for _, x := range p.Value { - encodeInt64(b, 2, x) - } - for _, x := range p.labelX { - encodeMessage(b, 3, x) - } -} - -var sampleDecoder = []decoder{ - nil, // 0 - // repeated uint64 location = 1 - func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) }, - // repeated int64 value = 2 - func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) }, - // repeated Label label = 3 - func(b *buffer, m message) error { - s := m.(*Sample) - n := len(s.labelX) - s.labelX = append(s.labelX, Label{}) - return decodeMessage(b, &s.labelX[n]) - }, -} - -func (p Label) decoder() []decoder { - return labelDecoder -} - -func (p Label) encode(b *buffer) { - encodeInt64Opt(b, 1, p.keyX) - encodeInt64Opt(b, 2, p.strX) - encodeInt64Opt(b, 3, p.numX) -} - -var labelDecoder = []decoder{ - nil, // 0 - // optional int64 key = 1 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).keyX) }, - // optional int64 str = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).strX) }, - // optional int64 num = 3 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Label).numX) }, -} - -func (p *Mapping) decoder() []decoder { - return mappingDecoder -} - -func (p *Mapping) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeUint64Opt(b, 2, p.Start) - encodeUint64Opt(b, 3, p.Limit) - encodeUint64Opt(b, 4, p.Offset) - encodeInt64Opt(b, 5, p.fileX) - encodeInt64Opt(b, 6, p.buildIDX) - encodeBoolOpt(b, 7, p.HasFunctions) - encodeBoolOpt(b, 8, p.HasFilenames) - encodeBoolOpt(b, 9, p.HasLineNumbers) - encodeBoolOpt(b, 10, p.HasInlineFrames) -} - -var mappingDecoder = []decoder{ - nil, // 0 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10 -} - -func (p *Location) decoder() []decoder { - return locationDecoder -} - -func (p *Location) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeUint64Opt(b, 2, p.mappingIDX) - encodeUint64Opt(b, 3, p.Address) - for i := range p.Line { - encodeMessage(b, 4, &p.Line[i]) - } -} - -var locationDecoder = []decoder{ - nil, // 0 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1; - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2; - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3; - func(b *buffer, m message) error { // repeated Line line = 4 - pp := m.(*Location) - n := len(pp.Line) - pp.Line = append(pp.Line, Line{}) - return decodeMessage(b, &pp.Line[n]) - }, -} - -func (p *Line) decoder() []decoder { - return lineDecoder -} - -func (p *Line) encode(b *buffer) { - encodeUint64Opt(b, 1, p.functionIDX) - encodeInt64Opt(b, 2, p.Line) -} - -var lineDecoder = []decoder{ - nil, // 0 - // optional uint64 function_id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) }, - // optional int64 line = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) }, -} - -func (p *Function) decoder() []decoder { - return functionDecoder -} - -func (p *Function) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeInt64Opt(b, 2, p.nameX) - encodeInt64Opt(b, 3, p.systemNameX) - encodeInt64Opt(b, 4, p.filenameX) - encodeInt64Opt(b, 5, p.StartLine) -} - -var functionDecoder = []decoder{ - nil, // 0 - // optional uint64 id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) }, - // optional int64 function_name = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) }, - // optional int64 function_system_name = 3 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) }, - // repeated int64 filename = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) }, - // optional int64 start_line = 5 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) }, -} - -func addString(strings map[string]int, s string) int64 { - i, ok := strings[s] - if !ok { - i = len(strings) - strings[s] = i - } - return int64(i) -} - -func getString(strings []string, strng *int64, err error) (string, error) { - if err != nil { - return "", err - } - s := int(*strng) - if s < 0 || s >= len(strings) { - return "", errMalformed - } - *strng = 0 - return strings[s], nil -} diff --git a/src/cmd/pprof/internal/profile/filter.go b/src/cmd/pprof/internal/profile/filter.go deleted file mode 100644 index 1baa096a49..0000000000 --- a/src/cmd/pprof/internal/profile/filter.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2014 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. - -// Implements methods to filter samples from profiles. - -package profile - -import "regexp" - -// FilterSamplesByName filters the samples in a profile and only keeps -// samples where at least one frame matches focus but none match ignore. -// Returns true is the corresponding regexp matched at least one sample. -func (p *Profile) FilterSamplesByName(focus, ignore, hide *regexp.Regexp) (fm, im, hm bool) { - focusOrIgnore := make(map[uint64]bool) - hidden := make(map[uint64]bool) - for _, l := range p.Location { - if ignore != nil && l.matchesName(ignore) { - im = true - focusOrIgnore[l.ID] = false - } else if focus == nil || l.matchesName(focus) { - fm = true - focusOrIgnore[l.ID] = true - } - if hide != nil && l.matchesName(hide) { - hm = true - l.Line = l.unmatchedLines(hide) - if len(l.Line) == 0 { - hidden[l.ID] = true - } - } - } - - s := make([]*Sample, 0, len(p.Sample)) - for _, sample := range p.Sample { - if focusedAndNotIgnored(sample.Location, focusOrIgnore) { - if len(hidden) > 0 { - var locs []*Location - for _, loc := range sample.Location { - if !hidden[loc.ID] { - locs = append(locs, loc) - } - } - if len(locs) == 0 { - // Remove sample with no locations (by not adding it to s). - continue - } - sample.Location = locs - } - s = append(s, sample) - } - } - p.Sample = s - - return -} - -// matchesName returns whether the function name or file in the -// location matches the regular expression. -func (loc *Location) matchesName(re *regexp.Regexp) bool { - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if re.MatchString(fn.Name) { - return true - } - if re.MatchString(fn.Filename) { - return true - } - } - } - return false -} - -// unmatchedLines returns the lines in the location that do not match -// the regular expression. -func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line { - var lines []Line - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if re.MatchString(fn.Name) { - continue - } - if re.MatchString(fn.Filename) { - continue - } - } - lines = append(lines, ln) - } - return lines -} - -// focusedAndNotIgnored looks up a slice of ids against a map of -// focused/ignored locations. The map only contains locations that are -// explicitly focused or ignored. Returns whether there is at least -// one focused location but no ignored locations. -func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool { - var f bool - for _, loc := range locs { - if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore { - if focus { - // Found focused location. Must keep searching in case there - // is an ignored one as well. - f = true - } else { - // Found ignored location. Can return false right away. - return false - } - } - } - return f -} - -// TagMatch selects tags for filtering -type TagMatch func(key, val string, nval int64) bool - -// FilterSamplesByTag removes all samples from the profile, except -// those that match focus and do not match the ignore regular -// expression. -func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) { - samples := make([]*Sample, 0, len(p.Sample)) - for _, s := range p.Sample { - focused, ignored := focusedSample(s, focus, ignore) - fm = fm || focused - im = im || ignored - if focused && !ignored { - samples = append(samples, s) - } - } - p.Sample = samples - return -} - -// focusedTag checks a sample against focus and ignore regexps. -// Returns whether the focus/ignore regexps match any tags -func focusedSample(s *Sample, focus, ignore TagMatch) (fm, im bool) { - fm = focus == nil - for key, vals := range s.Label { - for _, val := range vals { - if ignore != nil && ignore(key, val, 0) { - im = true - } - if !fm && focus(key, val, 0) { - fm = true - } - } - } - for key, vals := range s.NumLabel { - for _, val := range vals { - if ignore != nil && ignore(key, "", val) { - im = true - } - if !fm && focus(key, "", val) { - fm = true - } - } - } - return fm, im -} diff --git a/src/cmd/pprof/internal/profile/legacy_profile.go b/src/cmd/pprof/internal/profile/legacy_profile.go deleted file mode 100644 index e1f24c4c6d..0000000000 --- a/src/cmd/pprof/internal/profile/legacy_profile.go +++ /dev/null @@ -1,1251 +0,0 @@ -// Copyright 2014 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. - -// This file implements parsers to convert legacy profiles into the -// profile.proto format. - -package profile - -import ( - "bufio" - "bytes" - "fmt" - "io" - "math" - "regexp" - "strconv" - "strings" -) - -var ( - countStartRE = regexp.MustCompile(`\A(\w+) profile: total \d+\n\z`) - countRE = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\n\z`) - - heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`) - heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`) - - contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`) - - hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`) - - growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz`) - - fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz`) - - threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`) - threadStartRE = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`) - - procMapsRE = regexp.MustCompile(`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?`) - - briefMapsRE = regexp.MustCompile(`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?`) - - // LegacyHeapAllocated instructs the heapz parsers to use the - // allocated memory stats instead of the default in-use memory. Note - // that tcmalloc doesn't provide all allocated memory, only in-use - // stats. - LegacyHeapAllocated bool -) - -func isSpaceOrComment(line string) bool { - trimmed := strings.TrimSpace(line) - return len(trimmed) == 0 || trimmed[0] == '#' -} - -// parseGoCount parses a Go count profile (e.g., threadcreate or -// goroutine) and returns a new Profile. -func parseGoCount(b []byte) (*Profile, error) { - r := bytes.NewBuffer(b) - - var line string - var err error - for { - // Skip past comments and empty lines seeking a real header. - line, err = r.ReadString('\n') - if err != nil { - return nil, err - } - if !isSpaceOrComment(line) { - break - } - } - - m := countStartRE.FindStringSubmatch(line) - if m == nil { - return nil, errUnrecognized - } - profileType := string(m[1]) - p := &Profile{ - PeriodType: &ValueType{Type: profileType, Unit: "count"}, - Period: 1, - SampleType: []*ValueType{{Type: profileType, Unit: "count"}}, - } - locations := make(map[uint64]*Location) - for { - line, err = r.ReadString('\n') - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - if isSpaceOrComment(line) { - continue - } - if strings.HasPrefix(line, "---") { - break - } - m := countRE.FindStringSubmatch(line) - if m == nil { - return nil, errMalformed - } - n, err := strconv.ParseInt(string(m[1]), 0, 64) - if err != nil { - return nil, errMalformed - } - fields := strings.Fields(string(m[2])) - locs := make([]*Location, 0, len(fields)) - for _, stk := range fields { - addr, err := strconv.ParseUint(stk, 0, 64) - if err != nil { - return nil, errMalformed - } - // Adjust all frames by -1 (except the leaf) to land on top of - // the call instruction. - if len(locs) > 0 { - addr-- - } - loc := locations[addr] - if loc == nil { - loc = &Location{ - Address: addr, - } - locations[addr] = loc - p.Location = append(p.Location, loc) - } - locs = append(locs, loc) - } - p.Sample = append(p.Sample, &Sample{ - Location: locs, - Value: []int64{n}, - }) - } - - if err = parseAdditionalSections(strings.TrimSpace(line), r, p); err != nil { - return nil, err - } - return p, nil -} - -// remapLocationIDs ensures there is a location for each address -// referenced by a sample, and remaps the samples to point to the new -// location ids. -func (p *Profile) remapLocationIDs() { - seen := make(map[*Location]bool, len(p.Location)) - var locs []*Location - - for _, s := range p.Sample { - for _, l := range s.Location { - if seen[l] { - continue - } - l.ID = uint64(len(locs) + 1) - locs = append(locs, l) - seen[l] = true - } - } - p.Location = locs -} - -func (p *Profile) remapFunctionIDs() { - seen := make(map[*Function]bool, len(p.Function)) - var fns []*Function - - for _, l := range p.Location { - for _, ln := range l.Line { - fn := ln.Function - if fn == nil || seen[fn] { - continue - } - fn.ID = uint64(len(fns) + 1) - fns = append(fns, fn) - seen[fn] = true - } - } - p.Function = fns -} - -// remapMappingIDs matches location addresses with existing mappings -// and updates them appropriately. This is O(N*M), if this ever shows -// up as a bottleneck, evaluate sorting the mappings and doing a -// binary search, which would make it O(N*log(M)). -func (p *Profile) remapMappingIDs() { - if len(p.Mapping) == 0 { - return - } - - // Some profile handlers will incorrectly set regions for the main - // executable if its section is remapped. Fix them through heuristics. - - // Remove the initial mapping if named '/anon_hugepage' and has a - // consecutive adjacent mapping. - if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") { - if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start { - p.Mapping = p.Mapping[1:] - } - } - - // Subtract the offset from the start of the main mapping if it - // ends up at a recognizable start address. - const expectedStart = 0x400000 - if m := p.Mapping[0]; m.Start-m.Offset == expectedStart { - m.Start = expectedStart - m.Offset = 0 - } - - for _, l := range p.Location { - if a := l.Address; a != 0 { - for _, m := range p.Mapping { - if m.Start <= a && a < m.Limit { - l.Mapping = m - break - } - } - } - } - - // Reset all mapping IDs. - for i, m := range p.Mapping { - m.ID = uint64(i + 1) - } -} - -var cpuInts = []func([]byte) (uint64, []byte){ - get32l, - get32b, - get64l, - get64b, -} - -func get32l(b []byte) (uint64, []byte) { - if len(b) < 4 { - return 0, nil - } - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:] -} - -func get32b(b []byte) (uint64, []byte) { - if len(b) < 4 { - return 0, nil - } - return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:] -} - -func get64l(b []byte) (uint64, []byte) { - if len(b) < 8 { - return 0, nil - } - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:] -} - -func get64b(b []byte) (uint64, []byte) { - if len(b) < 8 { - return 0, nil - } - return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:] -} - -// ParseTracebacks parses a set of tracebacks and returns a newly -// populated profile. It will accept any text file and generate a -// Profile out of it with any hex addresses it can identify, including -// a process map if it can recognize one. Each sample will include a -// tag "source" with the addresses recognized in string format. -func ParseTracebacks(b []byte) (*Profile, error) { - r := bytes.NewBuffer(b) - - p := &Profile{ - PeriodType: &ValueType{Type: "trace", Unit: "count"}, - Period: 1, - SampleType: []*ValueType{ - {Type: "trace", Unit: "count"}, - }, - } - - var sources []string - var sloc []*Location - - locs := make(map[uint64]*Location) - for { - l, err := r.ReadString('\n') - if err != nil { - if err != io.EOF { - return nil, err - } - if l == "" { - break - } - } - if sectionTrigger(l) == memoryMapSection { - break - } - if s, addrs := extractHexAddresses(l); len(s) > 0 { - for _, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call - // (except for the leaf, which is not a call). - if len(sloc) > 0 { - addr-- - } - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - - sources = append(sources, s...) - } else { - if len(sources) > 0 || len(sloc) > 0 { - addTracebackSample(sloc, sources, p) - sloc, sources = nil, nil - } - } - } - - // Add final sample to save any leftover data. - if len(sources) > 0 || len(sloc) > 0 { - addTracebackSample(sloc, sources, p) - } - - if err := p.ParseMemoryMap(r); err != nil { - return nil, err - } - return p, nil -} - -func addTracebackSample(l []*Location, s []string, p *Profile) { - p.Sample = append(p.Sample, - &Sample{ - Value: []int64{1}, - Location: l, - Label: map[string][]string{"source": s}, - }) -} - -// parseCPU parses a profilez legacy profile and returns a newly -// populated Profile. -// -// The general format for profilez samples is a sequence of words in -// binary format. The first words are a header with the following data: -// 1st word -- 0 -// 2nd word -- 3 -// 3rd word -- 0 if a c++ application, 1 if a java application. -// 4th word -- Sampling period (in microseconds). -// 5th word -- Padding. -func parseCPU(b []byte) (*Profile, error) { - var parse func([]byte) (uint64, []byte) - var n1, n2, n3, n4, n5 uint64 - for _, parse = range cpuInts { - var tmp []byte - n1, tmp = parse(b) - n2, tmp = parse(tmp) - n3, tmp = parse(tmp) - n4, tmp = parse(tmp) - n5, tmp = parse(tmp) - - if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 { - b = tmp - return cpuProfile(b, int64(n4), parse) - } - } - return nil, errUnrecognized -} - -// cpuProfile returns a new Profile from C++ profilez data. -// b is the profile bytes after the header, period is the profiling -// period, and parse is a function to parse 8-byte chunks from the -// profile in its native endianness. -func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { - p := &Profile{ - Period: period * 1000, - PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, - SampleType: []*ValueType{ - {Type: "samples", Unit: "count"}, - {Type: "cpu", Unit: "nanoseconds"}, - }, - } - var err error - if b, _, err = parseCPUSamples(b, parse, true, p); err != nil { - return nil, err - } - - // If all samples have the same second-to-the-bottom frame, it - // strongly suggests that it is an uninteresting artifact of - // measurement -- a stack frame pushed by the signal handler. The - // bottom frame is always correct as it is picked up from the signal - // structure, not the stack. Check if this is the case and if so, - // remove. - if len(p.Sample) > 1 && len(p.Sample[0].Location) > 1 { - allSame := true - id1 := p.Sample[0].Location[1].Address - for _, s := range p.Sample { - if len(s.Location) < 2 || id1 != s.Location[1].Address { - allSame = false - break - } - } - if allSame { - for _, s := range p.Sample { - s.Location = append(s.Location[:1], s.Location[2:]...) - } - } - } - - if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil { - return nil, err - } - return p, nil -} - -// parseCPUSamples parses a collection of profilez samples from a -// profile. -// -// profilez samples are a repeated sequence of stack frames of the -// form: -// 1st word -- The number of times this stack was encountered. -// 2nd word -- The size of the stack (StackSize). -// 3rd word -- The first address on the stack. -// ... -// StackSize + 2 -- The last address on the stack -// The last stack trace is of the form: -// 1st word -- 0 -// 2nd word -- 1 -// 3rd word -- 0 -// -// Addresses from stack traces may point to the next instruction after -// each call. Optionally adjust by -1 to land somewhere on the actual -// call (except for the leaf, which is not a call). -func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) { - locs := make(map[uint64]*Location) - for len(b) > 0 { - var count, nstk uint64 - count, b = parse(b) - nstk, b = parse(b) - if b == nil || nstk > uint64(len(b)/4) { - return nil, nil, errUnrecognized - } - var sloc []*Location - addrs := make([]uint64, nstk) - for i := 0; i < int(nstk); i++ { - addrs[i], b = parse(b) - } - - if count == 0 && nstk == 1 && addrs[0] == 0 { - // End of data marker - break - } - for i, addr := range addrs { - if adjust && i > 0 { - addr-- - } - loc := locs[addr] - if loc == nil { - loc = &Location{ - Address: addr, - } - locs[addr] = loc - p.Location = append(p.Location, loc) - } - sloc = append(sloc, loc) - } - p.Sample = append(p.Sample, - &Sample{ - Value: []int64{int64(count), int64(count) * int64(p.Period)}, - Location: sloc, - }) - } - // Reached the end without finding the EOD marker. - return b, locs, nil -} - -// parseHeap parses a heapz legacy or a growthz profile and -// returns a newly populated Profile. -func parseHeap(b []byte) (p *Profile, err error) { - r := bytes.NewBuffer(b) - l, err := r.ReadString('\n') - if err != nil { - return nil, errUnrecognized - } - - sampling := "" - - if header := heapHeaderRE.FindStringSubmatch(l); header != nil { - p = &Profile{ - SampleType: []*ValueType{ - {Type: "objects", Unit: "count"}, - {Type: "space", Unit: "bytes"}, - }, - PeriodType: &ValueType{Type: "objects", Unit: "bytes"}, - } - - var period int64 - if len(header[6]) > 0 { - if period, err = strconv.ParseInt(string(header[6]), 10, 64); err != nil { - return nil, errUnrecognized - } - } - - switch header[5] { - case "heapz_v2", "heap_v2": - sampling, p.Period = "v2", period - case "heapprofile": - sampling, p.Period = "", 1 - case "heap": - sampling, p.Period = "v2", period/2 - default: - return nil, errUnrecognized - } - } else if header = growthHeaderRE.FindStringSubmatch(l); header != nil { - p = &Profile{ - SampleType: []*ValueType{ - {Type: "objects", Unit: "count"}, - {Type: "space", Unit: "bytes"}, - }, - PeriodType: &ValueType{Type: "heapgrowth", Unit: "count"}, - Period: 1, - } - } else if header = fragmentationHeaderRE.FindStringSubmatch(l); header != nil { - p = &Profile{ - SampleType: []*ValueType{ - {Type: "objects", Unit: "count"}, - {Type: "space", Unit: "bytes"}, - }, - PeriodType: &ValueType{Type: "allocations", Unit: "count"}, - Period: 1, - } - } else { - return nil, errUnrecognized - } - - if LegacyHeapAllocated { - for _, st := range p.SampleType { - st.Type = "alloc_" + st.Type - } - } else { - for _, st := range p.SampleType { - st.Type = "inuse_" + st.Type - } - } - - locs := make(map[uint64]*Location) - for { - l, err = r.ReadString('\n') - if err != nil { - if err != io.EOF { - return nil, err - } - - if l == "" { - break - } - } - - if isSpaceOrComment(l) { - continue - } - l = strings.TrimSpace(l) - - if sectionTrigger(l) != unrecognizedSection { - break - } - - value, blocksize, addrs, err := parseHeapSample(l, p.Period, sampling) - if err != nil { - return nil, err - } - var sloc []*Location - for i, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call - // (except for the leaf, which is not a call). - if i > 0 { - addr-- - } - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - - p.Sample = append(p.Sample, &Sample{ - Value: value, - Location: sloc, - NumLabel: map[string][]int64{"bytes": {blocksize}}, - }) - } - - if err = parseAdditionalSections(l, r, p); err != nil { - return nil, err - } - return p, nil -} - -// parseHeapSample parses a single row from a heap profile into a new Sample. -func parseHeapSample(line string, rate int64, sampling string) (value []int64, blocksize int64, addrs []uint64, err error) { - sampleData := heapSampleRE.FindStringSubmatch(line) - if len(sampleData) != 6 { - return value, blocksize, addrs, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData)) - } - - // Use first two values by default; tcmalloc sampling generates the - // same value for both, only the older heap-profile collect separate - // stats for in-use and allocated objects. - valueIndex := 1 - if LegacyHeapAllocated { - valueIndex = 3 - } - - var v1, v2 int64 - if v1, err = strconv.ParseInt(sampleData[valueIndex], 10, 64); err != nil { - return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) - } - if v2, err = strconv.ParseInt(sampleData[valueIndex+1], 10, 64); err != nil { - return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - if v1 == 0 { - if v2 != 0 { - return value, blocksize, addrs, fmt.Errorf("allocation count was 0 but allocation bytes was %d", v2) - } - } else { - blocksize = v2 / v1 - if sampling == "v2" { - v1, v2 = scaleHeapSample(v1, v2, rate) - } - } - - value = []int64{v1, v2} - addrs = parseHexAddresses(sampleData[5]) - - return value, blocksize, addrs, nil -} - -// extractHexAddresses extracts hex numbers from a string and returns -// them, together with their numeric value, in a slice. -func extractHexAddresses(s string) ([]string, []uint64) { - hexStrings := hexNumberRE.FindAllString(s, -1) - var ids []uint64 - for _, s := range hexStrings { - if id, err := strconv.ParseUint(s, 0, 64); err == nil { - ids = append(ids, id) - } else { - // Do not expect any parsing failures due to the regexp matching. - panic("failed to parse hex value:" + s) - } - } - return hexStrings, ids -} - -// parseHexAddresses parses hex numbers from a string and returns them -// in a slice. -func parseHexAddresses(s string) []uint64 { - _, ids := extractHexAddresses(s) - return ids -} - -// scaleHeapSample adjusts the data from a heapz Sample to -// account for its probability of appearing in the collected -// data. heapz profiles are a sampling of the memory allocations -// requests in a program. We estimate the unsampled value by dividing -// each collected sample by its probability of appearing in the -// profile. heapz v2 profiles rely on a poisson process to determine -// which samples to collect, based on the desired average collection -// rate R. The probability of a sample of size S to appear in that -// profile is 1-exp(-S/R). -func scaleHeapSample(count, size, rate int64) (int64, int64) { - if count == 0 || size == 0 { - return 0, 0 - } - - if rate <= 1 { - // if rate==1 all samples were collected so no adjustment is needed. - // if rate<1 treat as unknown and skip scaling. - return count, size - } - - avgSize := float64(size) / float64(count) - scale := 1 / (1 - math.Exp(-avgSize/float64(rate))) - - return int64(float64(count) * scale), int64(float64(size) * scale) -} - -// parseContention parses a contentionz profile and returns a newly -// populated Profile. -func parseContention(b []byte) (p *Profile, err error) { - r := bytes.NewBuffer(b) - l, err := r.ReadString('\n') - if err != nil { - return nil, errUnrecognized - } - - if !strings.HasPrefix(l, "--- contention") { - return nil, errUnrecognized - } - - p = &Profile{ - PeriodType: &ValueType{Type: "contentions", Unit: "count"}, - Period: 1, - SampleType: []*ValueType{ - {Type: "contentions", Unit: "count"}, - {Type: "delay", Unit: "nanoseconds"}, - }, - } - - var cpuHz int64 - // Parse text of the form "attribute = value" before the samples. - const delimiter = "=" - for { - l, err = r.ReadString('\n') - if err != nil { - if err != io.EOF { - return nil, err - } - - if l == "" { - break - } - } - - if l = strings.TrimSpace(l); l == "" { - continue - } - - if strings.HasPrefix(l, "---") { - break - } - - attr := strings.SplitN(l, delimiter, 2) - if len(attr) != 2 { - break - } - key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]) - var err error - switch key { - case "cycles/second": - if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil { - return nil, errUnrecognized - } - case "sampling period": - if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil { - return nil, errUnrecognized - } - case "ms since reset": - ms, err := strconv.ParseInt(val, 0, 64) - if err != nil { - return nil, errUnrecognized - } - p.DurationNanos = ms * 1000 * 1000 - case "format": - // CPP contentionz profiles don't have format. - return nil, errUnrecognized - case "resolution": - // CPP contentionz profiles don't have resolution. - return nil, errUnrecognized - case "discarded samples": - default: - return nil, errUnrecognized - } - } - - locs := make(map[uint64]*Location) - for { - if l = strings.TrimSpace(l); strings.HasPrefix(l, "---") { - break - } - value, addrs, err := parseContentionSample(l, p.Period, cpuHz) - if err != nil { - return nil, err - } - var sloc []*Location - for i, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call - // (except for the leaf, which is not a call). - if i > 0 { - addr-- - } - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - p.Sample = append(p.Sample, &Sample{ - Value: value, - Location: sloc, - }) - - if l, err = r.ReadString('\n'); err != nil { - if err != io.EOF { - return nil, err - } - if l == "" { - break - } - } - } - - if err = parseAdditionalSections(l, r, p); err != nil { - return nil, err - } - - return p, nil -} - -// parseContentionSample parses a single row from a contention profile -// into a new Sample. -func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) { - sampleData := contentionSampleRE.FindStringSubmatch(line) - if sampleData == nil { - return value, addrs, errUnrecognized - } - - v1, err := strconv.ParseInt(sampleData[1], 10, 64) - if err != nil { - return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) - } - v2, err := strconv.ParseInt(sampleData[2], 10, 64) - if err != nil { - return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - // Unsample values if period and cpuHz are available. - // - Delays are scaled to cycles and then to nanoseconds. - // - Contentions are scaled to cycles. - if period > 0 { - if cpuHz > 0 { - cpuGHz := float64(cpuHz) / 1e9 - v1 = int64(float64(v1) * float64(period) / cpuGHz) - } - v2 = v2 * period - } - - value = []int64{v2, v1} - addrs = parseHexAddresses(sampleData[3]) - - return value, addrs, nil -} - -// parseThread parses a Threadz profile and returns a new Profile. -func parseThread(b []byte) (*Profile, error) { - r := bytes.NewBuffer(b) - - var line string - var err error - for { - // Skip past comments and empty lines seeking a real header. - line, err = r.ReadString('\n') - if err != nil { - return nil, err - } - if !isSpaceOrComment(line) { - break - } - } - - if m := threadzStartRE.FindStringSubmatch(line); m != nil { - // Advance over initial comments until first stack trace. - for { - line, err = r.ReadString('\n') - if err != nil { - if err != io.EOF { - return nil, err - } - - if line == "" { - break - } - } - if sectionTrigger(line) != unrecognizedSection || line[0] == '-' { - break - } - } - } else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { - return nil, errUnrecognized - } - - p := &Profile{ - SampleType: []*ValueType{{Type: "thread", Unit: "count"}}, - PeriodType: &ValueType{Type: "thread", Unit: "count"}, - Period: 1, - } - - locs := make(map[uint64]*Location) - // Recognize each thread and populate profile samples. - for sectionTrigger(line) == unrecognizedSection { - if strings.HasPrefix(line, "---- no stack trace for") { - line = "" - break - } - if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { - return nil, errUnrecognized - } - - var addrs []uint64 - line, addrs, err = parseThreadSample(r) - if err != nil { - return nil, errUnrecognized - } - if len(addrs) == 0 { - // We got a --same as previous threads--. Bump counters. - if len(p.Sample) > 0 { - s := p.Sample[len(p.Sample)-1] - s.Value[0]++ - } - continue - } - - var sloc []*Location - for i, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call - // (except for the leaf, which is not a call). - if i > 0 { - addr-- - } - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - - p.Sample = append(p.Sample, &Sample{ - Value: []int64{1}, - Location: sloc, - }) - } - - if err = parseAdditionalSections(line, r, p); err != nil { - return nil, err - } - - return p, nil -} - -// parseThreadSample parses a symbolized or unsymbolized stack trace. -// Returns the first line after the traceback, the sample (or nil if -// it hits a 'same-as-previous' marker) and an error. -func parseThreadSample(b *bytes.Buffer) (nextl string, addrs []uint64, err error) { - var l string - sameAsPrevious := false - for { - if l, err = b.ReadString('\n'); err != nil { - if err != io.EOF { - return "", nil, err - } - if l == "" { - break - } - } - if l = strings.TrimSpace(l); l == "" { - continue - } - - if strings.HasPrefix(l, "---") { - break - } - if strings.Contains(l, "same as previous thread") { - sameAsPrevious = true - continue - } - - addrs = append(addrs, parseHexAddresses(l)...) - } - - if sameAsPrevious { - return l, nil, nil - } - return l, addrs, nil -} - -// parseAdditionalSections parses any additional sections in the -// profile, ignoring any unrecognized sections. -func parseAdditionalSections(l string, b *bytes.Buffer, p *Profile) (err error) { - for { - if sectionTrigger(l) == memoryMapSection { - break - } - // Ignore any unrecognized sections. - if l, err := b.ReadString('\n'); err != nil { - if err != io.EOF { - return err - } - if l == "" { - break - } - } - } - return p.ParseMemoryMap(b) -} - -// ParseMemoryMap parses a memory map in the format of -// /proc/self/maps, and overrides the mappings in the current profile. -// It renumbers the samples and locations in the profile correspondingly. -func (p *Profile) ParseMemoryMap(rd io.Reader) error { - b := bufio.NewReader(rd) - - var attrs []string - var r *strings.Replacer - const delimiter = "=" - for { - l, err := b.ReadString('\n') - if err != nil { - if err != io.EOF { - return err - } - if l == "" { - break - } - } - if l = strings.TrimSpace(l); l == "" { - continue - } - - if r != nil { - l = r.Replace(l) - } - m, err := parseMappingEntry(l) - if err != nil { - if err == errUnrecognized { - // Recognize assignments of the form: attr=value, and replace - // $attr with value on subsequent mappings. - if attr := strings.SplitN(l, delimiter, 2); len(attr) == 2 { - attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])) - r = strings.NewReplacer(attrs...) - } - // Ignore any unrecognized entries - continue - } - return err - } - if m == nil || (m.File == "" && len(p.Mapping) != 0) { - // In some cases the first entry may include the address range - // but not the name of the file. It should be followed by - // another entry with the name. - continue - } - if len(p.Mapping) == 1 && p.Mapping[0].File == "" { - // Update the name if this is the entry following that empty one. - p.Mapping[0].File = m.File - continue - } - p.Mapping = append(p.Mapping, m) - } - p.remapLocationIDs() - p.remapFunctionIDs() - p.remapMappingIDs() - return nil -} - -func parseMappingEntry(l string) (*Mapping, error) { - mapping := &Mapping{} - var err error - if me := procMapsRE.FindStringSubmatch(l); len(me) == 9 { - if !strings.Contains(me[3], "x") { - // Skip non-executable entries. - return nil, nil - } - if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil { - return nil, errUnrecognized - } - if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil { - return nil, errUnrecognized - } - if me[4] != "" { - if mapping.Offset, err = strconv.ParseUint(me[4], 16, 64); err != nil { - return nil, errUnrecognized - } - } - mapping.File = me[8] - return mapping, nil - } - - if me := briefMapsRE.FindStringSubmatch(l); len(me) == 6 { - if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil { - return nil, errUnrecognized - } - if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil { - return nil, errUnrecognized - } - mapping.File = me[3] - if me[5] != "" { - if mapping.Offset, err = strconv.ParseUint(me[5], 16, 64); err != nil { - return nil, errUnrecognized - } - } - return mapping, nil - } - - return nil, errUnrecognized -} - -type sectionType int - -const ( - unrecognizedSection sectionType = iota - memoryMapSection -) - -var memoryMapTriggers = []string{ - "--- Memory map: ---", - "MAPPED_LIBRARIES:", -} - -func sectionTrigger(line string) sectionType { - for _, trigger := range memoryMapTriggers { - if strings.Contains(line, trigger) { - return memoryMapSection - } - } - return unrecognizedSection -} - -func (p *Profile) addLegacyFrameInfo() { - switch { - case isProfileType(p, heapzSampleTypes) || - isProfileType(p, heapzInUseSampleTypes) || - isProfileType(p, heapzAllocSampleTypes): - p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr - case isProfileType(p, contentionzSampleTypes): - p.DropFrames, p.KeepFrames = lockRxStr, "" - default: - p.DropFrames, p.KeepFrames = cpuProfilerRxStr, "" - } -} - -var heapzSampleTypes = []string{"allocations", "size"} // early Go pprof profiles -var heapzInUseSampleTypes = []string{"inuse_objects", "inuse_space"} -var heapzAllocSampleTypes = []string{"alloc_objects", "alloc_space"} -var contentionzSampleTypes = []string{"contentions", "delay"} - -func isProfileType(p *Profile, t []string) bool { - st := p.SampleType - if len(st) != len(t) { - return false - } - - for i := range st { - if st[i].Type != t[i] { - return false - } - } - return true -} - -var allocRxStr = strings.Join([]string{ - // POSIX entry points. - `calloc`, - `cfree`, - `malloc`, - `free`, - `memalign`, - `do_memalign`, - `(__)?posix_memalign`, - `pvalloc`, - `valloc`, - `realloc`, - - // TC malloc. - `tcmalloc::.*`, - `tc_calloc`, - `tc_cfree`, - `tc_malloc`, - `tc_free`, - `tc_memalign`, - `tc_posix_memalign`, - `tc_pvalloc`, - `tc_valloc`, - `tc_realloc`, - `tc_new`, - `tc_delete`, - `tc_newarray`, - `tc_deletearray`, - `tc_new_nothrow`, - `tc_newarray_nothrow`, - - // Memory-allocation routines on OS X. - `malloc_zone_malloc`, - `malloc_zone_calloc`, - `malloc_zone_valloc`, - `malloc_zone_realloc`, - `malloc_zone_memalign`, - `malloc_zone_free`, - - // Go runtime - `runtime\..*`, - - // Other misc. memory allocation routines - `BaseArena::.*`, - `(::)?do_malloc_no_errno`, - `(::)?do_malloc_pages`, - `(::)?do_malloc`, - `DoSampledAllocation`, - `MallocedMemBlock::MallocedMemBlock`, - `_M_allocate`, - `__builtin_(vec_)?delete`, - `__builtin_(vec_)?new`, - `__gnu_cxx::new_allocator::allocate`, - `__libc_malloc`, - `__malloc_alloc_template::allocate`, - `allocate`, - `cpp_alloc`, - `operator new(\[\])?`, - `simple_alloc::allocate`, -}, `|`) - -var allocSkipRxStr = strings.Join([]string{ - // Preserve Go runtime frames that appear in the middle/bottom of - // the stack. - `runtime\.panic`, -}, `|`) - -var cpuProfilerRxStr = strings.Join([]string{ - `ProfileData::Add`, - `ProfileData::prof_handler`, - `CpuProfiler::prof_handler`, - `__pthread_sighandler`, - `__restore`, -}, `|`) - -var lockRxStr = strings.Join([]string{ - `RecordLockProfileData`, - `(base::)?RecordLockProfileData.*`, - `(base::)?SubmitMutexProfileData.*`, - `(base::)?SubmitSpinLockProfileData.*`, - `(Mutex::)?AwaitCommon.*`, - `(Mutex::)?Unlock.*`, - `(Mutex::)?UnlockSlow.*`, - `(Mutex::)?ReaderUnlock.*`, - `(MutexLock::)?~MutexLock.*`, - `(SpinLock::)?Unlock.*`, - `(SpinLock::)?SlowUnlock.*`, - `(SpinLockHolder::)?~SpinLockHolder.*`, -}, `|`) diff --git a/src/cmd/pprof/internal/profile/profile.go b/src/cmd/pprof/internal/profile/profile.go deleted file mode 100644 index 28e713d7be..0000000000 --- a/src/cmd/pprof/internal/profile/profile.go +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2014 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 profile provides a representation of profile.proto and -// methods to encode/decode profiles in this format. -package profile - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "regexp" - "strings" - "time" -) - -// Profile is an in-memory representation of profile.proto. -type Profile struct { - SampleType []*ValueType - Sample []*Sample - Mapping []*Mapping - Location []*Location - Function []*Function - - DropFrames string - KeepFrames string - - TimeNanos int64 - DurationNanos int64 - PeriodType *ValueType - Period int64 - - dropFramesX int64 - keepFramesX int64 - stringTable []string -} - -// ValueType corresponds to Profile.ValueType -type ValueType struct { - Type string // cpu, wall, inuse_space, etc - Unit string // seconds, nanoseconds, bytes, etc - - typeX int64 - unitX int64 -} - -// Sample corresponds to Profile.Sample -type Sample struct { - Location []*Location - Value []int64 - Label map[string][]string - NumLabel map[string][]int64 - - locationIDX []uint64 - labelX []Label -} - -// Label corresponds to Profile.Label -type Label struct { - keyX int64 - // Exactly one of the two following values must be set - strX int64 - numX int64 // Integer value for this label -} - -// Mapping corresponds to Profile.Mapping -type Mapping struct { - ID uint64 - Start uint64 - Limit uint64 - Offset uint64 - File string - BuildID string - HasFunctions bool - HasFilenames bool - HasLineNumbers bool - HasInlineFrames bool - - fileX int64 - buildIDX int64 -} - -// Location corresponds to Profile.Location -type Location struct { - ID uint64 - Mapping *Mapping - Address uint64 - Line []Line - - mappingIDX uint64 -} - -// Line corresponds to Profile.Line -type Line struct { - Function *Function - Line int64 - - functionIDX uint64 -} - -// Function corresponds to Profile.Function -type Function struct { - ID uint64 - Name string - SystemName string - Filename string - StartLine int64 - - nameX int64 - systemNameX int64 - filenameX int64 -} - -// Parse parses a profile and checks for its validity. The input -// may be a gzip-compressed encoded protobuf or one of many legacy -// profile formats which may be unsupported in the future. -func Parse(r io.Reader) (*Profile, error) { - orig, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - var p *Profile - if len(orig) >= 2 && orig[0] == 0x1f && orig[1] == 0x8b { - gz, err := gzip.NewReader(bytes.NewBuffer(orig)) - if err != nil { - return nil, fmt.Errorf("decompressing profile: %v", err) - } - data, err := ioutil.ReadAll(gz) - if err != nil { - return nil, fmt.Errorf("decompressing profile: %v", err) - } - orig = data - } - if p, err = parseUncompressed(orig); err != nil { - if p, err = parseLegacy(orig); err != nil { - return nil, fmt.Errorf("parsing profile: %v", err) - } - } - - if err := p.CheckValid(); err != nil { - return nil, fmt.Errorf("malformed profile: %v", err) - } - return p, nil -} - -var errUnrecognized = fmt.Errorf("unrecognized profile format") -var errMalformed = fmt.Errorf("malformed profile format") - -func parseLegacy(data []byte) (*Profile, error) { - parsers := []func([]byte) (*Profile, error){ - parseCPU, - parseHeap, - parseGoCount, // goroutine, threadcreate - parseThread, - parseContention, - } - - for _, parser := range parsers { - p, err := parser(data) - if err == nil { - p.setMain() - p.addLegacyFrameInfo() - return p, nil - } - if err != errUnrecognized { - return nil, err - } - } - return nil, errUnrecognized -} - -func parseUncompressed(data []byte) (*Profile, error) { - p := &Profile{} - if err := unmarshal(data, p); err != nil { - return nil, err - } - - if err := p.postDecode(); err != nil { - return nil, err - } - - return p, nil -} - -var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`) - -// setMain scans Mapping entries and guesses which entry is main -// because legacy profiles don't obey the convention of putting main -// first. -func (p *Profile) setMain() { - for i := 0; i < len(p.Mapping); i++ { - file := strings.TrimSpace(strings.Replace(p.Mapping[i].File, "(deleted)", "", -1)) - if len(file) == 0 { - continue - } - if len(libRx.FindStringSubmatch(file)) > 0 { - continue - } - if strings.HasPrefix(file, "[") { - continue - } - // Swap what we guess is main to position 0. - tmp := p.Mapping[i] - p.Mapping[i] = p.Mapping[0] - p.Mapping[0] = tmp - break - } -} - -// Write writes the profile as a gzip-compressed marshaled protobuf. -func (p *Profile) Write(w io.Writer) error { - p.preEncode() - b := marshal(p) - zw := gzip.NewWriter(w) - defer zw.Close() - _, err := zw.Write(b) - return err -} - -// CheckValid tests whether the profile is valid. Checks include, but are -// not limited to: -// - len(Profile.Sample[n].value) == len(Profile.value_unit) -// - Sample.id has a corresponding Profile.Location -func (p *Profile) CheckValid() error { - // Check that sample values are consistent - sampleLen := len(p.SampleType) - if sampleLen == 0 && len(p.Sample) != 0 { - return fmt.Errorf("missing sample type information") - } - for _, s := range p.Sample { - if len(s.Value) != sampleLen { - return fmt.Errorf("mismatch: sample has: %d values vs. %d types", len(s.Value), len(p.SampleType)) - } - } - - // Check that all mappings/locations/functions are in the tables - // Check that there are no duplicate ids - mappings := make(map[uint64]*Mapping, len(p.Mapping)) - for _, m := range p.Mapping { - if m.ID == 0 { - return fmt.Errorf("found mapping with reserved ID=0") - } - if mappings[m.ID] != nil { - return fmt.Errorf("multiple mappings with same id: %d", m.ID) - } - mappings[m.ID] = m - } - functions := make(map[uint64]*Function, len(p.Function)) - for _, f := range p.Function { - if f.ID == 0 { - return fmt.Errorf("found function with reserved ID=0") - } - if functions[f.ID] != nil { - return fmt.Errorf("multiple functions with same id: %d", f.ID) - } - functions[f.ID] = f - } - locations := make(map[uint64]*Location, len(p.Location)) - for _, l := range p.Location { - if l.ID == 0 { - return fmt.Errorf("found location with reserved id=0") - } - if locations[l.ID] != nil { - return fmt.Errorf("multiple locations with same id: %d", l.ID) - } - locations[l.ID] = l - if m := l.Mapping; m != nil { - if m.ID == 0 || mappings[m.ID] != m { - return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID) - } - } - for _, ln := range l.Line { - if f := ln.Function; f != nil { - if f.ID == 0 || functions[f.ID] != f { - return fmt.Errorf("inconsistent function %p: %d", f, f.ID) - } - } - } - } - return nil -} - -// Aggregate merges the locations in the profile into equivalence -// classes preserving the request attributes. It also updates the -// samples to point to the merged locations. -func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, address bool) error { - for _, m := range p.Mapping { - m.HasInlineFrames = m.HasInlineFrames && inlineFrame - m.HasFunctions = m.HasFunctions && function - m.HasFilenames = m.HasFilenames && filename - m.HasLineNumbers = m.HasLineNumbers && linenumber - } - - // Aggregate functions - if !function || !filename { - for _, f := range p.Function { - if !function { - f.Name = "" - f.SystemName = "" - } - if !filename { - f.Filename = "" - } - } - } - - // Aggregate locations - if !inlineFrame || !address || !linenumber { - for _, l := range p.Location { - if !inlineFrame && len(l.Line) > 1 { - l.Line = l.Line[len(l.Line)-1:] - } - if !linenumber { - for i := range l.Line { - l.Line[i].Line = 0 - } - } - if !address { - l.Address = 0 - } - } - } - - return p.CheckValid() -} - -// Print dumps a text representation of a profile. Intended mainly -// for debugging purposes. -func (p *Profile) String() string { - - ss := make([]string, 0, len(p.Sample)+len(p.Mapping)+len(p.Location)) - if pt := p.PeriodType; pt != nil { - ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit)) - } - ss = append(ss, fmt.Sprintf("Period: %d", p.Period)) - if p.TimeNanos != 0 { - ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos))) - } - if p.DurationNanos != 0 { - ss = append(ss, fmt.Sprintf("Duration: %v", time.Duration(p.DurationNanos))) - } - - ss = append(ss, "Samples:") - var sh1 string - for _, s := range p.SampleType { - sh1 = sh1 + fmt.Sprintf("%s/%s ", s.Type, s.Unit) - } - ss = append(ss, strings.TrimSpace(sh1)) - for _, s := range p.Sample { - var sv string - for _, v := range s.Value { - sv = fmt.Sprintf("%s %10d", sv, v) - } - sv = sv + ": " - for _, l := range s.Location { - sv = sv + fmt.Sprintf("%d ", l.ID) - } - ss = append(ss, sv) - const labelHeader = " " - if len(s.Label) > 0 { - ls := labelHeader - for k, v := range s.Label { - ls = ls + fmt.Sprintf("%s:%v ", k, v) - } - ss = append(ss, ls) - } - if len(s.NumLabel) > 0 { - ls := labelHeader - for k, v := range s.NumLabel { - ls = ls + fmt.Sprintf("%s:%v ", k, v) - } - ss = append(ss, ls) - } - } - - ss = append(ss, "Locations") - for _, l := range p.Location { - locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address) - if m := l.Mapping; m != nil { - locStr = locStr + fmt.Sprintf("M=%d ", m.ID) - } - if len(l.Line) == 0 { - ss = append(ss, locStr) - } - for li := range l.Line { - lnStr := "??" - if fn := l.Line[li].Function; fn != nil { - lnStr = fmt.Sprintf("%s %s:%d s=%d", - fn.Name, - fn.Filename, - l.Line[li].Line, - fn.StartLine) - if fn.Name != fn.SystemName { - lnStr = lnStr + "(" + fn.SystemName + ")" - } - } - ss = append(ss, locStr+lnStr) - // Do not print location details past the first line - locStr = " " - } - } - - ss = append(ss, "Mappings") - for _, m := range p.Mapping { - bits := "" - if m.HasFunctions { - bits = bits + "[FN]" - } - if m.HasFilenames { - bits = bits + "[FL]" - } - if m.HasLineNumbers { - bits = bits + "[LN]" - } - if m.HasInlineFrames { - bits = bits + "[IN]" - } - ss = append(ss, fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s", - m.ID, - m.Start, m.Limit, m.Offset, - m.File, - m.BuildID, - bits)) - } - - return strings.Join(ss, "\n") + "\n" -} - -// Merge adds profile p adjusted by ratio r into profile p. Profiles -// must be compatible (same Type and SampleType). -// TODO(rsilvera): consider normalizing the profiles based on the -// total samples collected. -func (p *Profile) Merge(pb *Profile, r float64) error { - if err := p.Compatible(pb); err != nil { - return err - } - - pb = pb.Copy() - - // Keep the largest of the two periods. - if pb.Period > p.Period { - p.Period = pb.Period - } - - p.DurationNanos += pb.DurationNanos - - p.Mapping = append(p.Mapping, pb.Mapping...) - for i, m := range p.Mapping { - m.ID = uint64(i + 1) - } - p.Location = append(p.Location, pb.Location...) - for i, l := range p.Location { - l.ID = uint64(i + 1) - } - p.Function = append(p.Function, pb.Function...) - for i, f := range p.Function { - f.ID = uint64(i + 1) - } - - if r != 1.0 { - for _, s := range pb.Sample { - for i, v := range s.Value { - s.Value[i] = int64((float64(v) * r)) - } - } - } - p.Sample = append(p.Sample, pb.Sample...) - return p.CheckValid() -} - -// Compatible determines if two profiles can be compared/merged. -// returns nil if the profiles are compatible; otherwise an error with -// details on the incompatibility. -func (p *Profile) Compatible(pb *Profile) error { - if !compatibleValueTypes(p.PeriodType, pb.PeriodType) { - return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType) - } - - if len(p.SampleType) != len(pb.SampleType) { - return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) - } - - for i := range p.SampleType { - if !compatibleValueTypes(p.SampleType[i], pb.SampleType[i]) { - return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) - } - } - - return nil -} - -// HasFunctions determines if all locations in this profile have -// symbolized function information. -func (p *Profile) HasFunctions() bool { - for _, l := range p.Location { - if l.Mapping == nil || !l.Mapping.HasFunctions { - return false - } - } - return true -} - -// HasFileLines determines if all locations in this profile have -// symbolized file and line number information. -func (p *Profile) HasFileLines() bool { - for _, l := range p.Location { - if l.Mapping == nil || (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) { - return false - } - } - return true -} - -func compatibleValueTypes(v1, v2 *ValueType) bool { - if v1 == nil || v2 == nil { - return true // No grounds to disqualify. - } - return v1.Type == v2.Type && v1.Unit == v2.Unit -} - -// Copy makes a fully independent copy of a profile. -func (p *Profile) Copy() *Profile { - p.preEncode() - b := marshal(p) - - pp := &Profile{} - if err := unmarshal(b, pp); err != nil { - panic(err) - } - if err := pp.postDecode(); err != nil { - panic(err) - } - - return pp -} - -// Demangler maps symbol names to a human-readable form. This may -// include C++ demangling and additional simplification. Names that -// are not demangled may be missing from the resulting map. -type Demangler func(name []string) (map[string]string, error) - -// Demangle attempts to demangle and optionally simplify any function -// names referenced in the profile. It works on a best-effort basis: -// it will silently preserve the original names in case of any errors. -func (p *Profile) Demangle(d Demangler) error { - // Collect names to demangle. - var names []string - for _, fn := range p.Function { - names = append(names, fn.SystemName) - } - - // Update profile with demangled names. - demangled, err := d(names) - if err != nil { - return err - } - for _, fn := range p.Function { - if dd, ok := demangled[fn.SystemName]; ok { - fn.Name = dd - } - } - return nil -} - -// Empty returns true if the profile contains no samples. -func (p *Profile) Empty() bool { - return len(p.Sample) == 0 -} diff --git a/src/cmd/pprof/internal/profile/profile_test.go b/src/cmd/pprof/internal/profile/profile_test.go deleted file mode 100644 index 09b11a456f..0000000000 --- a/src/cmd/pprof/internal/profile/profile_test.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 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 profile - -import ( - "bytes" - "testing" -) - -func TestEmptyProfile(t *testing.T) { - var buf bytes.Buffer - p, err := Parse(&buf) - if err != nil { - t.Error("Want no error, got", err) - } - if p == nil { - t.Fatal("Want a valid profile, got ") - } - if !p.Empty() { - t.Errorf("Profile should be empty, got %#v", p) - } -} diff --git a/src/cmd/pprof/internal/profile/proto.go b/src/cmd/pprof/internal/profile/proto.go deleted file mode 100644 index 11d7f9ff9b..0000000000 --- a/src/cmd/pprof/internal/profile/proto.go +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright 2014 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. - -// This file is a simple protocol buffer encoder and decoder. -// -// A protocol message must implement the message interface: -// decoder() []decoder -// encode(*buffer) -// -// The decode method returns a slice indexed by field number that gives the -// function to decode that field. -// The encode method encodes its receiver into the given buffer. -// -// The two methods are simple enough to be implemented by hand rather than -// by using a protocol compiler. -// -// See profile.go for examples of messages implementing this interface. -// -// There is no support for groups, message sets, or "has" bits. - -package profile - -import "errors" - -type buffer struct { - field int - typ int - u64 uint64 - data []byte - tmp [16]byte -} - -type decoder func(*buffer, message) error - -type message interface { - decoder() []decoder - encode(*buffer) -} - -func marshal(m message) []byte { - var b buffer - m.encode(&b) - return b.data -} - -func encodeVarint(b *buffer, x uint64) { - for x >= 128 { - b.data = append(b.data, byte(x)|0x80) - x >>= 7 - } - b.data = append(b.data, byte(x)) -} - -func encodeLength(b *buffer, tag int, len int) { - encodeVarint(b, uint64(tag)<<3|2) - encodeVarint(b, uint64(len)) -} - -func encodeUint64(b *buffer, tag int, x uint64) { - // append varint to b.data - encodeVarint(b, uint64(tag)<<3|0) - encodeVarint(b, x) -} - -func encodeUint64s(b *buffer, tag int, x []uint64) { - if len(x) > 2 { - // Use packed encoding - n1 := len(b.data) - for _, u := range x { - encodeVarint(b, u) - } - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) - return - } - for _, u := range x { - encodeUint64(b, tag, u) - } -} - -func encodeUint64Opt(b *buffer, tag int, x uint64) { - if x == 0 { - return - } - encodeUint64(b, tag, x) -} - -func encodeInt64(b *buffer, tag int, x int64) { - u := uint64(x) - encodeUint64(b, tag, u) -} - -func encodeInt64Opt(b *buffer, tag int, x int64) { - if x == 0 { - return - } - encodeInt64(b, tag, x) -} - -func encodeInt64s(b *buffer, tag int, x []int64) { - if len(x) > 2 { - // Use packed encoding - n1 := len(b.data) - for _, u := range x { - encodeVarint(b, uint64(u)) - } - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) - return - } - for _, u := range x { - encodeInt64(b, tag, u) - } -} - -func encodeString(b *buffer, tag int, x string) { - encodeLength(b, tag, len(x)) - b.data = append(b.data, x...) -} - -func encodeStrings(b *buffer, tag int, x []string) { - for _, s := range x { - encodeString(b, tag, s) - } -} - -func encodeStringOpt(b *buffer, tag int, x string) { - if x == "" { - return - } - encodeString(b, tag, x) -} - -func encodeBool(b *buffer, tag int, x bool) { - if x { - encodeUint64(b, tag, 1) - } else { - encodeUint64(b, tag, 0) - } -} - -func encodeBoolOpt(b *buffer, tag int, x bool) { - if x == false { - return - } - encodeBool(b, tag, x) -} - -func encodeMessage(b *buffer, tag int, m message) { - n1 := len(b.data) - m.encode(b) - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) -} - -func unmarshal(data []byte, m message) (err error) { - b := buffer{data: data, typ: 2} - return decodeMessage(&b, m) -} - -func le64(p []byte) uint64 { - return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 -} - -func le32(p []byte) uint32 { - return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 -} - -func decodeVarint(data []byte) (uint64, []byte, error) { - var i int - var u uint64 - for i = 0; ; i++ { - if i >= 10 || i >= len(data) { - return 0, nil, errors.New("bad varint") - } - u |= uint64(data[i]&0x7F) << uint(7*i) - if data[i]&0x80 == 0 { - return u, data[i+1:], nil - } - } -} - -func decodeField(b *buffer, data []byte) ([]byte, error) { - x, data, err := decodeVarint(data) - if err != nil { - return nil, err - } - b.field = int(x >> 3) - b.typ = int(x & 7) - b.data = nil - b.u64 = 0 - switch b.typ { - case 0: - b.u64, data, err = decodeVarint(data) - if err != nil { - return nil, err - } - case 1: - if len(data) < 8 { - return nil, errors.New("not enough data") - } - b.u64 = le64(data[:8]) - data = data[8:] - case 2: - var n uint64 - n, data, err = decodeVarint(data) - if err != nil { - return nil, err - } - if n > uint64(len(data)) { - return nil, errors.New("too much data") - } - b.data = data[:n] - data = data[n:] - case 5: - if len(data) < 4 { - return nil, errors.New("not enough data") - } - b.u64 = uint64(le32(data[:4])) - data = data[4:] - default: - return nil, errors.New("unknown type: " + string(b.typ)) - } - - return data, nil -} - -func checkType(b *buffer, typ int) error { - if b.typ != typ { - return errors.New("type mismatch") - } - return nil -} - -func decodeMessage(b *buffer, m message) error { - if err := checkType(b, 2); err != nil { - return err - } - dec := m.decoder() - data := b.data - for len(data) > 0 { - // pull varint field# + type - var err error - data, err = decodeField(b, data) - if err != nil { - return err - } - if b.field >= len(dec) || dec[b.field] == nil { - continue - } - if err := dec[b.field](b, m); err != nil { - return err - } - } - return nil -} - -func decodeInt64(b *buffer, x *int64) error { - if err := checkType(b, 0); err != nil { - return err - } - *x = int64(b.u64) - return nil -} - -func decodeInt64s(b *buffer, x *[]int64) error { - if b.typ == 2 { - // Packed encoding - data := b.data - for len(data) > 0 { - var u uint64 - var err error - - if u, data, err = decodeVarint(data); err != nil { - return err - } - *x = append(*x, int64(u)) - } - return nil - } - var i int64 - if err := decodeInt64(b, &i); err != nil { - return err - } - *x = append(*x, i) - return nil -} - -func decodeUint64(b *buffer, x *uint64) error { - if err := checkType(b, 0); err != nil { - return err - } - *x = b.u64 - return nil -} - -func decodeUint64s(b *buffer, x *[]uint64) error { - if b.typ == 2 { - data := b.data - // Packed encoding - for len(data) > 0 { - var u uint64 - var err error - - if u, data, err = decodeVarint(data); err != nil { - return err - } - *x = append(*x, u) - } - return nil - } - var u uint64 - if err := decodeUint64(b, &u); err != nil { - return err - } - *x = append(*x, u) - return nil -} - -func decodeString(b *buffer, x *string) error { - if err := checkType(b, 2); err != nil { - return err - } - *x = string(b.data) - return nil -} - -func decodeStrings(b *buffer, x *[]string) error { - var s string - if err := decodeString(b, &s); err != nil { - return err - } - *x = append(*x, s) - return nil -} - -func decodeBool(b *buffer, x *bool) error { - if err := checkType(b, 0); err != nil { - return err - } - if int64(b.u64) == 0 { - *x = false - } else { - *x = true - } - return nil -} diff --git a/src/cmd/pprof/internal/profile/proto_test.go b/src/cmd/pprof/internal/profile/proto_test.go deleted file mode 100644 index c2613fc375..0000000000 --- a/src/cmd/pprof/internal/profile/proto_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package profile - -import ( - "reflect" - "testing" -) - -func TestPackedEncoding(t *testing.T) { - - type testcase struct { - uint64s []uint64 - int64s []int64 - encoded []byte - } - for i, tc := range []testcase{ - { - []uint64{0, 1, 10, 100, 1000, 10000}, - []int64{1000, 0, 1000}, - []byte{10, 8, 0, 1, 10, 100, 232, 7, 144, 78, 18, 5, 232, 7, 0, 232, 7}, - }, - { - []uint64{10000}, - nil, - []byte{8, 144, 78}, - }, - { - nil, - []int64{-10000}, - []byte{16, 240, 177, 255, 255, 255, 255, 255, 255, 255, 1}, - }, - } { - source := &packedInts{tc.uint64s, tc.int64s} - if got, want := marshal(source), tc.encoded; !reflect.DeepEqual(got, want) { - t.Errorf("failed encode %d, got %v, want %v", i, got, want) - } - - dest := new(packedInts) - if err := unmarshal(tc.encoded, dest); err != nil { - t.Errorf("failed decode %d: %v", i, err) - continue - } - if got, want := dest.uint64s, tc.uint64s; !reflect.DeepEqual(got, want) { - t.Errorf("failed decode uint64s %d, got %v, want %v", i, got, want) - } - if got, want := dest.int64s, tc.int64s; !reflect.DeepEqual(got, want) { - t.Errorf("failed decode int64s %d, got %v, want %v", i, got, want) - } - } -} - -type packedInts struct { - uint64s []uint64 - int64s []int64 -} - -func (u *packedInts) decoder() []decoder { - return []decoder{ - nil, - func(b *buffer, m message) error { return decodeUint64s(b, &m.(*packedInts).uint64s) }, - func(b *buffer, m message) error { return decodeInt64s(b, &m.(*packedInts).int64s) }, - } -} - -func (u *packedInts) encode(b *buffer) { - encodeUint64s(b, 1, u.uint64s) - encodeInt64s(b, 2, u.int64s) -} diff --git a/src/cmd/pprof/internal/profile/prune.go b/src/cmd/pprof/internal/profile/prune.go deleted file mode 100644 index 1924fada7a..0000000000 --- a/src/cmd/pprof/internal/profile/prune.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2014 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. - -// Implements methods to remove frames from profiles. - -package profile - -import ( - "fmt" - "regexp" -) - -// Prune removes all nodes beneath a node matching dropRx, and not -// matching keepRx. If the root node of a Sample matches, the sample -// will have an empty stack. -func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) { - prune := make(map[uint64]bool) - pruneBeneath := make(map[uint64]bool) - - for _, loc := range p.Location { - var i int - for i = len(loc.Line) - 1; i >= 0; i-- { - if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { - funcName := fn.Name - // Account for leading '.' on the PPC ELF v1 ABI. - if funcName[0] == '.' { - funcName = funcName[1:] - } - if dropRx.MatchString(funcName) { - if keepRx == nil || !keepRx.MatchString(funcName) { - break - } - } - } - } - - if i >= 0 { - // Found matching entry to prune. - pruneBeneath[loc.ID] = true - - // Remove the matching location. - if i == len(loc.Line)-1 { - // Matched the top entry: prune the whole location. - prune[loc.ID] = true - } else { - loc.Line = loc.Line[i+1:] - } - } - } - - // Prune locs from each Sample - for _, sample := range p.Sample { - // Scan from the root to the leaves to find the prune location. - // Do not prune frames before the first user frame, to avoid - // pruning everything. - foundUser := false - for i := len(sample.Location) - 1; i >= 0; i-- { - id := sample.Location[i].ID - if !prune[id] && !pruneBeneath[id] { - foundUser = true - continue - } - if !foundUser { - continue - } - if prune[id] { - sample.Location = sample.Location[i+1:] - break - } - if pruneBeneath[id] { - sample.Location = sample.Location[i:] - break - } - } - } -} - -// RemoveUninteresting prunes and elides profiles using built-in -// tables of uninteresting function names. -func (p *Profile) RemoveUninteresting() error { - var keep, drop *regexp.Regexp - var err error - - if p.DropFrames != "" { - if drop, err = regexp.Compile("^(" + p.DropFrames + ")$"); err != nil { - return fmt.Errorf("failed to compile regexp %s: %v", p.DropFrames, err) - } - if p.KeepFrames != "" { - if keep, err = regexp.Compile("^(" + p.KeepFrames + ")$"); err != nil { - return fmt.Errorf("failed to compile regexp %s: %v", p.KeepFrames, err) - } - } - p.Prune(drop, keep) - } - return nil -} diff --git a/src/cmd/pprof/internal/report/report.go b/src/cmd/pprof/internal/report/report.go deleted file mode 100644 index 86bd4a280b..0000000000 --- a/src/cmd/pprof/internal/report/report.go +++ /dev/null @@ -1,1682 +0,0 @@ -// Copyright 2014 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 report summarizes a performance profile into a -// human-readable report. -package report - -import ( - "fmt" - "io" - "math" - "path/filepath" - "regexp" - "sort" - "strconv" - "strings" - "time" - - "cmd/pprof/internal/plugin" - "cmd/pprof/internal/profile" -) - -// Generate generates a report as directed by the Report. -func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error { - o := rpt.options - - switch o.OutputFormat { - case Dot: - return printDOT(w, rpt) - case Tree: - return printTree(w, rpt) - case Text: - return printText(w, rpt) - case Raw: - fmt.Fprint(w, rpt.prof.String()) - return nil - case Tags: - return printTags(w, rpt) - case Proto: - return rpt.prof.Write(w) - case Dis: - return printAssembly(w, rpt, obj) - case List: - return printSource(w, rpt) - case WebList: - return printWebSource(w, rpt, obj) - case Callgrind: - return printCallgrind(w, rpt) - } - return fmt.Errorf("unexpected output format") -} - -// printAssembly prints an annotated assembly listing. -func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error { - g, err := newGraph(rpt) - if err != nil { - return err - } - - o := rpt.options - prof := rpt.prof - - // If the regexp source can be parsed as an address, also match - // functions that land on that address. - var address *uint64 - if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil { - address = &hex - } - - fmt.Fprintln(w, "Total:", rpt.formatValue(rpt.total)) - symbols := symbolsFromBinaries(prof, g, o.Symbol, address, obj) - symNodes := nodesPerSymbol(g.ns, symbols) - // Sort function names for printing. - var syms objSymbols - for s := range symNodes { - syms = append(syms, s) - } - sort.Sort(syms) - - // Correlate the symbols from the binary with the profile samples. - for _, s := range syms { - sns := symNodes[s] - - // Gather samples for this symbol. - flatSum, cumSum := sumNodes(sns) - - // Get the function assembly. - insns, err := obj.Disasm(s.sym.File, s.sym.Start, s.sym.End) - if err != nil { - return err - } - - ns := annotateAssembly(insns, sns, s.base) - - fmt.Fprintf(w, "ROUTINE ======================== %s\n", s.sym.Name[0]) - for _, name := range s.sym.Name[1:] { - fmt.Fprintf(w, " AKA ======================== %s\n", name) - } - fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n", - rpt.formatValue(flatSum), rpt.formatValue(cumSum), - percentage(cumSum, rpt.total)) - - for _, n := range ns { - fmt.Fprintf(w, "%10s %10s %10x: %s\n", valueOrDot(n.flat, rpt), valueOrDot(n.cum, rpt), n.info.address, n.info.name) - } - } - return nil -} - -// symbolsFromBinaries examines the binaries listed on the profile -// that have associated samples, and identifies symbols matching rx. -func symbolsFromBinaries(prof *profile.Profile, g graph, rx *regexp.Regexp, address *uint64, obj plugin.ObjTool) []*objSymbol { - hasSamples := make(map[string]bool) - // Only examine mappings that have samples that match the - // regexp. This is an optimization to speed up pprof. - for _, n := range g.ns { - if name := n.info.prettyName(); rx.MatchString(name) && n.info.objfile != "" { - hasSamples[n.info.objfile] = true - } - } - - // Walk all mappings looking for matching functions with samples. - var objSyms []*objSymbol - for _, m := range prof.Mapping { - if !hasSamples[filepath.Base(m.File)] { - if address == nil || !(m.Start <= *address && *address <= m.Limit) { - continue - } - } - - f, err := obj.Open(m.File, m.Start) - if err != nil { - fmt.Printf("%v\n", err) - continue - } - - // Find symbols in this binary matching the user regexp. - var addr uint64 - if address != nil { - addr = *address - } - msyms, err := f.Symbols(rx, addr) - base := f.Base() - f.Close() - if err != nil { - continue - } - for _, ms := range msyms { - objSyms = append(objSyms, - &objSymbol{ - sym: ms, - base: base, - }, - ) - } - } - - return objSyms -} - -// objSym represents a symbol identified from a binary. It includes -// the SymbolInfo from the disasm package and the base that must be -// added to correspond to sample addresses -type objSymbol struct { - sym *plugin.Sym - base uint64 -} - -// objSymbols is a wrapper type to enable sorting of []*objSymbol. -type objSymbols []*objSymbol - -func (o objSymbols) Len() int { - return len(o) -} - -func (o objSymbols) Less(i, j int) bool { - if namei, namej := o[i].sym.Name[0], o[j].sym.Name[0]; namei != namej { - return namei < namej - } - return o[i].sym.Start < o[j].sym.Start -} - -func (o objSymbols) Swap(i, j int) { - o[i], o[j] = o[j], o[i] -} - -// nodesPerSymbol classifies nodes into a group of symbols. -func nodesPerSymbol(ns nodes, symbols []*objSymbol) map[*objSymbol]nodes { - symNodes := make(map[*objSymbol]nodes) - for _, s := range symbols { - // Gather samples for this symbol. - for _, n := range ns { - address := n.info.address - s.base - if address >= s.sym.Start && address < s.sym.End { - symNodes[s] = append(symNodes[s], n) - } - } - } - return symNodes -} - -// annotateAssembly annotates a set of assembly instructions with a -// set of samples. It returns a set of nodes to display. base is an -// offset to adjust the sample addresses. -func annotateAssembly(insns []plugin.Inst, samples nodes, base uint64) nodes { - // Add end marker to simplify printing loop. - insns = append(insns, plugin.Inst{^uint64(0), "", "", 0}) - - // Ensure samples are sorted by address. - samples.sort(addressOrder) - - var s int - var asm nodes - for ix, in := range insns[:len(insns)-1] { - n := node{ - info: nodeInfo{ - address: in.Addr, - name: in.Text, - file: trimPath(in.File), - lineno: in.Line, - }, - } - - // Sum all the samples until the next instruction (to account - // for samples attributed to the middle of an instruction). - for next := insns[ix+1].Addr; s < len(samples) && samples[s].info.address-base < next; s++ { - n.flat += samples[s].flat - n.cum += samples[s].cum - if samples[s].info.file != "" { - n.info.file = trimPath(samples[s].info.file) - n.info.lineno = samples[s].info.lineno - } - } - asm = append(asm, &n) - } - - return asm -} - -// valueOrDot formats a value according to a report, intercepting zero -// values. -func valueOrDot(value int64, rpt *Report) string { - if value == 0 { - return "." - } - return rpt.formatValue(value) -} - -// printTags collects all tags referenced in the profile and prints -// them in a sorted table. -func printTags(w io.Writer, rpt *Report) error { - p := rpt.prof - - // Hashtable to keep accumulate tags as key,value,count. - tagMap := make(map[string]map[string]int64) - for _, s := range p.Sample { - for key, vals := range s.Label { - for _, val := range vals { - if valueMap, ok := tagMap[key]; ok { - valueMap[val] = valueMap[val] + s.Value[0] - continue - } - valueMap := make(map[string]int64) - valueMap[val] = s.Value[0] - tagMap[key] = valueMap - } - } - for key, vals := range s.NumLabel { - for _, nval := range vals { - val := scaledValueLabel(nval, key, "auto") - if valueMap, ok := tagMap[key]; ok { - valueMap[val] = valueMap[val] + s.Value[0] - continue - } - valueMap := make(map[string]int64) - valueMap[val] = s.Value[0] - tagMap[key] = valueMap - } - } - } - - tagKeys := make(tags, 0, len(tagMap)) - for key := range tagMap { - tagKeys = append(tagKeys, &tag{name: key}) - } - sort.Sort(tagKeys) - - for _, tagKey := range tagKeys { - var total int64 - key := tagKey.name - tags := make(tags, 0, len(tagMap[key])) - for t, c := range tagMap[key] { - total += c - tags = append(tags, &tag{name: t, weight: c}) - } - - sort.Sort(tags) - fmt.Fprintf(w, "%s: Total %d\n", key, total) - for _, t := range tags { - if total > 0 { - fmt.Fprintf(w, " %8d (%s): %s\n", t.weight, - percentage(t.weight, total), t.name) - } else { - fmt.Fprintf(w, " %8d: %s\n", t.weight, t.name) - } - } - fmt.Fprintln(w) - } - return nil -} - -// printText prints a flat text report for a profile. -func printText(w io.Writer, rpt *Report) error { - g, err := newGraph(rpt) - if err != nil { - return err - } - - origCount, droppedNodes, _ := g.preprocess(rpt) - fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n")) - - fmt.Fprintf(w, "%10s %5s%% %5s%% %10s %5s%%\n", - "flat", "flat", "sum", "cum", "cum") - - var flatSum int64 - for _, n := range g.ns { - name, flat, cum := n.info.prettyName(), n.flat, n.cum - - flatSum += flat - fmt.Fprintf(w, "%10s %s %s %10s %s %s\n", - rpt.formatValue(flat), - percentage(flat, rpt.total), - percentage(flatSum, rpt.total), - rpt.formatValue(cum), - percentage(cum, rpt.total), - name) - } - return nil -} - -// printCallgrind prints a graph for a profile on callgrind format. -func printCallgrind(w io.Writer, rpt *Report) error { - g, err := newGraph(rpt) - if err != nil { - return err - } - - o := rpt.options - rpt.options.NodeFraction = 0 - rpt.options.EdgeFraction = 0 - rpt.options.NodeCount = 0 - - g.preprocess(rpt) - - fmt.Fprintln(w, "events:", o.SampleType+"("+o.OutputUnit+")") - - files := make(map[string]int) - names := make(map[string]int) - for _, n := range g.ns { - fmt.Fprintln(w, "fl="+callgrindName(files, n.info.file)) - fmt.Fprintln(w, "fn="+callgrindName(names, n.info.name)) - sv, _ := ScaleValue(n.flat, o.SampleUnit, o.OutputUnit) - fmt.Fprintf(w, "%d %d\n", n.info.lineno, int(sv)) - - // Print outgoing edges. - for _, out := range sortedEdges(n.out) { - c, _ := ScaleValue(out.weight, o.SampleUnit, o.OutputUnit) - count := fmt.Sprintf("%d", int(c)) - callee := out.dest - fmt.Fprintln(w, "cfl="+callgrindName(files, callee.info.file)) - fmt.Fprintln(w, "cfn="+callgrindName(names, callee.info.name)) - fmt.Fprintln(w, "calls="+count, callee.info.lineno) - fmt.Fprintln(w, n.info.lineno, count) - } - fmt.Fprintln(w) - } - - return nil -} - -// callgrindName implements the callgrind naming compression scheme. -// For names not previously seen returns "(N) name", where N is a -// unique index. For names previously seen returns "(N)" where N is -// the index returned the first time. -func callgrindName(names map[string]int, name string) string { - if name == "" { - return "" - } - if id, ok := names[name]; ok { - return fmt.Sprintf("(%d)", id) - } - id := len(names) + 1 - names[name] = id - return fmt.Sprintf("(%d) %s", id, name) -} - -// printTree prints a tree-based report in text form. -func printTree(w io.Writer, rpt *Report) error { - const separator = "----------------------------------------------------------+-------------" - const legend = " flat flat% sum% cum cum% calls calls% + context " - - g, err := newGraph(rpt) - if err != nil { - return err - } - - origCount, droppedNodes, _ := g.preprocess(rpt) - fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n")) - - fmt.Fprintln(w, separator) - fmt.Fprintln(w, legend) - var flatSum int64 - - rx := rpt.options.Symbol - for _, n := range g.ns { - name, flat, cum := n.info.prettyName(), n.flat, n.cum - - // Skip any entries that do not match the regexp (for the "peek" command). - if rx != nil && !rx.MatchString(name) { - continue - } - - fmt.Fprintln(w, separator) - // Print incoming edges. - inEdges := sortedEdges(n.in) - inSum := inEdges.sum() - for _, in := range inEdges { - fmt.Fprintf(w, "%50s %s | %s\n", rpt.formatValue(in.weight), - percentage(in.weight, inSum), in.src.info.prettyName()) - } - - // Print current node. - flatSum += flat - fmt.Fprintf(w, "%10s %s %s %10s %s | %s\n", - rpt.formatValue(flat), - percentage(flat, rpt.total), - percentage(flatSum, rpt.total), - rpt.formatValue(cum), - percentage(cum, rpt.total), - name) - - // Print outgoing edges. - outEdges := sortedEdges(n.out) - outSum := outEdges.sum() - for _, out := range outEdges { - fmt.Fprintf(w, "%50s %s | %s\n", rpt.formatValue(out.weight), - percentage(out.weight, outSum), out.dest.info.prettyName()) - } - } - if len(g.ns) > 0 { - fmt.Fprintln(w, separator) - } - return nil -} - -// printDOT prints an annotated callgraph in DOT format. -func printDOT(w io.Writer, rpt *Report) error { - g, err := newGraph(rpt) - if err != nil { - return err - } - - origCount, droppedNodes, droppedEdges := g.preprocess(rpt) - - prof := rpt.prof - graphname := "unnamed" - if len(prof.Mapping) > 0 { - graphname = filepath.Base(prof.Mapping[0].File) - } - fmt.Fprintln(w, `digraph "`+graphname+`" {`) - fmt.Fprintln(w, `node [style=filled fillcolor="#f8f8f8"]`) - fmt.Fprintln(w, dotLegend(rpt, g, origCount, droppedNodes, droppedEdges)) - - if len(g.ns) == 0 { - fmt.Fprintln(w, "}") - return nil - } - - // Make sure nodes have a unique consistent id. - nodeIndex := make(map[*node]int) - maxFlat := float64(g.ns[0].flat) - for i, n := range g.ns { - nodeIndex[n] = i + 1 - if float64(n.flat) > maxFlat { - maxFlat = float64(n.flat) - } - } - var edges edgeList - for _, n := range g.ns { - node := dotNode(rpt, maxFlat, nodeIndex[n], n) - fmt.Fprintln(w, node) - if nodelets := dotNodelets(rpt, nodeIndex[n], n); nodelets != "" { - fmt.Fprint(w, nodelets) - } - - // Collect outgoing edges. - for _, e := range n.out { - edges = append(edges, e) - } - } - // Sort edges by frequency as a hint to the graph layout engine. - sort.Sort(edges) - for _, e := range edges { - fmt.Fprintln(w, dotEdge(rpt, nodeIndex[e.src], nodeIndex[e.dest], e)) - } - fmt.Fprintln(w, "}") - return nil -} - -// percentage computes the percentage of total of a value, and encodes -// it as a string. At least two digits of precision are printed. -func percentage(value, total int64) string { - var ratio float64 - if total != 0 { - ratio = float64(value) / float64(total) * 100 - } - switch { - case ratio >= 99.95: - return " 100%" - case ratio >= 1.0: - return fmt.Sprintf("%5.2f%%", ratio) - default: - return fmt.Sprintf("%5.2g%%", ratio) - } -} - -// dotLegend generates the overall graph label for a report in DOT format. -func dotLegend(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) string { - label := legendLabels(rpt) - label = append(label, legendDetailLabels(rpt, g, origCount, droppedNodes, droppedEdges)...) - return fmt.Sprintf(`subgraph cluster_L { L [shape=box fontsize=32 label="%s\l"] }`, strings.Join(label, `\l`)) -} - -// legendLabels generates labels exclusive to graph visualization. -func legendLabels(rpt *Report) []string { - prof := rpt.prof - o := rpt.options - var label []string - if len(prof.Mapping) > 0 { - if prof.Mapping[0].File != "" { - label = append(label, "File: "+filepath.Base(prof.Mapping[0].File)) - } - if prof.Mapping[0].BuildID != "" { - label = append(label, "Build ID: "+prof.Mapping[0].BuildID) - } - } - if o.SampleType != "" { - label = append(label, "Type: "+o.SampleType) - } - if prof.TimeNanos != 0 { - const layout = "Jan 2, 2006 at 3:04pm (MST)" - label = append(label, "Time: "+time.Unix(0, prof.TimeNanos).Format(layout)) - } - if prof.DurationNanos != 0 { - label = append(label, fmt.Sprintf("Duration: %v", time.Duration(prof.DurationNanos))) - } - return label -} - -// legendDetailLabels generates labels common to graph and text visualization. -func legendDetailLabels(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) []string { - nodeFraction := rpt.options.NodeFraction - edgeFraction := rpt.options.EdgeFraction - nodeCount := rpt.options.NodeCount - - label := []string{} - - var flatSum int64 - for _, n := range g.ns { - flatSum = flatSum + n.flat - } - - label = append(label, fmt.Sprintf("%s of %s total (%s)", rpt.formatValue(flatSum), rpt.formatValue(rpt.total), percentage(flatSum, rpt.total))) - - if rpt.total > 0 { - if droppedNodes > 0 { - label = append(label, genLabel(droppedNodes, "node", "cum", - rpt.formatValue(int64(float64(rpt.total)*nodeFraction)))) - } - if droppedEdges > 0 { - label = append(label, genLabel(droppedEdges, "edge", "freq", - rpt.formatValue(int64(float64(rpt.total)*edgeFraction)))) - } - if nodeCount > 0 && nodeCount < origCount { - label = append(label, fmt.Sprintf("Showing top %d nodes out of %d (cum >= %s)", - nodeCount, origCount, - rpt.formatValue(g.ns[len(g.ns)-1].cum))) - } - } - return label -} - -func genLabel(d int, n, l, f string) string { - if d > 1 { - n = n + "s" - } - return fmt.Sprintf("Dropped %d %s (%s <= %s)", d, n, l, f) -} - -// dotNode generates a graph node in DOT format. -func dotNode(rpt *Report, maxFlat float64, rIndex int, n *node) string { - flat, cum := n.flat, n.cum - - labels := strings.Split(n.info.prettyName(), "::") - label := strings.Join(labels, `\n`) + `\n` - - flatValue := rpt.formatValue(flat) - if flat > 0 { - label = label + fmt.Sprintf(`%s(%s)`, - flatValue, - strings.TrimSpace(percentage(flat, rpt.total))) - } else { - label = label + "0" - } - cumValue := flatValue - if cum != flat { - if flat > 0 { - label = label + `\n` - } else { - label = label + " " - } - cumValue = rpt.formatValue(cum) - label = label + fmt.Sprintf(`of %s(%s)`, - cumValue, - strings.TrimSpace(percentage(cum, rpt.total))) - } - - // Scale font sizes from 8 to 24 based on percentage of flat frequency. - // Use non linear growth to emphasize the size difference. - baseFontSize, maxFontGrowth := 8, 16.0 - fontSize := baseFontSize - if maxFlat > 0 && flat > 0 && float64(flat) <= maxFlat { - fontSize += int(math.Ceil(maxFontGrowth * math.Sqrt(float64(flat)/maxFlat))) - } - return fmt.Sprintf(`N%d [label="%s" fontsize=%d shape=box tooltip="%s (%s)"]`, - rIndex, - label, - fontSize, n.info.prettyName(), cumValue) -} - -// dotEdge generates a graph edge in DOT format. -func dotEdge(rpt *Report, from, to int, e *edgeInfo) string { - w := rpt.formatValue(e.weight) - attr := fmt.Sprintf(`label=" %s"`, w) - if rpt.total > 0 { - if weight := 1 + int(e.weight*100/rpt.total); weight > 1 { - attr = fmt.Sprintf(`%s weight=%d`, attr, weight) - } - if width := 1 + int(e.weight*5/rpt.total); width > 1 { - attr = fmt.Sprintf(`%s penwidth=%d`, attr, width) - } - } - arrow := "->" - if e.residual { - arrow = "..." - } - tooltip := fmt.Sprintf(`"%s %s %s (%s)"`, - e.src.info.prettyName(), arrow, e.dest.info.prettyName(), w) - attr = fmt.Sprintf(`%s tooltip=%s labeltooltip=%s`, - attr, tooltip, tooltip) - - if e.residual { - attr = attr + ` style="dotted"` - } - - if len(e.src.tags) > 0 { - // Separate children further if source has tags. - attr = attr + " minlen=2" - } - return fmt.Sprintf("N%d -> N%d [%s]", from, to, attr) -} - -// dotNodelets generates the DOT boxes for the node tags. -func dotNodelets(rpt *Report, rIndex int, n *node) (dot string) { - const maxNodelets = 4 // Number of nodelets for alphanumeric labels - const maxNumNodelets = 4 // Number of nodelets for numeric labels - - var ts, nts tags - for _, t := range n.tags { - if t.unit == "" { - ts = append(ts, t) - } else { - nts = append(nts, t) - } - } - - // Select the top maxNodelets alphanumeric labels by weight - sort.Sort(ts) - if len(ts) > maxNodelets { - ts = ts[:maxNodelets] - } - for i, t := range ts { - weight := rpt.formatValue(t.weight) - dot += fmt.Sprintf(`N%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight) - dot += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight) - } - - // Collapse numeric labels into maxNumNodelets buckets, of the form: - // 1MB..2MB, 3MB..5MB, ... - nts = collapseTags(nts, maxNumNodelets) - sort.Sort(nts) - for i, t := range nts { - weight := rpt.formatValue(t.weight) - dot += fmt.Sprintf(`NN%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight) - dot += fmt.Sprintf(`N%d -> NN%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight) - } - - return dot -} - -// graph summarizes a performance profile into a format that is -// suitable for visualization. -type graph struct { - ns nodes -} - -// nodes is an ordered collection of graph nodes. -type nodes []*node - -// tags represent sample annotations -type tags []*tag -type tagMap map[string]*tag - -type tag struct { - name string - unit string // Describe the value, "" for non-numeric tags - value int64 - weight int64 -} - -func (t tags) Len() int { return len(t) } -func (t tags) Swap(i, j int) { t[i], t[j] = t[j], t[i] } -func (t tags) Less(i, j int) bool { - if t[i].weight == t[j].weight { - return t[i].name < t[j].name - } - return t[i].weight > t[j].weight -} - -// node is an entry on a profiling report. It represents a unique -// program location. It can include multiple names to represent -// inlined functions. -type node struct { - info nodeInfo // Information associated to this entry. - - // values associated to this node. - // flat is exclusive to this node, cum includes all descendents. - flat, cum int64 - - // in and out contains the nodes immediately reaching or reached by this nodes. - in, out edgeMap - - // tags provide additional information about subsets of a sample. - tags tagMap -} - -type nodeInfo struct { - name string - origName string - address uint64 - file string - startLine, lineno int - inline bool - lowPriority bool - objfile string - parent *node // Used only if creating a calltree -} - -func (n *node) addTags(s *profile.Sample, weight int64) { - // Add a tag with all string labels - var labels []string - for key, vals := range s.Label { - for _, v := range vals { - labels = append(labels, key+":"+v) - } - } - if len(labels) > 0 { - sort.Strings(labels) - l := n.tags.findOrAddTag(strings.Join(labels, `\n`), "", 0) - l.weight += weight - } - - for key, nvals := range s.NumLabel { - for _, v := range nvals { - label := scaledValueLabel(v, key, "auto") - l := n.tags.findOrAddTag(label, key, v) - l.weight += weight - } - } -} - -func (m tagMap) findOrAddTag(label, unit string, value int64) *tag { - if l := m[label]; l != nil { - return l - } - l := &tag{ - name: label, - unit: unit, - value: value, - } - m[label] = l - return l -} - -// collapseTags reduces the number of entries in a tagMap by merging -// adjacent nodes into ranges. It uses a greedy approach to merge -// starting with the entries with the lowest weight. -func collapseTags(ts tags, count int) tags { - if len(ts) <= count { - return ts - } - - sort.Sort(ts) - tagGroups := make([]tags, count) - for i, t := range ts[:count] { - tagGroups[i] = tags{t} - } - for _, t := range ts[count:] { - g, d := 0, tagDistance(t, tagGroups[0][0]) - for i := 1; i < count; i++ { - if nd := tagDistance(t, tagGroups[i][0]); nd < d { - g, d = i, nd - } - } - tagGroups[g] = append(tagGroups[g], t) - } - - var nts tags - for _, g := range tagGroups { - l, w := tagGroupLabel(g) - nts = append(nts, &tag{ - name: l, - weight: w, - }) - } - return nts -} - -func tagDistance(t, u *tag) float64 { - v, _ := ScaleValue(u.value, u.unit, t.unit) - if v < float64(t.value) { - return float64(t.value) - v - } - return v - float64(t.value) -} - -func tagGroupLabel(g tags) (string, int64) { - if len(g) == 1 { - t := g[0] - return scaledValueLabel(t.value, t.unit, "auto"), t.weight - } - min := g[0] - max := g[0] - w := min.weight - for _, t := range g[1:] { - if v, _ := ScaleValue(t.value, t.unit, min.unit); int64(v) < min.value { - min = t - } - if v, _ := ScaleValue(t.value, t.unit, max.unit); int64(v) > max.value { - max = t - } - w += t.weight - } - return scaledValueLabel(min.value, min.unit, "auto") + ".." + - scaledValueLabel(max.value, max.unit, "auto"), w -} - -// sumNodes adds the flat and sum values on a report. -func sumNodes(ns nodes) (flat int64, cum int64) { - for _, n := range ns { - flat += n.flat - cum += n.cum - } - return -} - -type edgeMap map[*node]*edgeInfo - -// edgeInfo contains any attributes to be represented about edges in a graph/ -type edgeInfo struct { - src, dest *node - // The summary weight of the edge - weight int64 - // residual edges connect nodes that were connected through a - // separate node, which has been removed from the report. - residual bool -} - -// bumpWeight increases the weight of an edge. If there isn't such an -// edge in the map one is created. -func bumpWeight(from, to *node, w int64, residual bool) { - if from.out[to] != to.in[from] { - panic(fmt.Errorf("asymmetric edges %v %v", *from, *to)) - } - - if n := from.out[to]; n != nil { - n.weight += w - if n.residual && !residual { - n.residual = false - } - return - } - - info := &edgeInfo{src: from, dest: to, weight: w, residual: residual} - from.out[to] = info - to.in[from] = info -} - -// Output formats. -const ( - Proto = iota - Dot - Tags - Tree - Text - Raw - Dis - List - WebList - Callgrind -) - -// Options are the formatting and filtering options used to generate a -// profile. -type Options struct { - OutputFormat int - - CumSort bool - CallTree bool - PrintAddresses bool - DropNegative bool - Ratio float64 - - NodeCount int - NodeFraction float64 - EdgeFraction float64 - - SampleType string - SampleUnit string // Unit for the sample data from the profile. - OutputUnit string // Units for data formatting in report. - - Symbol *regexp.Regexp // Symbols to include on disassembly report. -} - -// newGraph summarizes performance data from a profile into a graph. -func newGraph(rpt *Report) (g graph, err error) { - prof := rpt.prof - o := rpt.options - - // Generate a tree for graphical output if requested. - buildTree := o.CallTree && o.OutputFormat == Dot - - locations := make(map[uint64][]nodeInfo) - for _, l := range prof.Location { - locations[l.ID] = newLocInfo(l) - } - - nm := make(nodeMap) - for _, sample := range prof.Sample { - if sample.Location == nil { - continue - } - - // Construct list of node names for sample. - var stack []nodeInfo - for _, loc := range sample.Location { - id := loc.ID - stack = append(stack, locations[id]...) - } - - // Upfront pass to update the parent chains, to prevent the - // merging of nodes with different parents. - if buildTree { - var nn *node - for i := len(stack); i > 0; i-- { - n := &stack[i-1] - n.parent = nn - nn = nm.findOrInsertNode(*n) - } - } - - leaf := nm.findOrInsertNode(stack[0]) - weight := rpt.sampleValue(sample) - leaf.addTags(sample, weight) - - // Aggregate counter data. - leaf.flat += weight - seen := make(map[*node]bool) - var nn *node - for _, s := range stack { - n := nm.findOrInsertNode(s) - if !seen[n] { - seen[n] = true - n.cum += weight - - if nn != nil { - bumpWeight(n, nn, weight, false) - } - } - nn = n - } - } - - // Collect new nodes into a report. - ns := make(nodes, 0, len(nm)) - for _, n := range nm { - if rpt.options.DropNegative && n.flat < 0 { - continue - } - ns = append(ns, n) - } - - return graph{ns}, nil -} - -// Create a slice of formatted names for a location. -func newLocInfo(l *profile.Location) []nodeInfo { - var objfile string - - if m := l.Mapping; m != nil { - objfile = filepath.Base(m.File) - } - - if len(l.Line) == 0 { - return []nodeInfo{ - { - address: l.Address, - objfile: objfile, - }, - } - } - var info []nodeInfo - numInlineFrames := len(l.Line) - 1 - for li, line := range l.Line { - ni := nodeInfo{ - address: l.Address, - lineno: int(line.Line), - inline: li < numInlineFrames, - objfile: objfile, - } - - if line.Function != nil { - ni.name = line.Function.Name - ni.origName = line.Function.SystemName - ni.file = line.Function.Filename - ni.startLine = int(line.Function.StartLine) - } - - info = append(info, ni) - } - return info -} - -// nodeMap maps from a node info struct to a node. It is used to merge -// report entries with the same info. -type nodeMap map[nodeInfo]*node - -func (m nodeMap) findOrInsertNode(info nodeInfo) *node { - rr := m[info] - if rr == nil { - rr = &node{ - info: info, - in: make(edgeMap), - out: make(edgeMap), - tags: make(map[string]*tag), - } - m[info] = rr - } - return rr -} - -// preprocess does any required filtering/sorting according to the -// report options. Returns the mapping from each node to any nodes -// removed by path compression and statistics on the nodes/edges removed. -func (g *graph) preprocess(rpt *Report) (origCount, droppedNodes, droppedEdges int) { - o := rpt.options - - // Compute total weight of current set of nodes. - // This is <= rpt.total because of node filtering. - var totalValue int64 - for _, n := range g.ns { - totalValue += n.flat - } - - // Remove nodes with value <= total*nodeFraction - if nodeFraction := o.NodeFraction; nodeFraction > 0 { - var removed nodes - minValue := int64(float64(totalValue) * nodeFraction) - kept := make(nodes, 0, len(g.ns)) - for _, n := range g.ns { - if n.cum < minValue { - removed = append(removed, n) - } else { - kept = append(kept, n) - tagsKept := make(map[string]*tag) - for s, t := range n.tags { - if t.weight >= minValue { - tagsKept[s] = t - } - } - n.tags = tagsKept - } - } - droppedNodes = len(removed) - removeNodes(removed, false, false) - g.ns = kept - } - - // Remove edges below minimum frequency. - if edgeFraction := o.EdgeFraction; edgeFraction > 0 { - minEdge := int64(float64(totalValue) * edgeFraction) - for _, n := range g.ns { - for src, e := range n.in { - if e.weight < minEdge { - delete(n.in, src) - delete(src.out, n) - droppedEdges++ - } - } - } - } - - sortOrder := flatName - if o.CumSort { - // Force cum sorting for graph output, to preserve connectivity. - sortOrder = cumName - } - - // Nodes that have flat==0 and a single in/out do not provide much - // information. Give them first chance to be removed. Do not consider edges - // from/to nodes that are expected to be removed. - maxNodes := o.NodeCount - if o.OutputFormat == Dot { - if maxNodes > 0 && maxNodes < len(g.ns) { - sortOrder = cumName - g.ns.sort(cumName) - cumCutoff := g.ns[maxNodes].cum - for _, n := range g.ns { - if n.flat == 0 { - if count := countEdges(n.out, cumCutoff); count > 1 { - continue - } - if count := countEdges(n.in, cumCutoff); count != 1 { - continue - } - n.info.lowPriority = true - } - } - } - } - - g.ns.sort(sortOrder) - if maxNodes > 0 { - origCount = len(g.ns) - for index, nodes := 0, 0; index < len(g.ns); index++ { - nodes++ - // For DOT output, count the tags as nodes since we will draw - // boxes for them. - if o.OutputFormat == Dot { - nodes += len(g.ns[index].tags) - } - if nodes > maxNodes { - // Trim to the top n nodes. Create dotted edges to bridge any - // broken connections. - removeNodes(g.ns[index:], true, true) - g.ns = g.ns[:index] - break - } - } - } - removeRedundantEdges(g.ns) - - // Select best unit for profile output. - // Find the appropriate units for the smallest non-zero sample - if o.OutputUnit == "minimum" && len(g.ns) > 0 { - var maxValue, minValue int64 - - for _, n := range g.ns { - if n.flat > 0 && (minValue == 0 || n.flat < minValue) { - minValue = n.flat - } - if n.cum > maxValue { - maxValue = n.cum - } - } - if r := o.Ratio; r > 0 && r != 1 { - minValue = int64(float64(minValue) * r) - maxValue = int64(float64(maxValue) * r) - } - - _, minUnit := ScaleValue(minValue, o.SampleUnit, "minimum") - _, maxUnit := ScaleValue(maxValue, o.SampleUnit, "minimum") - - unit := minUnit - if minUnit != maxUnit && minValue*100 < maxValue && o.OutputFormat != Callgrind { - // Minimum and maximum values have different units. Scale - // minimum by 100 to use larger units, allowing minimum value to - // be scaled down to 0.01, except for callgrind reports since - // they can only represent integer values. - _, unit = ScaleValue(100*minValue, o.SampleUnit, "minimum") - } - - if unit != "" { - o.OutputUnit = unit - } else { - o.OutputUnit = o.SampleUnit - } - } - return -} - -// countEdges counts the number of edges below the specified cutoff. -func countEdges(el edgeMap, cutoff int64) int { - count := 0 - for _, e := range el { - if e.weight > cutoff { - count++ - } - } - return count -} - -// removeNodes removes nodes from a report, optionally bridging -// connections between in/out edges and spreading out their weights -// proportionally. residual marks new bridge edges as residual -// (dotted). -func removeNodes(toRemove nodes, bridge, residual bool) { - for _, n := range toRemove { - for ei := range n.in { - delete(ei.out, n) - } - if bridge { - for ei, wi := range n.in { - for eo, wo := range n.out { - var weight int64 - if n.cum != 0 { - weight = int64(float64(wo.weight) * (float64(wi.weight) / float64(n.cum))) - } - bumpWeight(ei, eo, weight, residual) - } - } - } - for eo := range n.out { - delete(eo.in, n) - } - } -} - -// removeRedundantEdges removes residual edges if the destination can -// be reached through another path. This is done to simplify the graph -// while preserving connectivity. -func removeRedundantEdges(ns nodes) { - // Walk the nodes and outgoing edges in reverse order to prefer - // removing edges with the lowest weight. - for i := len(ns); i > 0; i-- { - n := ns[i-1] - in := sortedEdges(n.in) - for j := len(in); j > 0; j-- { - if e := in[j-1]; e.residual && isRedundant(e) { - delete(e.src.out, e.dest) - delete(e.dest.in, e.src) - } - } - } -} - -// isRedundant determines if an edge can be removed without impacting -// connectivity of the whole graph. This is implemented by checking if the -// nodes have a common ancestor after removing the edge. -func isRedundant(e *edgeInfo) bool { - destPred := predecessors(e, e.dest) - if len(destPred) == 1 { - return false - } - srcPred := predecessors(e, e.src) - - for n := range srcPred { - if destPred[n] && n != e.dest { - return true - } - } - return false -} - -// predecessors collects all the predecessors to node n, excluding edge e. -func predecessors(e *edgeInfo, n *node) map[*node]bool { - seen := map[*node]bool{n: true} - queue := []*node{n} - for len(queue) > 0 { - n := queue[0] - queue = queue[1:] - for _, ie := range n.in { - if e == ie || seen[ie.src] { - continue - } - seen[ie.src] = true - queue = append(queue, ie.src) - } - } - return seen -} - -// nodeSorter is a mechanism used to allow a report to be sorted -// in different ways. -type nodeSorter struct { - rs nodes - less func(i, j int) bool -} - -func (s nodeSorter) Len() int { return len(s.rs) } -func (s nodeSorter) Swap(i, j int) { s.rs[i], s.rs[j] = s.rs[j], s.rs[i] } -func (s nodeSorter) Less(i, j int) bool { return s.less(i, j) } - -type nodeOrder int - -const ( - flatName nodeOrder = iota - flatCumName - cumName - nameOrder - fileOrder - addressOrder -) - -// sort reorders the entries in a report based on the specified -// ordering criteria. The result is sorted in decreasing order for -// numeric quantities, alphabetically for text, and increasing for -// addresses. -func (ns nodes) sort(o nodeOrder) error { - var s nodeSorter - - switch o { - case flatName: - s = nodeSorter{ns, - func(i, j int) bool { - if iv, jv := ns[i].flat, ns[j].flat; iv != jv { - return iv > jv - } - if ns[i].info.prettyName() != ns[j].info.prettyName() { - return ns[i].info.prettyName() < ns[j].info.prettyName() - } - iv, jv := ns[i].cum, ns[j].cum - return iv > jv - }, - } - case flatCumName: - s = nodeSorter{ns, - func(i, j int) bool { - if iv, jv := ns[i].flat, ns[j].flat; iv != jv { - return iv > jv - } - if iv, jv := ns[i].cum, ns[j].cum; iv != jv { - return iv > jv - } - return ns[i].info.prettyName() < ns[j].info.prettyName() - }, - } - case cumName: - s = nodeSorter{ns, - func(i, j int) bool { - if ns[i].info.lowPriority != ns[j].info.lowPriority { - return ns[j].info.lowPriority - } - if iv, jv := ns[i].cum, ns[j].cum; iv != jv { - return iv > jv - } - if ns[i].info.prettyName() != ns[j].info.prettyName() { - return ns[i].info.prettyName() < ns[j].info.prettyName() - } - iv, jv := ns[i].flat, ns[j].flat - return iv > jv - }, - } - case nameOrder: - s = nodeSorter{ns, - func(i, j int) bool { - return ns[i].info.name < ns[j].info.name - }, - } - case fileOrder: - s = nodeSorter{ns, - func(i, j int) bool { - return ns[i].info.file < ns[j].info.file - }, - } - case addressOrder: - s = nodeSorter{ns, - func(i, j int) bool { - return ns[i].info.address < ns[j].info.address - }, - } - default: - return fmt.Errorf("report: unrecognized sort ordering: %d", o) - } - sort.Sort(s) - return nil -} - -type edgeList []*edgeInfo - -// sortedEdges return a slice of the edges in the map, sorted for -// visualization. The sort order is first based on the edge weight -// (higher-to-lower) and then by the node names to avoid flakiness. -func sortedEdges(edges map[*node]*edgeInfo) edgeList { - el := make(edgeList, 0, len(edges)) - for _, w := range edges { - el = append(el, w) - } - - sort.Sort(el) - return el -} - -func (el edgeList) Len() int { - return len(el) -} - -func (el edgeList) Less(i, j int) bool { - if el[i].weight != el[j].weight { - return el[i].weight > el[j].weight - } - - from1 := el[i].src.info.prettyName() - from2 := el[j].src.info.prettyName() - if from1 != from2 { - return from1 < from2 - } - - to1 := el[i].dest.info.prettyName() - to2 := el[j].dest.info.prettyName() - - return to1 < to2 -} - -func (el edgeList) Swap(i, j int) { - el[i], el[j] = el[j], el[i] -} - -func (el edgeList) sum() int64 { - var ret int64 - for _, e := range el { - ret += e.weight - } - return ret -} - -// ScaleValue reformats a value from a unit to a different unit. -func ScaleValue(value int64, fromUnit, toUnit string) (sv float64, su string) { - // Avoid infinite recursion on overflow. - if value < 0 && -value > 0 { - v, u := ScaleValue(-value, fromUnit, toUnit) - return -v, u - } - if m, u, ok := memoryLabel(value, fromUnit, toUnit); ok { - return m, u - } - if t, u, ok := timeLabel(value, fromUnit, toUnit); ok { - return t, u - } - // Skip non-interesting units. - switch toUnit { - case "count", "sample", "unit", "minimum": - return float64(value), "" - default: - return float64(value), toUnit - } -} - -func scaledValueLabel(value int64, fromUnit, toUnit string) string { - v, u := ScaleValue(value, fromUnit, toUnit) - - sv := strings.TrimSuffix(fmt.Sprintf("%.2f", v), ".00") - if sv == "0" || sv == "-0" { - return "0" - } - return sv + u -} - -func memoryLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) { - fromUnit = strings.TrimSuffix(strings.ToLower(fromUnit), "s") - toUnit = strings.TrimSuffix(strings.ToLower(toUnit), "s") - - switch fromUnit { - case "byte", "b": - case "kilobyte", "kb": - value *= 1024 - case "megabyte", "mb": - value *= 1024 * 1024 - case "gigabyte", "gb": - value *= 1024 * 1024 * 1024 - default: - return 0, "", false - } - - if toUnit == "minimum" || toUnit == "auto" { - switch { - case value < 1024: - toUnit = "b" - case value < 1024*1024: - toUnit = "kb" - case value < 1024*1024*1024: - toUnit = "mb" - default: - toUnit = "gb" - } - } - - var output float64 - switch toUnit { - default: - output, toUnit = float64(value), "B" - case "kb", "kbyte", "kilobyte": - output, toUnit = float64(value)/1024, "kB" - case "mb", "mbyte", "megabyte": - output, toUnit = float64(value)/(1024*1024), "MB" - case "gb", "gbyte", "gigabyte": - output, toUnit = float64(value)/(1024*1024*1024), "GB" - } - return output, toUnit, true -} - -func timeLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) { - fromUnit = strings.ToLower(fromUnit) - if len(fromUnit) > 2 { - fromUnit = strings.TrimSuffix(fromUnit, "s") - } - - toUnit = strings.ToLower(toUnit) - if len(toUnit) > 2 { - toUnit = strings.TrimSuffix(toUnit, "s") - } - - var d time.Duration - switch fromUnit { - case "nanosecond", "ns": - d = time.Duration(value) * time.Nanosecond - case "microsecond": - d = time.Duration(value) * time.Microsecond - case "millisecond", "ms": - d = time.Duration(value) * time.Millisecond - case "second", "sec": - d = time.Duration(value) * time.Second - case "cycle": - return float64(value), "", true - default: - return 0, "", false - } - - if toUnit == "minimum" || toUnit == "auto" { - switch { - case d < 1*time.Microsecond: - toUnit = "ns" - case d < 1*time.Millisecond: - toUnit = "us" - case d < 1*time.Second: - toUnit = "ms" - case d < 1*time.Minute: - toUnit = "sec" - case d < 1*time.Hour: - toUnit = "min" - case d < 24*time.Hour: - toUnit = "hour" - case d < 15*24*time.Hour: - toUnit = "day" - case d < 120*24*time.Hour: - toUnit = "week" - default: - toUnit = "year" - } - } - - var output float64 - dd := float64(d) - switch toUnit { - case "ns", "nanosecond": - output, toUnit = dd/float64(time.Nanosecond), "ns" - case "us", "microsecond": - output, toUnit = dd/float64(time.Microsecond), "us" - case "ms", "millisecond": - output, toUnit = dd/float64(time.Millisecond), "ms" - case "min", "minute": - output, toUnit = dd/float64(time.Minute), "mins" - case "hour", "hr": - output, toUnit = dd/float64(time.Hour), "hrs" - case "day": - output, toUnit = dd/float64(24*time.Hour), "days" - case "week", "wk": - output, toUnit = dd/float64(7*24*time.Hour), "wks" - case "year", "yr": - output, toUnit = dd/float64(365*7*24*time.Hour), "yrs" - default: - fallthrough - case "sec", "second", "s": - output, toUnit = dd/float64(time.Second), "s" - } - return output, toUnit, true -} - -// prettyName determines the printable name to be used for a node. -func (info *nodeInfo) prettyName() string { - var name string - if info.address != 0 { - name = fmt.Sprintf("%016x", info.address) - } - - if info.name != "" { - name = name + " " + info.name - } - - if info.file != "" { - name += " " + trimPath(info.file) - if info.lineno != 0 { - name += fmt.Sprintf(":%d", info.lineno) - } - } - - if info.inline { - name = name + " (inline)" - } - - if name = strings.TrimSpace(name); name == "" && info.objfile != "" { - name = "[" + info.objfile + "]" - } - return name -} - -// New builds a new report indexing the sample values interpreting the -// samples with the provided function. -func New(prof *profile.Profile, options Options, value func(s *profile.Sample) int64, unit string) *Report { - o := &options - if o.SampleUnit == "" { - o.SampleUnit = unit - } - format := func(v int64) string { - if r := o.Ratio; r > 0 && r != 1 { - fv := float64(v) * r - v = int64(fv) - } - return scaledValueLabel(v, o.SampleUnit, o.OutputUnit) - } - return &Report{prof, computeTotal(prof, value), o, value, format} -} - -// NewDefault builds a new report indexing the sample values with the -// last value available. -func NewDefault(prof *profile.Profile, options Options) *Report { - index := len(prof.SampleType) - 1 - o := &options - if o.SampleUnit == "" { - o.SampleUnit = strings.ToLower(prof.SampleType[index].Unit) - } - value := func(s *profile.Sample) int64 { - return s.Value[index] - } - format := func(v int64) string { - if r := o.Ratio; r > 0 && r != 1 { - fv := float64(v) * r - v = int64(fv) - } - return scaledValueLabel(v, o.SampleUnit, o.OutputUnit) - } - return &Report{prof, computeTotal(prof, value), o, value, format} -} - -func computeTotal(prof *profile.Profile, value func(s *profile.Sample) int64) int64 { - var ret int64 - for _, sample := range prof.Sample { - ret += value(sample) - } - return ret -} - -// Report contains the data and associated routines to extract a -// report from a profile. -type Report struct { - prof *profile.Profile - total int64 - options *Options - sampleValue func(*profile.Sample) int64 - formatValue func(int64) string -} diff --git a/src/cmd/pprof/internal/report/source.go b/src/cmd/pprof/internal/report/source.go deleted file mode 100644 index 908be21424..0000000000 --- a/src/cmd/pprof/internal/report/source.go +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2014 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 report - -// This file contains routines related to the generation of annotated -// source listings. - -import ( - "bufio" - "fmt" - "html/template" - "io" - "os" - "path/filepath" - "sort" - "strconv" - "strings" - - "cmd/pprof/internal/plugin" -) - -// printSource prints an annotated source listing, include all -// functions with samples that match the regexp rpt.options.symbol. -// The sources are sorted by function name and then by filename to -// eliminate potential nondeterminism. -func printSource(w io.Writer, rpt *Report) error { - o := rpt.options - g, err := newGraph(rpt) - if err != nil { - return err - } - - // Identify all the functions that match the regexp provided. - // Group nodes for each matching function. - var functions nodes - functionNodes := make(map[string]nodes) - for _, n := range g.ns { - if !o.Symbol.MatchString(n.info.name) { - continue - } - if functionNodes[n.info.name] == nil { - functions = append(functions, n) - } - functionNodes[n.info.name] = append(functionNodes[n.info.name], n) - } - functions.sort(nameOrder) - - fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total)) - for _, fn := range functions { - name := fn.info.name - - // Identify all the source files associated to this function. - // Group nodes for each source file. - var sourceFiles nodes - fileNodes := make(map[string]nodes) - for _, n := range functionNodes[name] { - if n.info.file == "" { - continue - } - if fileNodes[n.info.file] == nil { - sourceFiles = append(sourceFiles, n) - } - fileNodes[n.info.file] = append(fileNodes[n.info.file], n) - } - - if len(sourceFiles) == 0 { - fmt.Printf("No source information for %s\n", name) - continue - } - - sourceFiles.sort(fileOrder) - - // Print each file associated with this function. - for _, fl := range sourceFiles { - filename := fl.info.file - fns := fileNodes[filename] - flatSum, cumSum := sumNodes(fns) - - fnodes, path, err := getFunctionSource(name, filename, fns, 0, 0) - fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, path) - fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n", - rpt.formatValue(flatSum), rpt.formatValue(cumSum), - percentage(cumSum, rpt.total)) - - if err != nil { - fmt.Fprintf(w, " Error: %v\n", err) - continue - } - - for _, fn := range fnodes { - fmt.Fprintf(w, "%10s %10s %6d:%s\n", valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt), fn.info.lineno, fn.info.name) - } - } - } - return nil -} - -// printWebSource prints an annotated source listing, include all -// functions with samples that match the regexp rpt.options.symbol. -func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error { - o := rpt.options - g, err := newGraph(rpt) - if err != nil { - return err - } - - // If the regexp source can be parsed as an address, also match - // functions that land on that address. - var address *uint64 - if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil { - address = &hex - } - - // Extract interesting symbols from binary files in the profile and - // classify samples per symbol. - symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj) - symNodes := nodesPerSymbol(g.ns, symbols) - - // Sort symbols for printing. - var syms objSymbols - for s := range symNodes { - syms = append(syms, s) - } - sort.Sort(syms) - - if len(syms) == 0 { - return fmt.Errorf("no samples found on routines matching: %s", o.Symbol.String()) - } - - printHeader(w, rpt) - for _, s := range syms { - name := s.sym.Name[0] - // Identify sources associated to a symbol by examining - // symbol samples. Classify samples per source file. - var sourceFiles nodes - fileNodes := make(map[string]nodes) - for _, n := range symNodes[s] { - if n.info.file == "" { - continue - } - if fileNodes[n.info.file] == nil { - sourceFiles = append(sourceFiles, n) - } - fileNodes[n.info.file] = append(fileNodes[n.info.file], n) - } - - if len(sourceFiles) == 0 { - fmt.Printf("No source information for %s\n", name) - continue - } - - sourceFiles.sort(fileOrder) - - // Print each file associated with this function. - for _, fl := range sourceFiles { - filename := fl.info.file - fns := fileNodes[filename] - - asm := assemblyPerSourceLine(symbols, fns, filename, obj) - start, end := sourceCoordinates(asm) - - fnodes, path, err := getFunctionSource(name, filename, fns, start, end) - if err != nil { - fnodes, path = getMissingFunctionSource(filename, asm, start, end) - } - - flatSum, cumSum := sumNodes(fnodes) - printFunctionHeader(w, name, path, flatSum, cumSum, rpt) - for _, fn := range fnodes { - printFunctionSourceLine(w, fn, asm[fn.info.lineno], rpt) - } - printFunctionClosing(w) - } - } - printPageClosing(w) - return nil -} - -// sourceCoordinates returns the lowest and highest line numbers from -// a set of assembly statements. -func sourceCoordinates(asm map[int]nodes) (start, end int) { - for l := range asm { - if start == 0 || l < start { - start = l - } - if end == 0 || l > end { - end = l - } - } - return start, end -} - -// assemblyPerSourceLine disassembles the binary containing a symbol -// and classifies the assembly instructions according to its -// corresponding source line, annotating them with a set of samples. -func assemblyPerSourceLine(objSyms []*objSymbol, rs nodes, src string, obj plugin.ObjTool) map[int]nodes { - assembly := make(map[int]nodes) - // Identify symbol to use for this collection of samples. - o := findMatchingSymbol(objSyms, rs) - if o == nil { - return assembly - } - - // Extract assembly for matched symbol - insns, err := obj.Disasm(o.sym.File, o.sym.Start, o.sym.End) - if err != nil { - return assembly - } - - srcBase := filepath.Base(src) - anodes := annotateAssembly(insns, rs, o.base) - var lineno = 0 - for _, an := range anodes { - if filepath.Base(an.info.file) == srcBase { - lineno = an.info.lineno - } - if lineno != 0 { - assembly[lineno] = append(assembly[lineno], an) - } - } - - return assembly -} - -// findMatchingSymbol looks for the symbol that corresponds to a set -// of samples, by comparing their addresses. -func findMatchingSymbol(objSyms []*objSymbol, ns nodes) *objSymbol { - for _, n := range ns { - for _, o := range objSyms { - if filepath.Base(o.sym.File) == n.info.objfile && - o.sym.Start <= n.info.address-o.base && - n.info.address-o.base <= o.sym.End { - return o - } - } - } - return nil -} - -// printHeader prints the page header for a weblist report. -func printHeader(w io.Writer, rpt *Report) { - fmt.Fprintln(w, weblistPageHeader) - - var labels []string - for _, l := range legendLabels(rpt) { - labels = append(labels, template.HTMLEscapeString(l)) - } - - fmt.Fprintf(w, `
%s
Total: %s
`, - strings.Join(labels, "
\n"), - rpt.formatValue(rpt.total), - ) -} - -// printFunctionHeader prints a function header for a weblist report. -func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64, rpt *Report) { - fmt.Fprintf(w, `

%s

%s -
-  Total:  %10s %10s (flat, cum) %s
-`,
-		template.HTMLEscapeString(name), template.HTMLEscapeString(path),
-		rpt.formatValue(flatSum), rpt.formatValue(cumSum),
-		percentage(cumSum, rpt.total))
-}
-
-// printFunctionSourceLine prints a source line and the corresponding assembly.
-func printFunctionSourceLine(w io.Writer, fn *node, assembly nodes, rpt *Report) {
-	if len(assembly) == 0 {
-		fmt.Fprintf(w,
-			" %6d   %10s %10s %s \n",
-			fn.info.lineno,
-			valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
-			template.HTMLEscapeString(fn.info.name))
-		return
-	}
-
-	fmt.Fprintf(w,
-		" %6d   %10s %10s %s ",
-		fn.info.lineno,
-		valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
-		template.HTMLEscapeString(fn.info.name))
-	fmt.Fprint(w, "")
-	for _, an := range assembly {
-		var fileline string
-		class := "disasmloc"
-		if an.info.file != "" {
-			fileline = fmt.Sprintf("%s:%d", template.HTMLEscapeString(an.info.file), an.info.lineno)
-			if an.info.lineno != fn.info.lineno {
-				class = "unimportant"
-			}
-		}
-		fmt.Fprintf(w, " %8s %10s %10s %8x: %-48s %s\n", "",
-			valueOrDot(an.flat, rpt), valueOrDot(an.cum, rpt),
-			an.info.address,
-			template.HTMLEscapeString(an.info.name),
-			class,
-			template.HTMLEscapeString(fileline))
-	}
-	fmt.Fprintln(w, "")
-}
-
-// printFunctionClosing prints the end of a function in a weblist report.
-func printFunctionClosing(w io.Writer) {
-	fmt.Fprintln(w, "
") -} - -// printPageClosing prints the end of the page in a weblist report. -func printPageClosing(w io.Writer) { - fmt.Fprintln(w, weblistPageClosing) -} - -// getFunctionSource collects the sources of a function from a source -// file and annotates it with the samples in fns. Returns the sources -// as nodes, using the info.name field to hold the source code. -func getFunctionSource(fun, file string, fns nodes, start, end int) (nodes, string, error) { - f, file, err := adjustSourcePath(file) - if err != nil { - return nil, file, err - } - - lineNodes := make(map[int]nodes) - - // Collect source coordinates from profile. - const margin = 5 // Lines before first/after last sample. - if start == 0 { - if fns[0].info.startLine != 0 { - start = fns[0].info.startLine - } else { - start = fns[0].info.lineno - margin - } - } else { - start -= margin - } - if end == 0 { - end = fns[0].info.lineno - } - end += margin - for _, n := range fns { - lineno := n.info.lineno - nodeStart := n.info.startLine - if nodeStart == 0 { - nodeStart = lineno - margin - } - nodeEnd := lineno + margin - if nodeStart < start { - start = nodeStart - } else if nodeEnd > end { - end = nodeEnd - } - lineNodes[lineno] = append(lineNodes[lineno], n) - } - - var src nodes - buf := bufio.NewReader(f) - lineno := 1 - for { - line, err := buf.ReadString('\n') - if err != nil { - if err != io.EOF { - return nil, file, err - } - if line == "" { - // end was at or past EOF; that's okay - break - } - } - if lineno >= start { - flat, cum := sumNodes(lineNodes[lineno]) - - src = append(src, &node{ - info: nodeInfo{ - name: strings.TrimRight(line, "\n"), - lineno: lineno, - }, - flat: flat, - cum: cum, - }) - } - lineno++ - if lineno > end { - break - } - } - return src, file, nil -} - -// getMissingFunctionSource creates a dummy function body to point to -// the source file and annotates it with the samples in asm. -func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int) (nodes, string) { - var fnodes nodes - for i := start; i <= end; i++ { - lrs := asm[i] - if len(lrs) == 0 { - continue - } - flat, cum := sumNodes(lrs) - fnodes = append(fnodes, &node{ - info: nodeInfo{ - name: "???", - lineno: i, - }, - flat: flat, - cum: cum, - }) - } - return fnodes, filename -} - -// adjustSourcePath adjusts the path for a source file by trimming -// known prefixes and searching for the file on all parents of the -// current working dir. -func adjustSourcePath(path string) (*os.File, string, error) { - path = trimPath(path) - f, err := os.Open(path) - if err == nil { - return f, path, nil - } - - if dir, wderr := os.Getwd(); wderr == nil { - for { - parent := filepath.Dir(dir) - if parent == dir { - break - } - if f, err := os.Open(filepath.Join(parent, path)); err == nil { - return f, filepath.Join(parent, path), nil - } - - dir = parent - } - } - - return nil, path, err -} - -// trimPath cleans up a path by removing prefixes that are commonly -// found on profiles. -func trimPath(path string) string { - basePaths := []string{ - "/proc/self/cwd/./", - "/proc/self/cwd/", - } - - sPath := filepath.ToSlash(path) - - for _, base := range basePaths { - if strings.HasPrefix(sPath, base) { - return filepath.FromSlash(sPath[len(base):]) - } - } - return path -} diff --git a/src/cmd/pprof/internal/report/source_html.go b/src/cmd/pprof/internal/report/source_html.go deleted file mode 100644 index 267fabdc4b..0000000000 --- a/src/cmd/pprof/internal/report/source_html.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2014 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 report - -const weblistPageHeader = ` - - - -Pprof listing - - - - -` - -const weblistPageClosing = ` - - -` diff --git a/src/cmd/pprof/internal/svg/svg.go b/src/cmd/pprof/internal/svg/svg.go deleted file mode 100644 index 04f6ff1870..0000000000 --- a/src/cmd/pprof/internal/svg/svg.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2014 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 svg provides tools related to handling of SVG files -package svg - -import ( - "bytes" - "regexp" - "strings" -) - -var ( - viewBox = regexp.MustCompile(``) -) - -// Massage enhances the SVG output from DOT to provide better -// panning inside a web browser. It uses the SVGPan library, which is -// included directly. -func Massage(in bytes.Buffer) string { - svg := string(in.Bytes()) - - // Work around for dot bug which misses quoting some ampersands, - // resulting on unparsable SVG. - svg = strings.Replace(svg, "&;", "&;", -1) - - //Dot's SVG output is - // - // - // - // ... - // - // - // - // Change it to - // - // - // - // - // - // ... - // - // - // - - if loc := viewBox.FindStringIndex(svg); loc != nil { - svg = svg[:loc[0]] + - `` + svgPanJS + `` + - `` + - svg[loc[0]:] - } - - if loc := svgClose.FindStringIndex(svg); loc != nil { - svg = svg[:loc[0]] + - `` + - svg[loc[0]:] - } - - return svg -} diff --git a/src/cmd/pprof/internal/svg/svgpan.go b/src/cmd/pprof/internal/svg/svgpan.go deleted file mode 100644 index 4975b103e3..0000000000 --- a/src/cmd/pprof/internal/svg/svgpan.go +++ /dev/null @@ -1,291 +0,0 @@ -// SVG pan and zoom library. -// See copyright notice in string constant below. - -package svg - -// https://www.cyberz.org/projects/SVGPan/SVGPan.js - -const svgPanJS = ` -/** - * SVGPan library 1.2.1 - * ====================== - * - * Given an unique existing element with id "viewport" (or when missing, the first g - * element), including the the library into any SVG adds the following capabilities: - * - * - Mouse panning - * - Mouse zooming (using the wheel) - * - Object dragging - * - * You can configure the behaviour of the pan/zoom/drag with the variables - * listed in the CONFIGURATION section of this file. - * - * Known issues: - * - * - Zooming (while panning) on Safari has still some issues - * - * Releases: - * - * 1.2.1, Mon Jul 4 00:33:18 CEST 2011, Andrea Leofreddi - * - Fixed a regression with mouse wheel (now working on Firefox 5) - * - Working with viewBox attribute (#4) - * - Added "use strict;" and fixed resulting warnings (#5) - * - Added configuration variables, dragging is disabled by default (#3) - * - * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui - * Fixed a bug with browser mouse handler interaction - * - * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui - * Updated the zoom code to support the mouse wheel on Safari/Chrome - * - * 1.0, Andrea Leofreddi - * First release - * - * This code is licensed under the following BSD license: - * - * Copyright 2009-2010 Andrea Leofreddi . All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ` + "``AS IS''" + ` AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of Andrea Leofreddi. - */ - -"use strict"; - -/// CONFIGURATION -/// ====> - -var enablePan = 1; // 1 or 0: enable or disable panning (default enabled) -var enableZoom = 1; // 1 or 0: enable or disable zooming (default enabled) -var enableDrag = 0; // 1 or 0: enable or disable dragging (default disabled) - -/// <==== -/// END OF CONFIGURATION - -var root = document.documentElement; - -var state = 'none', svgRoot, stateTarget, stateOrigin, stateTf; - -setupHandlers(root); - -/** - * Register handlers - */ -function setupHandlers(root){ - setAttributes(root, { - "onmouseup" : "handleMouseUp(evt)", - "onmousedown" : "handleMouseDown(evt)", - "onmousemove" : "handleMouseMove(evt)", - //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element - }); - - if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0) - window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari - else - window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others -} - -/** - * Retrieves the root element for SVG manipulation. The element is then cached into the svgRoot global variable. - */ -function getRoot(root) { - if(typeof(svgRoot) == "undefined") { - var g = null; - - g = root.getElementById("viewport"); - - if(g == null) - g = root.getElementsByTagName('g')[0]; - - if(g == null) - alert('Unable to obtain SVG root element'); - - setCTM(g, g.getCTM()); - - g.removeAttribute("viewBox"); - - svgRoot = g; - } - - return svgRoot; -} - -/** - * Instance an SVGPoint object with given event coordinates. - */ -function getEventPoint(evt) { - var p = root.createSVGPoint(); - - p.x = evt.clientX; - p.y = evt.clientY; - - return p; -} - -/** - * Sets the current transform matrix of an element. - */ -function setCTM(element, matrix) { - var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")"; - - element.setAttribute("transform", s); -} - -/** - * Dumps a matrix to a string (useful for debug). - */ -function dumpMatrix(matrix) { - var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]"; - - return s; -} - -/** - * Sets attributes of an element. - */ -function setAttributes(element, attributes){ - for (var i in attributes) - element.setAttributeNS(null, i, attributes[i]); -} - -/** - * Handle mouse wheel event. - */ -function handleMouseWheel(evt) { - if(!enableZoom) - return; - - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - var delta; - - if(evt.wheelDelta) - delta = evt.wheelDelta / 3600; // Chrome/Safari - else - delta = evt.detail / -90; // Mozilla - - var z = 1 + delta; // Zoom factor: 0.9/1.1 - - var g = getRoot(svgDoc); - - var p = getEventPoint(evt); - - p = p.matrixTransform(g.getCTM().inverse()); - - // Compute new scale matrix in current mouse position - var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y); - - setCTM(g, g.getCTM().multiply(k)); - - if(typeof(stateTf) == "undefined") - stateTf = g.getCTM().inverse(); - - stateTf = stateTf.multiply(k.inverse()); -} - -/** - * Handle mouse move event. - */ -function handleMouseMove(evt) { - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - var g = getRoot(svgDoc); - - if(state == 'pan' && enablePan) { - // Pan mode - var p = getEventPoint(evt).matrixTransform(stateTf); - - setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y)); - } else if(state == 'drag' && enableDrag) { - // Drag mode - var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse()); - - setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM())); - - stateOrigin = p; - } -} - -/** - * Handle click event. - */ -function handleMouseDown(evt) { - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - var g = getRoot(svgDoc); - - if( - evt.target.tagName == "svg" - || !enableDrag // Pan anyway when drag is disabled and the user clicked on an element - ) { - // Pan mode - state = 'pan'; - - stateTf = g.getCTM().inverse(); - - stateOrigin = getEventPoint(evt).matrixTransform(stateTf); - } else { - // Drag mode - state = 'drag'; - - stateTarget = evt.target; - - stateTf = g.getCTM().inverse(); - - stateOrigin = getEventPoint(evt).matrixTransform(stateTf); - } -} - -/** - * Handle mouse button release event. - */ -function handleMouseUp(evt) { - if(evt.preventDefault) - evt.preventDefault(); - - evt.returnValue = false; - - var svgDoc = evt.target.ownerDocument; - - if(state == 'pan' || state == 'drag') { - // Quit pan mode - state = ''; - } -} - -` diff --git a/src/cmd/pprof/internal/symbolizer/symbolizer.go b/src/cmd/pprof/internal/symbolizer/symbolizer.go deleted file mode 100644 index 86de5640d2..0000000000 --- a/src/cmd/pprof/internal/symbolizer/symbolizer.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2014 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 symbolizer provides a routine to populate a profile with -// symbol, file and line number information. It relies on the -// addr2liner and demangler packages to do the actual work. -package symbolizer - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "cmd/pprof/internal/plugin" - "cmd/pprof/internal/profile" -) - -// Symbolize adds symbol and line number information to all locations -// in a profile. mode enables some options to control -// symbolization. Currently only recognizes "force", which causes it -// to overwrite any existing data. -func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error { - force := false - // Disable some mechanisms based on mode string. - for _, o := range strings.Split(strings.ToLower(mode), ":") { - switch o { - case "force": - force = true - default: - } - } - - if len(prof.Mapping) == 0 { - return fmt.Errorf("no known mappings") - } - - mt, err := newMapping(prof, obj, ui, force) - if err != nil { - return err - } - defer mt.close() - - functions := make(map[profile.Function]*profile.Function) - for _, l := range mt.prof.Location { - m := l.Mapping - segment := mt.segments[m] - if segment == nil { - // Nothing to do - continue - } - - stack, err := segment.SourceLine(l.Address) - if err != nil || len(stack) == 0 { - // No answers from addr2line - continue - } - - l.Line = make([]profile.Line, len(stack)) - for i, frame := range stack { - if frame.Func != "" { - m.HasFunctions = true - } - if frame.File != "" { - m.HasFilenames = true - } - if frame.Line != 0 { - m.HasLineNumbers = true - } - f := &profile.Function{ - Name: frame.Func, - SystemName: frame.Func, - Filename: frame.File, - } - if fp := functions[*f]; fp != nil { - f = fp - } else { - functions[*f] = f - f.ID = uint64(len(mt.prof.Function)) + 1 - mt.prof.Function = append(mt.prof.Function, f) - } - l.Line[i] = profile.Line{ - Function: f, - Line: int64(frame.Line), - } - } - - if len(stack) > 0 { - m.HasInlineFrames = true - } - } - return nil -} - -// newMapping creates a mappingTable for a profile. -func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) { - mt := &mappingTable{ - prof: prof, - segments: make(map[*profile.Mapping]plugin.ObjFile), - } - - // Identify used mappings - mappings := make(map[*profile.Mapping]bool) - for _, l := range prof.Location { - mappings[l.Mapping] = true - } - - for _, m := range prof.Mapping { - if !mappings[m] { - continue - } - // Do not attempt to re-symbolize a mapping that has already been symbolized. - if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) { - continue - } - - f, err := locateFile(obj, m.File, m.BuildID, m.Start) - if err != nil { - ui.PrintErr("Local symbolization failed for ", filepath.Base(m.File), ": ", err) - // Move on to other mappings - continue - } - - if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID { - // Build ID mismatch - ignore. - f.Close() - continue - } - - mt.segments[m] = f - } - - return mt, nil -} - -// locateFile opens a local file for symbolization on the search path -// at $PPROF_BINARY_PATH. Looks inside these directories for files -// named $BUILDID/$BASENAME and $BASENAME (if build id is available). -func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) { - // Construct search path to examine - searchPath := os.Getenv("PPROF_BINARY_PATH") - if searchPath == "" { - // Use $HOME/pprof/binaries as default directory for local symbolization binaries - searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries") - } - - // Collect names to search: {buildid/basename, basename} - var fileNames []string - if baseName := filepath.Base(file); buildID != "" { - fileNames = []string{filepath.Join(buildID, baseName), baseName} - } else { - fileNames = []string{baseName} - } - for _, path := range filepath.SplitList(searchPath) { - for nameIndex, name := range fileNames { - file := filepath.Join(path, name) - if f, err := obj.Open(file, start); err == nil { - fileBuildID := f.BuildID() - if buildID == "" || buildID == fileBuildID { - return f, nil - } - f.Close() - if nameIndex == 0 { - // If this is the first name, the path includes the build id. Report inconsistency. - return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID) - } - } - } - } - // Try original file name - f, err := obj.Open(file, start) - if err == nil && buildID != "" { - if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID { - // Mismatched build IDs, ignore - f.Close() - return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID) - } - } - return f, err -} - -// mappingTable contains the mechanisms for symbolization of a -// profile. -type mappingTable struct { - prof *profile.Profile - segments map[*profile.Mapping]plugin.ObjFile -} - -// Close releases any external processes being used for the mapping. -func (mt *mappingTable) close() { - for _, segment := range mt.segments { - segment.Close() - } -} diff --git a/src/cmd/pprof/internal/symbolz/symbolz.go b/src/cmd/pprof/internal/symbolz/symbolz.go deleted file mode 100644 index 15b3b6df26..0000000000 --- a/src/cmd/pprof/internal/symbolz/symbolz.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2014 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 symbolz symbolizes a profile using the output from the symbolz -// service. -package symbolz - -import ( - "bytes" - "fmt" - "io" - "net/url" - "regexp" - "strconv" - "strings" - - "cmd/pprof/internal/profile" -) - -var ( - symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`) -) - -// Symbolize symbolizes profile p by parsing data returned by a -// symbolz handler. syms receives the symbolz query (hex addresses -// separated by '+') and returns the symbolz output in a string. It -// symbolizes all locations based on their addresses, regardless of -// mapping. -func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error { - if source = symbolz(source, p); source == "" { - // If the source is not a recognizable URL, do nothing. - return nil - } - - // Construct query of addresses to symbolize. - var a []string - for _, l := range p.Location { - if l.Address != 0 && len(l.Line) == 0 { - a = append(a, fmt.Sprintf("%#x", l.Address)) - } - } - - if len(a) == 0 { - // No addresses to symbolize. - return nil - } - lines := make(map[uint64]profile.Line) - functions := make(map[string]*profile.Function) - if b, err := syms(source, strings.Join(a, "+")); err == nil { - buf := bytes.NewBuffer(b) - for { - l, err := buf.ReadString('\n') - - if err != nil { - if err == io.EOF { - break - } - return err - } - - if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 { - addr, err := strconv.ParseUint(symbol[1], 0, 64) - if err != nil { - return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err) - } - - name := symbol[2] - fn := functions[name] - if fn == nil { - fn = &profile.Function{ - ID: uint64(len(p.Function) + 1), - Name: name, - SystemName: name, - } - functions[name] = fn - p.Function = append(p.Function, fn) - } - - lines[addr] = profile.Line{Function: fn} - } - } - } - - for _, l := range p.Location { - if line, ok := lines[l.Address]; ok { - l.Line = []profile.Line{line} - if l.Mapping != nil { - l.Mapping.HasFunctions = true - } - } - } - - return nil -} - -// symbolz returns the corresponding symbolz source for a profile URL. -func symbolz(source string, p *profile.Profile) string { - if url, err := url.Parse(source); err == nil && url.Host != "" { - if last := strings.LastIndex(url.Path, "/"); last != -1 { - if strings.HasSuffix(url.Path[:last], "pprof") { - url.Path = url.Path[:last] + "/symbol" - } else { - url.Path = url.Path[:last] + "/symbolz" - } - return url.String() - } - } - - return "" -} diff --git a/src/cmd/pprof/internal/tempfile/tempfile.go b/src/cmd/pprof/internal/tempfile/tempfile.go deleted file mode 100644 index 31c117690a..0000000000 --- a/src/cmd/pprof/internal/tempfile/tempfile.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2014 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 tempfile provides tools to create and delete temporary files -package tempfile - -import ( - "fmt" - "os" - "path/filepath" - "sync" -) - -// New returns an unused filename for output files. -func New(dir, prefix, suffix string) (*os.File, error) { - for index := 1; index < 10000; index++ { - path := filepath.Join(dir, fmt.Sprintf("%s%03d%s", prefix, index, suffix)) - if _, err := os.Stat(path); err != nil { - return os.Create(path) - } - } - // Give up - return nil, fmt.Errorf("could not create file of the form %s%03d%s", prefix, 1, suffix) -} - -var tempFiles []string -var tempFilesMu = sync.Mutex{} - -// DeferDelete marks a file to be deleted by next call to Cleanup() -func DeferDelete(path string) { - tempFilesMu.Lock() - tempFiles = append(tempFiles, path) - tempFilesMu.Unlock() -} - -// Cleanup removes any temporary files selected for deferred cleaning. -func Cleanup() { - tempFilesMu.Lock() - for _, f := range tempFiles { - os.Remove(f) - } - tempFiles = nil - tempFilesMu.Unlock() -} diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go index 1c55d05d5d..bce37dcb97 100644 --- a/src/cmd/pprof/pprof.go +++ b/src/cmd/pprof/pprof.go @@ -15,13 +15,13 @@ import ( "sync" "cmd/internal/objfile" - "cmd/pprof/internal/commands" - "cmd/pprof/internal/driver" - "cmd/pprof/internal/fetch" - "cmd/pprof/internal/plugin" - "cmd/pprof/internal/profile" - "cmd/pprof/internal/symbolizer" - "cmd/pprof/internal/symbolz" + "cmd/internal/pprof/commands" + "cmd/internal/pprof/driver" + "cmd/internal/pprof/fetch" + "cmd/internal/pprof/plugin" + "cmd/internal/pprof/profile" + "cmd/internal/pprof/symbolizer" + "cmd/internal/pprof/symbolz" ) func main() { -- cgit v1.3 From cd85f711c0b6847cbfe4e05f4402df075ea936de Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 11 Apr 2016 21:23:11 -0700 Subject: cmd/compile: add x.Uses==1 test to load combiners We need to make sure that when we combine loads, we only do so if there are no other uses of the load. We can't split one load into two because that can then lead to inconsistent loaded values in the presence of races. Add some aggressive copy removal code so that phantom "dead copy" uses of values are cleaned up promptly. This lets us use x.Uses==1 conditions reliably. Change-Id: I9037311db85665f3868dbeb3adb3de5c20728b38 Reviewed-on: https://go-review.googlesource.com/21853 Reviewed-by: Todd Neal --- src/cmd/compile/internal/gc/ssa_test.go | 2 + src/cmd/compile/internal/gc/testdata/dupLoad.go | 46 +++++++++++++++++ .../compile/internal/gc/testdata/namedReturn.go | 4 ++ src/cmd/compile/internal/ssa/gen/AMD64.rules | 12 ++--- src/cmd/compile/internal/ssa/nilcheck_test.go | 2 +- src/cmd/compile/internal/ssa/rewrite.go | 60 +++++++++++++++++++++- src/cmd/compile/internal/ssa/rewriteAMD64.go | 24 ++++----- 7 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 src/cmd/compile/internal/gc/testdata/dupLoad.go (limited to 'src') diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/gc/ssa_test.go index 0fb0f17778..46e1b0a7d3 100644 --- a/src/cmd/compile/internal/gc/ssa_test.go +++ b/src/cmd/compile/internal/gc/ssa_test.go @@ -101,3 +101,5 @@ func TestPhi(t *testing.T) { runTest(t, "phi_ssa.go") } func TestSlice(t *testing.T) { runTest(t, "slice.go") } func TestNamedReturn(t *testing.T) { runTest(t, "namedReturn.go") } + +func TestDuplicateLoad(t *testing.T) { runTest(t, "dupLoad.go") } diff --git a/src/cmd/compile/internal/gc/testdata/dupLoad.go b/src/cmd/compile/internal/gc/testdata/dupLoad.go new file mode 100644 index 0000000000..d12c26355a --- /dev/null +++ b/src/cmd/compile/internal/gc/testdata/dupLoad.go @@ -0,0 +1,46 @@ +// run + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This test makes sure that we don't split a single +// load up into two separate loads. + +package main + +import "fmt" + +//go:noinline +func read(b []byte) (uint16, uint16) { + // There is only a single read of b[0]. The two + // returned values must have the same low byte. + v := b[0] + return uint16(v), uint16(v) | uint16(b[1])<<8 +} + +const N = 100000 + +func main() { + done := make(chan struct{}) + b := make([]byte, 2) + go func() { + for i := 0; i < N; i++ { + b[0] = byte(i) + b[1] = byte(i) + } + done <- struct{}{} + }() + go func() { + for i := 0; i < N; i++ { + x, y := read(b) + if byte(x) != byte(y) { + fmt.Printf("x=%x y=%x\n", x, y) + panic("bad") + } + } + done <- struct{}{} + }() + <-done + <-done +} diff --git a/src/cmd/compile/internal/gc/testdata/namedReturn.go b/src/cmd/compile/internal/gc/testdata/namedReturn.go index dafb5d719f..19ef8a7e43 100644 --- a/src/cmd/compile/internal/gc/testdata/namedReturn.go +++ b/src/cmd/compile/internal/gc/testdata/namedReturn.go @@ -1,5 +1,9 @@ // run +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + // This test makes sure that naming named // return variables in a return statement works. // See issue #14904. diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index dcd5e6a5e1..21c74a9c1c 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -1369,13 +1369,13 @@ // There are many ways these combinations could occur. This is // designed to match the way encoding/binary.LittleEndian does it. (ORW x0:(MOVBload [i] {s} p mem) - (SHLWconst [8] x1:(MOVBload [i+1] {s} p mem))) && mergePoint(b,x0,x1) != nil -> @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem) + (SHLWconst [8] x1:(MOVBload [i+1] {s} p mem))) && x0.Uses == 1 && x1.Uses == 1 && mergePoint(b,x0,x1) != nil -> @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem) (ORL (ORL (ORL x0:(MOVBload [i] {s} p mem) (SHLLconst [8] x1:(MOVBload [i+1] {s} p mem))) (SHLLconst [16] x2:(MOVBload [i+2] {s} p mem))) - (SHLLconst [24] x3:(MOVBload [i+3] {s} p mem))) && mergePoint(b,x0,x1,x2,x3) != nil -> @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem) + (SHLLconst [24] x3:(MOVBload [i+3] {s} p mem))) && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil -> @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem) (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ x0:(MOVBload [i] {s} p mem) @@ -1385,16 +1385,16 @@ (SHLQconst [32] x4:(MOVBload [i+4] {s} p mem))) (SHLQconst [40] x5:(MOVBload [i+5] {s} p mem))) (SHLQconst [48] x6:(MOVBload [i+6] {s} p mem))) - (SHLQconst [56] x7:(MOVBload [i+7] {s} p mem))) && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem) + (SHLQconst [56] x7:(MOVBload [i+7] {s} p mem))) && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem) (ORW x0:(MOVBloadidx1 [i] {s} p idx mem) - (SHLWconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) && mergePoint(b,x0,x1) != nil -> @mergePoint(b,x0,x1) (MOVWloadidx1 [i] {s} p idx mem) + (SHLWconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) && x0.Uses == 1 && x1.Uses == 1 && mergePoint(b,x0,x1) != nil -> @mergePoint(b,x0,x1) (MOVWloadidx1 [i] {s} p idx mem) (ORL (ORL (ORL x0:(MOVBloadidx1 [i] {s} p idx mem) (SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) (SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem))) - (SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem))) && mergePoint(b,x0,x1,x2,x3) != nil -> @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 [i] {s} p idx mem) + (SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem))) && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil -> @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 [i] {s} p idx mem) (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ x0:(MOVBloadidx1 [i] {s} p idx mem) @@ -1404,4 +1404,4 @@ (SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem))) (SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem))) (SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p idx mem))) - (SHLQconst [56] x7:(MOVBloadidx1 [i+7] {s} p idx mem))) && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 [i] {s} p idx mem) + (SHLQconst [56] x7:(MOVBloadidx1 [i+7] {s} p idx mem))) && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 [i] {s} p idx mem) diff --git a/src/cmd/compile/internal/ssa/nilcheck_test.go b/src/cmd/compile/internal/ssa/nilcheck_test.go index c1c8f94767..af6cbe864a 100644 --- a/src/cmd/compile/internal/ssa/nilcheck_test.go +++ b/src/cmd/compile/internal/ssa/nilcheck_test.go @@ -418,7 +418,7 @@ func TestNilcheckBug(t *testing.T) { Goto("exit")), Bloc("exit", Valu("phi", OpPhi, TypeMem, 0, nil, "mem", "store"), - Exit("mem"))) + Exit("phi"))) CheckFunc(fun.f) // we need the opt here to rewrite the user nilcheck diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index e0cb7f517b..c2f8ceadaf 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -40,9 +40,44 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) } curb = nil for _, v := range b.Values { - change = copyelimValue(v) || change change = phielimValue(v) || change + // Eliminate copy inputs. + // If any copy input becomes unused, mark it + // as invalid and discard its argument. Repeat + // recursively on the discarded argument. + // This phase helps remove phantom "dead copy" uses + // of a value so that a x.Uses==1 rule condition + // fires reliably. + for i, a := range v.Args { + if a.Op != OpCopy { + continue + } + x := a.Args[0] + // Rewriting can generate OpCopy loops. + // They are harmless (see removePredecessor), + // but take care to stop if we find a cycle. + slow := x // advances every other iteration + var advance bool + for x.Op == OpCopy { + x = x.Args[0] + if slow == x { + break + } + if advance { + slow = slow.Args[0] + } + advance = !advance + } + v.SetArg(i, x) + change = true + for a.Uses == 0 { + b := a.Args[0] + a.reset(OpInvalid) + a = b + } + } + // apply rewrite function curv = v if rv(v, config) { @@ -52,7 +87,28 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) } } if !change { - return + break + } + } + // remove clobbered copies + for _, b := range f.Blocks { + j := 0 + for i, v := range b.Values { + if v.Op == OpInvalid { + f.freeValue(v) + continue + } + if i != j { + b.Values[j] = v + } + j++ + } + if j != len(b.Values) { + tail := b.Values[j:] + for j := range tail { + tail[j] = nil + } + b.Values = b.Values[:j] } } } diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index a6600513fa..d1793ad8c0 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -12666,7 +12666,7 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool { return true } // match: (ORL (ORL (ORL x0:(MOVBload [i] {s} p mem) (SHLLconst [8] x1:(MOVBload [i+1] {s} p mem))) (SHLLconst [16] x2:(MOVBload [i+2] {s} p mem))) (SHLLconst [24] x3:(MOVBload [i+3] {s} p mem))) - // cond: mergePoint(b,x0,x1,x2,x3) != nil + // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil // result: @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem) for { v_0 := v.Args[0] @@ -12754,7 +12754,7 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool { if mem != x3.Args[1] { break } - if !(mergePoint(b, x0, x1, x2, x3) != nil) { + if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil) { break } b = mergePoint(b, x0, x1, x2, x3) @@ -12768,7 +12768,7 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool { return true } // match: (ORL (ORL (ORL x0:(MOVBloadidx1 [i] {s} p idx mem) (SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) (SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem))) (SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem))) - // cond: mergePoint(b,x0,x1,x2,x3) != nil + // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && mergePoint(b,x0,x1,x2,x3) != nil // result: @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 [i] {s} p idx mem) for { v_0 := v.Args[0] @@ -12866,7 +12866,7 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool { if mem != x3.Args[2] { break } - if !(mergePoint(b, x0, x1, x2, x3) != nil) { + if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil) { break } b = mergePoint(b, x0, x1, x2, x3) @@ -12980,7 +12980,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool { return true } // match: (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ x0:(MOVBload [i] {s} p mem) (SHLQconst [8] x1:(MOVBload [i+1] {s} p mem))) (SHLQconst [16] x2:(MOVBload [i+2] {s} p mem))) (SHLQconst [24] x3:(MOVBload [i+3] {s} p mem))) (SHLQconst [32] x4:(MOVBload [i+4] {s} p mem))) (SHLQconst [40] x5:(MOVBload [i+5] {s} p mem))) (SHLQconst [48] x6:(MOVBload [i+6] {s} p mem))) (SHLQconst [56] x7:(MOVBload [i+7] {s} p mem))) - // cond: mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil + // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil // result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem) for { v_0 := v.Args[0] @@ -13176,7 +13176,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool { if mem != x7.Args[1] { break } - if !(mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil) { + if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil) { break } b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) @@ -13190,7 +13190,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool { return true } // match: (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ (ORQ x0:(MOVBloadidx1 [i] {s} p idx mem) (SHLQconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) (SHLQconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem))) (SHLQconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem))) (SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem))) (SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem))) (SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p idx mem))) (SHLQconst [56] x7:(MOVBloadidx1 [i+7] {s} p idx mem))) - // cond: mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil + // cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil // result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 [i] {s} p idx mem) for { v_0 := v.Args[0] @@ -13408,7 +13408,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool { if mem != x7.Args[2] { break } - if !(mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil) { + if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil) { break } b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) @@ -13514,7 +13514,7 @@ func rewriteValueAMD64_OpAMD64ORW(v *Value, config *Config) bool { return true } // match: (ORW x0:(MOVBload [i] {s} p mem) (SHLWconst [8] x1:(MOVBload [i+1] {s} p mem))) - // cond: mergePoint(b,x0,x1) != nil + // cond: x0.Uses == 1 && x1.Uses == 1 && mergePoint(b,x0,x1) != nil // result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem) for { x0 := v.Args[0] @@ -13548,7 +13548,7 @@ func rewriteValueAMD64_OpAMD64ORW(v *Value, config *Config) bool { if mem != x1.Args[1] { break } - if !(mergePoint(b, x0, x1) != nil) { + if !(x0.Uses == 1 && x1.Uses == 1 && mergePoint(b, x0, x1) != nil) { break } b = mergePoint(b, x0, x1) @@ -13562,7 +13562,7 @@ func rewriteValueAMD64_OpAMD64ORW(v *Value, config *Config) bool { return true } // match: (ORW x0:(MOVBloadidx1 [i] {s} p idx mem) (SHLWconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem))) - // cond: mergePoint(b,x0,x1) != nil + // cond: x0.Uses == 1 && x1.Uses == 1 && mergePoint(b,x0,x1) != nil // result: @mergePoint(b,x0,x1) (MOVWloadidx1 [i] {s} p idx mem) for { x0 := v.Args[0] @@ -13600,7 +13600,7 @@ func rewriteValueAMD64_OpAMD64ORW(v *Value, config *Config) bool { if mem != x1.Args[2] { break } - if !(mergePoint(b, x0, x1) != nil) { + if !(x0.Uses == 1 && x1.Uses == 1 && mergePoint(b, x0, x1) != nil) { break } b = mergePoint(b, x0, x1) -- cgit v1.3 From 7f5a063d157c777d8e78a567fc9538929bfd38f5 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Tue, 12 Apr 2016 10:27:16 -0400 Subject: cmd/compile/internal/gc: minor Cgen_checknil cleanup Most architectures can only generate nil checks when the the address to check is in a register. Currently only amd64 and 386 can generate checks for addresses that reside in memory. This is unlikely to change so the architecture check has been inverted. Change-Id: I73697488a183406c79a9039c62823712b510bb6a Reviewed-on: https://go-review.googlesource.com/21861 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/pgen.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index bfb65ade38..f6e9ab3b06 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -324,7 +324,12 @@ func Cgen_checknil(n *Node) { Fatalf("bad checknil") } - if (Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { + // Most architectures require that the address to be checked is + // in a register (it could be in memory). + needsReg := !Thearch.LinkArch.InFamily(sys.AMD64, sys.I386) + + // Move the address to be checked into a register if necessary. + if (needsReg && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { var reg Node Regalloc(®, Types[Tptr], n) Cgen(n, ®) -- cgit v1.3 From 7cbe7b1e867db9001db35ca41ee3e4a3b0de31c7 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 18 Mar 2016 19:13:59 -0400 Subject: runtime/internal/atomic: add s390x atomic operations Load and store instructions are atomic on the s390x. Change-Id: I0031ed2fba43f33863bca114d0fdec2e7d1ce807 Reviewed-on: https://go-review.googlesource.com/20938 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/runtime/internal/atomic/asm_s390x.s | 174 ++++++++++++++++++++++++++++ src/runtime/internal/atomic/atomic_s390x.go | 73 ++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 src/runtime/internal/atomic/asm_s390x.s create mode 100644 src/runtime/internal/atomic/atomic_s390x.go (limited to 'src') diff --git a/src/runtime/internal/atomic/asm_s390x.s b/src/runtime/internal/atomic/asm_s390x.s new file mode 100644 index 0000000000..c84718cb8f --- /dev/null +++ b/src/runtime/internal/atomic/asm_s390x.s @@ -0,0 +1,174 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func Cas(ptr *uint32, old, new uint32) bool +// Atomically: +// if *ptr == old { +// *val = new +// return 1 +// } else { +// return 0 +// } +TEXT ·Cas(SB), NOSPLIT, $0-17 + MOVD ptr+0(FP), R3 + MOVWZ old+8(FP), R4 + MOVWZ new+12(FP), R5 + CS R4, R5, 0(R3) // if (R4 == 0(R3)) then 0(R3)= R5 + BNE cas_fail + MOVB $1, ret+16(FP) + RET +cas_fail: + MOVB $0, ret+16(FP) + RET + +// func Cas64(ptr *uint64, old, new uint64) bool +// Atomically: +// if *ptr == old { +// *ptr = new +// return 1 +// } else { +// return 0 +// } +TEXT ·Cas64(SB), NOSPLIT, $0-25 + MOVD ptr+0(FP), R3 + MOVD old+8(FP), R4 + MOVD new+16(FP), R5 + CSG R4, R5, 0(R3) // if (R4 == 0(R3)) then 0(R3)= R5 + BNE cas64_fail + MOVB $1, ret+24(FP) + RET +cas64_fail: + MOVB $0, ret+24(FP) + RET + +// func Casuintptr(ptr *uintptr, old, new uintptr) bool +TEXT ·Casuintptr(SB), NOSPLIT, $0-25 + BR ·Cas64(SB) + +// func Loaduintptr(ptr *uintptr) uintptr +TEXT ·Loaduintptr(SB), NOSPLIT, $0-16 + BR ·Load64(SB) + +// func Loaduint(ptr *uint) uint +TEXT ·Loaduint(SB), NOSPLIT, $0-16 + BR ·Load64(SB) + +// func Storeuintptr(ptr *uintptr, new uintptr) +TEXT ·Storeuintptr(SB), NOSPLIT, $0-16 + BR ·Store64(SB) + +// func Loadint64(ptr *int64) int64 +TEXT ·Loadint64(SB), NOSPLIT, $0-16 + BR ·Load64(SB) + +// func Xadduintptr(ptr *uintptr, delta uintptr) uintptr +TEXT ·Xadduintptr(SB), NOSPLIT, $0-24 + BR ·Xadd64(SB) + +// func Xaddint64(ptr *int64, delta int64) int64 +TEXT ·Xaddint64(SB), NOSPLIT, $0-16 + BR ·Xadd64(SB) + +// func Casp1(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool +// Atomically: +// if *ptr == old { +// *ptr = new +// return 1 +// } else { +// return 0 +// } +TEXT ·Casp1(SB), NOSPLIT, $0-25 + BR ·Cas64(SB) + +// func Xadd(ptr *uint32, delta int32) uint32 +// Atomically: +// *ptr += delta +// return *ptr +TEXT ·Xadd(SB), NOSPLIT, $0-20 + MOVD ptr+0(FP), R4 + MOVW delta+8(FP), R5 + MOVW (R4), R3 +repeat: + ADD R5, R3, R6 + CS R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4) + BNE repeat + MOVW R6, ret+16(FP) + RET + +// func Xadd64(ptr *uint64, delta int64) uint64 +TEXT ·Xadd64(SB), NOSPLIT, $0-24 + MOVD ptr+0(FP), R4 + MOVD delta+8(FP), R5 + MOVD (R4), R3 +repeat: + ADD R5, R3, R6 + CSG R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4) + BNE repeat + MOVD R6, ret+16(FP) + RET + +// func Xchg(ptr *uint32, new uint32) uint32 +TEXT ·Xchg(SB), NOSPLIT, $0-20 + MOVD ptr+0(FP), R4 + MOVW new+8(FP), R3 + MOVW (R4), R6 +repeat: + CS R6, R3, (R4) // if R6==(R4) then (R4)=R3 else R6=(R4) + BNE repeat + MOVW R6, ret+16(FP) + RET + +// func Xchg64(ptr *uint64, new uint64) uint64 +TEXT ·Xchg64(SB), NOSPLIT, $0-24 + MOVD ptr+0(FP), R4 + MOVD new+8(FP), R3 + MOVD (R4), R6 +repeat: + CSG R6, R3, (R4) // if R6==(R4) then (R4)=R3 else R6=(R4) + BNE repeat + MOVD R6, ret+16(FP) + RET + +// func Xchguintptr(ptr *uintptr, new uintptr) uintptr +TEXT ·Xchguintptr(SB), NOSPLIT, $0-24 + BR ·Xchg64(SB) + +// func Or8(addr *uint8, v uint8) +TEXT ·Or8(SB), NOSPLIT, $0-9 + MOVD ptr+0(FP), R3 + MOVBZ val+8(FP), R4 + // Calculate shift. + AND $3, R3, R5 + XOR $3, R5 // big endian - flip direction + SLD $3, R5 // MUL $8, R5 + SLD R5, R4 + // Align ptr down to 4 bytes so we can use 32-bit load/store. + AND $-4, R3 + MOVWZ 0(R3), R6 +again: + OR R4, R6, R7 + CS R6, R7, 0(R3) // if R6==(R3) then (R3)=R7 else R6=(R3) + BNE again + RET + +// func And8(addr *uint8, v uint8) +TEXT ·And8(SB), NOSPLIT, $0-9 + MOVD ptr+0(FP), R3 + MOVBZ val+8(FP), R4 + // Calculate shift. + AND $3, R3, R5 + XOR $3, R5 // big endian - flip direction + SLD $3, R5 // MUL $8, R5 + OR $-256, R4 // create 0xffffffffffffffxx + RLLG R5, R4 + // Align ptr down to 4 bytes so we can use 32-bit load/store. + AND $-4, R3 + MOVWZ 0(R3), R6 +again: + AND R4, R6, R7 + CS R6, R7, 0(R3) // if R6==(R3) then (R3)=R7 else R6=(R3) + BNE again + RET diff --git a/src/runtime/internal/atomic/atomic_s390x.go b/src/runtime/internal/atomic/atomic_s390x.go new file mode 100644 index 0000000000..f31f1af444 --- /dev/null +++ b/src/runtime/internal/atomic/atomic_s390x.go @@ -0,0 +1,73 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package atomic + +import "unsafe" + +//go:nosplit +//go:noinline +func Load(ptr *uint32) uint32 { + return *ptr +} + +//go:nosplit +//go:noinline +func Loadp(ptr unsafe.Pointer) unsafe.Pointer { + return *(*unsafe.Pointer)(ptr) +} + +//go:nosplit +//go:noinline +func Load64(ptr *uint64) uint64 { + return *ptr +} + +//go:noinline +//go:nosplit +func Store(ptr *uint32, val uint32) { + *ptr = val +} + +//go:noinline +//go:nosplit +func Store64(ptr *uint64, val uint64) { + *ptr = val +} + +// NO go:noescape annotation; see atomic_pointer.go. +//go:noinline +//go:nosplit +func Storep1(ptr unsafe.Pointer, val unsafe.Pointer) { + *(*unsafe.Pointer)(ptr) = val +} + +//go:noescape +func And8(ptr *uint8, val uint8) + +//go:noescape +func Or8(ptr *uint8, val uint8) + +// NOTE: Do not add atomicxor8 (XOR is not idempotent). + +//go:noescape +func Xadd(ptr *uint32, delta int32) uint32 + +//go:noescape +func Xadd64(ptr *uint64, delta int64) uint64 + +//go:noescape +func Xadduintptr(ptr *uintptr, delta uintptr) uintptr + +//go:noescape +func Xchg(ptr *uint32, new uint32) uint32 + +//go:noescape +func Xchg64(ptr *uint64, new uint64) uint64 + +//go:noescape +func Xchguintptr(ptr *uintptr, new uintptr) uintptr + +//go:noescape +func Cas64(ptr *uint64, old, new uint64) bool -- cgit v1.3 From 921b2eba52906fc8b9bc4a8744dab63678f5ed3a Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Mon, 21 Mar 2016 13:30:50 -0400 Subject: debug/gosym: accept PC quantum of 2 (for s390x) Needed for the header check to accept the header generated for s390x as Go 1.2 style rather than Go 1.1 style. Change-Id: I7b3713d4cc7514cfc58f947a45702348f6d7b824 Reviewed-on: https://go-review.googlesource.com/20966 Reviewed-by: Minux Ma --- src/debug/gosym/pclntab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go index 01a9f11f05..291f102262 100644 --- a/src/debug/gosym/pclntab.go +++ b/src/debug/gosym/pclntab.go @@ -167,7 +167,7 @@ func (t *LineTable) go12Init() { // Check header: 4-byte magic, two zeros, pc quantum, pointer size. t.go12 = -1 // not Go 1.2 until proven otherwise if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || - (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum + (t.Data[6] != 1 && t.Data[6] != 2 && t.Data[6] != 4) || // pc quantum (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size return } -- cgit v1.3 From 78ecd61f6245197f701629f5f511be7f2bc1ff58 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 18 Mar 2016 19:20:34 -0400 Subject: runtime/cgo: add s390x support Change-Id: I64ada9fe34c3cfc4bd514ec5d8c8f4d4c99074fb Reviewed-on: https://go-review.googlesource.com/20950 Reviewed-by: Bill O'Farrell Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/runtime/cgo/asm_s390x.s | 44 +++++++++++++++++++++++++ src/runtime/cgo/gcc_linux_s390x.c | 68 +++++++++++++++++++++++++++++++++++++++ src/runtime/cgo/gcc_s390x.S | 43 +++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 src/runtime/cgo/asm_s390x.s create mode 100644 src/runtime/cgo/gcc_linux_s390x.c create mode 100644 src/runtime/cgo/gcc_s390x.S (limited to 'src') diff --git a/src/runtime/cgo/asm_s390x.s b/src/runtime/cgo/asm_s390x.s new file mode 100644 index 0000000000..5ed13cfe1e --- /dev/null +++ b/src/runtime/cgo/asm_s390x.s @@ -0,0 +1,44 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +/* + * void crosscall2(void (*fn)(void*, int32), void*, int32) + * Save registers and call fn with two arguments. + * crosscall2 obeys the C ABI; fn obeys the Go ABI. + */ +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + // Start with standard C stack frame layout and linkage + + // Save R6-R15, F0, F2, F4 and F6 in the + // register save area of the calling function + STMG R6, R15, 48(R15) + FMOVD F0, 128(R15) + FMOVD F2, 136(R15) + FMOVD F4, 144(R15) + FMOVD F6, 152(R15) + + // Initialize Go ABI environment + XOR R0, R0 + BL runtime·load_g(SB) + + // Allocate 24 bytes on the stack + SUB $24, R15 + + MOVD R3, 8(R15) // arg1 + MOVW R4, 16(R15) // arg2 + BL (R2) // fn(arg1, arg2) + + ADD $24, R15 + + // Restore R6-R15, F0, F2, F4 and F6 + LMG 48(R15), R6, R15 + FMOVD F0, 128(R15) + FMOVD F2, 136(R15) + FMOVD F4, 144(R15) + FMOVD F6, 152(R15) + + RET + diff --git a/src/runtime/cgo/gcc_linux_s390x.c b/src/runtime/cgo/gcc_linux_s390x.c new file mode 100644 index 0000000000..81e3b339b0 --- /dev/null +++ b/src/runtime/cgo/gcc_linux_s390x.c @@ -0,0 +1,68 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include "libcgo.h" + +static void *threadentry(void*); + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsbase) +{ + pthread_attr_t attr; + size_t size; + + setg_gcc = setg; + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stacklo = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstack will do the rest. + ts->g->stackhi = size; + err = pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall_s390x(void (*fn)(void), void *g); + +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + // Save g for this thread in C TLS + setg_gcc((void*)ts.g); + + crosscall_s390x(ts.fn, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_s390x.S b/src/runtime/cgo/gcc_s390x.S new file mode 100644 index 0000000000..6b163d0d21 --- /dev/null +++ b/src/runtime/cgo/gcc_s390x.S @@ -0,0 +1,43 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * void crosscall_s390x(void (*fn)(void), void *g) + * + * Calling into the go tool chain, where all registers are caller save. + * Called from standard s390x C ABI, where r6-r13, r15, and f0, f2, f4 and f6 are + * callee-save, so they must be saved explicitly. + */ +.globl crosscall_s390x +crosscall_s390x: + /* + * save r6-r15, f0, f2, f4 and f6 in the + * register save area of the calling function + */ + stmg %r6, %r15, 48(%r15) + stdy %f0, 128(%r15) + stdy %f2, 136(%r15) + stdy %f4, 144(%r15) + stdy %f6, 152(%r15) + + /* assumes this call does not clobber r2 or r15 */ + xgr %r0, %r0 + + /* grow stack 8 bytes and call fn */ + agfi %r15, -8 + basr %r14, %r2 + agfi %r15, 8 + + /* restore registers */ + lmg %r6, %r15, 48(%r15) + ldy %f0, 128(%r15) + ldy %f2, 136(%r15) + ldy %f4, 144(%r15) + ldy %f6, 152(%r15) + + br %r14 /* restored by lmg */ + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif -- cgit v1.3 From 8edf4cb27d07a81ae340b0fda4e519c12f139618 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Fri, 8 Apr 2016 13:30:41 -0400 Subject: hash/crc32: invert build tags for go implementation It seems cleaner and more consistent with other files to list the architectures that have assembly implementations rather than to list those that do not. This means we don't have to add s390x and future platforms to this list. Change-Id: I2ad3f66b76eb1711333c910236ca7f5151b698e5 Reviewed-on: https://go-review.googlesource.com/21770 Reviewed-by: Bill O'Farrell Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/hash/crc32/crc32_generic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/hash/crc32/crc32_generic.go b/src/hash/crc32/crc32_generic.go index 08988f4b38..62fa72028c 100644 --- a/src/hash/crc32/crc32_generic.go +++ b/src/hash/crc32/crc32_generic.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build 386 arm arm64 mips64 mips64le ppc64 ppc64le +// +build !amd64,!amd64p32 package crc32 -- cgit v1.3 From 811ebb6ac961162b815f4fd50976df81ba4c47b0 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 12 Apr 2016 09:22:26 -0700 Subject: cmd/compile: temporarily disable inplace append special case Fixes #15246 Re-opens #14969 Change-Id: Ic0b41c5aa42bbb229a0d62b7f3e5888c6b29293d Reviewed-on: https://go-review.googlesource.com/21891 Run-TryBot: Josh Bleecher Snyder TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/ssa.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index beb68b0385..fdd14953e6 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -699,7 +699,8 @@ func (s *state) stmt(n *Node) { // If the slice can be SSA'd, it'll be on the stack, // so there will be no write barriers, // so there's no need to attempt to prevent them. - if samesafeexpr(n.Left, rhs.List.First()) && !s.canSSA(n.Left) { + const doInPlaceAppend = false // issue 15246 + if doInPlaceAppend && samesafeexpr(n.Left, rhs.List.First()) && !s.canSSA(n.Left) { s.append(rhs, true) return } -- cgit v1.3 From 613ba6cda845fef442995d705027a622984c6b3a Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Tue, 12 Apr 2016 12:26:17 -0400 Subject: cmd/compile/internal/gc: add s390x support Allows instructions with a From3 field to be used in regopt so long as From3 represents a constant. This is needed because the storage-to-storage instructions on s390x place the length of the data into From3. Change-Id: I12cd32d4f997baf2fe97937bb7d45bbf716dfcb5 Reviewed-on: https://go-review.googlesource.com/20875 Reviewed-by: Matthew Dempsky Run-TryBot: Matthew Dempsky --- src/cmd/compile/internal/gc/cgen.go | 4 ++-- src/cmd/compile/internal/gc/gsubr.go | 8 +++++--- src/cmd/compile/internal/gc/pgen.go | 2 +- src/cmd/compile/internal/gc/reg.go | 2 +- src/cmd/compile/internal/gc/walk.go | 7 ++++++- 5 files changed, 15 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go index eacbc30f87..9de2a19f68 100644 --- a/src/cmd/compile/internal/gc/cgen.go +++ b/src/cmd/compile/internal/gc/cgen.go @@ -247,7 +247,7 @@ func cgen_wb(n, res *Node, wb bool) { return } - if Ctxt.Arch.InFamily(sys.AMD64, sys.I386) && n.Addable { + if Ctxt.Arch.InFamily(sys.AMD64, sys.I386, sys.S390X) && n.Addable { Thearch.Gmove(n, res) return } @@ -1829,7 +1829,7 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { // Some architectures might need a temporary or other help here, // but they don't support direct generation of a bool value yet. // We can fix that as we go. - mayNeedTemp := Ctxt.Arch.InFamily(sys.ARM, sys.ARM64, sys.MIPS64, sys.PPC64) + mayNeedTemp := Ctxt.Arch.InFamily(sys.ARM, sys.ARM64, sys.MIPS64, sys.PPC64, sys.S390X) if genval { if mayNeedTemp { diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index 63a8e969c3..f1316db8d8 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -58,7 +58,9 @@ func Ismem(n *Node) bool { return true case OADDR: - return Thearch.LinkArch.InFamily(sys.AMD64, sys.PPC64) // because 6g uses PC-relative addressing; TODO(rsc): not sure why 9g too + // amd64 and s390x use PC relative addressing. + // TODO(rsc): not sure why ppc64 needs this too. + return Thearch.LinkArch.InFamily(sys.AMD64, sys.PPC64, sys.S390X) } return false @@ -84,7 +86,7 @@ func Gbranch(as obj.As, t *Type, likely int) *obj.Prog { p := Prog(as) p.To.Type = obj.TYPE_BRANCH p.To.Val = nil - if as != obj.AJMP && likely != 0 && Thearch.LinkArch.Family != sys.PPC64 && Thearch.LinkArch.Family != sys.ARM64 && Thearch.LinkArch.Family != sys.MIPS64 { + if as != obj.AJMP && likely != 0 && !Thearch.LinkArch.InFamily(sys.PPC64, sys.ARM64, sys.MIPS64, sys.S390X) { p.From.Type = obj.TYPE_CONST if likely > 0 { p.From.Offset = 1 @@ -458,7 +460,7 @@ func Naddr(a *obj.Addr, n *Node) { case OADDR: Naddr(a, n.Left) a.Etype = uint8(Tptr) - if !Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) { // TODO(rsc): Do this even for arm, ppc64. + if !Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { // TODO(rsc): Do this even for these architectures. a.Width = int64(Widthptr) } if a.Type != obj.TYPE_MEM { diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index f6e9ab3b06..baa960bf75 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -287,7 +287,7 @@ func allocauto(ptxt *obj.Prog) { if haspointers(n.Type) { stkptrsize = Stksize } - if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) { + if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { Stksize = Rnd(Stksize, int64(Widthptr)) } if Stksize >= 1<<31 { diff --git a/src/cmd/compile/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go index 8705d6dfa4..138ad683c5 100644 --- a/src/cmd/compile/internal/gc/reg.go +++ b/src/cmd/compile/internal/gc/reg.go @@ -1115,7 +1115,7 @@ func regopt(firstp *obj.Prog) { // Currently we never generate three register forms. // If we do, this will need to change. - if p.From3Type() != obj.TYPE_NONE { + if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST { Fatalf("regopt not implemented for from3") } diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 586a8e9c4f..3e5f5161db 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -673,7 +673,7 @@ opswitch: walkexprlist(n.List.Slice(), init) if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" && n.Left.Sym.Pkg.Path == "math" { - if Thearch.LinkArch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.PPC64) { + if Thearch.LinkArch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { n.Op = OSQRT n.Left = n.List.First() n.List.Set(nil) @@ -3294,6 +3294,11 @@ func walkrotate(n *Node) *Node { // Constants adding to width? w := int(l.Type.Width * 8) + if Thearch.LinkArch.Family == sys.S390X && w != 32 && w != 64 { + // only supports 32-bit and 64-bit rotates + return n + } + if Smallintconst(l.Right) && Smallintconst(r.Right) { sl := int(l.Right.Int64()) if sl >= 0 { -- cgit v1.3 From b09c274bfabb3edef60b4df3375906852aab7da1 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Tue, 12 Apr 2016 12:46:54 -0400 Subject: net/http: fix TestLinuxSendfile on s390x s390x doesn't have sendfile64 so apply the same fix as MIPS (eebf7d27) and just use sendfile. Change-Id: If8fe2e974ed44a9883282430157c3545d5bd04bd Reviewed-on: https://go-review.googlesource.com/21892 Reviewed-by: Brad Fitzpatrick --- src/net/http/fs_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/net/http/fs_test.go b/src/net/http/fs_test.go index 9253ebe43a..c811891e87 100644 --- a/src/net/http/fs_test.go +++ b/src/net/http/fs_test.go @@ -978,9 +978,9 @@ func TestLinuxSendfile(t *testing.T) { syscalls := "sendfile,sendfile64" switch runtime.GOARCH { - case "mips64", "mips64le": - // mips64 strace doesn't support sendfile64 and will error out - // if we specify that with `-e trace='. + case "mips64", "mips64le", "s390x": + // strace on the above platforms doesn't support sendfile64 + // and will error out if we specify that with `-e trace='. syscalls = "sendfile" } -- cgit v1.3 From be7c786dd04db51076012618ea29ee528a654978 Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Tue, 12 Apr 2016 13:38:24 -0400 Subject: cmd/objdump: skip TestDisasm* on s390x The disassembler is not yet implemented on s390x. Updates #15255. Change-Id: Ibab319c8c087b1a619baa1529398305c1e721877 Reviewed-on: https://go-review.googlesource.com/21894 Reviewed-by: Brad Fitzpatrick --- src/cmd/objdump/objdump_test.go | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/cmd/objdump/objdump_test.go b/src/cmd/objdump/objdump_test.go index 8ceaba078c..899db06324 100644 --- a/src/cmd/objdump/objdump_test.go +++ b/src/cmd/objdump/objdump_test.go @@ -107,6 +107,8 @@ func TestDisasm(t *testing.T) { t.Skipf("skipping on %s, issue 10106", runtime.GOARCH) case "mips64", "mips64le": t.Skipf("skipping on %s, issue 12559", runtime.GOARCH) + case "s390x": + t.Skipf("skipping on %s, issue 15255", runtime.GOARCH) } testDisasm(t) } @@ -123,6 +125,8 @@ func TestDisasmExtld(t *testing.T) { t.Skipf("skipping on %s, issue 10106", runtime.GOARCH) case "mips64", "mips64le": t.Skipf("skipping on %s, issue 12559 and 12560", runtime.GOARCH) + case "s390x": + t.Skipf("skipping on %s, issue 15255", runtime.GOARCH) } // TODO(jsing): Reenable once openbsd/arm has external linking support. if runtime.GOOS == "openbsd" && runtime.GOARCH == "arm" { -- cgit v1.3 From b1851a3c11a179d4eb55f9d0dd25ef81668a9f81 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 12 Apr 2016 11:31:16 -0700 Subject: cmd/compile: move compiler-specific flags into compiler-spec. export data section Also: Adjust go/importer accordingly. Change-Id: Ia6669563793e218946af45b9fba1cf986a21c031 Reviewed-on: https://go-review.googlesource.com/21896 Reviewed-by: Alan Donovan --- src/cmd/compile/internal/gc/bexport.go | 18 +++++++----------- src/cmd/compile/internal/gc/bimport.go | 12 ++++++------ src/go/internal/gcimporter/bimport.go | 5 +---- 3 files changed, 14 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index 15e5e3ada6..cb438d7573 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -182,22 +182,12 @@ func export(out *bufio.Writer, trace bool) int { Fatalf("exporter: local package path not empty: %q", localpkg.Path) } p.pkg(localpkg) - - // write compiler-specific flags - // TODO(gri) move this into the compiler-specific export data section - { - var flags string - if safemode != 0 { - flags = "safe" - } - p.string(flags) - } if p.trace { p.tracef("\n") } // export objects - + // // First, export all exported (package-level) objects; i.e., all objects // in the current exportlist. These objects represent all information // required to import this package and type-check against it; i.e., this @@ -270,6 +260,12 @@ func export(out *bufio.Writer, trace bool) int { } } + // write compiler-specific flags + p.bool(safemode != 0) + if p.trace { + p.tracef("\n") + } + // Phase 2: Export objects added to exportlist during phase 1. // Don't use range since exportlist may grow during this phase // and we want to export all remaining objects. diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 7ad4d9dbb0..9cebafcaef 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -62,9 +62,6 @@ func Import(in *bufio.Reader) { Fatalf("importer: imported package not found in pkgList[0]") } - // read compiler-specific flags - importpkg.Safe = p.string() == "safe" - // defer some type-checking until all types are read in completely // (parser.go:import_package) tcok := typecheckok @@ -73,7 +70,7 @@ func Import(in *bufio.Reader) { // read objects - // Phase 1 + // phase 1 objcount := 0 for { tag := p.tagOrIndex() @@ -91,7 +88,10 @@ func Import(in *bufio.Reader) { // --- compiler-specific export data --- - // Phase 2 + // read compiler-specific flags + importpkg.Safe = p.bool() + + // phase 2 objcount = 0 for { tag := p.tagOrIndex() @@ -264,7 +264,7 @@ func (p *importer) obj(tag int) { } default: - Fatalf("importer: unexpected object tag") + Fatalf("importer: unexpected object (tag = %d)", tag) } } diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index aa9569de52..a9d678b021 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -78,9 +78,6 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i panic("imported packaged not found in pkgList[0]") } - // read compiler-specific flags - p.string() // discard - // read objects of phase 1 only (see cmd/compiler/internal/gc/bexport.go) objcount := 0 for { @@ -193,7 +190,7 @@ func (p *importer) obj(tag int) { p.declare(types.NewFunc(token.NoPos, pkg, name, sig)) default: - panic("unexpected object tag") + panic(fmt.Sprintf("unexpected object tag %d", tag)) } } -- cgit v1.3 From 98080a6c64c2d9bc2a759b66a9b861af4ef7367b Mon Sep 17 00:00:00 2001 From: Shawn Walker-Salas Date: Fri, 8 Apr 2016 15:59:04 -0700 Subject: net: broken sendfile on SmartOS/Solaris In the event of a partial write on Solaris and some BSDs, the offset pointer passed to sendfile() will be updated even though the function returns -1 if errno is set to EAGAIN/EINTR. In that case, calculate the bytes written based on the difference between the updated offset and the original offset. If no bytes were written, and errno is set to EAGAIN/EINTR, ignore the errno. Fixes #13892 Change-Id: I6334b5ef2edcbebdaa7db36fa4f7785967313c2d Reviewed-on: https://go-review.googlesource.com/21769 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- src/net/sendfile_solaris.go | 9 ++++- src/net/sendfile_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/net/sendfile_test.go (limited to 'src') diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go index eb9d2d1830..20d2cddeea 100644 --- a/src/net/sendfile_solaris.go +++ b/src/net/sendfile_solaris.go @@ -26,8 +26,6 @@ const maxSendfileSize int = 4 << 20 // // if handled == false, sendFile performed no work. func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { - return // Solaris sendfile is disabled until Issue 13892 is understood and fixed - // Solaris uses 0 as the "until EOF" value. If you pass in more bytes than the // file contains, it will loop back to the beginning ad nauseam until it's sent // exactly the number of bytes told to. As such, we need to know exactly how many @@ -78,6 +76,13 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { } pos1 := pos n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if err1 == syscall.EAGAIN || err1 == syscall.EINTR { + // partial write may have occurred + if n = int(pos1 - pos); n == 0 { + // nothing more to write + err1 = nil + } + } if n > 0 { pos += int64(n) written += int64(n) diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go new file mode 100644 index 0000000000..add2bcb5c6 --- /dev/null +++ b/src/net/sendfile_test.go @@ -0,0 +1,90 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "os" + "testing" +) + +const ( + twain = "../compress/testdata/Mark.Twain-Tom.Sawyer.txt" + twainLen = 387851 + twainSHA256 = "461eb7cb2d57d293fc680c836464c9125e4382be3596f7d415093ae9db8fcb0e" +) + +func TestSendFile(t *testing.T) { + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + errc := make(chan error, 1) + go func(ln Listener) { + // Wait for a connection. + conn, err := ln.Accept() + if err != nil { + errc <- err + close(errc) + return + } + + go func() { + defer close(errc) + defer conn.Close() + + f, err := os.Open(twain) + if err != nil { + errc <- err + return + } + defer f.Close() + + // Return file data using io.Copy, which should use + // sendFile if available. + sbytes, err := io.Copy(conn, f) + if err != nil { + errc <- err + return + } + + if sbytes != twainLen { + errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, twainLen) + return + } + }() + }(ln) + + // Connect to listener to retrieve file and verify digest matches + // expected. + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + h := sha256.New() + rbytes, err := io.Copy(h, c) + if err != nil { + t.Error(err) + } + + if rbytes != twainLen { + t.Errorf("received %d bytes; expected %d", rbytes, twainLen) + } + + if res := hex.EncodeToString(h.Sum(nil)); res != twainSHA256 { + t.Error("retrieved data hash did not match") + } + + for err := range errc { + t.Error(err) + } +} -- cgit v1.3 From e07a4459a155789fb57bbf4e2c8eaca5b369fd17 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 12 Apr 2016 12:16:20 -0700 Subject: cmd: replace x[i:][0] expressions with x[i] Passes toolstash -cmp. Change-Id: Id504e71ed1f23900e24a9aed25143c94f4d7d50c Reviewed-on: https://go-review.googlesource.com/21899 Run-TryBot: Matthew Dempsky Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/internal/obj/arm64/asm7.go | 2 +- src/cmd/internal/obj/mips/asm0.go | 2 +- src/cmd/internal/obj/s390x/asmz.go | 2 +- src/cmd/internal/obj/x86/asm6.go | 3 +-- src/cmd/link/internal/ld/decodesym.go | 4 ++-- src/cmd/link/internal/ld/ldelf.go | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index ff8d4fdf60..d0ae6115cb 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -1087,7 +1087,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int { func oplook(ctxt *obj.Link, p *obj.Prog) *Optab { a1 := int(p.Optab) if a1 != 0 { - return &optab[a1-1:][0] + return &optab[a1-1] } a1 = int(p.From.Class) if a1 == 0 { diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go index 5cb5d1cfd9..13e7600c21 100644 --- a/src/cmd/internal/obj/mips/asm0.go +++ b/src/cmd/internal/obj/mips/asm0.go @@ -611,7 +611,7 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab { a1 := int(p.Optab) if a1 != 0 { - return &optab[a1-1:][0] + return &optab[a1-1] } a1 = int(p.From.Class) if a1 == 0 { diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go index cf3b11424b..bae4dc3ce7 100644 --- a/src/cmd/internal/obj/s390x/asmz.go +++ b/src/cmd/internal/obj/s390x/asmz.go @@ -606,7 +606,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int { func oplook(ctxt *obj.Link, p *obj.Prog) *Optab { a1 := int(p.Optab) if a1 != 0 { - return &optab[a1-1:][0] + return &optab[a1-1] } a1 = int(p.From.Class) if a1 == 0 { diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go index c15b59b5e8..c563a7a48d 100644 --- a/src/cmd/internal/obj/x86/asm6.go +++ b/src/cmd/internal/obj/x86/asm6.go @@ -4452,9 +4452,8 @@ func asmins(ctxt *obj.Link, p *obj.Prog) { } n := ctxt.AsmBuf.Len() - var r *obj.Reloc for i := len(ctxt.Cursym.R) - 1; i >= 0; i-- { - r = &ctxt.Cursym.R[i:][0] + r := &ctxt.Cursym.R[i] if int64(r.Off) < p.Pc { break } diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 1066d220f7..7daa8bc812 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -17,9 +17,9 @@ import ( // ../gc/reflect.c stuffs in these. func decode_reloc(s *LSym, off int32) *Reloc { - for i := 0; i < len(s.R); i++ { + for i := range s.R { if s.R[i].Off == off { - return &s.R[i:][0] + return &s.R[i] } } return nil diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go index d9581a5189..d07a2a2c34 100644 --- a/src/cmd/link/internal/ld/ldelf.go +++ b/src/cmd/link/internal/ld/ldelf.go @@ -774,7 +774,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) { if sym.sym == nil { continue } - sect = &elfobj.sect[sym.shndx:][0] + sect = &elfobj.sect[sym.shndx] if sect.sym == nil { if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this continue -- cgit v1.3 From 37af06360039c96be707526596557a33885c3ad0 Mon Sep 17 00:00:00 2001 From: Dan Peterson Date: Tue, 12 Apr 2016 13:12:54 -0300 Subject: crypto/x509: remove broken link in ParsePKCS8PrivateKey documentation Fixes #14776 Change-Id: I55423ac643f18542b9fd1386ed98dec47fb678aa Reviewed-on: https://go-review.googlesource.com/21890 Reviewed-by: Brad Fitzpatrick --- src/crypto/x509/pkcs8.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/crypto/x509/pkcs8.go b/src/crypto/x509/pkcs8.go index ba19989cba..6e56752c0e 100644 --- a/src/crypto/x509/pkcs8.go +++ b/src/crypto/x509/pkcs8.go @@ -21,8 +21,8 @@ type pkcs8 struct { // optional attributes omitted. } -// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See -// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208. +// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. +// See RFC 5208. func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { var privKey pkcs8 if _, err := asn1.Unmarshal(der, &privKey); err != nil { -- cgit v1.3 From da224a5c42e7fce7f1d190a86962b1c46be454ef Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Sun, 10 Apr 2016 20:14:27 +0200 Subject: cmd/pprof: pass the event to pprof_toggle_asm for the weblist command Fixes #15225 Change-Id: I1f85590b2c3293463c6476beebcd3256adc1bf23 Reviewed-on: https://go-review.googlesource.com/21802 Reviewed-by: Brad Fitzpatrick --- src/cmd/internal/pprof/report/source.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cmd/internal/pprof/report/source.go b/src/cmd/internal/pprof/report/source.go index 7beea39562..608e4d561d 100644 --- a/src/cmd/internal/pprof/report/source.go +++ b/src/cmd/internal/pprof/report/source.go @@ -257,7 +257,7 @@ func printHeader(w io.Writer, rpt *Report) { // printFunctionHeader prints a function header for a weblist report. func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64, rpt *Report) { fmt.Fprintf(w, `

%s

%s -
+
   Total:  %10s %10s (flat, cum) %s
 `,
 		template.HTMLEscapeString(name), template.HTMLEscapeString(path),
-- 
cgit v1.3


From 23cbfa2545a735eca5ffc1ffd6c0e93c2eecac2a Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Tue, 12 Apr 2016 19:48:01 +0000
Subject: net: skip TestDialCancel on linux-arm64-buildlet

These builders (on Linaro) have a different network configuration
which is incompatible with this test. Or so it seems.

Updates #15191

Change-Id: Ibfeacddc98dac1da316e704b5c8491617a13e3bf
Reviewed-on: https://go-review.googlesource.com/21901
Reviewed-by: Matthew Dempsky 
---
 src/net/dial_test.go | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'src')

diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index f8e90abb48..466adf060e 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -823,6 +823,9 @@ func TestDialCancel(t *testing.T) {
 	if testing.Short() && !onGoBuildFarm {
 		t.Skip("skipping in short mode")
 	}
+	if testenv.Builder() == "linux-arm64-buildlet" {
+		t.Skip("skipping on linux-arm64-buildlet; incompatible network config? issue 15191")
+	}
 
 	blackholeIPPort := JoinHostPort(slowDst4, "1234")
 	if !supportsIPv4 {
-- 
cgit v1.3


From f028b9f9e2433662502283850d06e9e07e72a6bb Mon Sep 17 00:00:00 2001
From: David Crawshaw 
Date: Sun, 27 Mar 2016 10:21:48 -0400
Subject: cmd/link, etc: store typelinks as offsets

This is the first in a series of CLs to replace the use of pointers
in binary read-only data with offsets.

In standard Go binaries these CLs have a small effect, shrinking
8-byte pointers to 4-bytes. In position-independent code, it also
saves the dynamic relocation for the pointer. This has a significant
effect on the binary size when building as PIE, c-archive, or
c-shared.

darwin/amd64:
	cmd/go: -12KB (0.1%)
	jujud:  -82KB (0.1%)

linux/amd64 PIE:
	cmd/go:  -86KB (0.7%)
	jujud:  -569KB (0.7%)

For #6853.

Change-Id: Iad5625bbeba58dabfd4d334dbee3fcbfe04b2dcf
Reviewed-on: https://go-review.googlesource.com/21284
Reviewed-by: Ian Lance Taylor 
Run-TryBot: David Crawshaw 
TryBot-Result: Gobot Gobot 
---
 src/cmd/compile/internal/gc/go.go      |  2 --
 src/cmd/compile/internal/gc/main.go    |  4 ---
 src/cmd/compile/internal/gc/obj.go     |  6 +++++
 src/cmd/compile/internal/gc/reflect.go | 17 +++++-------
 src/cmd/internal/obj/data.go           | 21 ++++++++++++++-
 src/cmd/internal/obj/link.go           |  3 +++
 src/cmd/link/internal/ld/data.go       | 18 +++++++++++++
 src/cmd/link/internal/ld/symtab.go     |  4 +++
 src/reflect/export_test.go             |  8 +++---
 src/reflect/type.go                    | 47 ++++++++++++++++++++++------------
 src/runtime/runtime1.go                |  8 +++---
 src/runtime/symtab.go                  |  3 ++-
 12 files changed, 99 insertions(+), 42 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index d9b28ff8e6..5df49b56d6 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -171,8 +171,6 @@ var msanpkg *Pkg // package runtime/msan
 
 var typepkg *Pkg // fake package for runtime type info (headers)
 
-var typelinkpkg *Pkg // fake package for runtime type info (data)
-
 var unsafepkg *Pkg // package unsafe
 
 var trackpkg *Pkg // fake package for field tracking
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 26acf8861f..45a510d577 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -126,10 +126,6 @@ func Main() {
 	itabpkg.Name = "go.itab"
 	itabpkg.Prefix = "go.itab" // not go%2eitab
 
-	typelinkpkg = mkpkg("go.typelink")
-	typelinkpkg.Name = "go.typelink"
-	typelinkpkg.Prefix = "go.typelink" // not go%2etypelink
-
 	itablinkpkg = mkpkg("go.itablink")
 	itablinkpkg.Name = "go.itablink"
 	itablinkpkg.Prefix = "go.itablink" // not go%2eitablink
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 23c8be645c..eed0ed6e24 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -321,6 +321,12 @@ func dsymptrLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
 	return off
 }
 
+func dsymptrOffLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
+	s.WriteOff(Ctxt, int64(off), x, int64(xoff))
+	off += 4
+	return off
+}
+
 func gdata(nam *Node, nr *Node, wid int) {
 	if nam.Op != ONAME {
 		Fatalf("gdata nam op %v", opnames[nam.Op])
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index df9ef27b7a..ea67634260 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -879,7 +879,7 @@ func tracksym(t *Type, f *Field) *Sym {
 	return Pkglookup(Tconv(t, FmtLeft)+"."+f.Sym.Name, trackpkg)
 }
 
-func typelinksym(t *Type) *Sym {
+func typelinkLSym(t *Type) *obj.LSym {
 	// %-uT is what the generated Type's string field says.
 	// It uses (ambiguous) package names instead of import paths.
 	// %-T is the complete, unambiguous type name.
@@ -889,13 +889,8 @@ func typelinksym(t *Type) *Sym {
 	// ensure the types appear sorted by their string field. The
 	// names are a little long but they are discarded by the linker
 	// and do not end up in the symbol table of the final binary.
-	p := Tconv(t, FmtLeft|FmtUnsigned) + "\t" + Tconv(t, FmtLeft)
-
-	s := Pkglookup(p, typelinkpkg)
-
-	//print("typelinksym: %s -> %+S\n", p, s);
-
-	return s
+	name := "go.typelink." + Tconv(t, FmtLeft|FmtUnsigned) + "\t" + Tconv(t, FmtLeft)
+	return obj.Linklookup(Ctxt, name, 0)
 }
 
 func typesymprefix(prefix string, t *Type) *Sym {
@@ -1298,9 +1293,9 @@ ok:
 	if t.Sym == nil {
 		switch t.Etype {
 		case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSTRUCT:
-			slink := typelinksym(t)
-			dsymptr(slink, 0, s, 0)
-			ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA))
+			slink := typelinkLSym(t)
+			dsymptrOffLSym(slink, 0, Linksym(s), 0)
+			ggloblLSym(slink, 4, int16(dupok|obj.RODATA))
 		}
 	}
 
diff --git a/src/cmd/internal/obj/data.go b/src/cmd/internal/obj/data.go
index 37ab70bb0e..546ff37269 100644
--- a/src/cmd/internal/obj/data.go
+++ b/src/cmd/internal/obj/data.go
@@ -111,17 +111,36 @@ func (s *LSym) WriteInt(ctxt *Link, off int64, siz int, i int64) {
 // rsym and roff specify the relocation for the address.
 func (s *LSym) WriteAddr(ctxt *Link, off int64, siz int, rsym *LSym, roff int64) {
 	if siz != ctxt.Arch.PtrSize {
-		ctxt.Diag("WriteAddr: bad address size: %d", siz)
+		ctxt.Diag("WriteAddr: bad address size %d in %s", siz, s.Name)
 	}
 	s.prepwrite(ctxt, off, siz)
 	r := Addrel(s)
 	r.Off = int32(off)
+	if int64(r.Off) != off {
+		ctxt.Diag("WriteAddr: off overflow %d in %s", off, s.Name)
+	}
 	r.Siz = uint8(siz)
 	r.Sym = rsym
 	r.Type = R_ADDR
 	r.Add = roff
 }
 
+// WriteOff writes a 4 byte offset to rsym+roff into s at offset off.
+// After linking the 4 bytes stored at s+off will be
+// rsym+roff-(start of section that s is in).
+func (s *LSym) WriteOff(ctxt *Link, off int64, rsym *LSym, roff int64) {
+	s.prepwrite(ctxt, off, 4)
+	r := Addrel(s)
+	r.Off = int32(off)
+	if int64(r.Off) != off {
+		ctxt.Diag("WriteOff: off overflow %d in %s", off, s.Name)
+	}
+	r.Siz = 4
+	r.Sym = rsym
+	r.Type = R_ADDROFF
+	r.Add = roff
+}
+
 // WriteString writes a string of size siz into s at offset off.
 func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) {
 	if siz < len(str) {
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 62175f9ed8..d44d4398b1 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -457,6 +457,9 @@ const (
 	// R_ADDRMIPS (only used on mips64) resolves to a 32-bit external address,
 	// by loading the address into a register with two instructions (lui, ori).
 	R_ADDRMIPS
+	// R_ADDROFF resolves to an offset from the beginning of the section holding
+	// the data being relocated to the referenced symbol.
+	R_ADDROFF
 	R_SIZE
 	R_CALL
 	R_CALLARM
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index ae7c287f59..cf51b0a908 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -525,6 +525,9 @@ func relocsym(s *LSym) {
 			}
 			o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
 
+		case obj.R_ADDROFF:
+			o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
+
 			// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
 		case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL:
 			if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != obj.SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == obj.R_GOTPCREL) {
@@ -1599,6 +1602,10 @@ func dodata() {
 	sect.Vaddr = 0
 	Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect
 	Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect
+	if !UseRelro() {
+		Linklookup(Ctxt, "runtime.types", 0).Sect = sect
+		Linklookup(Ctxt, "runtime.etypes", 0).Sect = sect
+	}
 	for ; s != nil && s.Type < obj.STYPERELRO; s = s.Next {
 		datsize = aligndatsize(datsize, s)
 		s.Sect = sect
@@ -1631,6 +1638,8 @@ func dodata() {
 		sect.Align = maxalign(s, obj.STYPELINK-1)
 		datsize = Rnd(datsize, int64(sect.Align))
 		sect.Vaddr = 0
+		Linklookup(Ctxt, "runtime.types", 0).Sect = sect
+		Linklookup(Ctxt, "runtime.etypes", 0).Sect = sect
 		for ; s != nil && s.Type < obj.STYPELINK; s = s.Next {
 			datsize = aligndatsize(datsize, s)
 			if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect {
@@ -1970,10 +1979,12 @@ func address() {
 	} else {
 		rodata = text.Next
 	}
+	var relrodata *Section
 	typelink := rodata.Next
 	if UseRelro() {
 		// There is another section (.data.rel.ro) when building a shared
 		// object on elf systems.
+		relrodata = typelink
 		typelink = typelink.Next
 	}
 	itablink := typelink.Next
@@ -2007,6 +2018,11 @@ func address() {
 		s.Value = int64(sectSym.Sect.Vaddr + 16)
 	}
 
+	types := relrodata
+	if types == nil {
+		types = rodata
+	}
+
 	xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
 	xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
 	if HEADTYPE == obj.Hwindows {
@@ -2014,6 +2030,8 @@ func address() {
 	}
 	xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
 	xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length))
+	xdefine("runtime.types", obj.SRODATA, int64(types.Vaddr))
+	xdefine("runtime.etypes", obj.SRODATA, int64(types.Vaddr+types.Length))
 	xdefine("runtime.typelink", obj.SRODATA, int64(typelink.Vaddr))
 	xdefine("runtime.etypelink", obj.SRODATA, int64(typelink.Vaddr+typelink.Length))
 	xdefine("runtime.itablink", obj.SRODATA, int64(itablink.Vaddr))
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index ae0b17c259..678ed38730 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -329,6 +329,8 @@ func symtab() {
 	xdefine("runtime.eitablink", obj.SRODATA, 0)
 	xdefine("runtime.rodata", obj.SRODATA, 0)
 	xdefine("runtime.erodata", obj.SRODATA, 0)
+	xdefine("runtime.types", obj.SRODATA, 0)
+	xdefine("runtime.etypes", obj.SRODATA, 0)
 	xdefine("runtime.noptrdata", obj.SNOPTRDATA, 0)
 	xdefine("runtime.enoptrdata", obj.SNOPTRDATA, 0)
 	xdefine("runtime.data", obj.SDATA, 0)
@@ -537,6 +539,8 @@ func symtab() {
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.end", 0))
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcdata", 0))
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcbss", 0))
+	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.types", 0))
+	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.etypes", 0))
 	// The typelinks slice
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.typelink", 0))
 	adduint(Ctxt, moduledata, uint64(ntypelinks))
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index ddc64b46be..037c953718 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -46,9 +46,11 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr,
 
 func TypeLinks() []string {
 	var r []string
-	for _, m := range typelinks() {
-		for _, t := range m {
-			r = append(r, t.string)
+	sections, offset := typelinks()
+	for i, offs := range offset {
+		rodata := sections[i]
+		for _, off := range offs {
+			r = append(r, rtypeOff(rodata, off).string)
 		}
 	}
 	return r
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 8f13acf26e..7104fde60a 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1558,30 +1558,48 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
 }
 
 // typelinks is implemented in package runtime.
-// It returns a slice of all the 'typelink' information in the binary,
-// which is to say a slice of known types, sorted by string.
+// It returns a slice of the sections in each module,
+// and a slice of *rtype offsets in each module.
+//
+// The types in each module are sorted by string. That is, the first
+// two linked types of the first module are:
+//
+//	d0 := sections[0]
+//	t1 := (*rtype)(add(d0, offset[0][0]))
+//	t2 := (*rtype)(add(d0, offset[0][1]))
+//
+// and
+//
+//	t1.string < t2.string
+//
 // Note that strings are not unique identifiers for types:
 // there can be more than one with a given string.
 // Only types we might want to look up are included:
 // pointers, channels, maps, slices, and arrays.
-func typelinks() [][]*rtype
+func typelinks() (sections []unsafe.Pointer, offset [][]int32)
+
+func rtypeOff(section unsafe.Pointer, off int32) *rtype {
+	return (*rtype)(add(section, uintptr(off)))
+}
 
 // typesByString returns the subslice of typelinks() whose elements have
 // the given string representation.
 // It may be empty (no known types with that string) or may have
 // multiple elements (multiple types with that string).
 func typesByString(s string) []*rtype {
-	typs := typelinks()
+	sections, offset := typelinks()
 	var ret []*rtype
 
-	for _, typ := range typs {
+	for offsI, offs := range offset {
+		section := sections[offsI]
+
 		// We are looking for the first index i where the string becomes >= s.
 		// This is a copy of sort.Search, with f(h) replaced by (*typ[h].string >= s).
-		i, j := 0, len(typ)
+		i, j := 0, len(offs)
 		for i < j {
 			h := i + (j-i)/2 // avoid overflow when computing h
 			// i ≤ h < j
-			if !(typ[h].string >= s) {
+			if !(rtypeOff(section, offs[h]).string >= s) {
 				i = h + 1 // preserves f(i-1) == false
 			} else {
 				j = h // preserves f(j) == true
@@ -1592,17 +1610,12 @@ func typesByString(s string) []*rtype {
 		// Having found the first, linear scan forward to find the last.
 		// We could do a second binary search, but the caller is going
 		// to do a linear scan anyway.
-		j = i
-		for j < len(typ) && typ[j].string == s {
-			j++
-		}
-
-		if j > i {
-			if ret == nil {
-				ret = typ[i:j:j]
-			} else {
-				ret = append(ret, typ[i:j]...)
+		for j := i; j < len(offs); j++ {
+			typ := rtypeOff(section, offs[j])
+			if typ.string != s {
+				break
 			}
+			ret = append(ret, typ)
 		}
 	}
 	return ret
diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go
index 95bebac593..e1956569fd 100644
--- a/src/runtime/runtime1.go
+++ b/src/runtime/runtime1.go
@@ -477,10 +477,12 @@ func gomcache() *mcache {
 }
 
 //go:linkname reflect_typelinks reflect.typelinks
-func reflect_typelinks() [][]*_type {
-	ret := [][]*_type{firstmoduledata.typelinks}
+func reflect_typelinks() ([]unsafe.Pointer, [][]int32) {
+	sections := []unsafe.Pointer{unsafe.Pointer(firstmoduledata.types)}
+	ret := [][]int32{firstmoduledata.typelinks}
 	for datap := firstmoduledata.next; datap != nil; datap = datap.next {
+		sections = append(sections, unsafe.Pointer(datap.types))
 		ret = append(ret, datap.typelinks)
 	}
-	return ret
+	return sections, ret
 }
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 158bdcea0d..8c70f22c1f 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -127,8 +127,9 @@ type moduledata struct {
 	bss, ebss             uintptr
 	noptrbss, enoptrbss   uintptr
 	end, gcdata, gcbss    uintptr
+	types, etypes         uintptr
 
-	typelinks []*_type
+	typelinks []int32 // offsets from types
 	itablinks []*itab
 
 	modulename   string
-- 
cgit v1.3


From 24fc3234428e138e693584185fab4146de6088db Mon Sep 17 00:00:00 2001
From: Shahar Kohanim 
Date: Mon, 11 Apr 2016 17:35:55 +0300
Subject: cmd/internal/obj: split plist flushing from object writing

Only splits into separate files, no other changes.

Change-Id: Icc0da2c5f18e03e9ed7c0043bd7c790f741900f2
Reviewed-on: https://go-review.googlesource.com/21804
Reviewed-by: Matthew Dempsky 
Run-TryBot: Matthew Dempsky 
TryBot-Result: Gobot Gobot 
---
 src/cmd/internal/obj/link.go    |  21 ----
 src/cmd/internal/obj/objfile.go | 187 ----------------------------------
 src/cmd/internal/obj/plist.go   | 218 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 218 insertions(+), 208 deletions(-)
 create mode 100644 src/cmd/internal/obj/plist.go

(limited to 'src')

diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index d44d4398b1..146be6f98f 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -730,27 +730,6 @@ const (
 	Hwindows
 )
 
-type Plist struct {
-	Name    *LSym
-	Firstpc *Prog
-	Recur   int
-	Link    *Plist
-}
-
-/*
- * start a new Prog list.
- */
-func Linknewplist(ctxt *Link) *Plist {
-	pl := new(Plist)
-	if ctxt.Plist == nil {
-		ctxt.Plist = pl
-	} else {
-		ctxt.Plast.Link = pl
-	}
-	ctxt.Plast = pl
-	return pl
-}
-
 // AsmBuf is a simple buffer to assemble variable-length x86 instructions into.
 type AsmBuf struct {
 	buf [100]byte
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index ee21f39d10..7d88db2bcc 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -115,7 +115,6 @@ import (
 	"log"
 	"path/filepath"
 	"sort"
-	"strings"
 )
 
 // The Go and C compilers, and the assembler, call writeobj to write
@@ -126,192 +125,6 @@ func Writeobjdirect(ctxt *Link, b *bio.Writer) {
 	WriteObjFile(ctxt, b)
 }
 
-func Flushplist(ctxt *Link) {
-	flushplist(ctxt, ctxt.Debugasm == 0)
-}
-func FlushplistNoFree(ctxt *Link) {
-	flushplist(ctxt, false)
-}
-func flushplist(ctxt *Link, freeProgs bool) {
-	// Build list of symbols, and assign instructions to lists.
-	// Ignore ctxt->plist boundaries. There are no guarantees there,
-	// and the assemblers just use one big list.
-	var curtext *LSym
-	var etext *Prog
-	var text []*LSym
-
-	for pl := ctxt.Plist; pl != nil; pl = pl.Link {
-		var plink *Prog
-		for p := pl.Firstpc; p != nil; p = plink {
-			if ctxt.Debugasm != 0 && ctxt.Debugvlog != 0 {
-				fmt.Printf("obj: %v\n", p)
-			}
-			plink = p.Link
-			p.Link = nil
-
-			switch p.As {
-			case AEND:
-				continue
-
-			case ATYPE:
-				// Assume each TYPE instruction describes
-				// a different local variable or parameter,
-				// so no dedup.
-				// Using only the TYPE instructions means
-				// that we discard location information about local variables
-				// in C and assembly functions; that information is inferred
-				// from ordinary references, because there are no TYPE
-				// instructions there. Without the type information, gdb can't
-				// use the locations, so we don't bother to save them.
-				// If something else could use them, we could arrange to
-				// preserve them.
-				if curtext == nil {
-					continue
-				}
-				a := new(Auto)
-				a.Asym = p.From.Sym
-				a.Aoffset = int32(p.From.Offset)
-				a.Name = int16(p.From.Name)
-				a.Gotype = p.From.Gotype
-				a.Link = curtext.Autom
-				curtext.Autom = a
-				continue
-
-			case AGLOBL:
-				s := p.From.Sym
-				if s.Seenglobl {
-					fmt.Printf("duplicate %v\n", p)
-				}
-				s.Seenglobl = true
-				if s.Onlist {
-					log.Fatalf("symbol %s listed multiple times", s.Name)
-				}
-				s.Onlist = true
-				ctxt.Data = append(ctxt.Data, s)
-				s.Size = p.To.Offset
-				if s.Type == 0 || s.Type == SXREF {
-					s.Type = SBSS
-				}
-				flag := int(p.From3.Offset)
-				if flag&DUPOK != 0 {
-					s.Dupok = true
-				}
-				if flag&RODATA != 0 {
-					s.Type = SRODATA
-				} else if flag&NOPTR != 0 {
-					s.Type = SNOPTRBSS
-				} else if flag&TLSBSS != 0 {
-					s.Type = STLSBSS
-				}
-				continue
-
-			case ATEXT:
-				s := p.From.Sym
-				if s == nil {
-					// func _() { }
-					curtext = nil
-
-					continue
-				}
-
-				if s.Text != nil {
-					log.Fatalf("duplicate TEXT for %s", s.Name)
-				}
-				if s.Onlist {
-					log.Fatalf("symbol %s listed multiple times", s.Name)
-				}
-				s.Onlist = true
-				text = append(text, s)
-				flag := int(p.From3Offset())
-				if flag&DUPOK != 0 {
-					s.Dupok = true
-				}
-				if flag&NOSPLIT != 0 {
-					s.Nosplit = true
-				}
-				if flag&REFLECTMETHOD != 0 {
-					s.ReflectMethod = true
-				}
-				s.Type = STEXT
-				s.Text = p
-				etext = p
-				curtext = s
-				continue
-
-			case AFUNCDATA:
-				// Rewrite reference to go_args_stackmap(SB) to the Go-provided declaration information.
-				if curtext == nil { // func _() {}
-					continue
-				}
-				if p.To.Sym.Name == "go_args_stackmap" {
-					if p.From.Type != TYPE_CONST || p.From.Offset != FUNCDATA_ArgsPointerMaps {
-						ctxt.Diag("FUNCDATA use of go_args_stackmap(SB) without FUNCDATA_ArgsPointerMaps")
-					}
-					p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", curtext.Name), int(curtext.Version))
-				}
-
-			}
-
-			if curtext == nil {
-				etext = nil
-				continue
-			}
-			etext.Link = p
-			etext = p
-		}
-	}
-
-	// Add reference to Go arguments for C or assembly functions without them.
-	for _, s := range text {
-		if !strings.HasPrefix(s.Name, "\"\".") {
-			continue
-		}
-		found := false
-		var p *Prog
-		for p = s.Text; p != nil; p = p.Link {
-			if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == FUNCDATA_ArgsPointerMaps {
-				found = true
-				break
-			}
-		}
-
-		if !found {
-			p = Appendp(ctxt, s.Text)
-			p.As = AFUNCDATA
-			p.From.Type = TYPE_CONST
-			p.From.Offset = FUNCDATA_ArgsPointerMaps
-			p.To.Type = TYPE_MEM
-			p.To.Name = NAME_EXTERN
-			p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", s.Name), int(s.Version))
-		}
-	}
-
-	// Turn functions into machine code images.
-	for _, s := range text {
-		mkfwd(s)
-		linkpatch(ctxt, s)
-		if ctxt.Flag_optimize {
-			ctxt.Arch.Follow(ctxt, s)
-		}
-		ctxt.Arch.Preprocess(ctxt, s)
-		ctxt.Arch.Assemble(ctxt, s)
-		fieldtrack(ctxt, s)
-		linkpcln(ctxt, s)
-		if freeProgs {
-			s.Text = nil
-		}
-	}
-
-	// Add to running list in ctxt.
-	ctxt.Text = append(ctxt.Text, text...)
-	ctxt.Plist = nil
-	ctxt.Plast = nil
-	ctxt.Curp = nil
-	if freeProgs {
-		ctxt.freeProgs()
-	}
-}
-
 // objWriter writes Go object files.
 type objWriter struct {
 	wr   *bufio.Writer
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
new file mode 100644
index 0000000000..e55dbeca1e
--- /dev/null
+++ b/src/cmd/internal/obj/plist.go
@@ -0,0 +1,218 @@
+// Copyright 2013 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 obj
+
+import (
+	"fmt"
+	"log"
+	"strings"
+)
+
+type Plist struct {
+	Name    *LSym
+	Firstpc *Prog
+	Recur   int
+	Link    *Plist
+}
+
+/*
+ * start a new Prog list.
+ */
+func Linknewplist(ctxt *Link) *Plist {
+	pl := new(Plist)
+	if ctxt.Plist == nil {
+		ctxt.Plist = pl
+	} else {
+		ctxt.Plast.Link = pl
+	}
+	ctxt.Plast = pl
+	return pl
+}
+
+func Flushplist(ctxt *Link) {
+	flushplist(ctxt, ctxt.Debugasm == 0)
+}
+func FlushplistNoFree(ctxt *Link) {
+	flushplist(ctxt, false)
+}
+func flushplist(ctxt *Link, freeProgs bool) {
+	// Build list of symbols, and assign instructions to lists.
+	// Ignore ctxt->plist boundaries. There are no guarantees there,
+	// and the assemblers just use one big list.
+	var curtext *LSym
+	var etext *Prog
+	var text []*LSym
+
+	for pl := ctxt.Plist; pl != nil; pl = pl.Link {
+		var plink *Prog
+		for p := pl.Firstpc; p != nil; p = plink {
+			if ctxt.Debugasm != 0 && ctxt.Debugvlog != 0 {
+				fmt.Printf("obj: %v\n", p)
+			}
+			plink = p.Link
+			p.Link = nil
+
+			switch p.As {
+			case AEND:
+				continue
+
+			case ATYPE:
+				// Assume each TYPE instruction describes
+				// a different local variable or parameter,
+				// so no dedup.
+				// Using only the TYPE instructions means
+				// that we discard location information about local variables
+				// in C and assembly functions; that information is inferred
+				// from ordinary references, because there are no TYPE
+				// instructions there. Without the type information, gdb can't
+				// use the locations, so we don't bother to save them.
+				// If something else could use them, we could arrange to
+				// preserve them.
+				if curtext == nil {
+					continue
+				}
+				a := new(Auto)
+				a.Asym = p.From.Sym
+				a.Aoffset = int32(p.From.Offset)
+				a.Name = int16(p.From.Name)
+				a.Gotype = p.From.Gotype
+				a.Link = curtext.Autom
+				curtext.Autom = a
+				continue
+
+			case AGLOBL:
+				s := p.From.Sym
+				if s.Seenglobl {
+					fmt.Printf("duplicate %v\n", p)
+				}
+				s.Seenglobl = true
+				if s.Onlist {
+					log.Fatalf("symbol %s listed multiple times", s.Name)
+				}
+				s.Onlist = true
+				ctxt.Data = append(ctxt.Data, s)
+				s.Size = p.To.Offset
+				if s.Type == 0 || s.Type == SXREF {
+					s.Type = SBSS
+				}
+				flag := int(p.From3.Offset)
+				if flag&DUPOK != 0 {
+					s.Dupok = true
+				}
+				if flag&RODATA != 0 {
+					s.Type = SRODATA
+				} else if flag&NOPTR != 0 {
+					s.Type = SNOPTRBSS
+				} else if flag&TLSBSS != 0 {
+					s.Type = STLSBSS
+				}
+				continue
+
+			case ATEXT:
+				s := p.From.Sym
+				if s == nil {
+					// func _() { }
+					curtext = nil
+
+					continue
+				}
+
+				if s.Text != nil {
+					log.Fatalf("duplicate TEXT for %s", s.Name)
+				}
+				if s.Onlist {
+					log.Fatalf("symbol %s listed multiple times", s.Name)
+				}
+				s.Onlist = true
+				text = append(text, s)
+				flag := int(p.From3Offset())
+				if flag&DUPOK != 0 {
+					s.Dupok = true
+				}
+				if flag&NOSPLIT != 0 {
+					s.Nosplit = true
+				}
+				if flag&REFLECTMETHOD != 0 {
+					s.ReflectMethod = true
+				}
+				s.Type = STEXT
+				s.Text = p
+				etext = p
+				curtext = s
+				continue
+
+			case AFUNCDATA:
+				// Rewrite reference to go_args_stackmap(SB) to the Go-provided declaration information.
+				if curtext == nil { // func _() {}
+					continue
+				}
+				if p.To.Sym.Name == "go_args_stackmap" {
+					if p.From.Type != TYPE_CONST || p.From.Offset != FUNCDATA_ArgsPointerMaps {
+						ctxt.Diag("FUNCDATA use of go_args_stackmap(SB) without FUNCDATA_ArgsPointerMaps")
+					}
+					p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", curtext.Name), int(curtext.Version))
+				}
+
+			}
+
+			if curtext == nil {
+				etext = nil
+				continue
+			}
+			etext.Link = p
+			etext = p
+		}
+	}
+
+	// Add reference to Go arguments for C or assembly functions without them.
+	for _, s := range text {
+		if !strings.HasPrefix(s.Name, "\"\".") {
+			continue
+		}
+		found := false
+		var p *Prog
+		for p = s.Text; p != nil; p = p.Link {
+			if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == FUNCDATA_ArgsPointerMaps {
+				found = true
+				break
+			}
+		}
+
+		if !found {
+			p = Appendp(ctxt, s.Text)
+			p.As = AFUNCDATA
+			p.From.Type = TYPE_CONST
+			p.From.Offset = FUNCDATA_ArgsPointerMaps
+			p.To.Type = TYPE_MEM
+			p.To.Name = NAME_EXTERN
+			p.To.Sym = Linklookup(ctxt, fmt.Sprintf("%s.args_stackmap", s.Name), int(s.Version))
+		}
+	}
+
+	// Turn functions into machine code images.
+	for _, s := range text {
+		mkfwd(s)
+		linkpatch(ctxt, s)
+		if ctxt.Flag_optimize {
+			ctxt.Arch.Follow(ctxt, s)
+		}
+		ctxt.Arch.Preprocess(ctxt, s)
+		ctxt.Arch.Assemble(ctxt, s)
+		fieldtrack(ctxt, s)
+		linkpcln(ctxt, s)
+		if freeProgs {
+			s.Text = nil
+		}
+	}
+
+	// Add to running list in ctxt.
+	ctxt.Text = append(ctxt.Text, text...)
+	ctxt.Plist = nil
+	ctxt.Plast = nil
+	ctxt.Curp = nil
+	if freeProgs {
+		ctxt.freeProgs()
+	}
+}
-- 
cgit v1.3


From 022548cfe82915e5bf14ce7cb28f3ec651550662 Mon Sep 17 00:00:00 2001
From: Dan Peterson 
Date: Tue, 12 Apr 2016 16:58:56 -0300
Subject: all: standardize RFC mention format

Standardize on space between "RFC" and number. Additionally change
the couple "a RFC" instances to "an RFC."

Fixes #15258

Change-Id: I2b17ecd06be07dfbb4207c690f52a59ea9b04808
Reviewed-on: https://go-review.googlesource.com/21902
Reviewed-by: Brad Fitzpatrick 
---
 src/crypto/tls/common.go     | 2 +-
 src/crypto/tls/prf.go        | 2 +-
 src/crypto/x509/pkcs8.go     | 2 +-
 src/crypto/x509/sec1.go      | 4 ++--
 src/mime/encodedword.go      | 2 +-
 src/net/dnsname_test.go      | 2 +-
 src/net/http/request.go      | 2 +-
 src/net/http/response.go     | 2 +-
 src/net/http/server.go       | 2 +-
 src/net/http/transfer.go     | 4 ++--
 src/net/mail/message.go      | 4 ++--
 src/net/mail/message_test.go | 2 +-
 12 files changed, 15 insertions(+), 15 deletions(-)

(limited to 'src')

diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
index 572266bc8f..b3399b063c 100644
--- a/src/crypto/tls/common.go
+++ b/src/crypto/tls/common.go
@@ -114,7 +114,7 @@ const (
 	certTypeRSAFixedDH = 3 // A certificate containing a static DH key
 	certTypeDSSFixedDH = 4 // A certificate containing a static DH key
 
-	// See RFC4492 sections 3 and 5.5.
+	// See RFC 4492 sections 3 and 5.5.
 	certTypeECDSASign      = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA.
 	certTypeRSAFixedECDH   = 65 // A certificate containing an ECDH-capable public key, signed with RSA.
 	certTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA.
diff --git a/src/crypto/tls/prf.go b/src/crypto/tls/prf.go
index 747b817ba3..5833fc1963 100644
--- a/src/crypto/tls/prf.go
+++ b/src/crypto/tls/prf.go
@@ -85,7 +85,7 @@ func prf30(result, secret, label, seed []byte) {
 
 	done := 0
 	i := 0
-	// RFC5246 section 6.3 says that the largest PRF output needed is 128
+	// RFC 5246 section 6.3 says that the largest PRF output needed is 128
 	// bytes. Since no more ciphersuites will be added to SSLv3, this will
 	// remain true. Each iteration gives us 16 bytes so 10 iterations will
 	// be sufficient.
diff --git a/src/crypto/x509/pkcs8.go b/src/crypto/x509/pkcs8.go
index 6e56752c0e..b304a3f63c 100644
--- a/src/crypto/x509/pkcs8.go
+++ b/src/crypto/x509/pkcs8.go
@@ -13,7 +13,7 @@ import (
 
 // pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
 // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
-// and RFC5208.
+// and RFC 5208.
 type pkcs8 struct {
 	Version    int
 	Algo       pkix.AlgorithmIdentifier
diff --git a/src/crypto/x509/sec1.go b/src/crypto/x509/sec1.go
index 5f1b3ecc7c..33f376c072 100644
--- a/src/crypto/x509/sec1.go
+++ b/src/crypto/x509/sec1.go
@@ -17,9 +17,9 @@ const ecPrivKeyVersion = 1
 
 // ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
 // References:
-//   RFC5915
+//   RFC 5915
 //   SEC1 - http://www.secg.org/sec1-v2.pdf
-// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
+// Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
 // most cases it is not.
 type ecPrivateKey struct {
 	Version       int
diff --git a/src/mime/encodedword.go b/src/mime/encodedword.go
index e6cbebe946..c3ca4bacd1 100644
--- a/src/mime/encodedword.go
+++ b/src/mime/encodedword.go
@@ -16,7 +16,7 @@ import (
 	"unicode/utf8"
 )
 
-// A WordEncoder is a RFC 2047 encoded-word encoder.
+// A WordEncoder is an RFC 2047 encoded-word encoder.
 type WordEncoder byte
 
 const (
diff --git a/src/net/dnsname_test.go b/src/net/dnsname_test.go
index be07dc6a16..bc777b855e 100644
--- a/src/net/dnsname_test.go
+++ b/src/net/dnsname_test.go
@@ -15,7 +15,7 @@ type dnsNameTest struct {
 }
 
 var dnsNameTests = []dnsNameTest{
-	// RFC2181, section 11.
+	// RFC 2181, section 11.
 	{"_xmpp-server._tcp.google.com", true},
 	{"foo.com", true},
 	{"1foo.com", true},
diff --git a/src/net/http/request.go b/src/net/http/request.go
index 5bca888845..bac2de1a2e 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -817,7 +817,7 @@ func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err erro
 	}
 	req.Header = Header(mimeHeader)
 
-	// RFC2616: Must treat
+	// RFC 2616: Must treat
 	//	GET /index.html HTTP/1.1
 	//	Host: www.google.com
 	// and
diff --git a/src/net/http/response.go b/src/net/http/response.go
index b49b77d8b9..91d4ffb7ec 100644
--- a/src/net/http/response.go
+++ b/src/net/http/response.go
@@ -185,7 +185,7 @@ func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
 	return resp, nil
 }
 
-// RFC2616: Should treat
+// RFC 2616: Should treat
 //	Pragma: no-cache
 // like
 //	Cache-Control: no-cache
diff --git a/src/net/http/server.go b/src/net/http/server.go
index deb170c334..64529f1e96 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -1747,7 +1747,7 @@ func Redirect(w ResponseWriter, r *Request, urlStr string, code int) {
 	w.Header().Set("Location", urlStr)
 	w.WriteHeader(code)
 
-	// RFC2616 recommends that a short note "SHOULD" be included in the
+	// RFC 2616 recommends that a short note "SHOULD" be included in the
 	// response because older user agents may not understand 301/307.
 	// Shouldn't send the response for POST or HEAD; that leaves GET.
 	if r.Method == "GET" {
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index 4c130f0cc4..501e4be08c 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -276,7 +276,7 @@ func (t *transferReader) protoAtLeast(m, n int) bool {
 }
 
 // bodyAllowedForStatus reports whether a given response status code
-// permits a body. See RFC2616, section 4.4.
+// permits a body. See RFC 2616, section 4.4.
 func bodyAllowedForStatus(status int) bool {
 	switch {
 	case status >= 100 && status <= 199:
@@ -368,7 +368,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
 
 	// If there is no Content-Length or chunked Transfer-Encoding on a *Response
 	// and the status is not 1xx, 204 or 304, then the body is unbounded.
-	// See RFC2616, section 4.4.
+	// See RFC 2616, section 4.4.
 	switch msg.(type) {
 	case *Response:
 		if realLength == -1 &&
diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index 12342b368f..9e3a103a4f 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -570,7 +570,7 @@ func isQtext(c byte) bool {
 	return '!' <= c && c <= '~'
 }
 
-// quoteString renders a string as a RFC5322 quoted-string.
+// quoteString renders a string as an RFC 5322 quoted-string.
 func quoteString(s string) string {
 	var buf bytes.Buffer
 	buf.WriteByte('"')
@@ -594,7 +594,7 @@ func isVchar(c byte) bool {
 }
 
 // isWSP reports whether c is a WSP (white space).
-// WSP is a space or horizontal tab (RFC5234 Appendix B).
+// WSP is a space or horizontal tab (RFC 5234 Appendix B).
 func isWSP(c byte) bool {
 	return c == ' ' || c == '\t'
 }
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go
index cf86ace68f..2669325c13 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -92,7 +92,7 @@ func TestDateParsing(t *testing.T) {
 			"Fri, 21 Nov 1997 09:55:06 -0600",
 			time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
 		},
-		// RFC5322, Appendix A.6.2
+		// RFC 5322, Appendix A.6.2
 		// Obsolete date.
 		{
 			"21 Nov 97 09:55:06 GMT",
-- 
cgit v1.3


From 6fb8bf9d7940b2e2a90249e1894ad4e3d24fd3e7 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Tue, 12 Apr 2016 14:04:01 -0700
Subject: net: make two tests not parallel
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Running

stress -p 1 go test -short std

on a heavily loaded machine causes net timeouts
every 15 or 20 runs.
Making these tests not run in parallel helps.
With this change, I haven’t seen a single failure
in over 100 runs.

Fixes #14986

Change-Id: Ibaa14869ce8d95b00266aee94d62d195927ede68
Reviewed-on: https://go-review.googlesource.com/21905
Run-TryBot: Josh Bleecher Snyder 
Reviewed-by: Brad Fitzpatrick 
---
 src/net/timeout_test.go | 3 ---
 1 file changed, 3 deletions(-)

(limited to 'src')

diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go
index 3ea0ec1ebd..86010927b3 100644
--- a/src/net/timeout_test.go
+++ b/src/net/timeout_test.go
@@ -112,7 +112,6 @@ var dialTimeoutMaxDurationTests = []struct {
 }
 
 func TestDialTimeoutMaxDuration(t *testing.T) {
-	t.Parallel()
 	if runtime.GOOS == "openbsd" {
 		testenv.SkipFlaky(t, 15157)
 	}
@@ -315,8 +314,6 @@ var readTimeoutTests = []struct {
 }
 
 func TestReadTimeout(t *testing.T) {
-	t.Parallel()
-
 	switch runtime.GOOS {
 	case "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
-- 
cgit v1.3


From e46b00a43b62bd67ec13ca75c51037db3b312043 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky 
Date: Tue, 12 Apr 2016 14:44:49 -0700
Subject: cmd/internal/obj: remove unused Pciter type

Change-Id: Ie8323cfcd1193f390729d0d3dd67863aedf47d13
Reviewed-on: https://go-review.googlesource.com/21906
Run-TryBot: Matthew Dempsky 
Reviewed-by: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
---
 src/cmd/internal/obj/link.go | 13 -------------
 1 file changed, 13 deletions(-)

(limited to 'src')

diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 146be6f98f..42aaa5f4f0 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -600,19 +600,6 @@ type Pcdata struct {
 	P []byte
 }
 
-// Pcdata iterator.
-//      for(pciterinit(ctxt, &it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) }
-type Pciter struct {
-	d       Pcdata
-	p       []byte
-	pc      uint32
-	nextpc  uint32
-	pcscale uint32
-	value   int32
-	start   int
-	done    int
-}
-
 // symbol version, incremented each time a file is loaded.
 // version==1 is reserved for savehist.
 const (
-- 
cgit v1.3


From f6db855d5595e170bfc70d90f1eaa26034d2e191 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Tue, 12 Apr 2016 21:51:19 +0000
Subject: net: mirror Tom Sawyer in the net package for tests

Fixes the darwin/arm builder, which has a special test runner which
makes the assumption that tests never use testdata from another
package.

This looks large, but it's no more space in git.

Change-Id: I81921b516443d12d21b77617d323ddebedbe40f8
Reviewed-on: https://go-review.googlesource.com/21907
Reviewed-by: David Crawshaw 
Run-TryBot: David Crawshaw 
TryBot-Result: Gobot Gobot 
---
 src/net/sendfile_test.go                   |    4 +-
 src/net/testdata/Mark.Twain-Tom.Sawyer.txt | 8465 ++++++++++++++++++++++++++++
 2 files changed, 8467 insertions(+), 2 deletions(-)
 create mode 100644 src/net/testdata/Mark.Twain-Tom.Sawyer.txt

(limited to 'src')

diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go
index add2bcb5c6..2255e7c478 100644
--- a/src/net/sendfile_test.go
+++ b/src/net/sendfile_test.go
@@ -14,12 +14,12 @@ import (
 )
 
 const (
-	twain       = "../compress/testdata/Mark.Twain-Tom.Sawyer.txt"
+	twain       = "testdata/Mark.Twain-Tom.Sawyer.txt"
 	twainLen    = 387851
 	twainSHA256 = "461eb7cb2d57d293fc680c836464c9125e4382be3596f7d415093ae9db8fcb0e"
 )
 
-func TestSendFile(t *testing.T) {
+func TestSendfile(t *testing.T) {
 	ln, err := newLocalListener("tcp")
 	if err != nil {
 		t.Fatal(err)
diff --git a/src/net/testdata/Mark.Twain-Tom.Sawyer.txt b/src/net/testdata/Mark.Twain-Tom.Sawyer.txt
new file mode 100644
index 0000000000..c9106fd522
--- /dev/null
+++ b/src/net/testdata/Mark.Twain-Tom.Sawyer.txt
@@ -0,0 +1,8465 @@
+Produced by David Widger. The previous edition was updated by Jose
+Menendez.
+
+
+
+
+
+                   THE ADVENTURES OF TOM SAWYER
+                                BY
+                            MARK TWAIN
+                     (Samuel Langhorne Clemens)
+
+
+
+
+                           P R E F A C E
+
+MOST of the adventures recorded in this book really occurred; one or
+two were experiences of my own, the rest those of boys who were
+schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but
+not from an individual--he is a combination of the characteristics of
+three boys whom I knew, and therefore belongs to the composite order of
+architecture.
+
+The odd superstitions touched upon were all prevalent among children
+and slaves in the West at the period of this story--that is to say,
+thirty or forty years ago.
+
+Although my book is intended mainly for the entertainment of boys and
+girls, I hope it will not be shunned by men and women on that account,
+for part of my plan has been to try to pleasantly remind adults of what
+they once were themselves, and of how they felt and thought and talked,
+and what queer enterprises they sometimes engaged in.
+
+                                                            THE AUTHOR.
+
+HARTFORD, 1876.
+
+
+
+                          T O M   S A W Y E R
+
+
+
+CHAPTER I
+
+"TOM!"
+
+No answer.
+
+"TOM!"
+
+No answer.
+
+"What's gone with that boy,  I wonder? You TOM!"
+
+No answer.
+
+The old lady pulled her spectacles down and looked over them about the
+room; then she put them up and looked out under them. She seldom or
+never looked THROUGH them for so small a thing as a boy; they were her
+state pair, the pride of her heart, and were built for "style," not
+service--she could have seen through a pair of stove-lids just as well.
+She looked perplexed for a moment, and then said, not fiercely, but
+still loud enough for the furniture to hear:
+
+"Well, I lay if I get hold of you I'll--"
+
+She did not finish, for by this time she was bending down and punching
+under the bed with the broom, and so she needed breath to punctuate the
+punches with. She resurrected nothing but the cat.
+
+"I never did see the beat of that boy!"
+
+She went to the open door and stood in it and looked out among the
+tomato vines and "jimpson" weeds that constituted the garden. No Tom.
+So she lifted up her voice at an angle calculated for distance and
+shouted:
+
+"Y-o-u-u TOM!"
+
+There was a slight noise behind her and she turned just in time to
+seize a small boy by the slack of his roundabout and arrest his flight.
+
+"There! I might 'a' thought of that closet. What you been doing in
+there?"
+
+"Nothing."
+
+"Nothing! Look at your hands. And look at your mouth. What IS that
+truck?"
+
+"I don't know, aunt."
+
+"Well, I know. It's jam--that's what it is. Forty times I've said if
+you didn't let that jam alone I'd skin you. Hand me that switch."
+
+The switch hovered in the air--the peril was desperate--
+
+"My! Look behind you, aunt!"
+
+The old lady whirled round, and snatched her skirts out of danger. The
+lad fled on the instant, scrambled up the high board-fence, and
+disappeared over it.
+
+His aunt Polly stood surprised a moment, and then broke into a gentle
+laugh.
+
+"Hang the boy, can't I never learn anything? Ain't he played me tricks
+enough like that for me to be looking out for him by this time? But old
+fools is the biggest fools there is. Can't learn an old dog new tricks,
+as the saying is. But my goodness, he never plays them alike, two days,
+and how is a body to know what's coming? He 'pears to know just how
+long he can torment me before I get my dander up, and he knows if he
+can make out to put me off for a minute or make me laugh, it's all down
+again and I can't hit him a lick. I ain't doing my duty by that boy,
+and that's the Lord's truth, goodness knows. Spare the rod and spile
+the child, as the Good Book says. I'm a laying up sin and suffering for
+us both, I know. He's full of the Old Scratch, but laws-a-me! he's my
+own dead sister's boy, poor thing, and I ain't got the heart to lash
+him, somehow. Every time I let him off, my conscience does hurt me so,
+and every time I hit him my old heart most breaks. Well-a-well, man
+that is born of woman is of few days and full of trouble, as the
+Scripture says, and I reckon it's so. He'll play hookey this evening, *
+and [* Southwestern for "afternoon"] I'll just be obleeged to make him
+work, to-morrow, to punish him. It's mighty hard to make him work
+Saturdays, when all the boys is having holiday, but he hates work more
+than he hates anything else, and I've GOT to do some of my duty by him,
+or I'll be the ruination of the child."
+
+Tom did play hookey, and he had a very good time. He got back home
+barely in season to help Jim, the small colored boy, saw next-day's
+wood and split the kindlings before supper--at least he was there in
+time to tell his adventures to Jim while Jim did three-fourths of the
+work. Tom's younger brother (or rather half-brother) Sid was already
+through with his part of the work (picking up chips), for he was a
+quiet boy, and had no adventurous, troublesome ways.
+
+While Tom was eating his supper, and stealing sugar as opportunity
+offered, Aunt Polly asked him questions that were full of guile, and
+very deep--for she wanted to trap him into damaging revealments. Like
+many other simple-hearted souls, it was her pet vanity to believe she
+was endowed with a talent for dark and mysterious diplomacy, and she
+loved to contemplate her most transparent devices as marvels of low
+cunning. Said she:
+
+"Tom, it was middling warm in school, warn't it?"
+
+"Yes'm."
+
+"Powerful warm, warn't it?"
+
+"Yes'm."
+
+"Didn't you want to go in a-swimming, Tom?"
+
+A bit of a scare shot through Tom--a touch of uncomfortable suspicion.
+He searched Aunt Polly's face, but it told him nothing. So he said:
+
+"No'm--well, not very much."
+
+The old lady reached out her hand and felt Tom's shirt, and said:
+
+"But you ain't too warm now, though." And it flattered her to reflect
+that she had discovered that the shirt was dry without anybody knowing
+that that was what she had in her mind. But in spite of her, Tom knew
+where the wind lay, now. So he forestalled what might be the next move:
+
+"Some of us pumped on our heads--mine's damp yet. See?"
+
+Aunt Polly was vexed to think she had overlooked that bit of
+circumstantial evidence, and missed a trick. Then she had a new
+inspiration:
+
+"Tom, you didn't have to undo your shirt collar where I sewed it, to
+pump on your head, did you? Unbutton your jacket!"
+
+The trouble vanished out of Tom's face. He opened his jacket. His
+shirt collar was securely sewed.
+
+"Bother! Well, go 'long with you. I'd made sure you'd played hookey
+and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a
+singed cat, as the saying is--better'n you look. THIS time."
+
+She was half sorry her sagacity had miscarried, and half glad that Tom
+had stumbled into obedient conduct for once.
+
+But Sidney said:
+
+"Well, now, if I didn't think you sewed his collar with white thread,
+but it's black."
+
+"Why, I did sew it with white! Tom!"
+
+But Tom did not wait for the rest. As he went out at the door he said:
+
+"Siddy, I'll lick you for that."
+
+In a safe place Tom examined two large needles which were thrust into
+the lapels of his jacket, and had thread bound about them--one needle
+carried white thread and the other black. He said:
+
+"She'd never noticed if it hadn't been for Sid. Confound it! sometimes
+she sews it with white, and sometimes she sews it with black. I wish to
+geeminy she'd stick to one or t'other--I can't keep the run of 'em. But
+I bet you I'll lam Sid for that. I'll learn him!"
+
+He was not the Model Boy of the village. He knew the model boy very
+well though--and loathed him.
+
+Within two minutes, or even less, he had forgotten all his troubles.
+Not because his troubles were one whit less heavy and bitter to him
+than a man's are to a man, but because a new and powerful interest bore
+them down and drove them out of his mind for the time--just as men's
+misfortunes are forgotten in the excitement of new enterprises. This
+new interest was a valued novelty in whistling, which he had just
+acquired from a negro, and he was suffering to practise it undisturbed.
+It consisted in a peculiar bird-like turn, a sort of liquid warble,
+produced by touching the tongue to the roof of the mouth at short
+intervals in the midst of the music--the reader probably remembers how
+to do it, if he has ever been a boy. Diligence and attention soon gave
+him the knack of it, and he strode down the street with his mouth full
+of harmony and his soul full of gratitude. He felt much as an
+astronomer feels who has discovered a new planet--no doubt, as far as
+strong, deep, unalloyed pleasure is concerned, the advantage was with
+the boy, not the astronomer.
+
+The summer evenings were long. It was not dark, yet. Presently Tom
+checked his whistle. A stranger was before him--a boy a shade larger
+than himself. A new-comer of any age or either sex was an impressive
+curiosity in the poor little shabby village of St. Petersburg. This boy
+was well dressed, too--well dressed on a week-day. This was simply
+astounding. His cap was a dainty thing, his close-buttoned blue cloth
+roundabout was new and natty, and so were his pantaloons. He had shoes
+on--and it was only Friday. He even wore a necktie, a bright bit of
+ribbon. He had a citified air about him that ate into Tom's vitals. The
+more Tom stared at the splendid marvel, the higher he turned up his
+nose at his finery and the shabbier and shabbier his own outfit seemed
+to him to grow. Neither boy spoke. If one moved, the other moved--but
+only sidewise, in a circle; they kept face to face and eye to eye all
+the time. Finally Tom said:
+
+"I can lick you!"
+
+"I'd like to see you try it."
+
+"Well, I can do it."
+
+"No you can't, either."
+
+"Yes I can."
+
+"No you can't."
+
+"I can."
+
+"You can't."
+
+"Can!"
+
+"Can't!"
+
+An uncomfortable pause. Then Tom said:
+
+"What's your name?"
+
+"'Tisn't any of your business, maybe."
+
+"Well I 'low I'll MAKE it my business."
+
+"Well why don't you?"
+
+"If you say much, I will."
+
+"Much--much--MUCH. There now."
+
+"Oh, you think you're mighty smart, DON'T you? I could lick you with
+one hand tied behind me, if I wanted to."
+
+"Well why don't you DO it? You SAY you can do it."
+
+"Well I WILL, if you fool with me."
+
+"Oh yes--I've seen whole families in the same fix."
+
+"Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!"
+
+"You can lump that hat if you don't like it. I dare you to knock it
+off--and anybody that'll take a dare will suck eggs."
+
+"You're a liar!"
+
+"You're another."
+
+"You're a fighting liar and dasn't take it up."
+
+"Aw--take a walk!"
+
+"Say--if you give me much more of your sass I'll take and bounce a
+rock off'n your head."
+
+"Oh, of COURSE you will."
+
+"Well I WILL."
+
+"Well why don't you DO it then? What do you keep SAYING you will for?
+Why don't you DO it? It's because you're afraid."
+
+"I AIN'T afraid."
+
+"You are."
+
+"I ain't."
+
+"You are."
+
+Another pause, and more eying and sidling around each other. Presently
+they were shoulder to shoulder. Tom said:
+
+"Get away from here!"
+
+"Go away yourself!"
+
+"I won't."
+
+"I won't either."
+
+So they stood, each with a foot placed at an angle as a brace, and
+both shoving with might and main, and glowering at each other with
+hate. But neither could get an advantage. After struggling till both
+were hot and flushed, each relaxed his strain with watchful caution,
+and Tom said:
+
+"You're a coward and a pup. I'll tell my big brother on you, and he
+can thrash you with his little finger, and I'll make him do it, too."
+
+"What do I care for your big brother? I've got a brother that's bigger
+than he is--and what's more, he can throw him over that fence, too."
+[Both brothers were imaginary.]
+
+"That's a lie."
+
+"YOUR saying so don't make it so."
+
+Tom drew a line in the dust with his big toe, and said:
+
+"I dare you to step over that, and I'll lick you till you can't stand
+up. Anybody that'll take a dare will steal sheep."
+
+The new boy stepped over promptly, and said:
+
+"Now you said you'd do it, now let's see you do it."
+
+"Don't you crowd me now; you better look out."
+
+"Well, you SAID you'd do it--why don't you do it?"
+
+"By jingo! for two cents I WILL do it."
+
+The new boy took two broad coppers out of his pocket and held them out
+with derision. Tom struck them to the ground. In an instant both boys
+were rolling and tumbling in the dirt, gripped together like cats; and
+for the space of a minute they tugged and tore at each other's hair and
+clothes, punched and scratched each other's nose, and covered
+themselves with dust and glory. Presently the confusion took form, and
+through the fog of battle Tom appeared, seated astride the new boy, and
+pounding him with his fists. "Holler 'nuff!" said he.
+
+The boy only struggled to free himself. He was crying--mainly from rage.
+
+"Holler 'nuff!"--and the pounding went on.
+
+At last the stranger got out a smothered "'Nuff!" and Tom let him up
+and said:
+
+"Now that'll learn you. Better look out who you're fooling with next
+time."
+
+The new boy went off brushing the dust from his clothes, sobbing,
+snuffling, and occasionally looking back and shaking his head and
+threatening what he would do to Tom the "next time he caught him out."
+To which Tom responded with jeers, and started off in high feather, and
+as soon as his back was turned the new boy snatched up a stone, threw
+it and hit him between the shoulders and then turned tail and ran like
+an antelope. Tom chased the traitor home, and thus found out where he
+lived. He then held a position at the gate for some time, daring the
+enemy to come outside, but the enemy only made faces at him through the
+window and declined. At last the enemy's mother appeared, and called
+Tom a bad, vicious, vulgar child, and ordered him away. So he went
+away; but he said he "'lowed" to "lay" for that boy.
+
+He got home pretty late that night, and when he climbed cautiously in
+at the window, he uncovered an ambuscade, in the person of his aunt;
+and when she saw the state his clothes were in her resolution to turn
+his Saturday holiday into captivity at hard labor became adamantine in
+its firmness.
+
+
+
+CHAPTER II
+
+SATURDAY morning was come, and all the summer world was bright and
+fresh, and brimming with life. There was a song in every heart; and if
+the heart was young the music issued at the lips. There was cheer in
+every face and a spring in every step. The locust-trees were in bloom
+and the fragrance of the blossoms filled the air. Cardiff Hill, beyond
+the village and above it, was green with vegetation and it lay just far
+enough away to seem a Delectable Land, dreamy, reposeful, and inviting.
+
+Tom appeared on the sidewalk with a bucket of whitewash and a
+long-handled brush. He surveyed the fence, and all gladness left him and
+a deep melancholy settled down upon his spirit. Thirty yards of board
+fence nine feet high. Life to him seemed hollow, and existence but a
+burden. Sighing, he dipped his brush and passed it along the topmost
+plank; repeated the operation; did it again; compared the insignificant
+whitewashed streak with the far-reaching continent of unwhitewashed
+fence, and sat down on a tree-box discouraged. Jim came skipping out at
+the gate with a tin pail, and singing Buffalo Gals. Bringing water from
+the town pump had always been hateful work in Tom's eyes, before, but
+now it did not strike him so. He remembered that there was company at
+the pump. White, mulatto, and negro boys and girls were always there
+waiting their turns, resting, trading playthings, quarrelling,
+fighting, skylarking. And he remembered that although the pump was only
+a hundred and fifty yards off, Jim never got back with a bucket of
+water under an hour--and even then somebody generally had to go after
+him. Tom said:
+
+"Say, Jim, I'll fetch the water if you'll whitewash some."
+
+Jim shook his head and said:
+
+"Can't, Mars Tom. Ole missis, she tole me I got to go an' git dis
+water an' not stop foolin' roun' wid anybody. She say she spec' Mars
+Tom gwine to ax me to whitewash, an' so she tole me go 'long an' 'tend
+to my own business--she 'lowed SHE'D 'tend to de whitewashin'."
+
+"Oh, never you mind what she said, Jim. That's the way she always
+talks. Gimme the bucket--I won't be gone only a a minute. SHE won't
+ever know."
+
+"Oh, I dasn't, Mars Tom. Ole missis she'd take an' tar de head off'n
+me. 'Deed she would."
+
+"SHE! She never licks anybody--whacks 'em over the head with her
+thimble--and who cares for that, I'd like to know. She talks awful, but
+talk don't hurt--anyways it don't if she don't cry. Jim, I'll give you
+a marvel. I'll give you a white alley!"
+
+Jim began to waver.
+
+"White alley, Jim! And it's a bully taw."
+
+"My! Dat's a mighty gay marvel, I tell you! But Mars Tom I's powerful
+'fraid ole missis--"
+
+"And besides, if you will I'll show you my sore toe."
+
+Jim was only human--this attraction was too much for him. He put down
+his pail, took the white alley, and bent over the toe with absorbing
+interest while the bandage was being unwound. In another moment he was
+flying down the street with his pail and a tingling rear, Tom was
+whitewashing with vigor, and Aunt Polly was retiring from the field
+with a slipper in her hand and triumph in her eye.
+
+But Tom's energy did not last. He began to think of the fun he had
+planned for this day, and his sorrows multiplied. Soon the free boys
+would come tripping along on all sorts of delicious expeditions, and
+they would make a world of fun of him for having to work--the very
+thought of it burnt him like fire. He got out his worldly wealth and
+examined it--bits of toys, marbles, and trash; enough to buy an
+exchange of WORK, maybe, but not half enough to buy so much as half an
+hour of pure freedom. So he returned his straitened means to his
+pocket, and gave up the idea of trying to buy the boys. At this dark
+and hopeless moment an inspiration burst upon him! Nothing less than a
+great, magnificent inspiration.
+
+He took up his brush and went tranquilly to work. Ben Rogers hove in
+sight presently--the very boy, of all boys, whose ridicule he had been
+dreading. Ben's gait was the hop-skip-and-jump--proof enough that his
+heart was light and his anticipations high. He was eating an apple, and
+giving a long, melodious whoop, at intervals, followed by a deep-toned
+ding-dong-dong, ding-dong-dong, for he was personating a steamboat. As
+he drew near, he slackened speed, took the middle of the street, leaned
+far over to starboard and rounded to ponderously and with laborious
+pomp and circumstance--for he was personating the Big Missouri, and
+considered himself to be drawing nine feet of water. He was boat and
+captain and engine-bells combined, so he had to imagine himself
+standing on his own hurricane-deck giving the orders and executing them:
+
+"Stop her, sir! Ting-a-ling-ling!" The headway ran almost out, and he
+drew up slowly toward the sidewalk.
+
+"Ship up to back! Ting-a-ling-ling!" His arms straightened and
+stiffened down his sides.
+
+"Set her back on the stabboard! Ting-a-ling-ling! Chow! ch-chow-wow!
+Chow!" His right hand, meantime, describing stately circles--for it was
+representing a forty-foot wheel.
+
+"Let her go back on the labboard! Ting-a-lingling! Chow-ch-chow-chow!"
+The left hand began to describe circles.
+
+"Stop the stabboard! Ting-a-ling-ling! Stop the labboard! Come ahead
+on the stabboard! Stop her! Let your outside turn over slow!
+Ting-a-ling-ling! Chow-ow-ow! Get out that head-line! LIVELY now!
+Come--out with your spring-line--what're you about there! Take a turn
+round that stump with the bight of it! Stand by that stage, now--let her
+go! Done with the engines, sir! Ting-a-ling-ling! SH'T! S'H'T! SH'T!"
+(trying the gauge-cocks).
+
+Tom went on whitewashing--paid no attention to the steamboat. Ben
+stared a moment and then said: "Hi-YI! YOU'RE up a stump, ain't you!"
+
+No answer. Tom surveyed his last touch with the eye of an artist, then
+he gave his brush another gentle sweep and surveyed the result, as
+before. Ben ranged up alongside of him. Tom's mouth watered for the
+apple, but he stuck to his work. Ben said:
+
+"Hello, old chap, you got to work, hey?"
+
+Tom wheeled suddenly and said:
+
+"Why, it's you, Ben! I warn't noticing."
+
+"Say--I'm going in a-swimming, I am. Don't you wish you could? But of
+course you'd druther WORK--wouldn't you? Course you would!"
+
+Tom contemplated the boy a bit, and said:
+
+"What do you call work?"
+
+"Why, ain't THAT work?"
+
+Tom resumed his whitewashing, and answered carelessly:
+
+"Well, maybe it is, and maybe it ain't. All I know, is, it suits Tom
+Sawyer."
+
+"Oh come, now, you don't mean to let on that you LIKE it?"
+
+The brush continued to move.
+
+"Like it? Well, I don't see why I oughtn't to like it. Does a boy get
+a chance to whitewash a fence every day?"
+
+That put the thing in a new light. Ben stopped nibbling his apple. Tom
+swept his brush daintily back and forth--stepped back to note the
+effect--added a touch here and there--criticised the effect again--Ben
+watching every move and getting more and more interested, more and more
+absorbed. Presently he said:
+
+"Say, Tom, let ME whitewash a little."
+
+Tom considered, was about to consent; but he altered his mind:
+
+"No--no--I reckon it wouldn't hardly do, Ben. You see, Aunt Polly's
+awful particular about this fence--right here on the street, you know
+--but if it was the back fence I wouldn't mind and SHE wouldn't. Yes,
+she's awful particular about this fence; it's got to be done very
+careful; I reckon there ain't one boy in a thousand, maybe two
+thousand, that can do it the way it's got to be done."
+
+"No--is that so? Oh come, now--lemme just try. Only just a little--I'd
+let YOU, if you was me, Tom."
+
+"Ben, I'd like to, honest injun; but Aunt Polly--well, Jim wanted to
+do it, but she wouldn't let him; Sid wanted to do it, and she wouldn't
+let Sid. Now don't you see how I'm fixed? If you was to tackle this
+fence and anything was to happen to it--"
+
+"Oh, shucks, I'll be just as careful. Now lemme try. Say--I'll give
+you the core of my apple."
+
+"Well, here--No, Ben, now don't. I'm afeard--"
+
+"I'll give you ALL of it!"
+
+Tom gave up the brush with reluctance in his face, but alacrity in his
+heart. And while the late steamer Big Missouri worked and sweated in
+the sun, the retired artist sat on a barrel in the shade close by,
+dangled his legs, munched his apple, and planned the slaughter of more
+innocents. There was no lack of material; boys happened along every
+little while; they came to jeer, but remained to whitewash. By the time
+Ben was fagged out, Tom had traded the next chance to Billy Fisher for
+a kite, in good repair; and when he played out, Johnny Miller bought in
+for a dead rat and a string to swing it with--and so on, and so on,
+hour after hour. And when the middle of the afternoon came, from being
+a poor poverty-stricken boy in the morning, Tom was literally rolling
+in wealth. He had besides the things before mentioned, twelve marbles,
+part of a jews-harp, a piece of blue bottle-glass to look through, a
+spool cannon, a key that wouldn't unlock anything, a fragment of chalk,
+a glass stopper of a decanter, a tin soldier, a couple of tadpoles, six
+fire-crackers, a kitten with only one eye, a brass doorknob, a
+dog-collar--but no dog--the handle of a knife, four pieces of
+orange-peel, and a dilapidated old window sash.
+
+He had had a nice, good, idle time all the while--plenty of company
+--and the fence had three coats of whitewash on it! If he hadn't run out
+of whitewash he would have bankrupted every boy in the village.
+
+Tom said to himself that it was not such a hollow world, after all. He
+had discovered a great law of human action, without knowing it--namely,
+that in order to make a man or a boy covet a thing, it is only
+necessary to make the thing difficult to attain. If he had been a great
+and wise philosopher, like the writer of this book, he would now have
+comprehended that Work consists of whatever a body is OBLIGED to do,
+and that Play consists of whatever a body is not obliged to do. And
+this would help him to understand why constructing artificial flowers
+or performing on a tread-mill is work, while rolling ten-pins or
+climbing Mont Blanc is only amusement. There are wealthy gentlemen in
+England who drive four-horse passenger-coaches twenty or thirty miles
+on a daily line, in the summer, because the privilege costs them
+considerable money; but if they were offered wages for the service,
+that would turn it into work and then they would resign.
+
+The boy mused awhile over the substantial change which had taken place
+in his worldly circumstances, and then wended toward headquarters to
+report.
+
+
+
+CHAPTER III
+
+TOM presented himself before Aunt Polly, who was sitting by an open
+window in a pleasant rearward apartment, which was bedroom,
+breakfast-room, dining-room, and library, combined. The balmy summer
+air, the restful quiet, the odor of the flowers, and the drowsing murmur
+of the bees had had their effect, and she was nodding over her knitting
+--for she had no company but the cat, and it was asleep in her lap. Her
+spectacles were propped up on her gray head for safety. She had thought
+that of course Tom had deserted long ago, and she wondered at seeing him
+place himself in her power again in this intrepid way. He said: "Mayn't
+I go and play now, aunt?"
+
+"What, a'ready? How much have you done?"
+
+"It's all done, aunt."
+
+"Tom, don't lie to me--I can't bear it."
+
+"I ain't, aunt; it IS all done."
+
+Aunt Polly placed small trust in such evidence. She went out to see
+for herself; and she would have been content to find twenty per cent.
+of Tom's statement true. When she found the entire fence whitewashed,
+and not only whitewashed but elaborately coated and recoated, and even
+a streak added to the ground, her astonishment was almost unspeakable.
+She said:
+
+"Well, I never! There's no getting round it, you can work when you're
+a mind to, Tom." And then she diluted the compliment by adding, "But
+it's powerful seldom you're a mind to, I'm bound to say. Well, go 'long
+and play; but mind you get back some time in a week, or I'll tan you."
+
+She was so overcome by the splendor of his achievement that she took
+him into the closet and selected a choice apple and delivered it to
+him, along with an improving lecture upon the added value and flavor a
+treat took to itself when it came without sin through virtuous effort.
+And while she closed with a happy Scriptural flourish, he "hooked" a
+doughnut.
+
+Then he skipped out, and saw Sid just starting up the outside stairway
+that led to the back rooms on the second floor. Clods were handy and
+the air was full of them in a twinkling. They raged around Sid like a
+hail-storm; and before Aunt Polly could collect her surprised faculties
+and sally to the rescue, six or seven clods had taken personal effect,
+and Tom was over the fence and gone. There was a gate, but as a general
+thing he was too crowded for time to make use of it. His soul was at
+peace, now that he had settled with Sid for calling attention to his
+black thread and getting him into trouble.
+
+Tom skirted the block, and came round into a muddy alley that led by
+the back of his aunt's cow-stable. He presently got safely beyond the
+reach of capture and punishment, and hastened toward the public square
+of the village, where two "military" companies of boys had met for
+conflict, according to previous appointment. Tom was General of one of
+these armies, Joe Harper (a bosom friend) General of the other. These
+two great commanders did not condescend to fight in person--that being
+better suited to the still smaller fry--but sat together on an eminence
+and conducted the field operations by orders delivered through
+aides-de-camp. Tom's army won a great victory, after a long and
+hard-fought battle. Then the dead were counted, prisoners exchanged,
+the terms of the next disagreement agreed upon, and the day for the
+necessary battle appointed; after which the armies fell into line and
+marched away, and Tom turned homeward alone.
+
+As he was passing by the house where Jeff Thatcher lived, he saw a new
+girl in the garden--a lovely little blue-eyed creature with yellow hair
+plaited into two long-tails, white summer frock and embroidered
+pantalettes. The fresh-crowned hero fell without firing a shot. A
+certain Amy Lawrence vanished out of his heart and left not even a
+memory of herself behind. He had thought he loved her to distraction;
+he had regarded his passion as adoration; and behold it was only a poor
+little evanescent partiality. He had been months winning her; she had
+confessed hardly a week ago; he had been the happiest and the proudest
+boy in the world only seven short days, and here in one instant of time
+she had gone out of his heart like a casual stranger whose visit is
+done.
+
+He worshipped this new angel with furtive eye, till he saw that she
+had discovered him; then he pretended he did not know she was present,
+and began to "show off" in all sorts of absurd boyish ways, in order to
+win her admiration. He kept up this grotesque foolishness for some
+time; but by-and-by, while he was in the midst of some dangerous
+gymnastic performances, he glanced aside and saw that the little girl
+was wending her way toward the house. Tom came up to the fence and
+leaned on it, grieving, and hoping she would tarry yet awhile longer.
+She halted a moment on the steps and then moved toward the door. Tom
+heaved a great sigh as she put her foot on the threshold. But his face
+lit up, right away, for she tossed a pansy over the fence a moment
+before she disappeared.
+
+The boy ran around and stopped within a foot or two of the flower, and
+then shaded his eyes with his hand and began to look down street as if
+he had discovered something of interest going on in that direction.
+Presently he picked up a straw and began trying to balance it on his
+nose, with his head tilted far back; and as he moved from side to side,
+in his efforts, he edged nearer and nearer toward the pansy; finally
+his bare foot rested upon it, his pliant toes closed upon it, and he
+hopped away with the treasure and disappeared round the corner. But
+only for a minute--only while he could button the flower inside his
+jacket, next his heart--or next his stomach, possibly, for he was not
+much posted in anatomy, and not hypercritical, anyway.
+
+He returned, now, and hung about the fence till nightfall, "showing
+off," as before; but the girl never exhibited herself again, though Tom
+comforted himself a little with the hope that she had been near some
+window, meantime, and been aware of his attentions. Finally he strode
+home reluctantly, with his poor head full of visions.
+
+All through supper his spirits were so high that his aunt wondered
+"what had got into the child." He took a good scolding about clodding
+Sid, and did not seem to mind it in the least. He tried to steal sugar
+under his aunt's very nose, and got his knuckles rapped for it. He said:
+
+"Aunt, you don't whack Sid when he takes it."
+
+"Well, Sid don't torment a body the way you do. You'd be always into
+that sugar if I warn't watching you."
+
+Presently she stepped into the kitchen, and Sid, happy in his
+immunity, reached for the sugar-bowl--a sort of glorying over Tom which
+was wellnigh unbearable. But Sid's fingers slipped and the bowl dropped
+and broke. Tom was in ecstasies. In such ecstasies that he even
+controlled his tongue and was silent. He said to himself that he would
+not speak a word, even when his aunt came in, but would sit perfectly
+still till she asked who did the mischief; and then he would tell, and
+there would be nothing so good in the world as to see that pet model
+"catch it." He was so brimful of exultation that he could hardly hold
+himself when the old lady came back and stood above the wreck
+discharging lightnings of wrath from over her spectacles. He said to
+himself, "Now it's coming!" And the next instant he was sprawling on
+the floor! The potent palm was uplifted to strike again when Tom cried
+out:
+
+"Hold on, now, what 'er you belting ME for?--Sid broke it!"
+
+Aunt Polly paused, perplexed, and Tom looked for healing pity. But
+when she got her tongue again, she only said:
+
+"Umf! Well, you didn't get a lick amiss, I reckon. You been into some
+other audacious mischief when I wasn't around, like enough."
+
+Then her conscience reproached her, and she yearned to say something
+kind and loving; but she judged that this would be construed into a
+confession that she had been in the wrong, and discipline forbade that.
+So she kept silence, and went about her affairs with a troubled heart.
+Tom sulked in a corner and exalted his woes. He knew that in her heart
+his aunt was on her knees to him, and he was morosely gratified by the
+consciousness of it. He would hang out no signals, he would take notice
+of none. He knew that a yearning glance fell upon him, now and then,
+through a film of tears, but he refused recognition of it. He pictured
+himself lying sick unto death and his aunt bending over him beseeching
+one little forgiving word, but he would turn his face to the wall, and
+die with that word unsaid. Ah, how would she feel then? And he pictured
+himself brought home from the river, dead, with his curls all wet, and
+his sore heart at rest. How she would throw herself upon him, and how
+her tears would fall like rain, and her lips pray God to give her back
+her boy and she would never, never abuse him any more! But he would lie
+there cold and white and make no sign--a poor little sufferer, whose
+griefs were at an end. He so worked upon his feelings with the pathos
+of these dreams, that he had to keep swallowing, he was so like to
+choke; and his eyes swam in a blur of water, which overflowed when he
+winked, and ran down and trickled from the end of his nose. And such a
+luxury to him was this petting of his sorrows, that he could not bear
+to have any worldly cheeriness or any grating delight intrude upon it;
+it was too sacred for such contact; and so, presently, when his cousin
+Mary danced in, all alive with the joy of seeing home again after an
+age-long visit of one week to the country, he got up and moved in
+clouds and darkness out at one door as she brought song and sunshine in
+at the other.
+
+He wandered far from the accustomed haunts of boys, and sought
+desolate places that were in harmony with his spirit. A log raft in the
+river invited him, and he seated himself on its outer edge and
+contemplated the dreary vastness of the stream, wishing, the while,
+that he could only be drowned, all at once and unconsciously, without
+undergoing the uncomfortable routine devised by nature. Then he thought
+of his flower. He got it out, rumpled and wilted, and it mightily
+increased his dismal felicity. He wondered if she would pity him if she
+knew? Would she cry, and wish that she had a right to put her arms
+around his neck and comfort him? Or would she turn coldly away like all
+the hollow world? This picture brought such an agony of pleasurable
+suffering that he worked it over and over again in his mind and set it
+up in new and varied lights, till he wore it threadbare. At last he
+rose up sighing and departed in the darkness.
+
+About half-past nine or ten o'clock he came along the deserted street
+to where the Adored Unknown lived; he paused a moment; no sound fell
+upon his listening ear; a candle was casting a dull glow upon the
+curtain of a second-story window. Was the sacred presence there? He
+climbed the fence, threaded his stealthy way through the plants, till
+he stood under that window; he looked up at it long, and with emotion;
+then he laid him down on the ground under it, disposing himself upon
+his back, with his hands clasped upon his breast and holding his poor
+wilted flower. And thus he would die--out in the cold world, with no
+shelter over his homeless head, no friendly hand to wipe the
+death-damps from his brow, no loving face to bend pityingly over him
+when the great agony came. And thus SHE would see him when she looked
+out upon the glad morning, and oh! would she drop one little tear upon
+his poor, lifeless form, would she heave one little sigh to see a bright
+young life so rudely blighted, so untimely cut down?
+
+The window went up, a maid-servant's discordant voice profaned the
+holy calm, and a deluge of water drenched the prone martyr's remains!
+
+The strangling hero sprang up with a relieving snort. There was a whiz
+as of a missile in the air, mingled with the murmur of a curse, a sound
+as of shivering glass followed, and a small, vague form went over the
+fence and shot away in the gloom.
+
+Not long after, as Tom, all undressed for bed, was surveying his
+drenched garments by the light of a tallow dip, Sid woke up; but if he
+had any dim idea of making any "references to allusions," he thought
+better of it and held his peace, for there was danger in Tom's eye.
+
+Tom turned in without the added vexation of prayers, and Sid made
+mental note of the omission.
+
+
+
+CHAPTER IV
+
+THE sun rose upon a tranquil world, and beamed down upon the peaceful
+village like a benediction. Breakfast over, Aunt Polly had family
+worship: it began with a prayer built from the ground up of solid
+courses of Scriptural quotations, welded together with a thin mortar of
+originality; and from the summit of this she delivered a grim chapter
+of the Mosaic Law, as from Sinai.
+
+Then Tom girded up his loins, so to speak, and went to work to "get
+his verses." Sid had learned his lesson days before. Tom bent all his
+energies to the memorizing of five verses, and he chose part of the
+Sermon on the Mount, because he could find no verses that were shorter.
+At the end of half an hour Tom had a vague general idea of his lesson,
+but no more, for his mind was traversing the whole field of human
+thought, and his hands were busy with distracting recreations. Mary
+took his book to hear him recite, and he tried to find his way through
+the fog:
+
+"Blessed are the--a--a--"
+
+"Poor"--
+
+"Yes--poor; blessed are the poor--a--a--"
+
+"In spirit--"
+
+"In spirit; blessed are the poor in spirit, for they--they--"
+
+"THEIRS--"
+
+"For THEIRS. Blessed are the poor in spirit, for theirs is the kingdom
+of heaven. Blessed are they that mourn, for they--they--"
+
+"Sh--"
+
+"For they--a--"
+
+"S, H, A--"
+
+"For they S, H--Oh, I don't know what it is!"
+
+"SHALL!"
+
+"Oh, SHALL! for they shall--for they shall--a--a--shall mourn--a--a--
+blessed are they that shall--they that--a--they that shall mourn, for
+they shall--a--shall WHAT? Why don't you tell me, Mary?--what do you
+want to be so mean for?"
+
+"Oh, Tom, you poor thick-headed thing, I'm not teasing you. I wouldn't
+do that. You must go and learn it again. Don't you be discouraged, Tom,
+you'll manage it--and if you do, I'll give you something ever so nice.
+There, now, that's a good boy."
+
+"All right! What is it, Mary, tell me what it is."
+
+"Never you mind, Tom. You know if I say it's nice, it is nice."
+
+"You bet you that's so, Mary. All right, I'll tackle it again."
+
+And he did "tackle it again"--and under the double pressure of
+curiosity and prospective gain he did it with such spirit that he
+accomplished a shining success. Mary gave him a brand-new "Barlow"
+knife worth twelve and a half cents; and the convulsion of delight that
+swept his system shook him to his foundations. True, the knife would
+not cut anything, but it was a "sure-enough" Barlow, and there was
+inconceivable grandeur in that--though where the Western boys ever got
+the idea that such a weapon could possibly be counterfeited to its
+injury is an imposing mystery and will always remain so, perhaps. Tom
+contrived to scarify the cupboard with it, and was arranging to begin
+on the bureau, when he was called off to dress for Sunday-school.
+
+Mary gave him a tin basin of water and a piece of soap, and he went
+outside the door and set the basin on a little bench there; then he
+dipped the soap in the water and laid it down; turned up his sleeves;
+poured out the water on the ground, gently, and then entered the
+kitchen and began to wipe his face diligently on the towel behind the
+door. But Mary removed the towel and said:
+
+"Now ain't you ashamed, Tom. You mustn't be so bad. Water won't hurt
+you."
+
+Tom was a trifle disconcerted. The basin was refilled, and this time
+he stood over it a little while, gathering resolution; took in a big
+breath and began. When he entered the kitchen presently, with both eyes
+shut and groping for the towel with his hands, an honorable testimony
+of suds and water was dripping from his face. But when he emerged from
+the towel, he was not yet satisfactory, for the clean territory stopped
+short at his chin and his jaws, like a mask; below and beyond this line
+there was a dark expanse of unirrigated soil that spread downward in
+front and backward around his neck. Mary took him in hand, and when she
+was done with him he was a man and a brother, without distinction of
+color, and his saturated hair was neatly brushed, and its short curls
+wrought into a dainty and symmetrical general effect. [He privately
+smoothed out the curls, with labor and difficulty, and plastered his
+hair close down to his head; for he held curls to be effeminate, and
+his own filled his life with bitterness.] Then Mary got out a suit of
+his clothing that had been used only on Sundays during two years--they
+were simply called his "other clothes"--and so by that we know the
+size of his wardrobe. The girl "put him to rights" after he had dressed
+himself; she buttoned his neat roundabout up to his chin, turned his
+vast shirt collar down over his shoulders, brushed him off and crowned
+him with his speckled straw hat. He now looked exceedingly improved and
+uncomfortable. He was fully as uncomfortable as he looked; for there
+was a restraint about whole clothes and cleanliness that galled him. He
+hoped that Mary would forget his shoes, but the hope was blighted; she
+coated them thoroughly with tallow, as was the custom, and brought them
+out. He lost his temper and said he was always being made to do
+everything he didn't want to do. But Mary said, persuasively:
+
+"Please, Tom--that's a good boy."
+
+So he got into the shoes snarling. Mary was soon ready, and the three
+children set out for Sunday-school--a place that Tom hated with his
+whole heart; but Sid and Mary were fond of it.
+
+Sabbath-school hours were from nine to half-past ten; and then church
+service. Two of the children always remained for the sermon
+voluntarily, and the other always remained too--for stronger reasons.
+The church's high-backed, uncushioned pews would seat about three
+hundred persons; the edifice was but a small, plain affair, with a sort
+of pine board tree-box on top of it for a steeple. At the door Tom
+dropped back a step and accosted a Sunday-dressed comrade:
+
+"Say, Billy, got a yaller ticket?"
+
+"Yes."
+
+"What'll you take for her?"
+
+"What'll you give?"
+
+"Piece of lickrish and a fish-hook."
+
+"Less see 'em."
+
+Tom exhibited. They were satisfactory, and the property changed hands.
+Then Tom traded a couple of white alleys for three red tickets, and
+some small trifle or other for a couple of blue ones. He waylaid other
+boys as they came, and went on buying tickets of various colors ten or
+fifteen minutes longer. He entered the church, now, with a swarm of
+clean and noisy boys and girls, proceeded to his seat and started a
+quarrel with the first boy that came handy. The teacher, a grave,
+elderly man, interfered; then turned his back a moment and Tom pulled a
+boy's hair in the next bench, and was absorbed in his book when the boy
+turned around; stuck a pin in another boy, presently, in order to hear
+him say "Ouch!" and got a new reprimand from his teacher. Tom's whole
+class were of a pattern--restless, noisy, and troublesome. When they
+came to recite their lessons, not one of them knew his verses
+perfectly, but had to be prompted all along. However, they worried
+through, and each got his reward--in small blue tickets, each with a
+passage of Scripture on it; each blue ticket was pay for two verses of
+the recitation. Ten blue tickets equalled a red one, and could be
+exchanged for it; ten red tickets equalled a yellow one; for ten yellow
+tickets the superintendent gave a very plainly bound Bible (worth forty
+cents in those easy times) to the pupil. How many of my readers would
+have the industry and application to memorize two thousand verses, even
+for a Dore Bible? And yet Mary had acquired two Bibles in this way--it
+was the patient work of two years--and a boy of German parentage had
+won four or five. He once recited three thousand verses without
+stopping; but the strain upon his mental faculties was too great, and
+he was little better than an idiot from that day forth--a grievous
+misfortune for the school, for on great occasions, before company, the
+superintendent (as Tom expressed it) had always made this boy come out
+and "spread himself." Only the older pupils managed to keep their
+tickets and stick to their tedious work long enough to get a Bible, and
+so the delivery of one of these prizes was a rare and noteworthy
+circumstance; the successful pupil was so great and conspicuous for
+that day that on the spot every scholar's heart was fired with a fresh
+ambition that often lasted a couple of weeks. It is possible that Tom's
+mental stomach had never really hungered for one of those prizes, but
+unquestionably his entire being had for many a day longed for the glory
+and the eclat that came with it.
+
+In due course the superintendent stood up in front of the pulpit, with
+a closed hymn-book in his hand and his forefinger inserted between its
+leaves, and commanded attention. When a Sunday-school superintendent
+makes his customary little speech, a hymn-book in the hand is as
+necessary as is the inevitable sheet of music in the hand of a singer
+who stands forward on the platform and sings a solo at a concert
+--though why, is a mystery: for neither the hymn-book nor the sheet of
+music is ever referred to by the sufferer. This superintendent was a
+slim creature of thirty-five, with a sandy goatee and short sandy hair;
+he wore a stiff standing-collar whose upper edge almost reached his
+ears and whose sharp points curved forward abreast the corners of his
+mouth--a fence that compelled a straight lookout ahead, and a turning
+of the whole body when a side view was required; his chin was propped
+on a spreading cravat which was as broad and as long as a bank-note,
+and had fringed ends; his boot toes were turned sharply up, in the
+fashion of the day, like sleigh-runners--an effect patiently and
+laboriously produced by the young men by sitting with their toes
+pressed against a wall for hours together. Mr. Walters was very earnest
+of mien, and very sincere and honest at heart; and he held sacred
+things and places in such reverence, and so separated them from worldly
+matters, that unconsciously to himself his Sunday-school voice had
+acquired a peculiar intonation which was wholly absent on week-days. He
+began after this fashion:
+
+"Now, children, I want you all to sit up just as straight and pretty
+as you can and give me all your attention for a minute or two. There
+--that is it. That is the way good little boys and girls should do. I see
+one little girl who is looking out of the window--I am afraid she
+thinks I am out there somewhere--perhaps up in one of the trees making
+a speech to the little birds. [Applausive titter.] I want to tell you
+how good it makes me feel to see so many bright, clean little faces
+assembled in a place like this, learning to do right and be good." And
+so forth and so on. It is not necessary to set down the rest of the
+oration. It was of a pattern which does not vary, and so it is familiar
+to us all.
+
+The latter third of the speech was marred by the resumption of fights
+and other recreations among certain of the bad boys, and by fidgetings
+and whisperings that extended far and wide, washing even to the bases
+of isolated and incorruptible rocks like Sid and Mary. But now every
+sound ceased suddenly, with the subsidence of Mr. Walters' voice, and
+the conclusion of the speech was received with a burst of silent
+gratitude.
+
+A good part of the whispering had been occasioned by an event which
+was more or less rare--the entrance of visitors: lawyer Thatcher,
+accompanied by a very feeble and aged man; a fine, portly, middle-aged
+gentleman with iron-gray hair; and a dignified lady who was doubtless
+the latter's wife. The lady was leading a child. Tom had been restless
+and full of chafings and repinings; conscience-smitten, too--he could
+not meet Amy Lawrence's eye, he could not brook her loving gaze. But
+when he saw this small new-comer his soul was all ablaze with bliss in
+a moment. The next moment he was "showing off" with all his might
+--cuffing boys, pulling hair, making faces--in a word, using every art
+that seemed likely to fascinate a girl and win her applause. His
+exaltation had but one alloy--the memory of his humiliation in this
+angel's garden--and that record in sand was fast washing out, under
+the waves of happiness that were sweeping over it now.
+
+The visitors were given the highest seat of honor, and as soon as Mr.
+Walters' speech was finished, he introduced them to the school. The
+middle-aged man turned out to be a prodigious personage--no less a one
+than the county judge--altogether the most august creation these
+children had ever looked upon--and they wondered what kind of material
+he was made of--and they half wanted to hear him roar, and were half
+afraid he might, too. He was from Constantinople, twelve miles away--so
+he had travelled, and seen the world--these very eyes had looked upon
+the county court-house--which was said to have a tin roof. The awe
+which these reflections inspired was attested by the impressive silence
+and the ranks of staring eyes. This was the great Judge Thatcher,
+brother of their own lawyer. Jeff Thatcher immediately went forward, to
+be familiar with the great man and be envied by the school. It would
+have been music to his soul to hear the whisperings:
+
+"Look at him, Jim! He's a going up there. Say--look! he's a going to
+shake hands with him--he IS shaking hands with him! By jings, don't you
+wish you was Jeff?"
+
+Mr. Walters fell to "showing off," with all sorts of official
+bustlings and activities, giving orders, delivering judgments,
+discharging directions here, there, everywhere that he could find a
+target. The librarian "showed off"--running hither and thither with his
+arms full of books and making a deal of the splutter and fuss that
+insect authority delights in. The young lady teachers "showed off"
+--bending sweetly over pupils that were lately being boxed, lifting
+pretty warning fingers at bad little boys and patting good ones
+lovingly. The young gentlemen teachers "showed off" with small
+scoldings and other little displays of authority and fine attention to
+discipline--and most of the teachers, of both sexes, found business up
+at the library, by the pulpit; and it was business that frequently had
+to be done over again two or three times (with much seeming vexation).
+The little girls "showed off" in various ways, and the little boys
+"showed off" with such diligence that the air was thick with paper wads
+and the murmur of scufflings. And above it all the great man sat and
+beamed a majestic judicial smile upon all the house, and warmed himself
+in the sun of his own grandeur--for he was "showing off," too.
+
+There was only one thing wanting to make Mr. Walters' ecstasy
+complete, and that was a chance to deliver a Bible-prize and exhibit a
+prodigy. Several pupils had a few yellow tickets, but none had enough
+--he had been around among the star pupils inquiring. He would have given
+worlds, now, to have that German lad back again with a sound mind.
+
+And now at this moment, when hope was dead, Tom Sawyer came forward
+with nine yellow tickets, nine red tickets, and ten blue ones, and
+demanded a Bible. This was a thunderbolt out of a clear sky. Walters
+was not expecting an application from this source for the next ten
+years. But there was no getting around it--here were the certified
+checks, and they were good for their face. Tom was therefore elevated
+to a place with the Judge and the other elect, and the great news was
+announced from headquarters. It was the most stunning surprise of the
+decade, and so profound was the sensation that it lifted the new hero
+up to the judicial one's altitude, and the school had two marvels to
+gaze upon in place of one. The boys were all eaten up with envy--but
+those that suffered the bitterest pangs were those who perceived too
+late that they themselves had contributed to this hated splendor by
+trading tickets to Tom for the wealth he had amassed in selling
+whitewashing privileges. These despised themselves, as being the dupes
+of a wily fraud, a guileful snake in the grass.
+
+The prize was delivered to Tom with as much effusion as the
+superintendent could pump up under the circumstances; but it lacked
+somewhat of the true gush, for the poor fellow's instinct taught him
+that there was a mystery here that could not well bear the light,
+perhaps; it was simply preposterous that this boy had warehoused two
+thousand sheaves of Scriptural wisdom on his premises--a dozen would
+strain his capacity, without a doubt.
+
+Amy Lawrence was proud and glad, and she tried to make Tom see it in
+her face--but he wouldn't look. She wondered; then she was just a grain
+troubled; next a dim suspicion came and went--came again; she watched;
+a furtive glance told her worlds--and then her heart broke, and she was
+jealous, and angry, and the tears came and she hated everybody. Tom
+most of all (she thought).
+
+Tom was introduced to the Judge; but his tongue was tied, his breath
+would hardly come, his heart quaked--partly because of the awful
+greatness of the man, but mainly because he was her parent. He would
+have liked to fall down and worship him, if it were in the dark. The
+Judge put his hand on Tom's head and called him a fine little man, and
+asked him what his name was. The boy stammered, gasped, and got it out:
+
+"Tom."
+
+"Oh, no, not Tom--it is--"
+
+"Thomas."
+
+"Ah, that's it. I thought there was more to it, maybe. That's very
+well. But you've another one I daresay, and you'll tell it to me, won't
+you?"
+
+"Tell the gentleman your other name, Thomas," said Walters, "and say
+sir. You mustn't forget your manners."
+
+"Thomas Sawyer--sir."
+
+"That's it! That's a good boy. Fine boy. Fine, manly little fellow.
+Two thousand verses is a great many--very, very great many. And you
+never can be sorry for the trouble you took to learn them; for
+knowledge is worth more than anything there is in the world; it's what
+makes great men and good men; you'll be a great man and a good man
+yourself, some day, Thomas, and then you'll look back and say, It's all
+owing to the precious Sunday-school privileges of my boyhood--it's all
+owing to my dear teachers that taught me to learn--it's all owing to
+the good superintendent, who encouraged me, and watched over me, and
+gave me a beautiful Bible--a splendid elegant Bible--to keep and have
+it all for my own, always--it's all owing to right bringing up! That is
+what you will say, Thomas--and you wouldn't take any money for those
+two thousand verses--no indeed you wouldn't. And now you wouldn't mind
+telling me and this lady some of the things you've learned--no, I know
+you wouldn't--for we are proud of little boys that learn. Now, no
+doubt you know the names of all the twelve disciples. Won't you tell us
+the names of the first two that were appointed?"
+
+Tom was tugging at a button-hole and looking sheepish. He blushed,
+now, and his eyes fell. Mr. Walters' heart sank within him. He said to
+himself, it is not possible that the boy can answer the simplest
+question--why DID the Judge ask him? Yet he felt obliged to speak up
+and say:
+
+"Answer the gentleman, Thomas--don't be afraid."
+
+Tom still hung fire.
+
+"Now I know you'll tell me," said the lady. "The names of the first
+two disciples were--"
+
+"DAVID AND GOLIAH!"
+
+Let us draw the curtain of charity over the rest of the scene.
+
+
+
+CHAPTER V
+
+ABOUT half-past ten the cracked bell of the small church began to
+ring, and presently the people began to gather for the morning sermon.
+The Sunday-school children distributed themselves about the house and
+occupied pews with their parents, so as to be under supervision. Aunt
+Polly came, and Tom and Sid and Mary sat with her--Tom being placed
+next the aisle, in order that he might be as far away from the open
+window and the seductive outside summer scenes as possible. The crowd
+filed up the aisles: the aged and needy postmaster, who had seen better
+days; the mayor and his wife--for they had a mayor there, among other
+unnecessaries; the justice of the peace; the widow Douglass, fair,
+smart, and forty, a generous, good-hearted soul and well-to-do, her
+hill mansion the only palace in the town, and the most hospitable and
+much the most lavish in the matter of festivities that St. Petersburg
+could boast; the bent and venerable Major and Mrs. Ward; lawyer
+Riverson, the new notable from a distance; next the belle of the
+village, followed by a troop of lawn-clad and ribbon-decked young
+heart-breakers; then all the young clerks in town in a body--for they
+had stood in the vestibule sucking their cane-heads, a circling wall of
+oiled and simpering admirers, till the last girl had run their gantlet;
+and last of all came the Model Boy, Willie Mufferson, taking as heedful
+care of his mother as if she were cut glass. He always brought his
+mother to church, and was the pride of all the matrons. The boys all
+hated him, he was so good. And besides, he had been "thrown up to them"
+so much. His white handkerchief was hanging out of his pocket behind, as
+usual on Sundays--accidentally. Tom had no handkerchief, and he looked
+upon boys who had as snobs.
+
+The congregation being fully assembled, now, the bell rang once more,
+to warn laggards and stragglers, and then a solemn hush fell upon the
+church which was only broken by the tittering and whispering of the
+choir in the gallery. The choir always tittered and whispered all
+through service. There was once a church choir that was not ill-bred,
+but I have forgotten where it was, now. It was a great many years ago,
+and I can scarcely remember anything about it, but I think it was in
+some foreign country.
+
+The minister gave out the hymn, and read it through with a relish, in
+a peculiar style which was much admired in that part of the country.
+His voice began on a medium key and climbed steadily up till it reached
+a certain point, where it bore with strong emphasis upon the topmost
+word and then plunged down as if from a spring-board:
+
+  Shall I be car-ri-ed toe the skies, on flow'ry BEDS of ease,
+
+  Whilst others fight to win the prize, and sail thro' BLOODY seas?
+
+He was regarded as a wonderful reader. At church "sociables" he was
+always called upon to read poetry; and when he was through, the ladies
+would lift up their hands and let them fall helplessly in their laps,
+and "wall" their eyes, and shake their heads, as much as to say, "Words
+cannot express it; it is too beautiful, TOO beautiful for this mortal
+earth."
+
+After the hymn had been sung, the Rev. Mr. Sprague turned himself into
+a bulletin-board, and read off "notices" of meetings and societies and
+things till it seemed that the list would stretch out to the crack of
+doom--a queer custom which is still kept up in America, even in cities,
+away here in this age of abundant newspapers. Often, the less there is
+to justify a traditional custom, the harder it is to get rid of it.
+
+And now the minister prayed. A good, generous prayer it was, and went
+into details: it pleaded for the church, and the little children of the
+church; for the other churches of the village; for the village itself;
+for the county; for the State; for the State officers; for the United
+States; for the churches of the United States; for Congress; for the
+President; for the officers of the Government; for poor sailors, tossed
+by stormy seas; for the oppressed millions groaning under the heel of
+European monarchies and Oriental despotisms; for such as have the light
+and the good tidings, and yet have not eyes to see nor ears to hear
+withal; for the heathen in the far islands of the sea; and closed with
+a supplication that the words he was about to speak might find grace
+and favor, and be as seed sown in fertile ground, yielding in time a
+grateful harvest of good. Amen.
+
+There was a rustling of dresses, and the standing congregation sat
+down. The boy whose history this book relates did not enjoy the prayer,
+he only endured it--if he even did that much. He was restive all
+through it; he kept tally of the details of the prayer, unconsciously
+--for he was not listening, but he knew the ground of old, and the
+clergyman's regular route over it--and when a little trifle of new
+matter was interlarded, his ear detected it and his whole nature
+resented it; he considered additions unfair, and scoundrelly. In the
+midst of the prayer a fly had lit on the back of the pew in front of
+him and tortured his spirit by calmly rubbing its hands together,
+embracing its head with its arms, and polishing it so vigorously that
+it seemed to almost part company with the body, and the slender thread
+of a neck was exposed to view; scraping its wings with its hind legs
+and smoothing them to its body as if they had been coat-tails; going
+through its whole toilet as tranquilly as if it knew it was perfectly
+safe. As indeed it was; for as sorely as Tom's hands itched to grab for
+it they did not dare--he believed his soul would be instantly destroyed
+if he did such a thing while the prayer was going on. But with the
+closing sentence his hand began to curve and steal forward; and the
+instant the "Amen" was out the fly was a prisoner of war. His aunt
+detected the act and made him let it go.
+
+The minister gave out his text and droned along monotonously through
+an argument that was so prosy that many a head by and by began to nod
+--and yet it was an argument that dealt in limitless fire and brimstone
+and thinned the predestined elect down to a company so small as to be
+hardly worth the saving. Tom counted the pages of the sermon; after
+church he always knew how many pages there had been, but he seldom knew
+anything else about the discourse. However, this time he was really
+interested for a little while. The minister made a grand and moving
+picture of the assembling together of the world's hosts at the
+millennium when the lion and the lamb should lie down together and a
+little child should lead them. But the pathos, the lesson, the moral of
+the great spectacle were lost upon the boy; he only thought of the
+conspicuousness of the principal character before the on-looking
+nations; his face lit with the thought, and he said to himself that he
+wished he could be that child, if it was a tame lion.
+
+Now he lapsed into suffering again, as the dry argument was resumed.
+Presently he bethought him of a treasure he had and got it out. It was
+a large black beetle with formidable jaws--a "pinchbug," he called it.
+It was in a percussion-cap box. The first thing the beetle did was to
+take him by the finger. A natural fillip followed, the beetle went
+floundering into the aisle and lit on its back, and the hurt finger
+went into the boy's mouth. The beetle lay there working its helpless
+legs, unable to turn over. Tom eyed it, and longed for it; but it was
+safe out of his reach. Other people uninterested in the sermon found
+relief in the beetle, and they eyed it too. Presently a vagrant poodle
+dog came idling along, sad at heart, lazy with the summer softness and
+the quiet, weary of captivity, sighing for change. He spied the beetle;
+the drooping tail lifted and wagged. He surveyed the prize; walked
+around it; smelt at it from a safe distance; walked around it again;
+grew bolder, and took a closer smell; then lifted his lip and made a
+gingerly snatch at it, just missing it; made another, and another;
+began to enjoy the diversion; subsided to his stomach with the beetle
+between his paws, and continued his experiments; grew weary at last,
+and then indifferent and absent-minded. His head nodded, and little by
+little his chin descended and touched the enemy, who seized it. There
+was a sharp yelp, a flirt of the poodle's head, and the beetle fell a
+couple of yards away, and lit on its back once more. The neighboring
+spectators shook with a gentle inward joy, several faces went behind
+fans and handkerchiefs, and Tom was entirely happy. The dog looked
+foolish, and probably felt so; but there was resentment in his heart,
+too, and a craving for revenge. So he went to the beetle and began a
+wary attack on it again; jumping at it from every point of a circle,
+lighting with his fore-paws within an inch of the creature, making even
+closer snatches at it with his teeth, and jerking his head till his
+ears flapped again. But he grew tired once more, after a while; tried
+to amuse himself with a fly but found no relief; followed an ant
+around, with his nose close to the floor, and quickly wearied of that;
+yawned, sighed, forgot the beetle entirely, and sat down on it. Then
+there was a wild yelp of agony and the poodle went sailing up the
+aisle; the yelps continued, and so did the dog; he crossed the house in
+front of the altar; he flew down the other aisle; he crossed before the
+doors; he clamored up the home-stretch; his anguish grew with his
+progress, till presently he was but a woolly comet moving in its orbit
+with the gleam and the speed of light. At last the frantic sufferer
+sheered from its course, and sprang into its master's lap; he flung it
+out of the window, and the voice of distress quickly thinned away and
+died in the distance.
+
+By this time the whole church was red-faced and suffocating with
+suppressed laughter, and the sermon had come to a dead standstill. The
+discourse was resumed presently, but it went lame and halting, all
+possibility of impressiveness being at an end; for even the gravest
+sentiments were constantly being received with a smothered burst of
+unholy mirth, under cover of some remote pew-back, as if the poor
+parson had said a rarely facetious thing. It was a genuine relief to
+the whole congregation when the ordeal was over and the benediction
+pronounced.
+
+Tom Sawyer went home quite cheerful, thinking to himself that there
+was some satisfaction about divine service when there was a bit of
+variety in it. He had but one marring thought; he was willing that the
+dog should play with his pinchbug, but he did not think it was upright
+in him to carry it off.
+
+
+
+CHAPTER VI
+
+MONDAY morning found Tom Sawyer miserable. Monday morning always found
+him so--because it began another week's slow suffering in school. He
+generally began that day with wishing he had had no intervening
+holiday, it made the going into captivity and fetters again so much
+more odious.
+
+Tom lay thinking. Presently it occurred to him that he wished he was
+sick; then he could stay home from school. Here was a vague
+possibility. He canvassed his system. No ailment was found, and he
+investigated again. This time he thought he could detect colicky
+symptoms, and he began to encourage them with considerable hope. But
+they soon grew feeble, and presently died wholly away. He reflected
+further. Suddenly he discovered something. One of his upper front teeth
+was loose. This was lucky; he was about to begin to groan, as a
+"starter," as he called it, when it occurred to him that if he came
+into court with that argument, his aunt would pull it out, and that
+would hurt. So he thought he would hold the tooth in reserve for the
+present, and seek further. Nothing offered for some little time, and
+then he remembered hearing the doctor tell about a certain thing that
+laid up a patient for two or three weeks and threatened to make him
+lose a finger. So the boy eagerly drew his sore toe from under the
+sheet and held it up for inspection. But now he did not know the
+necessary symptoms. However, it seemed well worth while to chance it,
+so he fell to groaning with considerable spirit.
+
+But Sid slept on unconscious.
+
+Tom groaned louder, and fancied that he began to feel pain in the toe.
+
+No result from Sid.
+
+Tom was panting with his exertions by this time. He took a rest and
+then swelled himself up and fetched a succession of admirable groans.
+
+Sid snored on.
+
+Tom was aggravated. He said, "Sid, Sid!" and shook him. This course
+worked well, and Tom began to groan again. Sid yawned, stretched, then
+brought himself up on his elbow with a snort, and began to stare at
+Tom. Tom went on groaning. Sid said:
+
+"Tom! Say, Tom!" [No response.] "Here, Tom! TOM! What is the matter,
+Tom?" And he shook him and looked in his face anxiously.
+
+Tom moaned out:
+
+"Oh, don't, Sid. Don't joggle me."
+
+"Why, what's the matter, Tom? I must call auntie."
+
+"No--never mind. It'll be over by and by, maybe. Don't call anybody."
+
+"But I must! DON'T groan so, Tom, it's awful. How long you been this
+way?"
+
+"Hours. Ouch! Oh, don't stir so, Sid, you'll kill me."
+
+"Tom, why didn't you wake me sooner? Oh, Tom, DON'T! It makes my
+flesh crawl to hear you. Tom, what is the matter?"
+
+"I forgive you everything, Sid. [Groan.] Everything you've ever done
+to me. When I'm gone--"
+
+"Oh, Tom, you ain't dying, are you? Don't, Tom--oh, don't. Maybe--"
+
+"I forgive everybody, Sid. [Groan.] Tell 'em so, Sid. And Sid, you
+give my window-sash and my cat with one eye to that new girl that's
+come to town, and tell her--"
+
+But Sid had snatched his clothes and gone. Tom was suffering in
+reality, now, so handsomely was his imagination working, and so his
+groans had gathered quite a genuine tone.
+
+Sid flew down-stairs and said:
+
+"Oh, Aunt Polly, come! Tom's dying!"
+
+"Dying!"
+
+"Yes'm. Don't wait--come quick!"
+
+"Rubbage! I don't believe it!"
+
+But she fled up-stairs, nevertheless, with Sid and Mary at her heels.
+And her face grew white, too, and her lip trembled. When she reached
+the bedside she gasped out:
+
+"You, Tom! Tom, what's the matter with you?"
+
+"Oh, auntie, I'm--"
+
+"What's the matter with you--what is the matter with you, child?"
+
+"Oh, auntie, my sore toe's mortified!"
+
+The old lady sank down into a chair and laughed a little, then cried a
+little, then did both together. This restored her and she said:
+
+"Tom, what a turn you did give me. Now you shut up that nonsense and
+climb out of this."
+
+The groans ceased and the pain vanished from the toe. The boy felt a
+little foolish, and he said:
+
+"Aunt Polly, it SEEMED mortified, and it hurt so I never minded my
+tooth at all."
+
+"Your tooth, indeed! What's the matter with your tooth?"
+
+"One of them's loose, and it aches perfectly awful."
+
+"There, there, now, don't begin that groaning again. Open your mouth.
+Well--your tooth IS loose, but you're not going to die about that.
+Mary, get me a silk thread, and a chunk of fire out of the kitchen."
+
+Tom said:
+
+"Oh, please, auntie, don't pull it out. It don't hurt any more. I wish
+I may never stir if it does. Please don't, auntie. I don't want to stay
+home from school."
+
+"Oh, you don't, don't you? So all this row was because you thought
+you'd get to stay home from school and go a-fishing? Tom, Tom, I love
+you so, and you seem to try every way you can to break my old heart
+with your outrageousness." By this time the dental instruments were
+ready. The old lady made one end of the silk thread fast to Tom's tooth
+with a loop and tied the other to the bedpost. Then she seized the
+chunk of fire and suddenly thrust it almost into the boy's face. The
+tooth hung dangling by the bedpost, now.
+
+But all trials bring their compensations. As Tom wended to school
+after breakfast, he was the envy of every boy he met because the gap in
+his upper row of teeth enabled him to expectorate in a new and
+admirable way. He gathered quite a following of lads interested in the
+exhibition; and one that had cut his finger and had been a centre of
+fascination and homage up to this time, now found himself suddenly
+without an adherent, and shorn of his glory. His heart was heavy, and
+he said with a disdain which he did not feel that it wasn't anything to
+spit like Tom Sawyer; but another boy said, "Sour grapes!" and he
+wandered away a dismantled hero.
+
+Shortly Tom came upon the juvenile pariah of the village, Huckleberry
+Finn, son of the town drunkard. Huckleberry was cordially hated and
+dreaded by all the mothers of the town, because he was idle and lawless
+and vulgar and bad--and because all their children admired him so, and
+delighted in his forbidden society, and wished they dared to be like
+him. Tom was like the rest of the respectable boys, in that he envied
+Huckleberry his gaudy outcast condition, and was under strict orders
+not to play with him. So he played with him every time he got a chance.
+Huckleberry was always dressed in the cast-off clothes of full-grown
+men, and they were in perennial bloom and fluttering with rags. His hat
+was a vast ruin with a wide crescent lopped out of its brim; his coat,
+when he wore one, hung nearly to his heels and had the rearward buttons
+far down the back; but one suspender supported his trousers; the seat
+of the trousers bagged low and contained nothing, the fringed legs
+dragged in the dirt when not rolled up.
+
+Huckleberry came and went, at his own free will. He slept on doorsteps
+in fine weather and in empty hogsheads in wet; he did not have to go to
+school or to church, or call any being master or obey anybody; he could
+go fishing or swimming when and where he chose, and stay as long as it
+suited him; nobody forbade him to fight; he could sit up as late as he
+pleased; he was always the first boy that went barefoot in the spring
+and the last to resume leather in the fall; he never had to wash, nor
+put on clean clothes; he could swear wonderfully. In a word, everything
+that goes to make life precious that boy had. So thought every
+harassed, hampered, respectable boy in St. Petersburg.
+
+Tom hailed the romantic outcast:
+
+"Hello, Huckleberry!"
+
+"Hello yourself, and see how you like it."
+
+"What's that you got?"
+
+"Dead cat."
+
+"Lemme see him, Huck. My, he's pretty stiff. Where'd you get him?"
+
+"Bought him off'n a boy."
+
+"What did you give?"
+
+"I give a blue ticket and a bladder that I got at the slaughter-house."
+
+"Where'd you get the blue ticket?"
+
+"Bought it off'n Ben Rogers two weeks ago for a hoop-stick."
+
+"Say--what is dead cats good for, Huck?"
+
+"Good for? Cure warts with."
+
+"No! Is that so? I know something that's better."
+
+"I bet you don't. What is it?"
+
+"Why, spunk-water."
+
+"Spunk-water! I wouldn't give a dern for spunk-water."
+
+"You wouldn't, wouldn't you? D'you ever try it?"
+
+"No, I hain't. But Bob Tanner did."
+
+"Who told you so!"
+
+"Why, he told Jeff Thatcher, and Jeff told Johnny Baker, and Johnny
+told Jim Hollis, and Jim told Ben Rogers, and Ben told a nigger, and
+the nigger told me. There now!"
+
+"Well, what of it? They'll all lie. Leastways all but the nigger. I
+don't know HIM. But I never see a nigger that WOULDN'T lie. Shucks! Now
+you tell me how Bob Tanner done it, Huck."
+
+"Why, he took and dipped his hand in a rotten stump where the
+rain-water was."
+
+"In the daytime?"
+
+"Certainly."
+
+"With his face to the stump?"
+
+"Yes. Least I reckon so."
+
+"Did he say anything?"
+
+"I don't reckon he did. I don't know."
+
+"Aha! Talk about trying to cure warts with spunk-water such a blame
+fool way as that! Why, that ain't a-going to do any good. You got to go
+all by yourself, to the middle of the woods, where you know there's a
+spunk-water stump, and just as it's midnight you back up against the
+stump and jam your hand in and say:
+
+  'Barley-corn, barley-corn, injun-meal shorts,
+   Spunk-water, spunk-water, swaller these warts,'
+
+and then walk away quick, eleven steps, with your eyes shut, and then
+turn around three times and walk home without speaking to anybody.
+Because if you speak the charm's busted."
+
+"Well, that sounds like a good way; but that ain't the way Bob Tanner
+done."
+
+"No, sir, you can bet he didn't, becuz he's the wartiest boy in this
+town; and he wouldn't have a wart on him if he'd knowed how to work
+spunk-water. I've took off thousands of warts off of my hands that way,
+Huck. I play with frogs so much that I've always got considerable many
+warts. Sometimes I take 'em off with a bean."
+
+"Yes, bean's good. I've done that."
+
+"Have you? What's your way?"
+
+"You take and split the bean, and cut the wart so as to get some
+blood, and then you put the blood on one piece of the bean and take and
+dig a hole and bury it 'bout midnight at the crossroads in the dark of
+the moon, and then you burn up the rest of the bean. You see that piece
+that's got the blood on it will keep drawing and drawing, trying to
+fetch the other piece to it, and so that helps the blood to draw the
+wart, and pretty soon off she comes."
+
+"Yes, that's it, Huck--that's it; though when you're burying it if you
+say 'Down bean; off wart; come no more to bother me!' it's better.
+That's the way Joe Harper does, and he's been nearly to Coonville and
+most everywheres. But say--how do you cure 'em with dead cats?"
+
+"Why, you take your cat and go and get in the graveyard 'long about
+midnight when somebody that was wicked has been buried; and when it's
+midnight a devil will come, or maybe two or three, but you can't see
+'em, you can only hear something like the wind, or maybe hear 'em talk;
+and when they're taking that feller away, you heave your cat after 'em
+and say, 'Devil follow corpse, cat follow devil, warts follow cat, I'm
+done with ye!' That'll fetch ANY wart."
+
+"Sounds right. D'you ever try it, Huck?"
+
+"No, but old Mother Hopkins told me."
+
+"Well, I reckon it's so, then. Becuz they say she's a witch."
+
+"Say! Why, Tom, I KNOW she is. She witched pap. Pap says so his own
+self. He come along one day, and he see she was a-witching him, so he
+took up a rock, and if she hadn't dodged, he'd a got her. Well, that
+very night he rolled off'n a shed wher' he was a layin drunk, and broke
+his arm."
+
+"Why, that's awful. How did he know she was a-witching him?"
+
+"Lord, pap can tell, easy. Pap says when they keep looking at you
+right stiddy, they're a-witching you. Specially if they mumble. Becuz
+when they mumble they're saying the Lord's Prayer backards."
+
+"Say, Hucky, when you going to try the cat?"
+
+"To-night. I reckon they'll come after old Hoss Williams to-night."
+
+"But they buried him Saturday. Didn't they get him Saturday night?"
+
+"Why, how you talk! How could their charms work till midnight?--and
+THEN it's Sunday. Devils don't slosh around much of a Sunday, I don't
+reckon."
+
+"I never thought of that. That's so. Lemme go with you?"
+
+"Of course--if you ain't afeard."
+
+"Afeard! 'Tain't likely. Will you meow?"
+
+"Yes--and you meow back, if you get a chance. Last time, you kep' me
+a-meowing around till old Hays went to throwing rocks at me and says
+'Dern that cat!' and so I hove a brick through his window--but don't
+you tell."
+
+"I won't. I couldn't meow that night, becuz auntie was watching me,
+but I'll meow this time. Say--what's that?"
+
+"Nothing but a tick."
+
+"Where'd you get him?"
+
+"Out in the woods."
+
+"What'll you take for him?"
+
+"I don't know. I don't want to sell him."
+
+"All right. It's a mighty small tick, anyway."
+
+"Oh, anybody can run a tick down that don't belong to them. I'm
+satisfied with it. It's a good enough tick for me."
+
+"Sho, there's ticks a plenty. I could have a thousand of 'em if I
+wanted to."
+
+"Well, why don't you? Becuz you know mighty well you can't. This is a
+pretty early tick, I reckon. It's the first one I've seen this year."
+
+"Say, Huck--I'll give you my tooth for him."
+
+"Less see it."
+
+Tom got out a bit of paper and carefully unrolled it. Huckleberry
+viewed it wistfully. The temptation was very strong. At last he said:
+
+"Is it genuwyne?"
+
+Tom lifted his lip and showed the vacancy.
+
+"Well, all right," said Huckleberry, "it's a trade."
+
+Tom enclosed the tick in the percussion-cap box that had lately been
+the pinchbug's prison, and the boys separated, each feeling wealthier
+than before.
+
+When Tom reached the little isolated frame schoolhouse, he strode in
+briskly, with the manner of one who had come with all honest speed.
+He hung his hat on a peg and flung himself into his seat with
+business-like alacrity. The master, throned on high in his great
+splint-bottom arm-chair, was dozing, lulled by the drowsy hum of study.
+The interruption roused him.
+
+"Thomas Sawyer!"
+
+Tom knew that when his name was pronounced in full, it meant trouble.
+
+"Sir!"
+
+"Come up here. Now, sir, why are you late again, as usual?"
+
+Tom was about to take refuge in a lie, when he saw two long tails of
+yellow hair hanging down a back that he recognized by the electric
+sympathy of love; and by that form was THE ONLY VACANT PLACE on the
+girls' side of the schoolhouse. He instantly said:
+
+"I STOPPED TO TALK WITH HUCKLEBERRY FINN!"
+
+The master's pulse stood still, and he stared helplessly. The buzz of
+study ceased. The pupils wondered if this foolhardy boy had lost his
+mind. The master said:
+
+"You--you did what?"
+
+"Stopped to talk with Huckleberry Finn."
+
+There was no mistaking the words.
+
+"Thomas Sawyer, this is the most astounding confession I have ever
+listened to. No mere ferule will answer for this offence. Take off your
+jacket."
+
+The master's arm performed until it was tired and the stock of
+switches notably diminished. Then the order followed:
+
+"Now, sir, go and sit with the girls! And let this be a warning to you."
+
+The titter that rippled around the room appeared to abash the boy, but
+in reality that result was caused rather more by his worshipful awe of
+his unknown idol and the dread pleasure that lay in his high good
+fortune. He sat down upon the end of the pine bench and the girl
+hitched herself away from him with a toss of her head. Nudges and winks
+and whispers traversed the room, but Tom sat still, with his arms upon
+the long, low desk before him, and seemed to study his book.
+
+By and by attention ceased from him, and the accustomed school murmur
+rose upon the dull air once more. Presently the boy began to steal
+furtive glances at the girl. She observed it, "made a mouth" at him and
+gave him the back of her head for the space of a minute. When she
+cautiously faced around again, a peach lay before her. She thrust it
+away. Tom gently put it back. She thrust it away again, but with less
+animosity. Tom patiently returned it to its place. Then she let it
+remain. Tom scrawled on his slate, "Please take it--I got more." The
+girl glanced at the words, but made no sign. Now the boy began to draw
+something on the slate, hiding his work with his left hand. For a time
+the girl refused to notice; but her human curiosity presently began to
+manifest itself by hardly perceptible signs. The boy worked on,
+apparently unconscious. The girl made a sort of noncommittal attempt to
+see, but the boy did not betray that he was aware of it. At last she
+gave in and hesitatingly whispered:
+
+"Let me see it."
+
+Tom partly uncovered a dismal caricature of a house with two gable
+ends to it and a corkscrew of smoke issuing from the chimney. Then the
+girl's interest began to fasten itself upon the work and she forgot
+everything else. When it was finished, she gazed a moment, then
+whispered:
+
+"It's nice--make a man."
+
+The artist erected a man in the front yard, that resembled a derrick.
+He could have stepped over the house; but the girl was not
+hypercritical; she was satisfied with the monster, and whispered:
+
+"It's a beautiful man--now make me coming along."
+
+Tom drew an hour-glass with a full moon and straw limbs to it and
+armed the spreading fingers with a portentous fan. The girl said:
+
+"It's ever so nice--I wish I could draw."
+
+"It's easy," whispered Tom, "I'll learn you."
+
+"Oh, will you? When?"
+
+"At noon. Do you go home to dinner?"
+
+"I'll stay if you will."
+
+"Good--that's a whack. What's your name?"
+
+"Becky Thatcher. What's yours? Oh, I know. It's Thomas Sawyer."
+
+"That's the name they lick me by. I'm Tom when I'm good. You call me
+Tom, will you?"
+
+"Yes."
+
+Now Tom began to scrawl something on the slate, hiding the words from
+the girl. But she was not backward this time. She begged to see. Tom
+said:
+
+"Oh, it ain't anything."
+
+"Yes it is."
+
+"No it ain't. You don't want to see."
+
+"Yes I do, indeed I do. Please let me."
+
+"You'll tell."
+
+"No I won't--deed and deed and double deed won't."
+
+"You won't tell anybody at all? Ever, as long as you live?"
+
+"No, I won't ever tell ANYbody. Now let me."
+
+"Oh, YOU don't want to see!"
+
+"Now that you treat me so, I WILL see." And she put her small hand
+upon his and a little scuffle ensued, Tom pretending to resist in
+earnest but letting his hand slip by degrees till these words were
+revealed: "I LOVE YOU."
+
+"Oh, you bad thing!" And she hit his hand a smart rap, but reddened
+and looked pleased, nevertheless.
+
+Just at this juncture the boy felt a slow, fateful grip closing on his
+ear, and a steady lifting impulse. In that wise he was borne across the
+house and deposited in his own seat, under a peppering fire of giggles
+from the whole school. Then the master stood over him during a few
+awful moments, and finally moved away to his throne without saying a
+word. But although Tom's ear tingled, his heart was jubilant.
+
+As the school quieted down Tom made an honest effort to study, but the
+turmoil within him was too great. In turn he took his place in the
+reading class and made a botch of it; then in the geography class and
+turned lakes into mountains, mountains into rivers, and rivers into
+continents, till chaos was come again; then in the spelling class, and
+got "turned down," by a succession of mere baby words, till he brought
+up at the foot and yielded up the pewter medal which he had worn with
+ostentation for months.
+
+
+
+CHAPTER VII
+
+THE harder Tom tried to fasten his mind on his book, the more his
+ideas wandered. So at last, with a sigh and a yawn, he gave it up. It
+seemed to him that the noon recess would never come. The air was
+utterly dead. There was not a breath stirring. It was the sleepiest of
+sleepy days. The drowsing murmur of the five and twenty studying
+scholars soothed the soul like the spell that is in the murmur of bees.
+Away off in the flaming sunshine, Cardiff Hill lifted its soft green
+sides through a shimmering veil of heat, tinted with the purple of
+distance; a few birds floated on lazy wing high in the air; no other
+living thing was visible but some cows, and they were asleep. Tom's
+heart ached to be free, or else to have something of interest to do to
+pass the dreary time. His hand wandered into his pocket and his face
+lit up with a glow of gratitude that was prayer, though he did not know
+it. Then furtively the percussion-cap box came out. He released the
+tick and put him on the long flat desk. The creature probably glowed
+with a gratitude that amounted to prayer, too, at this moment, but it
+was premature: for when he started thankfully to travel off, Tom turned
+him aside with a pin and made him take a new direction.
+
+Tom's bosom friend sat next him, suffering just as Tom had been, and
+now he was deeply and gratefully interested in this entertainment in an
+instant. This bosom friend was Joe Harper. The two boys were sworn
+friends all the week, and embattled enemies on Saturdays. Joe took a
+pin out of his lapel and began to assist in exercising the prisoner.
+The sport grew in interest momently. Soon Tom said that they were
+interfering with each other, and neither getting the fullest benefit of
+the tick. So he put Joe's slate on the desk and drew a line down the
+middle of it from top to bottom.
+
+"Now," said he, "as long as he is on your side you can stir him up and
+I'll let him alone; but if you let him get away and get on my side,
+you're to leave him alone as long as I can keep him from crossing over."
+
+"All right, go ahead; start him up."
+
+The tick escaped from Tom, presently, and crossed the equator. Joe
+harassed him awhile, and then he got away and crossed back again. This
+change of base occurred often. While one boy was worrying the tick with
+absorbing interest, the other would look on with interest as strong,
+the two heads bowed together over the slate, and the two souls dead to
+all things else. At last luck seemed to settle and abide with Joe. The
+tick tried this, that, and the other course, and got as excited and as
+anxious as the boys themselves, but time and again just as he would
+have victory in his very grasp, so to speak, and Tom's fingers would be
+twitching to begin, Joe's pin would deftly head him off, and keep
+possession. At last Tom could stand it no longer. The temptation was
+too strong. So he reached out and lent a hand with his pin. Joe was
+angry in a moment. Said he:
+
+"Tom, you let him alone."
+
+"I only just want to stir him up a little, Joe."
+
+"No, sir, it ain't fair; you just let him alone."
+
+"Blame it, I ain't going to stir him much."
+
+"Let him alone, I tell you."
+
+"I won't!"
+
+"You shall--he's on my side of the line."
+
+"Look here, Joe Harper, whose is that tick?"
+
+"I don't care whose tick he is--he's on my side of the line, and you
+sha'n't touch him."
+
+"Well, I'll just bet I will, though. He's my tick and I'll do what I
+blame please with him, or die!"
+
+A tremendous whack came down on Tom's shoulders, and its duplicate on
+Joe's; and for the space of two minutes the dust continued to fly from
+the two jackets and the whole school to enjoy it. The boys had been too
+absorbed to notice the hush that had stolen upon the school awhile
+before when the master came tiptoeing down the room and stood over
+them. He had contemplated a good part of the performance before he
+contributed his bit of variety to it.
+
+When school broke up at noon, Tom flew to Becky Thatcher, and
+whispered in her ear:
+
+"Put on your bonnet and let on you're going home; and when you get to
+the corner, give the rest of 'em the slip, and turn down through the
+lane and come back. I'll go the other way and come it over 'em the same
+way."
+
+So the one went off with one group of scholars, and the other with
+another. In a little while the two met at the bottom of the lane, and
+when they reached the school they had it all to themselves. Then they
+sat together, with a slate before them, and Tom gave Becky the pencil
+and held her hand in his, guiding it, and so created another surprising
+house. When the interest in art began to wane, the two fell to talking.
+Tom was swimming in bliss. He said:
+
+"Do you love rats?"
+
+"No! I hate them!"
+
+"Well, I do, too--LIVE ones. But I mean dead ones, to swing round your
+head with a string."
+
+"No, I don't care for rats much, anyway. What I like is chewing-gum."
+
+"Oh, I should say so! I wish I had some now."
+
+"Do you? I've got some. I'll let you chew it awhile, but you must give
+it back to me."
+
+That was agreeable, so they chewed it turn about, and dangled their
+legs against the bench in excess of contentment.
+
+"Was you ever at a circus?" said Tom.
+
+"Yes, and my pa's going to take me again some time, if I'm good."
+
+"I been to the circus three or four times--lots of times. Church ain't
+shucks to a circus. There's things going on at a circus all the time.
+I'm going to be a clown in a circus when I grow up."
+
+"Oh, are you! That will be nice. They're so lovely, all spotted up."
+
+"Yes, that's so. And they get slathers of money--most a dollar a day,
+Ben Rogers says. Say, Becky, was you ever engaged?"
+
+"What's that?"
+
+"Why, engaged to be married."
+
+"No."
+
+"Would you like to?"
+
+"I reckon so. I don't know. What is it like?"
+
+"Like? Why it ain't like anything. You only just tell a boy you won't
+ever have anybody but him, ever ever ever, and then you kiss and that's
+all. Anybody can do it."
+
+"Kiss? What do you kiss for?"
+
+"Why, that, you know, is to--well, they always do that."
+
+"Everybody?"
+
+"Why, yes, everybody that's in love with each other. Do you remember
+what I wrote on the slate?"
+
+"Ye--yes."
+
+"What was it?"
+
+"I sha'n't tell you."
+
+"Shall I tell YOU?"
+
+"Ye--yes--but some other time."
+
+"No, now."
+
+"No, not now--to-morrow."
+
+"Oh, no, NOW. Please, Becky--I'll whisper it, I'll whisper it ever so
+easy."
+
+Becky hesitating, Tom took silence for consent, and passed his arm
+about her waist and whispered the tale ever so softly, with his mouth
+close to her ear. And then he added:
+
+"Now you whisper it to me--just the same."
+
+She resisted, for a while, and then said:
+
+"You turn your face away so you can't see, and then I will. But you
+mustn't ever tell anybody--WILL you, Tom? Now you won't, WILL you?"
+
+"No, indeed, indeed I won't. Now, Becky."
+
+He turned his face away. She bent timidly around till her breath
+stirred his curls and whispered, "I--love--you!"
+
+Then she sprang away and ran around and around the desks and benches,
+with Tom after her, and took refuge in a corner at last, with her
+little white apron to her face. Tom clasped her about her neck and
+pleaded:
+
+"Now, Becky, it's all done--all over but the kiss. Don't you be afraid
+of that--it ain't anything at all. Please, Becky." And he tugged at her
+apron and the hands.
+
+By and by she gave up, and let her hands drop; her face, all glowing
+with the struggle, came up and submitted. Tom kissed the red lips and
+said:
+
+"Now it's all done, Becky. And always after this, you know, you ain't
+ever to love anybody but me, and you ain't ever to marry anybody but
+me, ever never and forever. Will you?"
+
+"No, I'll never love anybody but you, Tom, and I'll never marry
+anybody but you--and you ain't to ever marry anybody but me, either."
+
+"Certainly. Of course. That's PART of it. And always coming to school
+or when we're going home, you're to walk with me, when there ain't
+anybody looking--and you choose me and I choose you at parties, because
+that's the way you do when you're engaged."
+
+"It's so nice. I never heard of it before."
+
+"Oh, it's ever so gay! Why, me and Amy Lawrence--"
+
+The big eyes told Tom his blunder and he stopped, confused.
+
+"Oh, Tom! Then I ain't the first you've ever been engaged to!"
+
+The child began to cry. Tom said:
+
+"Oh, don't cry, Becky, I don't care for her any more."
+
+"Yes, you do, Tom--you know you do."
+
+Tom tried to put his arm about her neck, but she pushed him away and
+turned her face to the wall, and went on crying. Tom tried again, with
+soothing words in his mouth, and was repulsed again. Then his pride was
+up, and he strode away and went outside. He stood about, restless and
+uneasy, for a while, glancing at the door, every now and then, hoping
+she would repent and come to find him. But she did not. Then he began
+to feel badly and fear that he was in the wrong. It was a hard struggle
+with him to make new advances, now, but he nerved himself to it and
+entered. She was still standing back there in the corner, sobbing, with
+her face to the wall. Tom's heart smote him. He went to her and stood a
+moment, not knowing exactly how to proceed. Then he said hesitatingly:
+
+"Becky, I--I don't care for anybody but you."
+
+No reply--but sobs.
+
+"Becky"--pleadingly. "Becky, won't you say something?"
+
+More sobs.
+
+Tom got out his chiefest jewel, a brass knob from the top of an
+andiron, and passed it around her so that she could see it, and said:
+
+"Please, Becky, won't you take it?"
+
+She struck it to the floor. Then Tom marched out of the house and over
+the hills and far away, to return to school no more that day. Presently
+Becky began to suspect. She ran to the door; he was not in sight; she
+flew around to the play-yard; he was not there. Then she called:
+
+"Tom! Come back, Tom!"
+
+She listened intently, but there was no answer. She had no companions
+but silence and loneliness. So she sat down to cry again and upbraid
+herself; and by this time the scholars began to gather again, and she
+had to hide her griefs and still her broken heart and take up the cross
+of a long, dreary, aching afternoon, with none among the strangers
+about her to exchange sorrows with.
+
+
+
+CHAPTER VIII
+
+TOM dodged hither and thither through lanes until he was well out of
+the track of returning scholars, and then fell into a moody jog. He
+crossed a small "branch" two or three times, because of a prevailing
+juvenile superstition that to cross water baffled pursuit. Half an hour
+later he was disappearing behind the Douglas mansion on the summit of
+Cardiff Hill, and the schoolhouse was hardly distinguishable away off
+in the valley behind him. He entered a dense wood, picked his pathless
+way to the centre of it, and sat down on a mossy spot under a spreading
+oak. There was not even a zephyr stirring; the dead noonday heat had
+even stilled the songs of the birds; nature lay in a trance that was
+broken by no sound but the occasional far-off hammering of a
+woodpecker, and this seemed to render the pervading silence and sense
+of loneliness the more profound. The boy's soul was steeped in
+melancholy; his feelings were in happy accord with his surroundings. He
+sat long with his elbows on his knees and his chin in his hands,
+meditating. It seemed to him that life was but a trouble, at best, and
+he more than half envied Jimmy Hodges, so lately released; it must be
+very peaceful, he thought, to lie and slumber and dream forever and
+ever, with the wind whispering through the trees and caressing the
+grass and the flowers over the grave, and nothing to bother and grieve
+about, ever any more. If he only had a clean Sunday-school record he
+could be willing to go, and be done with it all. Now as to this girl.
+What had he done? Nothing. He had meant the best in the world, and been
+treated like a dog--like a very dog. She would be sorry some day--maybe
+when it was too late. Ah, if he could only die TEMPORARILY!
+
+But the elastic heart of youth cannot be compressed into one
+constrained shape long at a time. Tom presently began to drift
+insensibly back into the concerns of this life again. What if he turned
+his back, now, and disappeared mysteriously? What if he went away--ever
+so far away, into unknown countries beyond the seas--and never came
+back any more! How would she feel then! The idea of being a clown
+recurred to him now, only to fill him with disgust. For frivolity and
+jokes and spotted tights were an offense, when they intruded themselves
+upon a spirit that was exalted into the vague august realm of the
+romantic. No, he would be a soldier, and return after long years, all
+war-worn and illustrious. No--better still, he would join the Indians,
+and hunt buffaloes and go on the warpath in the mountain ranges and the
+trackless great plains of the Far West, and away in the future come
+back a great chief, bristling with feathers, hideous with paint, and
+prance into Sunday-school, some drowsy summer morning, with a
+bloodcurdling war-whoop, and sear the eyeballs of all his companions
+with unappeasable envy. But no, there was something gaudier even than
+this. He would be a pirate! That was it! NOW his future lay plain
+before him, and glowing with unimaginable splendor. How his name would
+fill the world, and make people shudder! How gloriously he would go
+plowing the dancing seas, in his long, low, black-hulled racer, the
+Spirit of the Storm, with his grisly flag flying at the fore! And at
+the zenith of his fame, how he would suddenly appear at the old village
+and stalk into church, brown and weather-beaten, in his black velvet
+doublet and trunks, his great jack-boots, his crimson sash, his belt
+bristling with horse-pistols, his crime-rusted cutlass at his side, his
+slouch hat with waving plumes, his black flag unfurled, with the skull
+and crossbones on it, and hear with swelling ecstasy the whisperings,
+"It's Tom Sawyer the Pirate!--the Black Avenger of the Spanish Main!"
+
+Yes, it was settled; his career was determined. He would run away from
+home and enter upon it. He would start the very next morning. Therefore
+he must now begin to get ready. He would collect his resources
+together. He went to a rotten log near at hand and began to dig under
+one end of it with his Barlow knife. He soon struck wood that sounded
+hollow. He put his hand there and uttered this incantation impressively:
+
+"What hasn't come here, come! What's here, stay here!"
+
+Then he scraped away the dirt, and exposed a pine shingle. He took it
+up and disclosed a shapely little treasure-house whose bottom and sides
+were of shingles. In it lay a marble. Tom's astonishment was boundless!
+He scratched his head with a perplexed air, and said:
+
+"Well, that beats anything!"
+
+Then he tossed the marble away pettishly, and stood cogitating. The
+truth was, that a superstition of his had failed, here, which he and
+all his comrades had always looked upon as infallible. If you buried a
+marble with certain necessary incantations, and left it alone a
+fortnight, and then opened the place with the incantation he had just
+used, you would find that all the marbles you had ever lost had
+gathered themselves together there, meantime, no matter how widely they
+had been separated. But now, this thing had actually and unquestionably
+failed. Tom's whole structure of faith was shaken to its foundations.
+He had many a time heard of this thing succeeding but never of its
+failing before. It did not occur to him that he had tried it several
+times before, himself, but could never find the hiding-places
+afterward. He puzzled over the matter some time, and finally decided
+that some witch had interfered and broken the charm. He thought he
+would satisfy himself on that point; so he searched around till he
+found a small sandy spot with a little funnel-shaped depression in it.
+He laid himself down and put his mouth close to this depression and
+called--
+
+"Doodle-bug, doodle-bug, tell me what I want to know! Doodle-bug,
+doodle-bug, tell me what I want to know!"
+
+The sand began to work, and presently a small black bug appeared for a
+second and then darted under again in a fright.
+
+"He dasn't tell! So it WAS a witch that done it. I just knowed it."
+
+He well knew the futility of trying to contend against witches, so he
+gave up discouraged. But it occurred to him that he might as well have
+the marble he had just thrown away, and therefore he went and made a
+patient search for it. But he could not find it. Now he went back to
+his treasure-house and carefully placed himself just as he had been
+standing when he tossed the marble away; then he took another marble
+from his pocket and tossed it in the same way, saying:
+
+"Brother, go find your brother!"
+
+He watched where it stopped, and went there and looked. But it must
+have fallen short or gone too far; so he tried twice more. The last
+repetition was successful. The two marbles lay within a foot of each
+other.
+
+Just here the blast of a toy tin trumpet came faintly down the green
+aisles of the forest. Tom flung off his jacket and trousers, turned a
+suspender into a belt, raked away some brush behind the rotten log,
+disclosing a rude bow and arrow, a lath sword and a tin trumpet, and in
+a moment had seized these things and bounded away, barelegged, with
+fluttering shirt. He presently halted under a great elm, blew an
+answering blast, and then began to tiptoe and look warily out, this way
+and that. He said cautiously--to an imaginary company:
+
+"Hold, my merry men! Keep hid till I blow."
+
+Now appeared Joe Harper, as airily clad and elaborately armed as Tom.
+Tom called:
+
+"Hold! Who comes here into Sherwood Forest without my pass?"
+
+"Guy of Guisborne wants no man's pass. Who art thou that--that--"
+
+"Dares to hold such language," said Tom, prompting--for they talked
+"by the book," from memory.
+
+"Who art thou that dares to hold such language?"
+
+"I, indeed! I am Robin Hood, as thy caitiff carcase soon shall know."
+
+"Then art thou indeed that famous outlaw? Right gladly will I dispute
+with thee the passes of the merry wood. Have at thee!"
+
+They took their lath swords, dumped their other traps on the ground,
+struck a fencing attitude, foot to foot, and began a grave, careful
+combat, "two up and two down." Presently Tom said:
+
+"Now, if you've got the hang, go it lively!"
+
+So they "went it lively," panting and perspiring with the work. By and
+by Tom shouted:
+
+"Fall! fall! Why don't you fall?"
+
+"I sha'n't! Why don't you fall yourself? You're getting the worst of
+it."
+
+"Why, that ain't anything. I can't fall; that ain't the way it is in
+the book. The book says, 'Then with one back-handed stroke he slew poor
+Guy of Guisborne.' You're to turn around and let me hit you in the
+back."
+
+There was no getting around the authorities, so Joe turned, received
+the whack and fell.
+
+"Now," said Joe, getting up, "you got to let me kill YOU. That's fair."
+
+"Why, I can't do that, it ain't in the book."
+
+"Well, it's blamed mean--that's all."
+
+"Well, say, Joe, you can be Friar Tuck or Much the miller's son, and
+lam me with a quarter-staff; or I'll be the Sheriff of Nottingham and
+you be Robin Hood a little while and kill me."
+
+This was satisfactory, and so these adventures were carried out. Then
+Tom became Robin Hood again, and was allowed by the treacherous nun to
+bleed his strength away through his neglected wound. And at last Joe,
+representing a whole tribe of weeping outlaws, dragged him sadly forth,
+gave his bow into his feeble hands, and Tom said, "Where this arrow
+falls, there bury poor Robin Hood under the greenwood tree." Then he
+shot the arrow and fell back and would have died, but he lit on a
+nettle and sprang up too gaily for a corpse.
+
+The boys dressed themselves, hid their accoutrements, and went off
+grieving that there were no outlaws any more, and wondering what modern
+civilization could claim to have done to compensate for their loss.
+They said they would rather be outlaws a year in Sherwood Forest than
+President of the United States forever.
+
+
+
+CHAPTER IX
+
+AT half-past nine, that night, Tom and Sid were sent to bed, as usual.
+They said their prayers, and Sid was soon asleep. Tom lay awake and
+waited, in restless impatience. When it seemed to him that it must be
+nearly daylight, he heard the clock strike ten! This was despair. He
+would have tossed and fidgeted, as his nerves demanded, but he was
+afraid he might wake Sid. So he lay still, and stared up into the dark.
+Everything was dismally still. By and by, out of the stillness, little,
+scarcely perceptible noises began to emphasize themselves. The ticking
+of the clock began to bring itself into notice. Old beams began to
+crack mysteriously. The stairs creaked faintly. Evidently spirits were
+abroad. A measured, muffled snore issued from Aunt Polly's chamber. And
+now the tiresome chirping of a cricket that no human ingenuity could
+locate, began. Next the ghastly ticking of a deathwatch in the wall at
+the bed's head made Tom shudder--it meant that somebody's days were
+numbered. Then the howl of a far-off dog rose on the night air, and was
+answered by a fainter howl from a remoter distance. Tom was in an
+agony. At last he was satisfied that time had ceased and eternity
+begun; he began to doze, in spite of himself; the clock chimed eleven,
+but he did not hear it. And then there came, mingling with his
+half-formed dreams, a most melancholy caterwauling. The raising of a
+neighboring window disturbed him. A cry of "Scat! you devil!" and the
+crash of an empty bottle against the back of his aunt's woodshed
+brought him wide awake, and a single minute later he was dressed and
+out of the window and creeping along the roof of the "ell" on all
+fours. He "meow'd" with caution once or twice, as he went; then jumped
+to the roof of the woodshed and thence to the ground. Huckleberry Finn
+was there, with his dead cat. The boys moved off and disappeared in the
+gloom. At the end of half an hour they were wading through the tall
+grass of the graveyard.
+
+It was a graveyard of the old-fashioned Western kind. It was on a
+hill, about a mile and a half from the village. It had a crazy board
+fence around it, which leaned inward in places, and outward the rest of
+the time, but stood upright nowhere. Grass and weeds grew rank over the
+whole cemetery. All the old graves were sunken in, there was not a
+tombstone on the place; round-topped, worm-eaten boards staggered over
+the graves, leaning for support and finding none. "Sacred to the memory
+of" So-and-So had been painted on them once, but it could no longer
+have been read, on the most of them, now, even if there had been light.
+
+A faint wind moaned through the trees, and Tom feared it might be the
+spirits of the dead, complaining at being disturbed. The boys talked
+little, and only under their breath, for the time and the place and the
+pervading solemnity and silence oppressed their spirits. They found the
+sharp new heap they were seeking, and ensconced themselves within the
+protection of three great elms that grew in a bunch within a few feet
+of the grave.
+
+Then they waited in silence for what seemed a long time. The hooting
+of a distant owl was all the sound that troubled the dead stillness.
+Tom's reflections grew oppressive. He must force some talk. So he said
+in a whisper:
+
+"Hucky, do you believe the dead people like it for us to be here?"
+
+Huckleberry whispered:
+
+"I wisht I knowed. It's awful solemn like, AIN'T it?"
+
+"I bet it is."
+
+There was a considerable pause, while the boys canvassed this matter
+inwardly. Then Tom whispered:
+
+"Say, Hucky--do you reckon Hoss Williams hears us talking?"
+
+"O' course he does. Least his sperrit does."
+
+Tom, after a pause:
+
+"I wish I'd said Mister Williams. But I never meant any harm.
+Everybody calls him Hoss."
+
+"A body can't be too partic'lar how they talk 'bout these-yer dead
+people, Tom."
+
+This was a damper, and conversation died again.
+
+Presently Tom seized his comrade's arm and said:
+
+"Sh!"
+
+"What is it, Tom?" And the two clung together with beating hearts.
+
+"Sh! There 'tis again! Didn't you hear it?"
+
+"I--"
+
+"There! Now you hear it."
+
+"Lord, Tom, they're coming! They're coming, sure. What'll we do?"
+
+"I dono. Think they'll see us?"
+
+"Oh, Tom, they can see in the dark, same as cats. I wisht I hadn't
+come."
+
+"Oh, don't be afeard. I don't believe they'll bother us. We ain't
+doing any harm. If we keep perfectly still, maybe they won't notice us
+at all."
+
+"I'll try to, Tom, but, Lord, I'm all of a shiver."
+
+"Listen!"
+
+The boys bent their heads together and scarcely breathed. A muffled
+sound of voices floated up from the far end of the graveyard.
+
+"Look! See there!" whispered Tom. "What is it?"
+
+"It's devil-fire. Oh, Tom, this is awful."
+
+Some vague figures approached through the gloom, swinging an
+old-fashioned tin lantern that freckled the ground with innumerable
+little spangles of light. Presently Huckleberry whispered with a
+shudder:
+
+"It's the devils sure enough. Three of 'em! Lordy, Tom, we're goners!
+Can you pray?"
+
+"I'll try, but don't you be afeard. They ain't going to hurt us. 'Now
+I lay me down to sleep, I--'"
+
+"Sh!"
+
+"What is it, Huck?"
+
+"They're HUMANS! One of 'em is, anyway. One of 'em's old Muff Potter's
+voice."
+
+"No--'tain't so, is it?"
+
+"I bet I know it. Don't you stir nor budge. He ain't sharp enough to
+notice us. Drunk, the same as usual, likely--blamed old rip!"
+
+"All right, I'll keep still. Now they're stuck. Can't find it. Here
+they come again. Now they're hot. Cold again. Hot again. Red hot!
+They're p'inted right, this time. Say, Huck, I know another o' them
+voices; it's Injun Joe."
+
+"That's so--that murderin' half-breed! I'd druther they was devils a
+dern sight. What kin they be up to?"
+
+The whisper died wholly out, now, for the three men had reached the
+grave and stood within a few feet of the boys' hiding-place.
+
+"Here it is," said the third voice; and the owner of it held the
+lantern up and revealed the face of young Doctor Robinson.
+
+Potter and Injun Joe were carrying a handbarrow with a rope and a
+couple of shovels on it. They cast down their load and began to open
+the grave. The doctor put the lantern at the head of the grave and came
+and sat down with his back against one of the elm trees. He was so
+close the boys could have touched him.
+
+"Hurry, men!" he said, in a low voice; "the moon might come out at any
+moment."
+
+They growled a response and went on digging. For some time there was
+no noise but the grating sound of the spades discharging their freight
+of mould and gravel. It was very monotonous. Finally a spade struck
+upon the coffin with a dull woody accent, and within another minute or
+two the men had hoisted it out on the ground. They pried off the lid
+with their shovels, got out the body and dumped it rudely on the
+ground. The moon drifted from behind the clouds and exposed the pallid
+face. The barrow was got ready and the corpse placed on it, covered
+with a blanket, and bound to its place with the rope. Potter took out a
+large spring-knife and cut off the dangling end of the rope and then
+said:
+
+"Now the cussed thing's ready, Sawbones, and you'll just out with
+another five, or here she stays."
+
+"That's the talk!" said Injun Joe.
+
+"Look here, what does this mean?" said the doctor. "You required your
+pay in advance, and I've paid you."
+
+"Yes, and you done more than that," said Injun Joe, approaching the
+doctor, who was now standing. "Five years ago you drove me away from
+your father's kitchen one night, when I come to ask for something to
+eat, and you said I warn't there for any good; and when I swore I'd get
+even with you if it took a hundred years, your father had me jailed for
+a vagrant. Did you think I'd forget? The Injun blood ain't in me for
+nothing. And now I've GOT you, and you got to SETTLE, you know!"
+
+He was threatening the doctor, with his fist in his face, by this
+time. The doctor struck out suddenly and stretched the ruffian on the
+ground. Potter dropped his knife, and exclaimed:
+
+"Here, now, don't you hit my pard!" and the next moment he had
+grappled with the doctor and the two were struggling with might and
+main, trampling the grass and tearing the ground with their heels.
+Injun Joe sprang to his feet, his eyes flaming with passion, snatched
+up Potter's knife, and went creeping, catlike and stooping, round and
+round about the combatants, seeking an opportunity. All at once the
+doctor flung himself free, seized the heavy headboard of Williams'
+grave and felled Potter to the earth with it--and in the same instant
+the half-breed saw his chance and drove the knife to the hilt in the
+young man's breast. He reeled and fell partly upon Potter, flooding him
+with his blood, and in the same moment the clouds blotted out the
+dreadful spectacle and the two frightened boys went speeding away in
+the dark.
+
+Presently, when the moon emerged again, Injun Joe was standing over
+the two forms, contemplating them. The doctor murmured inarticulately,
+gave a long gasp or two and was still. The half-breed muttered:
+
+"THAT score is settled--damn you."
+
+Then he robbed the body. After which he put the fatal knife in
+Potter's open right hand, and sat down on the dismantled coffin. Three
+--four--five minutes passed, and then Potter began to stir and moan. His
+hand closed upon the knife; he raised it, glanced at it, and let it
+fall, with a shudder. Then he sat up, pushing the body from him, and
+gazed at it, and then around him, confusedly. His eyes met Joe's.
+
+"Lord, how is this, Joe?" he said.
+
+"It's a dirty business," said Joe, without moving.
+
+"What did you do it for?"
+
+"I! I never done it!"
+
+"Look here! That kind of talk won't wash."
+
+Potter trembled and grew white.
+
+"I thought I'd got sober. I'd no business to drink to-night. But it's
+in my head yet--worse'n when we started here. I'm all in a muddle;
+can't recollect anything of it, hardly. Tell me, Joe--HONEST, now, old
+feller--did I do it? Joe, I never meant to--'pon my soul and honor, I
+never meant to, Joe. Tell me how it was, Joe. Oh, it's awful--and him
+so young and promising."
+
+"Why, you two was scuffling, and he fetched you one with the headboard
+and you fell flat; and then up you come, all reeling and staggering
+like, and snatched the knife and jammed it into him, just as he fetched
+you another awful clip--and here you've laid, as dead as a wedge til
+now."
+
+"Oh, I didn't know what I was a-doing. I wish I may die this minute if
+I did. It was all on account of the whiskey and the excitement, I
+reckon. I never used a weepon in my life before, Joe. I've fought, but
+never with weepons. They'll all say that. Joe, don't tell! Say you
+won't tell, Joe--that's a good feller. I always liked you, Joe, and
+stood up for you, too. Don't you remember? You WON'T tell, WILL you,
+Joe?" And the poor creature dropped on his knees before the stolid
+murderer, and clasped his appealing hands.
+
+"No, you've always been fair and square with me, Muff Potter, and I
+won't go back on you. There, now, that's as fair as a man can say."
+
+"Oh, Joe, you're an angel. I'll bless you for this the longest day I
+live." And Potter began to cry.
+
+"Come, now, that's enough of that. This ain't any time for blubbering.
+You be off yonder way and I'll go this. Move, now, and don't leave any
+tracks behind you."
+
+Potter started on a trot that quickly increased to a run. The
+half-breed stood looking after him. He muttered:
+
+"If he's as much stunned with the lick and fuddled with the rum as he
+had the look of being, he won't think of the knife till he's gone so
+far he'll be afraid to come back after it to such a place by himself
+--chicken-heart!"
+
+Two or three minutes later the murdered man, the blanketed corpse, the
+lidless coffin, and the open grave were under no inspection but the
+moon's. The stillness was complete again, too.
+
+
+
+CHAPTER X
+
+THE two boys flew on and on, toward the village, speechless with
+horror. They glanced backward over their shoulders from time to time,
+apprehensively, as if they feared they might be followed. Every stump
+that started up in their path seemed a man and an enemy, and made them
+catch their breath; and as they sped by some outlying cottages that lay
+near the village, the barking of the aroused watch-dogs seemed to give
+wings to their feet.
+
+"If we can only get to the old tannery before we break down!"
+whispered Tom, in short catches between breaths. "I can't stand it much
+longer."
+
+Huckleberry's hard pantings were his only reply, and the boys fixed
+their eyes on the goal of their hopes and bent to their work to win it.
+They gained steadily on it, and at last, breast to breast, they burst
+through the open door and fell grateful and exhausted in the sheltering
+shadows beyond. By and by their pulses slowed down, and Tom whispered:
+
+"Huckleberry, what do you reckon'll come of this?"
+
+"If Doctor Robinson dies, I reckon hanging'll come of it."
+
+"Do you though?"
+
+"Why, I KNOW it, Tom."
+
+Tom thought a while, then he said:
+
+"Who'll tell? We?"
+
+"What are you talking about? S'pose something happened and Injun Joe
+DIDN'T hang? Why, he'd kill us some time or other, just as dead sure as
+we're a laying here."
+
+"That's just what I was thinking to myself, Huck."
+
+"If anybody tells, let Muff Potter do it, if he's fool enough. He's
+generally drunk enough."
+
+Tom said nothing--went on thinking. Presently he whispered:
+
+"Huck, Muff Potter don't know it. How can he tell?"
+
+"What's the reason he don't know it?"
+
+"Because he'd just got that whack when Injun Joe done it. D'you reckon
+he could see anything? D'you reckon he knowed anything?"
+
+"By hokey, that's so, Tom!"
+
+"And besides, look-a-here--maybe that whack done for HIM!"
+
+"No, 'taint likely, Tom. He had liquor in him; I could see that; and
+besides, he always has. Well, when pap's full, you might take and belt
+him over the head with a church and you couldn't phase him. He says so,
+his own self. So it's the same with Muff Potter, of course. But if a
+man was dead sober, I reckon maybe that whack might fetch him; I dono."
+
+After another reflective silence, Tom said:
+
+"Hucky, you sure you can keep mum?"
+
+"Tom, we GOT to keep mum. You know that. That Injun devil wouldn't
+make any more of drownding us than a couple of cats, if we was to
+squeak 'bout this and they didn't hang him. Now, look-a-here, Tom, less
+take and swear to one another--that's what we got to do--swear to keep
+mum."
+
+"I'm agreed. It's the best thing. Would you just hold hands and swear
+that we--"
+
+"Oh no, that wouldn't do for this. That's good enough for little
+rubbishy common things--specially with gals, cuz THEY go back on you
+anyway, and blab if they get in a huff--but there orter be writing
+'bout a big thing like this. And blood."
+
+Tom's whole being applauded this idea. It was deep, and dark, and
+awful; the hour, the circumstances, the surroundings, were in keeping
+with it. He picked up a clean pine shingle that lay in the moonlight,
+took a little fragment of "red keel" out of his pocket, got the moon on
+his work, and painfully scrawled these lines, emphasizing each slow
+down-stroke by clamping his tongue between his teeth, and letting up
+the pressure on the up-strokes. [See next page.]
+
+   "Huck Finn and
+    Tom Sawyer swears
+    they will keep mum
+    about This and They
+    wish They may Drop
+    down dead in Their
+    Tracks if They ever
+    Tell and Rot."
+
+Huckleberry was filled with admiration of Tom's facility in writing,
+and the sublimity of his language. He at once took a pin from his lapel
+and was going to prick his flesh, but Tom said:
+
+"Hold on! Don't do that. A pin's brass. It might have verdigrease on
+it."
+
+"What's verdigrease?"
+
+"It's p'ison. That's what it is. You just swaller some of it once
+--you'll see."
+
+So Tom unwound the thread from one of his needles, and each boy
+pricked the ball of his thumb and squeezed out a drop of blood. In
+time, after many squeezes, Tom managed to sign his initials, using the
+ball of his little finger for a pen. Then he showed Huckleberry how to
+make an H and an F, and the oath was complete. They buried the shingle
+close to the wall, with some dismal ceremonies and incantations, and
+the fetters that bound their tongues were considered to be locked and
+the key thrown away.
+
+A figure crept stealthily through a break in the other end of the
+ruined building, now, but they did not notice it.
+
+"Tom," whispered Huckleberry, "does this keep us from EVER telling
+--ALWAYS?"
+
+"Of course it does. It don't make any difference WHAT happens, we got
+to keep mum. We'd drop down dead--don't YOU know that?"
+
+"Yes, I reckon that's so."
+
+They continued to whisper for some little time. Presently a dog set up
+a long, lugubrious howl just outside--within ten feet of them. The boys
+clasped each other suddenly, in an agony of fright.
+
+"Which of us does he mean?" gasped Huckleberry.
+
+"I dono--peep through the crack. Quick!"
+
+"No, YOU, Tom!"
+
+"I can't--I can't DO it, Huck!"
+
+"Please, Tom. There 'tis again!"
+
+"Oh, lordy, I'm thankful!" whispered Tom. "I know his voice. It's Bull
+Harbison." *
+
+[* If Mr. Harbison owned a slave named Bull, Tom would have spoken of
+him as "Harbison's Bull," but a son or a dog of that name was "Bull
+Harbison."]
+
+"Oh, that's good--I tell you, Tom, I was most scared to death; I'd a
+bet anything it was a STRAY dog."
+
+The dog howled again. The boys' hearts sank once more.
+
+"Oh, my! that ain't no Bull Harbison!" whispered Huckleberry. "DO, Tom!"
+
+Tom, quaking with fear, yielded, and put his eye to the crack. His
+whisper was hardly audible when he said:
+
+"Oh, Huck, IT S A STRAY DOG!"
+
+"Quick, Tom, quick! Who does he mean?"
+
+"Huck, he must mean us both--we're right together."
+
+"Oh, Tom, I reckon we're goners. I reckon there ain't no mistake 'bout
+where I'LL go to. I been so wicked."
+
+"Dad fetch it! This comes of playing hookey and doing everything a
+feller's told NOT to do. I might a been good, like Sid, if I'd a tried
+--but no, I wouldn't, of course. But if ever I get off this time, I lay
+I'll just WALLER in Sunday-schools!" And Tom began to snuffle a little.
+
+"YOU bad!" and Huckleberry began to snuffle too. "Consound it, Tom
+Sawyer, you're just old pie, 'longside o' what I am. Oh, LORDY, lordy,
+lordy, I wisht I only had half your chance."
+
+Tom choked off and whispered:
+
+"Look, Hucky, look! He's got his BACK to us!"
+
+Hucky looked, with joy in his heart.
+
+"Well, he has, by jingoes! Did he before?"
+
+"Yes, he did. But I, like a fool, never thought. Oh, this is bully,
+you know. NOW who can he mean?"
+
+The howling stopped. Tom pricked up his ears.
+
+"Sh! What's that?" he whispered.
+
+"Sounds like--like hogs grunting. No--it's somebody snoring, Tom."
+
+"That IS it! Where 'bouts is it, Huck?"
+
+"I bleeve it's down at 'tother end. Sounds so, anyway. Pap used to
+sleep there, sometimes, 'long with the hogs, but laws bless you, he
+just lifts things when HE snores. Besides, I reckon he ain't ever
+coming back to this town any more."
+
+The spirit of adventure rose in the boys' souls once more.
+
+"Hucky, do you das't to go if I lead?"
+
+"I don't like to, much. Tom, s'pose it's Injun Joe!"
+
+Tom quailed. But presently the temptation rose up strong again and the
+boys agreed to try, with the understanding that they would take to
+their heels if the snoring stopped. So they went tiptoeing stealthily
+down, the one behind the other. When they had got to within five steps
+of the snorer, Tom stepped on a stick, and it broke with a sharp snap.
+The man moaned, writhed a little, and his face came into the moonlight.
+It was Muff Potter. The boys' hearts had stood still, and their hopes
+too, when the man moved, but their fears passed away now. They tiptoed
+out, through the broken weather-boarding, and stopped at a little
+distance to exchange a parting word. That long, lugubrious howl rose on
+the night air again! They turned and saw the strange dog standing
+within a few feet of where Potter was lying, and FACING Potter, with
+his nose pointing heavenward.
+
+"Oh, geeminy, it's HIM!" exclaimed both boys, in a breath.
+
+"Say, Tom--they say a stray dog come howling around Johnny Miller's
+house, 'bout midnight, as much as two weeks ago; and a whippoorwill
+come in and lit on the banisters and sung, the very same evening; and
+there ain't anybody dead there yet."
+
+"Well, I know that. And suppose there ain't. Didn't Gracie Miller fall
+in the kitchen fire and burn herself terrible the very next Saturday?"
+
+"Yes, but she ain't DEAD. And what's more, she's getting better, too."
+
+"All right, you wait and see. She's a goner, just as dead sure as Muff
+Potter's a goner. That's what the niggers say, and they know all about
+these kind of things, Huck."
+
+Then they separated, cogitating. When Tom crept in at his bedroom
+window the night was almost spent. He undressed with excessive caution,
+and fell asleep congratulating himself that nobody knew of his
+escapade. He was not aware that the gently-snoring Sid was awake, and
+had been so for an hour.
+
+When Tom awoke, Sid was dressed and gone. There was a late look in the
+light, a late sense in the atmosphere. He was startled. Why had he not
+been called--persecuted till he was up, as usual? The thought filled
+him with bodings. Within five minutes he was dressed and down-stairs,
+feeling sore and drowsy. The family were still at table, but they had
+finished breakfast. There was no voice of rebuke; but there were
+averted eyes; there was a silence and an air of solemnity that struck a
+chill to the culprit's heart. He sat down and tried to seem gay, but it
+was up-hill work; it roused no smile, no response, and he lapsed into
+silence and let his heart sink down to the depths.
+
+After breakfast his aunt took him aside, and Tom almost brightened in
+the hope that he was going to be flogged; but it was not so. His aunt
+wept over him and asked him how he could go and break her old heart so;
+and finally told him to go on, and ruin himself and bring her gray
+hairs with sorrow to the grave, for it was no use for her to try any
+more. This was worse than a thousand whippings, and Tom's heart was
+sorer now than his body. He cried, he pleaded for forgiveness, promised
+to reform over and over again, and then received his dismissal, feeling
+that he had won but an imperfect forgiveness and established but a
+feeble confidence.
+
+He left the presence too miserable to even feel revengeful toward Sid;
+and so the latter's prompt retreat through the back gate was
+unnecessary. He moped to school gloomy and sad, and took his flogging,
+along with Joe Harper, for playing hookey the day before, with the air
+of one whose heart was busy with heavier woes and wholly dead to
+trifles. Then he betook himself to his seat, rested his elbows on his
+desk and his jaws in his hands, and stared at the wall with the stony
+stare of suffering that has reached the limit and can no further go.
+His elbow was pressing against some hard substance. After a long time
+he slowly and sadly changed his position, and took up this object with
+a sigh. It was in a paper. He unrolled it. A long, lingering, colossal
+sigh followed, and his heart broke. It was his brass andiron knob!
+
+This final feather broke the camel's back.
+
+
+
+CHAPTER XI
+
+CLOSE upon the hour of noon the whole village was suddenly electrified
+with the ghastly news. No need of the as yet undreamed-of telegraph;
+the tale flew from man to man, from group to group, from house to
+house, with little less than telegraphic speed. Of course the
+schoolmaster gave holiday for that afternoon; the town would have
+thought strangely of him if he had not.
+
+A gory knife had been found close to the murdered man, and it had been
+recognized by somebody as belonging to Muff Potter--so the story ran.
+And it was said that a belated citizen had come upon Potter washing
+himself in the "branch" about one or two o'clock in the morning, and
+that Potter had at once sneaked off--suspicious circumstances,
+especially the washing which was not a habit with Potter. It was also
+said that the town had been ransacked for this "murderer" (the public
+are not slow in the matter of sifting evidence and arriving at a
+verdict), but that he could not be found. Horsemen had departed down
+all the roads in every direction, and the Sheriff "was confident" that
+he would be captured before night.
+
+All the town was drifting toward the graveyard. Tom's heartbreak
+vanished and he joined the procession, not because he would not a
+thousand times rather go anywhere else, but because an awful,
+unaccountable fascination drew him on. Arrived at the dreadful place,
+he wormed his small body through the crowd and saw the dismal
+spectacle. It seemed to him an age since he was there before. Somebody
+pinched his arm. He turned, and his eyes met Huckleberry's. Then both
+looked elsewhere at once, and wondered if anybody had noticed anything
+in their mutual glance. But everybody was talking, and intent upon the
+grisly spectacle before them.
+
+"Poor fellow!" "Poor young fellow!" "This ought to be a lesson to
+grave robbers!" "Muff Potter'll hang for this if they catch him!" This
+was the drift of remark; and the minister said, "It was a judgment; His
+hand is here."
+
+Now Tom shivered from head to heel; for his eye fell upon the stolid
+face of Injun Joe. At this moment the crowd began to sway and struggle,
+and voices shouted, "It's him! it's him! he's coming himself!"
+
+"Who? Who?" from twenty voices.
+
+"Muff Potter!"
+
+"Hallo, he's stopped!--Look out, he's turning! Don't let him get away!"
+
+People in the branches of the trees over Tom's head said he wasn't
+trying to get away--he only looked doubtful and perplexed.
+
+"Infernal impudence!" said a bystander; "wanted to come and take a
+quiet look at his work, I reckon--didn't expect any company."
+
+The crowd fell apart, now, and the Sheriff came through,
+ostentatiously leading Potter by the arm. The poor fellow's face was
+haggard, and his eyes showed the fear that was upon him. When he stood
+before the murdered man, he shook as with a palsy, and he put his face
+in his hands and burst into tears.
+
+"I didn't do it, friends," he sobbed; "'pon my word and honor I never
+done it."
+
+"Who's accused you?" shouted a voice.
+
+This shot seemed to carry home. Potter lifted his face and looked
+around him with a pathetic hopelessness in his eyes. He saw Injun Joe,
+and exclaimed:
+
+"Oh, Injun Joe, you promised me you'd never--"
+
+"Is that your knife?" and it was thrust before him by the Sheriff.
+
+Potter would have fallen if they had not caught him and eased him to
+the ground. Then he said:
+
+"Something told me 't if I didn't come back and get--" He shuddered;
+then waved his nerveless hand with a vanquished gesture and said, "Tell
+'em, Joe, tell 'em--it ain't any use any more."
+
+Then Huckleberry and Tom stood dumb and staring, and heard the
+stony-hearted liar reel off his serene statement, they expecting every
+moment that the clear sky would deliver God's lightnings upon his head,
+and wondering to see how long the stroke was delayed. And when he had
+finished and still stood alive and whole, their wavering impulse to
+break their oath and save the poor betrayed prisoner's life faded and
+vanished away, for plainly this miscreant had sold himself to Satan and
+it would be fatal to meddle with the property of such a power as that.
+
+"Why didn't you leave? What did you want to come here for?" somebody
+said.
+
+"I couldn't help it--I couldn't help it," Potter moaned. "I wanted to
+run away, but I couldn't seem to come anywhere but here." And he fell
+to sobbing again.
+
+Injun Joe repeated his statement, just as calmly, a few minutes
+afterward on the inquest, under oath; and the boys, seeing that the
+lightnings were still withheld, were confirmed in their belief that Joe
+had sold himself to the devil. He was now become, to them, the most
+balefully interesting object they had ever looked upon, and they could
+not take their fascinated eyes from his face.
+
+They inwardly resolved to watch him nights, when opportunity should
+offer, in the hope of getting a glimpse of his dread master.
+
+Injun Joe helped to raise the body of the murdered man and put it in a
+wagon for removal; and it was whispered through the shuddering crowd
+that the wound bled a little! The boys thought that this happy
+circumstance would turn suspicion in the right direction; but they were
+disappointed, for more than one villager remarked:
+
+"It was within three feet of Muff Potter when it done it."
+
+Tom's fearful secret and gnawing conscience disturbed his sleep for as
+much as a week after this; and at breakfast one morning Sid said:
+
+"Tom, you pitch around and talk in your sleep so much that you keep me
+awake half the time."
+
+Tom blanched and dropped his eyes.
+
+"It's a bad sign," said Aunt Polly, gravely. "What you got on your
+mind, Tom?"
+
+"Nothing. Nothing 't I know of." But the boy's hand shook so that he
+spilled his coffee.
+
+"And you do talk such stuff," Sid said. "Last night you said, 'It's
+blood, it's blood, that's what it is!' You said that over and over. And
+you said, 'Don't torment me so--I'll tell!' Tell WHAT? What is it
+you'll tell?"
+
+Everything was swimming before Tom. There is no telling what might
+have happened, now, but luckily the concern passed out of Aunt Polly's
+face and she came to Tom's relief without knowing it. She said:
+
+"Sho! It's that dreadful murder. I dream about it most every night
+myself. Sometimes I dream it's me that done it."
+
+Mary said she had been affected much the same way. Sid seemed
+satisfied. Tom got out of the presence as quick as he plausibly could,
+and after that he complained of toothache for a week, and tied up his
+jaws every night. He never knew that Sid lay nightly watching, and
+frequently slipped the bandage free and then leaned on his elbow
+listening a good while at a time, and afterward slipped the bandage
+back to its place again. Tom's distress of mind wore off gradually and
+the toothache grew irksome and was discarded. If Sid really managed to
+make anything out of Tom's disjointed mutterings, he kept it to himself.
+
+It seemed to Tom that his schoolmates never would get done holding
+inquests on dead cats, and thus keeping his trouble present to his
+mind. Sid noticed that Tom never was coroner at one of these inquiries,
+though it had been his habit to take the lead in all new enterprises;
+he noticed, too, that Tom never acted as a witness--and that was
+strange; and Sid did not overlook the fact that Tom even showed a
+marked aversion to these inquests, and always avoided them when he
+could. Sid marvelled, but said nothing. However, even inquests went out
+of vogue at last, and ceased to torture Tom's conscience.
+
+Every day or two, during this time of sorrow, Tom watched his
+opportunity and went to the little grated jail-window and smuggled such
+small comforts through to the "murderer" as he could get hold of. The
+jail was a trifling little brick den that stood in a marsh at the edge
+of the village, and no guards were afforded for it; indeed, it was
+seldom occupied. These offerings greatly helped to ease Tom's
+conscience.
+
+The villagers had a strong desire to tar-and-feather Injun Joe and
+ride him on a rail, for body-snatching, but so formidable was his
+character that nobody could be found who was willing to take the lead
+in the matter, so it was dropped. He had been careful to begin both of
+his inquest-statements with the fight, without confessing the
+grave-robbery that preceded it; therefore it was deemed wisest not
+to try the case in the courts at present.
+
+
+
+CHAPTER XII
+
+ONE of the reasons why Tom's mind had drifted away from its secret
+troubles was, that it had found a new and weighty matter to interest
+itself about. Becky Thatcher had stopped coming to school. Tom had
+struggled with his pride a few days, and tried to "whistle her down the
+wind," but failed. He began to find himself hanging around her father's
+house, nights, and feeling very miserable. She was ill. What if she
+should die! There was distraction in the thought. He no longer took an
+interest in war, nor even in piracy. The charm of life was gone; there
+was nothing but dreariness left. He put his hoop away, and his bat;
+there was no joy in them any more. His aunt was concerned. She began to
+try all manner of remedies on him. She was one of those people who are
+infatuated with patent medicines and all new-fangled methods of
+producing health or mending it. She was an inveterate experimenter in
+these things. When something fresh in this line came out she was in a
+fever, right away, to try it; not on herself, for she was never ailing,
+but on anybody else that came handy. She was a subscriber for all the
+"Health" periodicals and phrenological frauds; and the solemn ignorance
+they were inflated with was breath to her nostrils. All the "rot" they
+contained about ventilation, and how to go to bed, and how to get up,
+and what to eat, and what to drink, and how much exercise to take, and
+what frame of mind to keep one's self in, and what sort of clothing to
+wear, was all gospel to her, and she never observed that her
+health-journals of the current month customarily upset everything they
+had recommended the month before. She was as simple-hearted and honest
+as the day was long, and so she was an easy victim. She gathered
+together her quack periodicals and her quack medicines, and thus armed
+with death, went about on her pale horse, metaphorically speaking, with
+"hell following after." But she never suspected that she was not an
+angel of healing and the balm of Gilead in disguise, to the suffering
+neighbors.
+
+The water treatment was new, now, and Tom's low condition was a
+windfall to her. She had him out at daylight every morning, stood him
+up in the woodshed and drowned him with a deluge of cold water; then
+she scrubbed him down with a towel like a file, and so brought him to;
+then she rolled him up in a wet sheet and put him away under blankets
+till she sweated his soul clean and "the yellow stains of it came
+through his pores"--as Tom said.
+
+Yet notwithstanding all this, the boy grew more and more melancholy
+and pale and dejected. She added hot baths, sitz baths, shower baths,
+and plunges. The boy remained as dismal as a hearse. She began to
+assist the water with a slim oatmeal diet and blister-plasters. She
+calculated his capacity as she would a jug's, and filled him up every
+day with quack cure-alls.
+
+Tom had become indifferent to persecution by this time. This phase
+filled the old lady's heart with consternation. This indifference must
+be broken up at any cost. Now she heard of Pain-killer for the first
+time. She ordered a lot at once. She tasted it and was filled with
+gratitude. It was simply fire in a liquid form. She dropped the water
+treatment and everything else, and pinned her faith to Pain-killer. She
+gave Tom a teaspoonful and watched with the deepest anxiety for the
+result. Her troubles were instantly at rest, her soul at peace again;
+for the "indifference" was broken up. The boy could not have shown a
+wilder, heartier interest, if she had built a fire under him.
+
+Tom felt that it was time to wake up; this sort of life might be
+romantic enough, in his blighted condition, but it was getting to have
+too little sentiment and too much distracting variety about it. So he
+thought over various plans for relief, and finally hit pon that of
+professing to be fond of Pain-killer. He asked for it so often that he
+became a nuisance, and his aunt ended by telling him to help himself
+and quit bothering her. If it had been Sid, she would have had no
+misgivings to alloy her delight; but since it was Tom, she watched the
+bottle clandestinely. She found that the medicine did really diminish,
+but it did not occur to her that the boy was mending the health of a
+crack in the sitting-room floor with it.
+
+One day Tom was in the act of dosing the crack when his aunt's yellow
+cat came along, purring, eying the teaspoon avariciously, and begging
+for a taste. Tom said:
+
+"Don't ask for it unless you want it, Peter."
+
+But Peter signified that he did want it.
+
+"You better make sure."
+
+Peter was sure.
+
+"Now you've asked for it, and I'll give it to you, because there ain't
+anything mean about me; but if you find you don't like it, you mustn't
+blame anybody but your own self."
+
+Peter was agreeable. So Tom pried his mouth open and poured down the
+Pain-killer. Peter sprang a couple of yards in the air, and then
+delivered a war-whoop and set off round and round the room, banging
+against furniture, upsetting flower-pots, and making general havoc.
+Next he rose on his hind feet and pranced around, in a frenzy of
+enjoyment, with his head over his shoulder and his voice proclaiming
+his unappeasable happiness. Then he went tearing around the house again
+spreading chaos and destruction in his path. Aunt Polly entered in time
+to see him throw a few double summersets, deliver a final mighty
+hurrah, and sail through the open window, carrying the rest of the
+flower-pots with him. The old lady stood petrified with astonishment,
+peering over her glasses; Tom lay on the floor expiring with laughter.
+
+"Tom, what on earth ails that cat?"
+
+"I don't know, aunt," gasped the boy.
+
+"Why, I never see anything like it. What did make him act so?"
+
+"Deed I don't know, Aunt Polly; cats always act so when they're having
+a good time."
+
+"They do, do they?" There was something in the tone that made Tom
+apprehensive.
+
+"Yes'm. That is, I believe they do."
+
+"You DO?"
+
+"Yes'm."
+
+The old lady was bending down, Tom watching, with interest emphasized
+by anxiety. Too late he divined her "drift." The handle of the telltale
+teaspoon was visible under the bed-valance. Aunt Polly took it, held it
+up. Tom winced, and dropped his eyes. Aunt Polly raised him by the
+usual handle--his ear--and cracked his head soundly with her thimble.
+
+"Now, sir, what did you want to treat that poor dumb beast so, for?"
+
+"I done it out of pity for him--because he hadn't any aunt."
+
+"Hadn't any aunt!--you numskull. What has that got to do with it?"
+
+"Heaps. Because if he'd had one she'd a burnt him out herself! She'd a
+roasted his bowels out of him 'thout any more feeling than if he was a
+human!"
+
+Aunt Polly felt a sudden pang of remorse. This was putting the thing
+in a new light; what was cruelty to a cat MIGHT be cruelty to a boy,
+too. She began to soften; she felt sorry. Her eyes watered a little,
+and she put her hand on Tom's head and said gently:
+
+"I was meaning for the best, Tom. And, Tom, it DID do you good."
+
+Tom looked up in her face with just a perceptible twinkle peeping
+through his gravity.
+
+"I know you was meaning for the best, aunty, and so was I with Peter.
+It done HIM good, too. I never see him get around so since--"
+
+"Oh, go 'long with you, Tom, before you aggravate me again. And you
+try and see if you can't be a good boy, for once, and you needn't take
+any more medicine."
+
+Tom reached school ahead of time. It was noticed that this strange
+thing had been occurring every day latterly. And now, as usual of late,
+he hung about the gate of the schoolyard instead of playing with his
+comrades. He was sick, he said, and he looked it. He tried to seem to
+be looking everywhere but whither he really was looking--down the road.
+Presently Jeff Thatcher hove in sight, and Tom's face lighted; he gazed
+a moment, and then turned sorrowfully away. When Jeff arrived, Tom
+accosted him; and "led up" warily to opportunities for remark about
+Becky, but the giddy lad never could see the bait. Tom watched and
+watched, hoping whenever a frisking frock came in sight, and hating the
+owner of it as soon as he saw she was not the right one. At last frocks
+ceased to appear, and he dropped hopelessly into the dumps; he entered
+the empty schoolhouse and sat down to suffer. Then one more frock
+passed in at the gate, and Tom's heart gave a great bound. The next
+instant he was out, and "going on" like an Indian; yelling, laughing,
+chasing boys, jumping over the fence at risk of life and limb, throwing
+handsprings, standing on his head--doing all the heroic things he could
+conceive of, and keeping a furtive eye out, all the while, to see if
+Becky Thatcher was noticing. But she seemed to be unconscious of it
+all; she never looked. Could it be possible that she was not aware that
+he was there? He carried his exploits to her immediate vicinity; came
+war-whooping around, snatched a boy's cap, hurled it to the roof of the
+schoolhouse, broke through a group of boys, tumbling them in every
+direction, and fell sprawling, himself, under Becky's nose, almost
+upsetting her--and she turned, with her nose in the air, and he heard
+her say: "Mf! some people think they're mighty smart--always showing
+off!"
+
+Tom's cheeks burned. He gathered himself up and sneaked off, crushed
+and crestfallen.
+
+
+
+CHAPTER XIII
+
+TOM'S mind was made up now. He was gloomy and desperate. He was a
+forsaken, friendless boy, he said; nobody loved him; when they found
+out what they had driven him to, perhaps they would be sorry; he had
+tried to do right and get along, but they would not let him; since
+nothing would do them but to be rid of him, let it be so; and let them
+blame HIM for the consequences--why shouldn't they? What right had the
+friendless to complain? Yes, they had forced him to it at last: he
+would lead a life of crime. There was no choice.
+
+By this time he was far down Meadow Lane, and the bell for school to
+"take up" tinkled faintly upon his ear. He sobbed, now, to think he
+should never, never hear that old familiar sound any more--it was very
+hard, but it was forced on him; since he was driven out into the cold
+world, he must submit--but he forgave them. Then the sobs came thick
+and fast.
+
+Just at this point he met his soul's sworn comrade, Joe Harper
+--hard-eyed, and with evidently a great and dismal purpose in his heart.
+Plainly here were "two souls with but a single thought." Tom, wiping
+his eyes with his sleeve, began to blubber out something about a
+resolution to escape from hard usage and lack of sympathy at home by
+roaming abroad into the great world never to return; and ended by
+hoping that Joe would not forget him.
+
+But it transpired that this was a request which Joe had just been
+going to make of Tom, and had come to hunt him up for that purpose. His
+mother had whipped him for drinking some cream which he had never
+tasted and knew nothing about; it was plain that she was tired of him
+and wished him to go; if she felt that way, there was nothing for him
+to do but succumb; he hoped she would be happy, and never regret having
+driven her poor boy out into the unfeeling world to suffer and die.
+
+As the two boys walked sorrowing along, they made a new compact to
+stand by each other and be brothers and never separate till death
+relieved them of their troubles. Then they began to lay their plans.
+Joe was for being a hermit, and living on crusts in a remote cave, and
+dying, some time, of cold and want and grief; but after listening to
+Tom, he conceded that there were some conspicuous advantages about a
+life of crime, and so he consented to be a pirate.
+
+Three miles below St. Petersburg, at a point where the Mississippi
+River was a trifle over a mile wide, there was a long, narrow, wooded
+island, with a shallow bar at the head of it, and this offered well as
+a rendezvous. It was not inhabited; it lay far over toward the further
+shore, abreast a dense and almost wholly unpeopled forest. So Jackson's
+Island was chosen. Who were to be the subjects of their piracies was a
+matter that did not occur to them. Then they hunted up Huckleberry
+Finn, and he joined them promptly, for all careers were one to him; he
+was indifferent. They presently separated to meet at a lonely spot on
+the river-bank two miles above the village at the favorite hour--which
+was midnight. There was a small log raft there which they meant to
+capture. Each would bring hooks and lines, and such provision as he
+could steal in the most dark and mysterious way--as became outlaws. And
+before the afternoon was done, they had all managed to enjoy the sweet
+glory of spreading the fact that pretty soon the town would "hear
+something." All who got this vague hint were cautioned to "be mum and
+wait."
+
+About midnight Tom arrived with a boiled ham and a few trifles,
+and stopped in a dense undergrowth on a small bluff overlooking the
+meeting-place. It was starlight, and very still. The mighty river lay
+like an ocean at rest. Tom listened a moment, but no sound disturbed the
+quiet. Then he gave a low, distinct whistle. It was answered from under
+the bluff. Tom whistled twice more; these signals were answered in the
+same way. Then a guarded voice said:
+
+"Who goes there?"
+
+"Tom Sawyer, the Black Avenger of the Spanish Main. Name your names."
+
+"Huck Finn the Red-Handed, and Joe Harper the Terror of the Seas." Tom
+had furnished these titles, from his favorite literature.
+
+"'Tis well. Give the countersign."
+
+Two hoarse whispers delivered the same awful word simultaneously to
+the brooding night:
+
+"BLOOD!"
+
+Then Tom tumbled his ham over the bluff and let himself down after it,
+tearing both skin and clothes to some extent in the effort. There was
+an easy, comfortable path along the shore under the bluff, but it
+lacked the advantages of difficulty and danger so valued by a pirate.
+
+The Terror of the Seas had brought a side of bacon, and had about worn
+himself out with getting it there. Finn the Red-Handed had stolen a
+skillet and a quantity of half-cured leaf tobacco, and had also brought
+a few corn-cobs to make pipes with. But none of the pirates smoked or
+"chewed" but himself. The Black Avenger of the Spanish Main said it
+would never do to start without some fire. That was a wise thought;
+matches were hardly known there in that day. They saw a fire
+smouldering upon a great raft a hundred yards above, and they went
+stealthily thither and helped themselves to a chunk. They made an
+imposing adventure of it, saying, "Hist!" every now and then, and
+suddenly halting with finger on lip; moving with hands on imaginary
+dagger-hilts; and giving orders in dismal whispers that if "the foe"
+stirred, to "let him have it to the hilt," because "dead men tell no
+tales." They knew well enough that the raftsmen were all down at the
+village laying in stores or having a spree, but still that was no
+excuse for their conducting this thing in an unpiratical way.
+
+They shoved off, presently, Tom in command, Huck at the after oar and
+Joe at the forward. Tom stood amidships, gloomy-browed, and with folded
+arms, and gave his orders in a low, stern whisper:
+
+"Luff, and bring her to the wind!"
+
+"Aye-aye, sir!"
+
+"Steady, steady-y-y-y!"
+
+"Steady it is, sir!"
+
+"Let her go off a point!"
+
+"Point it is, sir!"
+
+As the boys steadily and monotonously drove the raft toward mid-stream
+it was no doubt understood that these orders were given only for
+"style," and were not intended to mean anything in particular.
+
+"What sail's she carrying?"
+
+"Courses, tops'ls, and flying-jib, sir."
+
+"Send the r'yals up! Lay out aloft, there, half a dozen of ye
+--foretopmaststuns'l! Lively, now!"
+
+"Aye-aye, sir!"
+
+"Shake out that maintogalans'l! Sheets and braces! NOW my hearties!"
+
+"Aye-aye, sir!"
+
+"Hellum-a-lee--hard a port! Stand by to meet her when she comes! Port,
+port! NOW, men! With a will! Stead-y-y-y!"
+
+"Steady it is, sir!"
+
+The raft drew beyond the middle of the river; the boys pointed her
+head right, and then lay on their oars. The river was not high, so
+there was not more than a two or three mile current. Hardly a word was
+said during the next three-quarters of an hour. Now the raft was
+passing before the distant town. Two or three glimmering lights showed
+where it lay, peacefully sleeping, beyond the vague vast sweep of
+star-gemmed water, unconscious of the tremendous event that was happening.
+The Black Avenger stood still with folded arms, "looking his last" upon
+the scene of his former joys and his later sufferings, and wishing
+"she" could see him now, abroad on the wild sea, facing peril and death
+with dauntless heart, going to his doom with a grim smile on his lips.
+It was but a small strain on his imagination to remove Jackson's Island
+beyond eyeshot of the village, and so he "looked his last" with a
+broken and satisfied heart. The other pirates were looking their last,
+too; and they all looked so long that they came near letting the
+current drift them out of the range of the island. But they discovered
+the danger in time, and made shift to avert it. About two o'clock in
+the morning the raft grounded on the bar two hundred yards above the
+head of the island, and they waded back and forth until they had landed
+their freight. Part of the little raft's belongings consisted of an old
+sail, and this they spread over a nook in the bushes for a tent to
+shelter their provisions; but they themselves would sleep in the open
+air in good weather, as became outlaws.
+
+They built a fire against the side of a great log twenty or thirty
+steps within the sombre depths of the forest, and then cooked some
+bacon in the frying-pan for supper, and used up half of the corn "pone"
+stock they had brought. It seemed glorious sport to be feasting in that
+wild, free way in the virgin forest of an unexplored and uninhabited
+island, far from the haunts of men, and they said they never would
+return to civilization. The climbing fire lit up their faces and threw
+its ruddy glare upon the pillared tree-trunks of their forest temple,
+and upon the varnished foliage and festooning vines.
+
+When the last crisp slice of bacon was gone, and the last allowance of
+corn pone devoured, the boys stretched themselves out on the grass,
+filled with contentment. They could have found a cooler place, but they
+would not deny themselves such a romantic feature as the roasting
+camp-fire.
+
+"AIN'T it gay?" said Joe.
+
+"It's NUTS!" said Tom. "What would the boys say if they could see us?"
+
+"Say? Well, they'd just die to be here--hey, Hucky!"
+
+"I reckon so," said Huckleberry; "anyways, I'm suited. I don't want
+nothing better'n this. I don't ever get enough to eat, gen'ally--and
+here they can't come and pick at a feller and bullyrag him so."
+
+"It's just the life for me," said Tom. "You don't have to get up,
+mornings, and you don't have to go to school, and wash, and all that
+blame foolishness. You see a pirate don't have to do ANYTHING, Joe,
+when he's ashore, but a hermit HE has to be praying considerable, and
+then he don't have any fun, anyway, all by himself that way."
+
+"Oh yes, that's so," said Joe, "but I hadn't thought much about it,
+you know. I'd a good deal rather be a pirate, now that I've tried it."
+
+"You see," said Tom, "people don't go much on hermits, nowadays, like
+they used to in old times, but a pirate's always respected. And a
+hermit's got to sleep on the hardest place he can find, and put
+sackcloth and ashes on his head, and stand out in the rain, and--"
+
+"What does he put sackcloth and ashes on his head for?" inquired Huck.
+
+"I dono. But they've GOT to do it. Hermits always do. You'd have to do
+that if you was a hermit."
+
+"Dern'd if I would," said Huck.
+
+"Well, what would you do?"
+
+"I dono. But I wouldn't do that."
+
+"Why, Huck, you'd HAVE to. How'd you get around it?"
+
+"Why, I just wouldn't stand it. I'd run away."
+
+"Run away! Well, you WOULD be a nice old slouch of a hermit. You'd be
+a disgrace."
+
+The Red-Handed made no response, being better employed. He had
+finished gouging out a cob, and now he fitted a weed stem to it, loaded
+it with tobacco, and was pressing a coal to the charge and blowing a
+cloud of fragrant smoke--he was in the full bloom of luxurious
+contentment. The other pirates envied him this majestic vice, and
+secretly resolved to acquire it shortly. Presently Huck said:
+
+"What does pirates have to do?"
+
+Tom said:
+
+"Oh, they have just a bully time--take ships and burn them, and get
+the money and bury it in awful places in their island where there's
+ghosts and things to watch it, and kill everybody in the ships--make
+'em walk a plank."
+
+"And they carry the women to the island," said Joe; "they don't kill
+the women."
+
+"No," assented Tom, "they don't kill the women--they're too noble. And
+the women's always beautiful, too.
+
+"And don't they wear the bulliest clothes! Oh no! All gold and silver
+and di'monds," said Joe, with enthusiasm.
+
+"Who?" said Huck.
+
+"Why, the pirates."
+
+Huck scanned his own clothing forlornly.
+
+"I reckon I ain't dressed fitten for a pirate," said he, with a
+regretful pathos in his voice; "but I ain't got none but these."
+
+But the other boys told him the fine clothes would come fast enough,
+after they should have begun their adventures. They made him understand
+that his poor rags would do to begin with, though it was customary for
+wealthy pirates to start with a proper wardrobe.
+
+Gradually their talk died out and drowsiness began to steal upon the
+eyelids of the little waifs. The pipe dropped from the fingers of the
+Red-Handed, and he slept the sleep of the conscience-free and the
+weary. The Terror of the Seas and the Black Avenger of the Spanish Main
+had more difficulty in getting to sleep. They said their prayers
+inwardly, and lying down, since there was nobody there with authority
+to make them kneel and recite aloud; in truth, they had a mind not to
+say them at all, but they were afraid to proceed to such lengths as
+that, lest they might call down a sudden and special thunderbolt from
+heaven. Then at once they reached and hovered upon the imminent verge
+of sleep--but an intruder came, now, that would not "down." It was
+conscience. They began to feel a vague fear that they had been doing
+wrong to run away; and next they thought of the stolen meat, and then
+the real torture came. They tried to argue it away by reminding
+conscience that they had purloined sweetmeats and apples scores of
+times; but conscience was not to be appeased by such thin
+plausibilities; it seemed to them, in the end, that there was no
+getting around the stubborn fact that taking sweetmeats was only
+"hooking," while taking bacon and hams and such valuables was plain
+simple stealing--and there was a command against that in the Bible. So
+they inwardly resolved that so long as they remained in the business,
+their piracies should not again be sullied with the crime of stealing.
+Then conscience granted a truce, and these curiously inconsistent
+pirates fell peacefully to sleep.
+
+
+
+CHAPTER XIV
+
+WHEN Tom awoke in the morning, he wondered where he was. He sat up and
+rubbed his eyes and looked around. Then he comprehended. It was the
+cool gray dawn, and there was a delicious sense of repose and peace in
+the deep pervading calm and silence of the woods. Not a leaf stirred;
+not a sound obtruded upon great Nature's meditation. Beaded dewdrops
+stood upon the leaves and grasses. A white layer of ashes covered the
+fire, and a thin blue breath of smoke rose straight into the air. Joe
+and Huck still slept.
+
+Now, far away in the woods a bird called; another answered; presently
+the hammering of a woodpecker was heard. Gradually the cool dim gray of
+the morning whitened, and as gradually sounds multiplied and life
+manifested itself. The marvel of Nature shaking off sleep and going to
+work unfolded itself to the musing boy. A little green worm came
+crawling over a dewy leaf, lifting two-thirds of his body into the air
+from time to time and "sniffing around," then proceeding again--for he
+was measuring, Tom said; and when the worm approached him, of its own
+accord, he sat as still as a stone, with his hopes rising and falling,
+by turns, as the creature still came toward him or seemed inclined to
+go elsewhere; and when at last it considered a painful moment with its
+curved body in the air and then came decisively down upon Tom's leg and
+began a journey over him, his whole heart was glad--for that meant that
+he was going to have a new suit of clothes--without the shadow of a
+doubt a gaudy piratical uniform. Now a procession of ants appeared,
+from nowhere in particular, and went about their labors; one struggled
+manfully by with a dead spider five times as big as itself in its arms,
+and lugged it straight up a tree-trunk. A brown spotted lady-bug
+climbed the dizzy height of a grass blade, and Tom bent down close to
+it and said, "Lady-bug, lady-bug, fly away home, your house is on fire,
+your children's alone," and she took wing and went off to see about it
+--which did not surprise the boy, for he knew of old that this insect was
+credulous about conflagrations, and he had practised upon its
+simplicity more than once. A tumblebug came next, heaving sturdily at
+its ball, and Tom touched the creature, to see it shut its legs against
+its body and pretend to be dead. The birds were fairly rioting by this
+time. A catbird, the Northern mocker, lit in a tree over Tom's head,
+and trilled out her imitations of her neighbors in a rapture of
+enjoyment; then a shrill jay swept down, a flash of blue flame, and
+stopped on a twig almost within the boy's reach, cocked his head to one
+side and eyed the strangers with a consuming curiosity; a gray squirrel
+and a big fellow of the "fox" kind came skurrying along, sitting up at
+intervals to inspect and chatter at the boys, for the wild things had
+probably never seen a human being before and scarcely knew whether to
+be afraid or not. All Nature was wide awake and stirring, now; long
+lances of sunlight pierced down through the dense foliage far and near,
+and a few butterflies came fluttering upon the scene.
+
+Tom stirred up the other pirates and they all clattered away with a
+shout, and in a minute or two were stripped and chasing after and
+tumbling over each other in the shallow limpid water of the white
+sandbar. They felt no longing for the little village sleeping in the
+distance beyond the majestic waste of water. A vagrant current or a
+slight rise in the river had carried off their raft, but this only
+gratified them, since its going was something like burning the bridge
+between them and civilization.
+
+They came back to camp wonderfully refreshed, glad-hearted, and
+ravenous; and they soon had the camp-fire blazing up again. Huck found
+a spring of clear cold water close by, and the boys made cups of broad
+oak or hickory leaves, and felt that water, sweetened with such a
+wildwood charm as that, would be a good enough substitute for coffee.
+While Joe was slicing bacon for breakfast, Tom and Huck asked him to
+hold on a minute; they stepped to a promising nook in the river-bank
+and threw in their lines; almost immediately they had reward. Joe had
+not had time to get impatient before they were back again with some
+handsome bass, a couple of sun-perch and a small catfish--provisions
+enough for quite a family. They fried the fish with the bacon, and were
+astonished; for no fish had ever seemed so delicious before. They did
+not know that the quicker a fresh-water fish is on the fire after he is
+caught the better he is; and they reflected little upon what a sauce
+open-air sleeping, open-air exercise, bathing, and a large ingredient
+of hunger make, too.
+
+They lay around in the shade, after breakfast, while Huck had a smoke,
+and then went off through the woods on an exploring expedition. They
+tramped gayly along, over decaying logs, through tangled underbrush,
+among solemn monarchs of the forest, hung from their crowns to the
+ground with a drooping regalia of grape-vines. Now and then they came
+upon snug nooks carpeted with grass and jeweled with flowers.
+
+They found plenty of things to be delighted with, but nothing to be
+astonished at. They discovered that the island was about three miles
+long and a quarter of a mile wide, and that the shore it lay closest to
+was only separated from it by a narrow channel hardly two hundred yards
+wide. They took a swim about every hour, so it was close upon the
+middle of the afternoon when they got back to camp. They were too
+hungry to stop to fish, but they fared sumptuously upon cold ham, and
+then threw themselves down in the shade to talk. But the talk soon
+began to drag, and then died. The stillness, the solemnity that brooded
+in the woods, and the sense of loneliness, began to tell upon the
+spirits of the boys. They fell to thinking. A sort of undefined longing
+crept upon them. This took dim shape, presently--it was budding
+homesickness. Even Finn the Red-Handed was dreaming of his doorsteps
+and empty hogsheads. But they were all ashamed of their weakness, and
+none was brave enough to speak his thought.
+
+For some time, now, the boys had been dully conscious of a peculiar
+sound in the distance, just as one sometimes is of the ticking of a
+clock which he takes no distinct note of. But now this mysterious sound
+became more pronounced, and forced a recognition. The boys started,
+glanced at each other, and then each assumed a listening attitude.
+There was a long silence, profound and unbroken; then a deep, sullen
+boom came floating down out of the distance.
+
+"What is it!" exclaimed Joe, under his breath.
+
+"I wonder," said Tom in a whisper.
+
+"'Tain't thunder," said Huckleberry, in an awed tone, "becuz thunder--"
+
+"Hark!" said Tom. "Listen--don't talk."
+
+They waited a time that seemed an age, and then the same muffled boom
+troubled the solemn hush.
+
+"Let's go and see."
+
+They sprang to their feet and hurried to the shore toward the town.
+They parted the bushes on the bank and peered out over the water. The
+little steam ferryboat was about a mile below the village, drifting
+with the current. Her broad deck seemed crowded with people. There were
+a great many skiffs rowing about or floating with the stream in the
+neighborhood of the ferryboat, but the boys could not determine what
+the men in them were doing. Presently a great jet of white smoke burst
+from the ferryboat's side, and as it expanded and rose in a lazy cloud,
+that same dull throb of sound was borne to the listeners again.
+
+"I know now!" exclaimed Tom; "somebody's drownded!"
+
+"That's it!" said Huck; "they done that last summer, when Bill Turner
+got drownded; they shoot a cannon over the water, and that makes him
+come up to the top. Yes, and they take loaves of bread and put
+quicksilver in 'em and set 'em afloat, and wherever there's anybody
+that's drownded, they'll float right there and stop."
+
+"Yes, I've heard about that," said Joe. "I wonder what makes the bread
+do that."
+
+"Oh, it ain't the bread, so much," said Tom; "I reckon it's mostly
+what they SAY over it before they start it out."
+
+"But they don't say anything over it," said Huck. "I've seen 'em and
+they don't."
+
+"Well, that's funny," said Tom. "But maybe they say it to themselves.
+Of COURSE they do. Anybody might know that."
+
+The other boys agreed that there was reason in what Tom said, because
+an ignorant lump of bread, uninstructed by an incantation, could not be
+expected to act very intelligently when set upon an errand of such
+gravity.
+
+"By jings, I wish I was over there, now," said Joe.
+
+"I do too" said Huck "I'd give heaps to know who it is."
+
+The boys still listened and watched. Presently a revealing thought
+flashed through Tom's mind, and he exclaimed:
+
+"Boys, I know who's drownded--it's us!"
+
+They felt like heroes in an instant. Here was a gorgeous triumph; they
+were missed; they were mourned; hearts were breaking on their account;
+tears were being shed; accusing memories of unkindness to these poor
+lost lads were rising up, and unavailing regrets and remorse were being
+indulged; and best of all, the departed were the talk of the whole
+town, and the envy of all the boys, as far as this dazzling notoriety
+was concerned. This was fine. It was worth while to be a pirate, after
+all.
+
+As twilight drew on, the ferryboat went back to her accustomed
+business and the skiffs disappeared. The pirates returned to camp. They
+were jubilant with vanity over their new grandeur and the illustrious
+trouble they were making. They caught fish, cooked supper and ate it,
+and then fell to guessing at what the village was thinking and saying
+about them; and the pictures they drew of the public distress on their
+account were gratifying to look upon--from their point of view. But
+when the shadows of night closed them in, they gradually ceased to
+talk, and sat gazing into the fire, with their minds evidently
+wandering elsewhere. The excitement was gone, now, and Tom and Joe
+could not keep back thoughts of certain persons at home who were not
+enjoying this fine frolic as much as they were. Misgivings came; they
+grew troubled and unhappy; a sigh or two escaped, unawares. By and by
+Joe timidly ventured upon a roundabout "feeler" as to how the others
+might look upon a return to civilization--not right now, but--
+
+Tom withered him with derision! Huck, being uncommitted as yet, joined
+in with Tom, and the waverer quickly "explained," and was glad to get
+out of the scrape with as little taint of chicken-hearted homesickness
+clinging to his garments as he could. Mutiny was effectually laid to
+rest for the moment.
+
+As the night deepened, Huck began to nod, and presently to snore. Joe
+followed next. Tom lay upon his elbow motionless, for some time,
+watching the two intently. At last he got up cautiously, on his knees,
+and went searching among the grass and the flickering reflections flung
+by the camp-fire. He picked up and inspected several large
+semi-cylinders of the thin white bark of a sycamore, and finally chose
+two which seemed to suit him. Then he knelt by the fire and painfully
+wrote something upon each of these with his "red keel"; one he rolled up
+and put in his jacket pocket, and the other he put in Joe's hat and
+removed it to a little distance from the owner. And he also put into the
+hat certain schoolboy treasures of almost inestimable value--among them
+a lump of chalk, an India-rubber ball, three fishhooks, and one of that
+kind of marbles known as a "sure 'nough crystal." Then he tiptoed his
+way cautiously among the trees till he felt that he was out of hearing,
+and straightway broke into a keen run in the direction of the sandbar.
+
+
+
+CHAPTER XV
+
+A FEW minutes later Tom was in the shoal water of the bar, wading
+toward the Illinois shore. Before the depth reached his middle he was
+half-way over; the current would permit no more wading, now, so he
+struck out confidently to swim the remaining hundred yards. He swam
+quartering upstream, but still was swept downward rather faster than he
+had expected. However, he reached the shore finally, and drifted along
+till he found a low place and drew himself out. He put his hand on his
+jacket pocket, found his piece of bark safe, and then struck through
+the woods, following the shore, with streaming garments. Shortly before
+ten o'clock he came out into an open place opposite the village, and
+saw the ferryboat lying in the shadow of the trees and the high bank.
+Everything was quiet under the blinking stars. He crept down the bank,
+watching with all his eyes, slipped into the water, swam three or four
+strokes and climbed into the skiff that did "yawl" duty at the boat's
+stern. He laid himself down under the thwarts and waited, panting.
+
+Presently the cracked bell tapped and a voice gave the order to "cast
+off." A minute or two later the skiff's head was standing high up,
+against the boat's swell, and the voyage was begun. Tom felt happy in
+his success, for he knew it was the boat's last trip for the night. At
+the end of a long twelve or fifteen minutes the wheels stopped, and Tom
+slipped overboard and swam ashore in the dusk, landing fifty yards
+downstream, out of danger of possible stragglers.
+
+He flew along unfrequented alleys, and shortly found himself at his
+aunt's back fence. He climbed over, approached the "ell," and looked in
+at the sitting-room window, for a light was burning there. There sat
+Aunt Polly, Sid, Mary, and Joe Harper's mother, grouped together,
+talking. They were by the bed, and the bed was between them and the
+door. Tom went to the door and began to softly lift the latch; then he
+pressed gently and the door yielded a crack; he continued pushing
+cautiously, and quaking every time it creaked, till he judged he might
+squeeze through on his knees; so he put his head through and began,
+warily.
+
+"What makes the candle blow so?" said Aunt Polly. Tom hurried up.
+"Why, that door's open, I believe. Why, of course it is. No end of
+strange things now. Go 'long and shut it, Sid."
+
+Tom disappeared under the bed just in time. He lay and "breathed"
+himself for a time, and then crept to where he could almost touch his
+aunt's foot.
+
+"But as I was saying," said Aunt Polly, "he warn't BAD, so to say
+--only mischEEvous. Only just giddy, and harum-scarum, you know. He
+warn't any more responsible than a colt. HE never meant any harm, and
+he was the best-hearted boy that ever was"--and she began to cry.
+
+"It was just so with my Joe--always full of his devilment, and up to
+every kind of mischief, but he was just as unselfish and kind as he
+could be--and laws bless me, to think I went and whipped him for taking
+that cream, never once recollecting that I throwed it out myself
+because it was sour, and I never to see him again in this world, never,
+never, never, poor abused boy!" And Mrs. Harper sobbed as if her heart
+would break.
+
+"I hope Tom's better off where he is," said Sid, "but if he'd been
+better in some ways--"
+
+"SID!" Tom felt the glare of the old lady's eye, though he could not
+see it. "Not a word against my Tom, now that he's gone! God'll take
+care of HIM--never you trouble YOURself, sir! Oh, Mrs. Harper, I don't
+know how to give him up! I don't know how to give him up! He was such a
+comfort to me, although he tormented my old heart out of me, 'most."
+
+"The Lord giveth and the Lord hath taken away--Blessed be the name of
+the Lord! But it's so hard--Oh, it's so hard! Only last Saturday my
+Joe busted a firecracker right under my nose and I knocked him
+sprawling. Little did I know then, how soon--Oh, if it was to do over
+again I'd hug him and bless him for it."
+
+"Yes, yes, yes, I know just how you feel, Mrs. Harper, I know just
+exactly how you feel. No longer ago than yesterday noon, my Tom took
+and filled the cat full of Pain-killer, and I did think the cretur
+would tear the house down. And God forgive me, I cracked Tom's head
+with my thimble, poor boy, poor dead boy. But he's out of all his
+troubles now. And the last words I ever heard him say was to reproach--"
+
+But this memory was too much for the old lady, and she broke entirely
+down. Tom was snuffling, now, himself--and more in pity of himself than
+anybody else. He could hear Mary crying, and putting in a kindly word
+for him from time to time. He began to have a nobler opinion of himself
+than ever before. Still, he was sufficiently touched by his aunt's
+grief to long to rush out from under the bed and overwhelm her with
+joy--and the theatrical gorgeousness of the thing appealed strongly to
+his nature, too, but he resisted and lay still.
+
+He went on listening, and gathered by odds and ends that it was
+conjectured at first that the boys had got drowned while taking a swim;
+then the small raft had been missed; next, certain boys said the
+missing lads had promised that the village should "hear something"
+soon; the wise-heads had "put this and that together" and decided that
+the lads had gone off on that raft and would turn up at the next town
+below, presently; but toward noon the raft had been found, lodged
+against the Missouri shore some five or six miles below the village
+--and then hope perished; they must be drowned, else hunger would have
+driven them home by nightfall if not sooner. It was believed that the
+search for the bodies had been a fruitless effort merely because the
+drowning must have occurred in mid-channel, since the boys, being good
+swimmers, would otherwise have escaped to shore. This was Wednesday
+night. If the bodies continued missing until Sunday, all hope would be
+given over, and the funerals would be preached on that morning. Tom
+shuddered.
+
+Mrs. Harper gave a sobbing good-night and turned to go. Then with a
+mutual impulse the two bereaved women flung themselves into each
+other's arms and had a good, consoling cry, and then parted. Aunt Polly
+was tender far beyond her wont, in her good-night to Sid and Mary. Sid
+snuffled a bit and Mary went off crying with all her heart.
+
+Aunt Polly knelt down and prayed for Tom so touchingly, so
+appealingly, and with such measureless love in her words and her old
+trembling voice, that he was weltering in tears again, long before she
+was through.
+
+He had to keep still long after she went to bed, for she kept making
+broken-hearted ejaculations from time to time, tossing unrestfully, and
+turning over. But at last she was still, only moaning a little in her
+sleep. Now the boy stole out, rose gradually by the bedside, shaded the
+candle-light with his hand, and stood regarding her. His heart was full
+of pity for her. He took out his sycamore scroll and placed it by the
+candle. But something occurred to him, and he lingered considering. His
+face lighted with a happy solution of his thought; he put the bark
+hastily in his pocket. Then he bent over and kissed the faded lips, and
+straightway made his stealthy exit, latching the door behind him.
+
+He threaded his way back to the ferry landing, found nobody at large
+there, and walked boldly on board the boat, for he knew she was
+tenantless except that there was a watchman, who always turned in and
+slept like a graven image. He untied the skiff at the stern, slipped
+into it, and was soon rowing cautiously upstream. When he had pulled a
+mile above the village, he started quartering across and bent himself
+stoutly to his work. He hit the landing on the other side neatly, for
+this was a familiar bit of work to him. He was moved to capture the
+skiff, arguing that it might be considered a ship and therefore
+legitimate prey for a pirate, but he knew a thorough search would be
+made for it and that might end in revelations. So he stepped ashore and
+entered the woods.
+
+He sat down and took a long rest, torturing himself meanwhile to keep
+awake, and then started warily down the home-stretch. The night was far
+spent. It was broad daylight before he found himself fairly abreast the
+island bar. He rested again until the sun was well up and gilding the
+great river with its splendor, and then he plunged into the stream. A
+little later he paused, dripping, upon the threshold of the camp, and
+heard Joe say:
+
+"No, Tom's true-blue, Huck, and he'll come back. He won't desert. He
+knows that would be a disgrace to a pirate, and Tom's too proud for
+that sort of thing. He's up to something or other. Now I wonder what?"
+
+"Well, the things is ours, anyway, ain't they?"
+
+"Pretty near, but not yet, Huck. The writing says they are if he ain't
+back here to breakfast."
+
+"Which he is!" exclaimed Tom, with fine dramatic effect, stepping
+grandly into camp.
+
+A sumptuous breakfast of bacon and fish was shortly provided, and as
+the boys set to work upon it, Tom recounted (and adorned) his
+adventures. They were a vain and boastful company of heroes when the
+tale was done. Then Tom hid himself away in a shady nook to sleep till
+noon, and the other pirates got ready to fish and explore.
+
+
+
+CHAPTER XVI
+
+AFTER dinner all the gang turned out to hunt for turtle eggs on the
+bar. They went about poking sticks into the sand, and when they found a
+soft place they went down on their knees and dug with their hands.
+Sometimes they would take fifty or sixty eggs out of one hole. They
+were perfectly round white things a trifle smaller than an English
+walnut. They had a famous fried-egg feast that night, and another on
+Friday morning.
+
+After breakfast they went whooping and prancing out on the bar, and
+chased each other round and round, shedding clothes as they went, until
+they were naked, and then continued the frolic far away up the shoal
+water of the bar, against the stiff current, which latter tripped their
+legs from under them from time to time and greatly increased the fun.
+And now and then they stooped in a group and splashed water in each
+other's faces with their palms, gradually approaching each other, with
+averted faces to avoid the strangling sprays, and finally gripping and
+struggling till the best man ducked his neighbor, and then they all
+went under in a tangle of white legs and arms and came up blowing,
+sputtering, laughing, and gasping for breath at one and the same time.
+
+When they were well exhausted, they would run out and sprawl on the
+dry, hot sand, and lie there and cover themselves up with it, and by
+and by break for the water again and go through the original
+performance once more. Finally it occurred to them that their naked
+skin represented flesh-colored "tights" very fairly; so they drew a
+ring in the sand and had a circus--with three clowns in it, for none
+would yield this proudest post to his neighbor.
+
+Next they got their marbles and played "knucks" and "ring-taw" and
+"keeps" till that amusement grew stale. Then Joe and Huck had another
+swim, but Tom would not venture, because he found that in kicking off
+his trousers he had kicked his string of rattlesnake rattles off his
+ankle, and he wondered how he had escaped cramp so long without the
+protection of this mysterious charm. He did not venture again until he
+had found it, and by that time the other boys were tired and ready to
+rest. They gradually wandered apart, dropped into the "dumps," and fell
+to gazing longingly across the wide river to where the village lay
+drowsing in the sun. Tom found himself writing "BECKY" in the sand with
+his big toe; he scratched it out, and was angry with himself for his
+weakness. But he wrote it again, nevertheless; he could not help it. He
+erased it once more and then took himself out of temptation by driving
+the other boys together and joining them.
+
+But Joe's spirits had gone down almost beyond resurrection. He was so
+homesick that he could hardly endure the misery of it. The tears lay
+very near the surface. Huck was melancholy, too. Tom was downhearted,
+but tried hard not to show it. He had a secret which he was not ready
+to tell, yet, but if this mutinous depression was not broken up soon,
+he would have to bring it out. He said, with a great show of
+cheerfulness:
+
+"I bet there's been pirates on this island before, boys. We'll explore
+it again. They've hid treasures here somewhere. How'd you feel to light
+on a rotten chest full of gold and silver--hey?"
+
+But it roused only faint enthusiasm, which faded out, with no reply.
+Tom tried one or two other seductions; but they failed, too. It was
+discouraging work. Joe sat poking up the sand with a stick and looking
+very gloomy. Finally he said:
+
+"Oh, boys, let's give it up. I want to go home. It's so lonesome."
+
+"Oh no, Joe, you'll feel better by and by," said Tom. "Just think of
+the fishing that's here."
+
+"I don't care for fishing. I want to go home."
+
+"But, Joe, there ain't such another swimming-place anywhere."
+
+"Swimming's no good. I don't seem to care for it, somehow, when there
+ain't anybody to say I sha'n't go in. I mean to go home."
+
+"Oh, shucks! Baby! You want to see your mother, I reckon."
+
+"Yes, I DO want to see my mother--and you would, too, if you had one.
+I ain't any more baby than you are." And Joe snuffled a little.
+
+"Well, we'll let the cry-baby go home to his mother, won't we, Huck?
+Poor thing--does it want to see its mother? And so it shall. You like
+it here, don't you, Huck? We'll stay, won't we?"
+
+Huck said, "Y-e-s"--without any heart in it.
+
+"I'll never speak to you again as long as I live," said Joe, rising.
+"There now!" And he moved moodily away and began to dress himself.
+
+"Who cares!" said Tom. "Nobody wants you to. Go 'long home and get
+laughed at. Oh, you're a nice pirate. Huck and me ain't cry-babies.
+We'll stay, won't we, Huck? Let him go if he wants to. I reckon we can
+get along without him, per'aps."
+
+But Tom was uneasy, nevertheless, and was alarmed to see Joe go
+sullenly on with his dressing. And then it was discomforting to see
+Huck eying Joe's preparations so wistfully, and keeping up such an
+ominous silence. Presently, without a parting word, Joe began to wade
+off toward the Illinois shore. Tom's heart began to sink. He glanced at
+Huck. Huck could not bear the look, and dropped his eyes. Then he said:
+
+"I want to go, too, Tom. It was getting so lonesome anyway, and now
+it'll be worse. Let's us go, too, Tom."
+
+"I won't! You can all go, if you want to. I mean to stay."
+
+"Tom, I better go."
+
+"Well, go 'long--who's hendering you."
+
+Huck began to pick up his scattered clothes. He said:
+
+"Tom, I wisht you'd come, too. Now you think it over. We'll wait for
+you when we get to shore."
+
+"Well, you'll wait a blame long time, that's all."
+
+Huck started sorrowfully away, and Tom stood looking after him, with a
+strong desire tugging at his heart to yield his pride and go along too.
+He hoped the boys would stop, but they still waded slowly on. It
+suddenly dawned on Tom that it was become very lonely and still. He
+made one final struggle with his pride, and then darted after his
+comrades, yelling:
+
+"Wait! Wait! I want to tell you something!"
+
+They presently stopped and turned around. When he got to where they
+were, he began unfolding his secret, and they listened moodily till at
+last they saw the "point" he was driving at, and then they set up a
+war-whoop of applause and said it was "splendid!" and said if he had
+told them at first, they wouldn't have started away. He made a plausible
+excuse; but his real reason had been the fear that not even the secret
+would keep them with him any very great length of time, and so he had
+meant to hold it in reserve as a last seduction.
+
+The lads came gayly back and went at their sports again with a will,
+chattering all the time about Tom's stupendous plan and admiring the
+genius of it. After a dainty egg and fish dinner, Tom said he wanted to
+learn to smoke, now. Joe caught at the idea and said he would like to
+try, too. So Huck made pipes and filled them. These novices had never
+smoked anything before but cigars made of grape-vine, and they "bit"
+the tongue, and were not considered manly anyway.
+
+Now they stretched themselves out on their elbows and began to puff,
+charily, and with slender confidence. The smoke had an unpleasant
+taste, and they gagged a little, but Tom said:
+
+"Why, it's just as easy! If I'd a knowed this was all, I'd a learnt
+long ago."
+
+"So would I," said Joe. "It's just nothing."
+
+"Why, many a time I've looked at people smoking, and thought well I
+wish I could do that; but I never thought I could," said Tom.
+
+"That's just the way with me, hain't it, Huck? You've heard me talk
+just that way--haven't you, Huck? I'll leave it to Huck if I haven't."
+
+"Yes--heaps of times," said Huck.
+
+"Well, I have too," said Tom; "oh, hundreds of times. Once down by the
+slaughter-house. Don't you remember, Huck? Bob Tanner was there, and
+Johnny Miller, and Jeff Thatcher, when I said it. Don't you remember,
+Huck, 'bout me saying that?"
+
+"Yes, that's so," said Huck. "That was the day after I lost a white
+alley. No, 'twas the day before."
+
+"There--I told you so," said Tom. "Huck recollects it."
+
+"I bleeve I could smoke this pipe all day," said Joe. "I don't feel
+sick."
+
+"Neither do I," said Tom. "I could smoke it all day. But I bet you
+Jeff Thatcher couldn't."
+
+"Jeff Thatcher! Why, he'd keel over just with two draws. Just let him
+try it once. HE'D see!"
+
+"I bet he would. And Johnny Miller--I wish could see Johnny Miller
+tackle it once."
+
+"Oh, don't I!" said Joe. "Why, I bet you Johnny Miller couldn't any
+more do this than nothing. Just one little snifter would fetch HIM."
+
+"'Deed it would, Joe. Say--I wish the boys could see us now."
+
+"So do I."
+
+"Say--boys, don't say anything about it, and some time when they're
+around, I'll come up to you and say, 'Joe, got a pipe? I want a smoke.'
+And you'll say, kind of careless like, as if it warn't anything, you'll
+say, 'Yes, I got my OLD pipe, and another one, but my tobacker ain't
+very good.' And I'll say, 'Oh, that's all right, if it's STRONG
+enough.' And then you'll out with the pipes, and we'll light up just as
+ca'm, and then just see 'em look!"
+
+"By jings, that'll be gay, Tom! I wish it was NOW!"
+
+"So do I! And when we tell 'em we learned when we was off pirating,
+won't they wish they'd been along?"
+
+"Oh, I reckon not! I'll just BET they will!"
+
+So the talk ran on. But presently it began to flag a trifle, and grow
+disjointed. The silences widened; the expectoration marvellously
+increased. Every pore inside the boys' cheeks became a spouting
+fountain; they could scarcely bail out the cellars under their tongues
+fast enough to prevent an inundation; little overflowings down their
+throats occurred in spite of all they could do, and sudden retchings
+followed every time. Both boys were looking very pale and miserable,
+now. Joe's pipe dropped from his nerveless fingers. Tom's followed.
+Both fountains were going furiously and both pumps bailing with might
+and main. Joe said feebly:
+
+"I've lost my knife. I reckon I better go and find it."
+
+Tom said, with quivering lips and halting utterance:
+
+"I'll help you. You go over that way and I'll hunt around by the
+spring. No, you needn't come, Huck--we can find it."
+
+So Huck sat down again, and waited an hour. Then he found it lonesome,
+and went to find his comrades. They were wide apart in the woods, both
+very pale, both fast asleep. But something informed him that if they
+had had any trouble they had got rid of it.
+
+They were not talkative at supper that night. They had a humble look,
+and when Huck prepared his pipe after the meal and was going to prepare
+theirs, they said no, they were not feeling very well--something they
+ate at dinner had disagreed with them.
+
+About midnight Joe awoke, and called the boys. There was a brooding
+oppressiveness in the air that seemed to bode something. The boys
+huddled themselves together and sought the friendly companionship of
+the fire, though the dull dead heat of the breathless atmosphere was
+stifling. They sat still, intent and waiting. The solemn hush
+continued. Beyond the light of the fire everything was swallowed up in
+the blackness of darkness. Presently there came a quivering glow that
+vaguely revealed the foliage for a moment and then vanished. By and by
+another came, a little stronger. Then another. Then a faint moan came
+sighing through the branches of the forest and the boys felt a fleeting
+breath upon their cheeks, and shuddered with the fancy that the Spirit
+of the Night had gone by. There was a pause. Now a weird flash turned
+night into day and showed every little grass-blade, separate and
+distinct, that grew about their feet. And it showed three white,
+startled faces, too. A deep peal of thunder went rolling and tumbling
+down the heavens and lost itself in sullen rumblings in the distance. A
+sweep of chilly air passed by, rustling all the leaves and snowing the
+flaky ashes broadcast about the fire. Another fierce glare lit up the
+forest and an instant crash followed that seemed to rend the tree-tops
+right over the boys' heads. They clung together in terror, in the thick
+gloom that followed. A few big rain-drops fell pattering upon the
+leaves.
+
+"Quick! boys, go for the tent!" exclaimed Tom.
+
+They sprang away, stumbling over roots and among vines in the dark, no
+two plunging in the same direction. A furious blast roared through the
+trees, making everything sing as it went. One blinding flash after
+another came, and peal on peal of deafening thunder. And now a
+drenching rain poured down and the rising hurricane drove it in sheets
+along the ground. The boys cried out to each other, but the roaring
+wind and the booming thunder-blasts drowned their voices utterly.
+However, one by one they straggled in at last and took shelter under
+the tent, cold, scared, and streaming with water; but to have company
+in misery seemed something to be grateful for. They could not talk, the
+old sail flapped so furiously, even if the other noises would have
+allowed them. The tempest rose higher and higher, and presently the
+sail tore loose from its fastenings and went winging away on the blast.
+The boys seized each others' hands and fled, with many tumblings and
+bruises, to the shelter of a great oak that stood upon the river-bank.
+Now the battle was at its highest. Under the ceaseless conflagration of
+lightning that flamed in the skies, everything below stood out in
+clean-cut and shadowless distinctness: the bending trees, the billowy
+river, white with foam, the driving spray of spume-flakes, the dim
+outlines of the high bluffs on the other side, glimpsed through the
+drifting cloud-rack and the slanting veil of rain. Every little while
+some giant tree yielded the fight and fell crashing through the younger
+growth; and the unflagging thunder-peals came now in ear-splitting
+explosive bursts, keen and sharp, and unspeakably appalling. The storm
+culminated in one matchless effort that seemed likely to tear the island
+to pieces, burn it up, drown it to the tree-tops, blow it away, and
+deafen every creature in it, all at one and the same moment. It was a
+wild night for homeless young heads to be out in.
+
+But at last the battle was done, and the forces retired with weaker
+and weaker threatenings and grumblings, and peace resumed her sway. The
+boys went back to camp, a good deal awed; but they found there was
+still something to be thankful for, because the great sycamore, the
+shelter of their beds, was a ruin, now, blasted by the lightnings, and
+they were not under it when the catastrophe happened.
+
+Everything in camp was drenched, the camp-fire as well; for they were
+but heedless lads, like their generation, and had made no provision
+against rain. Here was matter for dismay, for they were soaked through
+and chilled. They were eloquent in their distress; but they presently
+discovered that the fire had eaten so far up under the great log it had
+been built against (where it curved upward and separated itself from
+the ground), that a handbreadth or so of it had escaped wetting; so
+they patiently wrought until, with shreds and bark gathered from the
+under sides of sheltered logs, they coaxed the fire to burn again. Then
+they piled on great dead boughs till they had a roaring furnace, and
+were glad-hearted once more. They dried their boiled ham and had a
+feast, and after that they sat by the fire and expanded and glorified
+their midnight adventure until morning, for there was not a dry spot to
+sleep on, anywhere around.
+
+As the sun began to steal in upon the boys, drowsiness came over them,
+and they went out on the sandbar and lay down to sleep. They got
+scorched out by and by, and drearily set about getting breakfast. After
+the meal they felt rusty, and stiff-jointed, and a little homesick once
+more. Tom saw the signs, and fell to cheering up the pirates as well as
+he could. But they cared nothing for marbles, or circus, or swimming,
+or anything. He reminded them of the imposing secret, and raised a ray
+of cheer. While it lasted, he got them interested in a new device. This
+was to knock off being pirates, for a while, and be Indians for a
+change. They were attracted by this idea; so it was not long before
+they were stripped, and striped from head to heel with black mud, like
+so many zebras--all of them chiefs, of course--and then they went
+tearing through the woods to attack an English settlement.
+
+By and by they separated into three hostile tribes, and darted upon
+each other from ambush with dreadful war-whoops, and killed and scalped
+each other by thousands. It was a gory day. Consequently it was an
+extremely satisfactory one.
+
+They assembled in camp toward supper-time, hungry and happy; but now a
+difficulty arose--hostile Indians could not break the bread of
+hospitality together without first making peace, and this was a simple
+impossibility without smoking a pipe of peace. There was no other
+process that ever they had heard of. Two of the savages almost wished
+they had remained pirates. However, there was no other way; so with
+such show of cheerfulness as they could muster they called for the pipe
+and took their whiff as it passed, in due form.
+
+And behold, they were glad they had gone into savagery, for they had
+gained something; they found that they could now smoke a little without
+having to go and hunt for a lost knife; they did not get sick enough to
+be seriously uncomfortable. They were not likely to fool away this high
+promise for lack of effort. No, they practised cautiously, after
+supper, with right fair success, and so they spent a jubilant evening.
+They were prouder and happier in their new acquirement than they would
+have been in the scalping and skinning of the Six Nations. We will
+leave them to smoke and chatter and brag, since we have no further use
+for them at present.
+
+
+
+CHAPTER XVII
+
+BUT there was no hilarity in the little town that same tranquil
+Saturday afternoon. The Harpers, and Aunt Polly's family, were being
+put into mourning, with great grief and many tears. An unusual quiet
+possessed the village, although it was ordinarily quiet enough, in all
+conscience. The villagers conducted their concerns with an absent air,
+and talked little; but they sighed often. The Saturday holiday seemed a
+burden to the children. They had no heart in their sports, and
+gradually gave them up.
+
+In the afternoon Becky Thatcher found herself moping about the
+deserted schoolhouse yard, and feeling very melancholy. But she found
+nothing there to comfort her. She soliloquized:
+
+"Oh, if I only had a brass andiron-knob again! But I haven't got
+anything now to remember him by." And she choked back a little sob.
+
+Presently she stopped, and said to herself:
+
+"It was right here. Oh, if it was to do over again, I wouldn't say
+that--I wouldn't say it for the whole world. But he's gone now; I'll
+never, never, never see him any more."
+
+This thought broke her down, and she wandered away, with tears rolling
+down her cheeks. Then quite a group of boys and girls--playmates of
+Tom's and Joe's--came by, and stood looking over the paling fence and
+talking in reverent tones of how Tom did so-and-so the last time they
+saw him, and how Joe said this and that small trifle (pregnant with
+awful prophecy, as they could easily see now!)--and each speaker
+pointed out the exact spot where the lost lads stood at the time, and
+then added something like "and I was a-standing just so--just as I am
+now, and as if you was him--I was as close as that--and he smiled, just
+this way--and then something seemed to go all over me, like--awful, you
+know--and I never thought what it meant, of course, but I can see now!"
+
+Then there was a dispute about who saw the dead boys last in life, and
+many claimed that dismal distinction, and offered evidences, more or
+less tampered with by the witness; and when it was ultimately decided
+who DID see the departed last, and exchanged the last words with them,
+the lucky parties took upon themselves a sort of sacred importance, and
+were gaped at and envied by all the rest. One poor chap, who had no
+other grandeur to offer, said with tolerably manifest pride in the
+remembrance:
+
+"Well, Tom Sawyer he licked me once."
+
+But that bid for glory was a failure. Most of the boys could say that,
+and so that cheapened the distinction too much. The group loitered
+away, still recalling memories of the lost heroes, in awed voices.
+
+When the Sunday-school hour was finished, the next morning, the bell
+began to toll, instead of ringing in the usual way. It was a very still
+Sabbath, and the mournful sound seemed in keeping with the musing hush
+that lay upon nature. The villagers began to gather, loitering a moment
+in the vestibule to converse in whispers about the sad event. But there
+was no whispering in the house; only the funereal rustling of dresses
+as the women gathered to their seats disturbed the silence there. None
+could remember when the little church had been so full before. There
+was finally a waiting pause, an expectant dumbness, and then Aunt Polly
+entered, followed by Sid and Mary, and they by the Harper family, all
+in deep black, and the whole congregation, the old minister as well,
+rose reverently and stood until the mourners were seated in the front
+pew. There was another communing silence, broken at intervals by
+muffled sobs, and then the minister spread his hands abroad and prayed.
+A moving hymn was sung, and the text followed: "I am the Resurrection
+and the Life."
+
+As the service proceeded, the clergyman drew such pictures of the
+graces, the winning ways, and the rare promise of the lost lads that
+every soul there, thinking he recognized these pictures, felt a pang in
+remembering that he had persistently blinded himself to them always
+before, and had as persistently seen only faults and flaws in the poor
+boys. The minister related many a touching incident in the lives of the
+departed, too, which illustrated their sweet, generous natures, and the
+people could easily see, now, how noble and beautiful those episodes
+were, and remembered with grief that at the time they occurred they had
+seemed rank rascalities, well deserving of the cowhide. The
+congregation became more and more moved, as the pathetic tale went on,
+till at last the whole company broke down and joined the weeping
+mourners in a chorus of anguished sobs, the preacher himself giving way
+to his feelings, and crying in the pulpit.
+
+There was a rustle in the gallery, which nobody noticed; a moment
+later the church door creaked; the minister raised his streaming eyes
+above his handkerchief, and stood transfixed! First one and then
+another pair of eyes followed the minister's, and then almost with one
+impulse the congregation rose and stared while the three dead boys came
+marching up the aisle, Tom in the lead, Joe next, and Huck, a ruin of
+drooping rags, sneaking sheepishly in the rear! They had been hid in
+the unused gallery listening to their own funeral sermon!
+
+Aunt Polly, Mary, and the Harpers threw themselves upon their restored
+ones, smothered them with kisses and poured out thanksgivings, while
+poor Huck stood abashed and uncomfortable, not knowing exactly what to
+do or where to hide from so many unwelcoming eyes. He wavered, and
+started to slink away, but Tom seized him and said:
+
+"Aunt Polly, it ain't fair. Somebody's got to be glad to see Huck."
+
+"And so they shall. I'm glad to see him, poor motherless thing!" And
+the loving attentions Aunt Polly lavished upon him were the one thing
+capable of making him more uncomfortable than he was before.
+
+Suddenly the minister shouted at the top of his voice: "Praise God
+from whom all blessings flow--SING!--and put your hearts in it!"
+
+And they did. Old Hundred swelled up with a triumphant burst, and
+while it shook the rafters Tom Sawyer the Pirate looked around upon the
+envying juveniles about him and confessed in his heart that this was
+the proudest moment of his life.
+
+As the "sold" congregation trooped out they said they would almost be
+willing to be made ridiculous again to hear Old Hundred sung like that
+once more.
+
+Tom got more cuffs and kisses that day--according to Aunt Polly's
+varying moods--than he had earned before in a year; and he hardly knew
+which expressed the most gratefulness to God and affection for himself.
+
+
+
+CHAPTER XVIII
+
+THAT was Tom's great secret--the scheme to return home with his
+brother pirates and attend their own funerals. They had paddled over to
+the Missouri shore on a log, at dusk on Saturday, landing five or six
+miles below the village; they had slept in the woods at the edge of the
+town till nearly daylight, and had then crept through back lanes and
+alleys and finished their sleep in the gallery of the church among a
+chaos of invalided benches.
+
+At breakfast, Monday morning, Aunt Polly and Mary were very loving to
+Tom, and very attentive to his wants. There was an unusual amount of
+talk. In the course of it Aunt Polly said:
+
+"Well, I don't say it wasn't a fine joke, Tom, to keep everybody
+suffering 'most a week so you boys had a good time, but it is a pity
+you could be so hard-hearted as to let me suffer so. If you could come
+over on a log to go to your funeral, you could have come over and give
+me a hint some way that you warn't dead, but only run off."
+
+"Yes, you could have done that, Tom," said Mary; "and I believe you
+would if you had thought of it."
+
+"Would you, Tom?" said Aunt Polly, her face lighting wistfully. "Say,
+now, would you, if you'd thought of it?"
+
+"I--well, I don't know. 'Twould 'a' spoiled everything."
+
+"Tom, I hoped you loved me that much," said Aunt Polly, with a grieved
+tone that discomforted the boy. "It would have been something if you'd
+cared enough to THINK of it, even if you didn't DO it."
+
+"Now, auntie, that ain't any harm," pleaded Mary; "it's only Tom's
+giddy way--he is always in such a rush that he never thinks of
+anything."
+
+"More's the pity. Sid would have thought. And Sid would have come and
+DONE it, too. Tom, you'll look back, some day, when it's too late, and
+wish you'd cared a little more for me when it would have cost you so
+little."
+
+"Now, auntie, you know I do care for you," said Tom.
+
+"I'd know it better if you acted more like it."
+
+"I wish now I'd thought," said Tom, with a repentant tone; "but I
+dreamt about you, anyway. That's something, ain't it?"
+
+"It ain't much--a cat does that much--but it's better than nothing.
+What did you dream?"
+
+"Why, Wednesday night I dreamt that you was sitting over there by the
+bed, and Sid was sitting by the woodbox, and Mary next to him."
+
+"Well, so we did. So we always do. I'm glad your dreams could take
+even that much trouble about us."
+
+"And I dreamt that Joe Harper's mother was here."
+
+"Why, she was here! Did you dream any more?"
+
+"Oh, lots. But it's so dim, now."
+
+"Well, try to recollect--can't you?"
+
+"Somehow it seems to me that the wind--the wind blowed the--the--"
+
+"Try harder, Tom! The wind did blow something. Come!"
+
+Tom pressed his fingers on his forehead an anxious minute, and then
+said:
+
+"I've got it now! I've got it now! It blowed the candle!"
+
+"Mercy on us! Go on, Tom--go on!"
+
+"And it seems to me that you said, 'Why, I believe that that door--'"
+
+"Go ON, Tom!"
+
+"Just let me study a moment--just a moment. Oh, yes--you said you
+believed the door was open."
+
+"As I'm sitting here, I did! Didn't I, Mary! Go on!"
+
+"And then--and then--well I won't be certain, but it seems like as if
+you made Sid go and--and--"
+
+"Well? Well? What did I make him do, Tom? What did I make him do?"
+
+"You made him--you--Oh, you made him shut it."
+
+"Well, for the land's sake! I never heard the beat of that in all my
+days! Don't tell ME there ain't anything in dreams, any more. Sereny
+Harper shall know of this before I'm an hour older. I'd like to see her
+get around THIS with her rubbage 'bout superstition. Go on, Tom!"
+
+"Oh, it's all getting just as bright as day, now. Next you said I
+warn't BAD, only mischeevous and harum-scarum, and not any more
+responsible than--than--I think it was a colt, or something."
+
+"And so it was! Well, goodness gracious! Go on, Tom!"
+
+"And then you began to cry."
+
+"So I did. So I did. Not the first time, neither. And then--"
+
+"Then Mrs. Harper she began to cry, and said Joe was just the same,
+and she wished she hadn't whipped him for taking cream when she'd
+throwed it out her own self--"
+
+"Tom! The sperrit was upon you! You was a prophesying--that's what you
+was doing! Land alive, go on, Tom!"
+
+"Then Sid he said--he said--"
+
+"I don't think I said anything," said Sid.
+
+"Yes you did, Sid," said Mary.
+
+"Shut your heads and let Tom go on! What did he say, Tom?"
+
+"He said--I THINK he said he hoped I was better off where I was gone
+to, but if I'd been better sometimes--"
+
+"THERE, d'you hear that! It was his very words!"
+
+"And you shut him up sharp."
+
+"I lay I did! There must 'a' been an angel there. There WAS an angel
+there, somewheres!"
+
+"And Mrs. Harper told about Joe scaring her with a firecracker, and
+you told about Peter and the Painkiller--"
+
+"Just as true as I live!"
+
+"And then there was a whole lot of talk 'bout dragging the river for
+us, and 'bout having the funeral Sunday, and then you and old Miss
+Harper hugged and cried, and she went."
+
+"It happened just so! It happened just so, as sure as I'm a-sitting in
+these very tracks. Tom, you couldn't told it more like if you'd 'a'
+seen it! And then what? Go on, Tom!"
+
+"Then I thought you prayed for me--and I could see you and hear every
+word you said. And you went to bed, and I was so sorry that I took and
+wrote on a piece of sycamore bark, 'We ain't dead--we are only off
+being pirates,' and put it on the table by the candle; and then you
+looked so good, laying there asleep, that I thought I went and leaned
+over and kissed you on the lips."
+
+"Did you, Tom, DID you! I just forgive you everything for that!" And
+she seized the boy in a crushing embrace that made him feel like the
+guiltiest of villains.
+
+"It was very kind, even though it was only a--dream," Sid soliloquized
+just audibly.
+
+"Shut up, Sid! A body does just the same in a dream as he'd do if he
+was awake. Here's a big Milum apple I've been saving for you, Tom, if
+you was ever found again--now go 'long to school. I'm thankful to the
+good God and Father of us all I've got you back, that's long-suffering
+and merciful to them that believe on Him and keep His word, though
+goodness knows I'm unworthy of it, but if only the worthy ones got His
+blessings and had His hand to help them over the rough places, there's
+few enough would smile here or ever enter into His rest when the long
+night comes. Go 'long Sid, Mary, Tom--take yourselves off--you've
+hendered me long enough."
+
+The children left for school, and the old lady to call on Mrs. Harper
+and vanquish her realism with Tom's marvellous dream. Sid had better
+judgment than to utter the thought that was in his mind as he left the
+house. It was this: "Pretty thin--as long a dream as that, without any
+mistakes in it!"
+
+What a hero Tom was become, now! He did not go skipping and prancing,
+but moved with a dignified swagger as became a pirate who felt that the
+public eye was on him. And indeed it was; he tried not to seem to see
+the looks or hear the remarks as he passed along, but they were food
+and drink to him. Smaller boys than himself flocked at his heels, as
+proud to be seen with him, and tolerated by him, as if he had been the
+drummer at the head of a procession or the elephant leading a menagerie
+into town. Boys of his own size pretended not to know he had been away
+at all; but they were consuming with envy, nevertheless. They would
+have given anything to have that swarthy suntanned skin of his, and his
+glittering notoriety; and Tom would not have parted with either for a
+circus.
+
+At school the children made so much of him and of Joe, and delivered
+such eloquent admiration from their eyes, that the two heroes were not
+long in becoming insufferably "stuck-up." They began to tell their
+adventures to hungry listeners--but they only began; it was not a thing
+likely to have an end, with imaginations like theirs to furnish
+material. And finally, when they got out their pipes and went serenely
+puffing around, the very summit of glory was reached.
+
+Tom decided that he could be independent of Becky Thatcher now. Glory
+was sufficient. He would live for glory. Now that he was distinguished,
+maybe she would be wanting to "make up." Well, let her--she should see
+that he could be as indifferent as some other people. Presently she
+arrived. Tom pretended not to see her. He moved away and joined a group
+of boys and girls and began to talk. Soon he observed that she was
+tripping gayly back and forth with flushed face and dancing eyes,
+pretending to be busy chasing schoolmates, and screaming with laughter
+when she made a capture; but he noticed that she always made her
+captures in his vicinity, and that she seemed to cast a conscious eye
+in his direction at such times, too. It gratified all the vicious
+vanity that was in him; and so, instead of winning him, it only "set
+him up" the more and made him the more diligent to avoid betraying that
+he knew she was about. Presently she gave over skylarking, and moved
+irresolutely about, sighing once or twice and glancing furtively and
+wistfully toward Tom. Then she observed that now Tom was talking more
+particularly to Amy Lawrence than to any one else. She felt a sharp
+pang and grew disturbed and uneasy at once. She tried to go away, but
+her feet were treacherous, and carried her to the group instead. She
+said to a girl almost at Tom's elbow--with sham vivacity:
+
+"Why, Mary Austin! you bad girl, why didn't you come to Sunday-school?"
+
+"I did come--didn't you see me?"
+
+"Why, no! Did you? Where did you sit?"
+
+"I was in Miss Peters' class, where I always go. I saw YOU."
+
+"Did you? Why, it's funny I didn't see you. I wanted to tell you about
+the picnic."
+
+"Oh, that's jolly. Who's going to give it?"
+
+"My ma's going to let me have one."
+
+"Oh, goody; I hope she'll let ME come."
+
+"Well, she will. The picnic's for me. She'll let anybody come that I
+want, and I want you."
+
+"That's ever so nice. When is it going to be?"
+
+"By and by. Maybe about vacation."
+
+"Oh, won't it be fun! You going to have all the girls and boys?"
+
+"Yes, every one that's friends to me--or wants to be"; and she glanced
+ever so furtively at Tom, but he talked right along to Amy Lawrence
+about the terrible storm on the island, and how the lightning tore the
+great sycamore tree "all to flinders" while he was "standing within
+three feet of it."
+
+"Oh, may I come?" said Grace Miller.
+
+"Yes."
+
+"And me?" said Sally Rogers.
+
+"Yes."
+
+"And me, too?" said Susy Harper. "And Joe?"
+
+"Yes."
+
+And so on, with clapping of joyful hands till all the group had begged
+for invitations but Tom and Amy. Then Tom turned coolly away, still
+talking, and took Amy with him. Becky's lips trembled and the tears
+came to her eyes; she hid these signs with a forced gayety and went on
+chattering, but the life had gone out of the picnic, now, and out of
+everything else; she got away as soon as she could and hid herself and
+had what her sex call "a good cry." Then she sat moody, with wounded
+pride, till the bell rang. She roused up, now, with a vindictive cast
+in her eye, and gave her plaited tails a shake and said she knew what
+SHE'D do.
+
+At recess Tom continued his flirtation with Amy with jubilant
+self-satisfaction. And he kept drifting about to find Becky and lacerate
+her with the performance. At last he spied her, but there was a sudden
+falling of his mercury. She was sitting cosily on a little bench behind
+the schoolhouse looking at a picture-book with Alfred Temple--and so
+absorbed were they, and their heads so close together over the book,
+that they did not seem to be conscious of anything in the world besides.
+Jealousy ran red-hot through Tom's veins. He began to hate himself for
+throwing away the chance Becky had offered for a reconciliation. He
+called himself a fool, and all the hard names he could think of. He
+wanted to cry with vexation. Amy chatted happily along, as they walked,
+for her heart was singing, but Tom's tongue had lost its function. He
+did not hear what Amy was saying, and whenever she paused expectantly he
+could only stammer an awkward assent, which was as often misplaced as
+otherwise. He kept drifting to the rear of the schoolhouse, again and
+again, to sear his eyeballs with the hateful spectacle there. He could
+not help it. And it maddened him to see, as he thought he saw, that
+Becky Thatcher never once suspected that he was even in the land of the
+living. But she did see, nevertheless; and she knew she was winning her
+fight, too, and was glad to see him suffer as she had suffered.
+
+Amy's happy prattle became intolerable. Tom hinted at things he had to
+attend to; things that must be done; and time was fleeting. But in
+vain--the girl chirped on. Tom thought, "Oh, hang her, ain't I ever
+going to get rid of her?" At last he must be attending to those
+things--and she said artlessly that she would be "around" when school
+let out. And he hastened away, hating her for it.
+
+"Any other boy!" Tom thought, grating his teeth. "Any boy in the whole
+town but that Saint Louis smarty that thinks he dresses so fine and is
+aristocracy! Oh, all right, I licked you the first day you ever saw
+this town, mister, and I'll lick you again! You just wait till I catch
+you out! I'll just take and--"
+
+And he went through the motions of thrashing an imaginary boy
+--pummelling the air, and kicking and gouging. "Oh, you do, do you? You
+holler 'nough, do you? Now, then, let that learn you!" And so the
+imaginary flogging was finished to his satisfaction.
+
+Tom fled home at noon. His conscience could not endure any more of
+Amy's grateful happiness, and his jealousy could bear no more of the
+other distress. Becky resumed her picture inspections with Alfred, but
+as the minutes dragged along and no Tom came to suffer, her triumph
+began to cloud and she lost interest; gravity and absent-mindedness
+followed, and then melancholy; two or three times she pricked up her
+ear at a footstep, but it was a false hope; no Tom came. At last she
+grew entirely miserable and wished she hadn't carried it so far. When
+poor Alfred, seeing that he was losing her, he did not know how, kept
+exclaiming: "Oh, here's a jolly one! look at this!" she lost patience
+at last, and said, "Oh, don't bother me! I don't care for them!" and
+burst into tears, and got up and walked away.
+
+Alfred dropped alongside and was going to try to comfort her, but she
+said:
+
+"Go away and leave me alone, can't you! I hate you!"
+
+So the boy halted, wondering what he could have done--for she had said
+she would look at pictures all through the nooning--and she walked on,
+crying. Then Alfred went musing into the deserted schoolhouse. He was
+humiliated and angry. He easily guessed his way to the truth--the girl
+had simply made a convenience of him to vent her spite upon Tom Sawyer.
+He was far from hating Tom the less when this thought occurred to him.
+He wished there was some way to get that boy into trouble without much
+risk to himself. Tom's spelling-book fell under his eye. Here was his
+opportunity. He gratefully opened to the lesson for the afternoon and
+poured ink upon the page.
+
+Becky, glancing in at a window behind him at the moment, saw the act,
+and moved on, without discovering herself. She started homeward, now,
+intending to find Tom and tell him; Tom would be thankful and their
+troubles would be healed. Before she was half way home, however, she
+had changed her mind. The thought of Tom's treatment of her when she
+was talking about her picnic came scorching back and filled her with
+shame. She resolved to let him get whipped on the damaged
+spelling-book's account, and to hate him forever, into the bargain.
+
+
+
+CHAPTER XIX
+
+TOM arrived at home in a dreary mood, and the first thing his aunt
+said to him showed him that he had brought his sorrows to an
+unpromising market:
+
+"Tom, I've a notion to skin you alive!"
+
+"Auntie, what have I done?"
+
+"Well, you've done enough. Here I go over to Sereny Harper, like an
+old softy, expecting I'm going to make her believe all that rubbage
+about that dream, when lo and behold you she'd found out from Joe that
+you was over here and heard all the talk we had that night. Tom, I
+don't know what is to become of a boy that will act like that. It makes
+me feel so bad to think you could let me go to Sereny Harper and make
+such a fool of myself and never say a word."
+
+This was a new aspect of the thing. His smartness of the morning had
+seemed to Tom a good joke before, and very ingenious. It merely looked
+mean and shabby now. He hung his head and could not think of anything
+to say for a moment. Then he said:
+
+"Auntie, I wish I hadn't done it--but I didn't think."
+
+"Oh, child, you never think. You never think of anything but your own
+selfishness. You could think to come all the way over here from
+Jackson's Island in the night to laugh at our troubles, and you could
+think to fool me with a lie about a dream; but you couldn't ever think
+to pity us and save us from sorrow."
+
+"Auntie, I know now it was mean, but I didn't mean to be mean. I
+didn't, honest. And besides, I didn't come over here to laugh at you
+that night."
+
+"What did you come for, then?"
+
+"It was to tell you not to be uneasy about us, because we hadn't got
+drownded."
+
+"Tom, Tom, I would be the thankfullest soul in this world if I could
+believe you ever had as good a thought as that, but you know you never
+did--and I know it, Tom."
+
+"Indeed and 'deed I did, auntie--I wish I may never stir if I didn't."
+
+"Oh, Tom, don't lie--don't do it. It only makes things a hundred times
+worse."
+
+"It ain't a lie, auntie; it's the truth. I wanted to keep you from
+grieving--that was all that made me come."
+
+"I'd give the whole world to believe that--it would cover up a power
+of sins, Tom. I'd 'most be glad you'd run off and acted so bad. But it
+ain't reasonable; because, why didn't you tell me, child?"
+
+"Why, you see, when you got to talking about the funeral, I just got
+all full of the idea of our coming and hiding in the church, and I
+couldn't somehow bear to spoil it. So I just put the bark back in my
+pocket and kept mum."
+
+"What bark?"
+
+"The bark I had wrote on to tell you we'd gone pirating. I wish, now,
+you'd waked up when I kissed you--I do, honest."
+
+The hard lines in his aunt's face relaxed and a sudden tenderness
+dawned in her eyes.
+
+"DID you kiss me, Tom?"
+
+"Why, yes, I did."
+
+"Are you sure you did, Tom?"
+
+"Why, yes, I did, auntie--certain sure."
+
+"What did you kiss me for, Tom?"
+
+"Because I loved you so, and you laid there moaning and I was so sorry."
+
+The words sounded like truth. The old lady could not hide a tremor in
+her voice when she said:
+
+"Kiss me again, Tom!--and be off with you to school, now, and don't
+bother me any more."
+
+The moment he was gone, she ran to a closet and got out the ruin of a
+jacket which Tom had gone pirating in. Then she stopped, with it in her
+hand, and said to herself:
+
+"No, I don't dare. Poor boy, I reckon he's lied about it--but it's a
+blessed, blessed lie, there's such a comfort come from it. I hope the
+Lord--I KNOW the Lord will forgive him, because it was such
+goodheartedness in him to tell it. But I don't want to find out it's a
+lie. I won't look."
+
+She put the jacket away, and stood by musing a minute. Twice she put
+out her hand to take the garment again, and twice she refrained. Once
+more she ventured, and this time she fortified herself with the
+thought: "It's a good lie--it's a good lie--I won't let it grieve me."
+So she sought the jacket pocket. A moment later she was reading Tom's
+piece of bark through flowing tears and saying: "I could forgive the
+boy, now, if he'd committed a million sins!"
+
+
+
+CHAPTER XX
+
+THERE was something about Aunt Polly's manner, when she kissed Tom,
+that swept away his low spirits and made him lighthearted and happy
+again. He started to school and had the luck of coming upon Becky
+Thatcher at the head of Meadow Lane. His mood always determined his
+manner. Without a moment's hesitation he ran to her and said:
+
+"I acted mighty mean to-day, Becky, and I'm so sorry. I won't ever,
+ever do that way again, as long as ever I live--please make up, won't
+you?"
+
+The girl stopped and looked him scornfully in the face:
+
+"I'll thank you to keep yourself TO yourself, Mr. Thomas Sawyer. I'll
+never speak to you again."
+
+She tossed her head and passed on. Tom was so stunned that he had not
+even presence of mind enough to say "Who cares, Miss Smarty?" until the
+right time to say it had gone by. So he said nothing. But he was in a
+fine rage, nevertheless. He moped into the schoolyard wishing she were
+a boy, and imagining how he would trounce her if she were. He presently
+encountered her and delivered a stinging remark as he passed. She
+hurled one in return, and the angry breach was complete. It seemed to
+Becky, in her hot resentment, that she could hardly wait for school to
+"take in," she was so impatient to see Tom flogged for the injured
+spelling-book. If she had had any lingering notion of exposing Alfred
+Temple, Tom's offensive fling had driven it entirely away.
+
+Poor girl, she did not know how fast she was nearing trouble herself.
+The master, Mr. Dobbins, had reached middle age with an unsatisfied
+ambition. The darling of his desires was, to be a doctor, but poverty
+had decreed that he should be nothing higher than a village
+schoolmaster. Every day he took a mysterious book out of his desk and
+absorbed himself in it at times when no classes were reciting. He kept
+that book under lock and key. There was not an urchin in school but was
+perishing to have a glimpse of it, but the chance never came. Every boy
+and girl had a theory about the nature of that book; but no two
+theories were alike, and there was no way of getting at the facts in
+the case. Now, as Becky was passing by the desk, which stood near the
+door, she noticed that the key was in the lock! It was a precious
+moment. She glanced around; found herself alone, and the next instant
+she had the book in her hands. The title-page--Professor Somebody's
+ANATOMY--carried no information to her mind; so she began to turn the
+leaves. She came at once upon a handsomely engraved and colored
+frontispiece--a human figure, stark naked. At that moment a shadow fell
+on the page and Tom Sawyer stepped in at the door and caught a glimpse
+of the picture. Becky snatched at the book to close it, and had the
+hard luck to tear the pictured page half down the middle. She thrust
+the volume into the desk, turned the key, and burst out crying with
+shame and vexation.
+
+"Tom Sawyer, you are just as mean as you can be, to sneak up on a
+person and look at what they're looking at."
+
+"How could I know you was looking at anything?"
+
+"You ought to be ashamed of yourself, Tom Sawyer; you know you're
+going to tell on me, and oh, what shall I do, what shall I do! I'll be
+whipped, and I never was whipped in school."
+
+Then she stamped her little foot and said:
+
+"BE so mean if you want to! I know something that's going to happen.
+You just wait and you'll see! Hateful, hateful, hateful!"--and she
+flung out of the house with a new explosion of crying.
+
+Tom stood still, rather flustered by this onslaught. Presently he said
+to himself:
+
+"What a curious kind of a fool a girl is! Never been licked in school!
+Shucks! What's a licking! That's just like a girl--they're so
+thin-skinned and chicken-hearted. Well, of course I ain't going to tell
+old Dobbins on this little fool, because there's other ways of getting
+even on her, that ain't so mean; but what of it? Old Dobbins will ask
+who it was tore his book. Nobody'll answer. Then he'll do just the way
+he always does--ask first one and then t'other, and when he comes to the
+right girl he'll know it, without any telling. Girls' faces always tell
+on them. They ain't got any backbone. She'll get licked. Well, it's a
+kind of a tight place for Becky Thatcher, because there ain't any way
+out of it." Tom conned the thing a moment longer, and then added: "All
+right, though; she'd like to see me in just such a fix--let her sweat it
+out!"
+
+Tom joined the mob of skylarking scholars outside. In a few moments
+the master arrived and school "took in." Tom did not feel a strong
+interest in his studies. Every time he stole a glance at the girls'
+side of the room Becky's face troubled him. Considering all things, he
+did not want to pity her, and yet it was all he could do to help it. He
+could get up no exultation that was really worthy the name. Presently
+the spelling-book discovery was made, and Tom's mind was entirely full
+of his own matters for a while after that. Becky roused up from her
+lethargy of distress and showed good interest in the proceedings. She
+did not expect that Tom could get out of his trouble by denying that he
+spilt the ink on the book himself; and she was right. The denial only
+seemed to make the thing worse for Tom. Becky supposed she would be
+glad of that, and she tried to believe she was glad of it, but she
+found she was not certain. When the worst came to the worst, she had an
+impulse to get up and tell on Alfred Temple, but she made an effort and
+forced herself to keep still--because, said she to herself, "he'll tell
+about me tearing the picture sure. I wouldn't say a word, not to save
+his life!"
+
+Tom took his whipping and went back to his seat not at all
+broken-hearted, for he thought it was possible that he had unknowingly
+upset the ink on the spelling-book himself, in some skylarking bout--he
+had denied it for form's sake and because it was custom, and had stuck
+to the denial from principle.
+
+A whole hour drifted by, the master sat nodding in his throne, the air
+was drowsy with the hum of study. By and by, Mr. Dobbins straightened
+himself up, yawned, then unlocked his desk, and reached for his book,
+but seemed undecided whether to take it out or leave it. Most of the
+pupils glanced up languidly, but there were two among them that watched
+his movements with intent eyes. Mr. Dobbins fingered his book absently
+for a while, then took it out and settled himself in his chair to read!
+Tom shot a glance at Becky. He had seen a hunted and helpless rabbit
+look as she did, with a gun levelled at its head. Instantly he forgot
+his quarrel with her. Quick--something must be done! done in a flash,
+too! But the very imminence of the emergency paralyzed his invention.
+Good!--he had an inspiration! He would run and snatch the book, spring
+through the door and fly. But his resolution shook for one little
+instant, and the chance was lost--the master opened the volume. If Tom
+only had the wasted opportunity back again! Too late. There was no help
+for Becky now, he said. The next moment the master faced the school.
+Every eye sank under his gaze. There was that in it which smote even
+the innocent with fear. There was silence while one might count ten
+--the master was gathering his wrath. Then he spoke: "Who tore this book?"
+
+There was not a sound. One could have heard a pin drop. The stillness
+continued; the master searched face after face for signs of guilt.
+
+"Benjamin Rogers, did you tear this book?"
+
+A denial. Another pause.
+
+"Joseph Harper, did you?"
+
+Another denial. Tom's uneasiness grew more and more intense under the
+slow torture of these proceedings. The master scanned the ranks of
+boys--considered a while, then turned to the girls:
+
+"Amy Lawrence?"
+
+A shake of the head.
+
+"Gracie Miller?"
+
+The same sign.
+
+"Susan Harper, did you do this?"
+
+Another negative. The next girl was Becky Thatcher. Tom was trembling
+from head to foot with excitement and a sense of the hopelessness of
+the situation.
+
+"Rebecca Thatcher" [Tom glanced at her face--it was white with terror]
+--"did you tear--no, look me in the face" [her hands rose in appeal]
+--"did you tear this book?"
+
+A thought shot like lightning through Tom's brain. He sprang to his
+feet and shouted--"I done it!"
+
+The school stared in perplexity at this incredible folly. Tom stood a
+moment, to gather his dismembered faculties; and when he stepped
+forward to go to his punishment the surprise, the gratitude, the
+adoration that shone upon him out of poor Becky's eyes seemed pay
+enough for a hundred floggings. Inspired by the splendor of his own
+act, he took without an outcry the most merciless flaying that even Mr.
+Dobbins had ever administered; and also received with indifference the
+added cruelty of a command to remain two hours after school should be
+dismissed--for he knew who would wait for him outside till his
+captivity was done, and not count the tedious time as loss, either.
+
+Tom went to bed that night planning vengeance against Alfred Temple;
+for with shame and repentance Becky had told him all, not forgetting
+her own treachery; but even the longing for vengeance had to give way,
+soon, to pleasanter musings, and he fell asleep at last with Becky's
+latest words lingering dreamily in his ear--
+
+"Tom, how COULD you be so noble!"
+
+
+
+CHAPTER XXI
+
+VACATION was approaching. The schoolmaster, always severe, grew
+severer and more exacting than ever, for he wanted the school to make a
+good showing on "Examination" day. His rod and his ferule were seldom
+idle now--at least among the smaller pupils. Only the biggest boys, and
+young ladies of eighteen and twenty, escaped lashing. Mr. Dobbins'
+lashings were very vigorous ones, too; for although he carried, under
+his wig, a perfectly bald and shiny head, he had only reached middle
+age, and there was no sign of feebleness in his muscle. As the great
+day approached, all the tyranny that was in him came to the surface; he
+seemed to take a vindictive pleasure in punishing the least
+shortcomings. The consequence was, that the smaller boys spent their
+days in terror and suffering and their nights in plotting revenge. They
+threw away no opportunity to do the master a mischief. But he kept
+ahead all the time. The retribution that followed every vengeful
+success was so sweeping and majestic that the boys always retired from
+the field badly worsted. At last they conspired together and hit upon a
+plan that promised a dazzling victory. They swore in the sign-painter's
+boy, told him the scheme, and asked his help. He had his own reasons
+for being delighted, for the master boarded in his father's family and
+had given the boy ample cause to hate him. The master's wife would go
+on a visit to the country in a few days, and there would be nothing to
+interfere with the plan; the master always prepared himself for great
+occasions by getting pretty well fuddled, and the sign-painter's boy
+said that when the dominie had reached the proper condition on
+Examination Evening he would "manage the thing" while he napped in his
+chair; then he would have him awakened at the right time and hurried
+away to school.
+
+In the fulness of time the interesting occasion arrived. At eight in
+the evening the schoolhouse was brilliantly lighted, and adorned with
+wreaths and festoons of foliage and flowers. The master sat throned in
+his great chair upon a raised platform, with his blackboard behind him.
+He was looking tolerably mellow. Three rows of benches on each side and
+six rows in front of him were occupied by the dignitaries of the town
+and by the parents of the pupils. To his left, back of the rows of
+citizens, was a spacious temporary platform upon which were seated the
+scholars who were to take part in the exercises of the evening; rows of
+small boys, washed and dressed to an intolerable state of discomfort;
+rows of gawky big boys; snowbanks of girls and young ladies clad in
+lawn and muslin and conspicuously conscious of their bare arms, their
+grandmothers' ancient trinkets, their bits of pink and blue ribbon and
+the flowers in their hair. All the rest of the house was filled with
+non-participating scholars.
+
+The exercises began. A very little boy stood up and sheepishly
+recited, "You'd scarce expect one of my age to speak in public on the
+stage," etc.--accompanying himself with the painfully exact and
+spasmodic gestures which a machine might have used--supposing the
+machine to be a trifle out of order. But he got through safely, though
+cruelly scared, and got a fine round of applause when he made his
+manufactured bow and retired.
+
+A little shamefaced girl lisped, "Mary had a little lamb," etc.,
+performed a compassion-inspiring curtsy, got her meed of applause, and
+sat down flushed and happy.
+
+Tom Sawyer stepped forward with conceited confidence and soared into
+the unquenchable and indestructible "Give me liberty or give me death"
+speech, with fine fury and frantic gesticulation, and broke down in the
+middle of it. A ghastly stage-fright seized him, his legs quaked under
+him and he was like to choke. True, he had the manifest sympathy of the
+house but he had the house's silence, too, which was even worse than
+its sympathy. The master frowned, and this completed the disaster. Tom
+struggled awhile and then retired, utterly defeated. There was a weak
+attempt at applause, but it died early.
+
+"The Boy Stood on the Burning Deck" followed; also "The Assyrian Came
+Down," and other declamatory gems. Then there were reading exercises,
+and a spelling fight. The meagre Latin class recited with honor. The
+prime feature of the evening was in order, now--original "compositions"
+by the young ladies. Each in her turn stepped forward to the edge of
+the platform, cleared her throat, held up her manuscript (tied with
+dainty ribbon), and proceeded to read, with labored attention to
+"expression" and punctuation. The themes were the same that had been
+illuminated upon similar occasions by their mothers before them, their
+grandmothers, and doubtless all their ancestors in the female line
+clear back to the Crusades. "Friendship" was one; "Memories of Other
+Days"; "Religion in History"; "Dream Land"; "The Advantages of
+Culture"; "Forms of Political Government Compared and Contrasted";
+"Melancholy"; "Filial Love"; "Heart Longings," etc., etc.
+
+A prevalent feature in these compositions was a nursed and petted
+melancholy; another was a wasteful and opulent gush of "fine language";
+another was a tendency to lug in by the ears particularly prized words
+and phrases until they were worn entirely out; and a peculiarity that
+conspicuously marked and marred them was the inveterate and intolerable
+sermon that wagged its crippled tail at the end of each and every one
+of them. No matter what the subject might be, a brain-racking effort
+was made to squirm it into some aspect or other that the moral and
+religious mind could contemplate with edification. The glaring
+insincerity of these sermons was not sufficient to compass the
+banishment of the fashion from the schools, and it is not sufficient
+to-day; it never will be sufficient while the world stands, perhaps.
+There is no school in all our land where the young ladies do not feel
+obliged to close their compositions with a sermon; and you will find
+that the sermon of the most frivolous and the least religious girl in
+the school is always the longest and the most relentlessly pious. But
+enough of this. Homely truth is unpalatable.
+
+Let us return to the "Examination." The first composition that was
+read was one entitled "Is this, then, Life?" Perhaps the reader can
+endure an extract from it:
+
+  "In the common walks of life, with what delightful
+   emotions does the youthful mind look forward to some
+   anticipated scene of festivity! Imagination is busy
+   sketching rose-tinted pictures of joy. In fancy, the
+   voluptuous votary of fashion sees herself amid the
+   festive throng, 'the observed of all observers.' Her
+   graceful form, arrayed in snowy robes, is whirling
+   through the mazes of the joyous dance; her eye is
+   brightest, her step is lightest in the gay assembly.
+
+  "In such delicious fancies time quickly glides by,
+   and the welcome hour arrives for her entrance into
+   the Elysian world, of which she has had such bright
+   dreams. How fairy-like does everything appear to
+   her enchanted vision! Each new scene is more charming
+   than the last. But after a while she finds that
+   beneath this goodly exterior, all is vanity, the
+   flattery which once charmed her soul, now grates
+   harshly upon her ear; the ball-room has lost its
+   charms; and with wasted health and imbittered heart,
+   she turns away with the conviction that earthly
+   pleasures cannot satisfy the longings of the soul!"
+
+And so forth and so on. There was a buzz of gratification from time to
+time during the reading, accompanied by whispered ejaculations of "How
+sweet!" "How eloquent!" "So true!" etc., and after the thing had closed
+with a peculiarly afflicting sermon the applause was enthusiastic.
+
+Then arose a slim, melancholy girl, whose face had the "interesting"
+paleness that comes of pills and indigestion, and read a "poem." Two
+stanzas of it will do:
+
+   "A MISSOURI MAIDEN'S FAREWELL TO ALABAMA
+
+   "Alabama, good-bye! I love thee well!
+      But yet for a while do I leave thee now!
+    Sad, yes, sad thoughts of thee my heart doth swell,
+      And burning recollections throng my brow!
+    For I have wandered through thy flowery woods;
+      Have roamed and read near Tallapoosa's stream;
+    Have listened to Tallassee's warring floods,
+      And wooed on Coosa's side Aurora's beam.
+
+   "Yet shame I not to bear an o'er-full heart,
+      Nor blush to turn behind my tearful eyes;
+    'Tis from no stranger land I now must part,
+      'Tis to no strangers left I yield these sighs.
+    Welcome and home were mine within this State,
+      Whose vales I leave--whose spires fade fast from me
+    And cold must be mine eyes, and heart, and tete,
+      When, dear Alabama! they turn cold on thee!"
+
+There were very few there who knew what "tete" meant, but the poem was
+very satisfactory, nevertheless.
+
+Next appeared a dark-complexioned, black-eyed, black-haired young
+lady, who paused an impressive moment, assumed a tragic expression, and
+began to read in a measured, solemn tone:
+
+  "A VISION
+
+   "Dark and tempestuous was night. Around the
+   throne on high not a single star quivered; but
+   the deep intonations of the heavy thunder
+   constantly vibrated upon the ear; whilst the
+   terrific lightning revelled in angry mood
+   through the cloudy chambers of heaven, seeming
+   to scorn the power exerted over its terror by
+   the illustrious Franklin! Even the boisterous
+   winds unanimously came forth from their mystic
+   homes, and blustered about as if to enhance by
+   their aid the wildness of the scene.
+
+   "At such a time, so dark, so dreary, for human
+   sympathy my very spirit sighed; but instead thereof,
+
+   "'My dearest friend, my counsellor, my comforter
+   and guide--My joy in grief, my second bliss
+   in joy,' came to my side. She moved like one of
+   those bright beings pictured in the sunny walks
+   of fancy's Eden by the romantic and young, a
+   queen of beauty unadorned save by her own
+   transcendent loveliness. So soft was her step, it
+   failed to make even a sound, and but for the
+   magical thrill imparted by her genial touch, as
+   other unobtrusive beauties, she would have glided
+   away un-perceived--unsought. A strange sadness
+   rested upon her features, like icy tears upon
+   the robe of December, as she pointed to the
+   contending elements without, and bade me contemplate
+   the two beings presented."
+
+This nightmare occupied some ten pages of manuscript and wound up with
+a sermon so destructive of all hope to non-Presbyterians that it took
+the first prize. This composition was considered to be the very finest
+effort of the evening. The mayor of the village, in delivering the
+prize to the author of it, made a warm speech in which he said that it
+was by far the most "eloquent" thing he had ever listened to, and that
+Daniel Webster himself might well be proud of it.
+
+It may be remarked, in passing, that the number of compositions in
+which the word "beauteous" was over-fondled, and human experience
+referred to as "life's page," was up to the usual average.
+
+Now the master, mellow almost to the verge of geniality, put his chair
+aside, turned his back to the audience, and began to draw a map of
+America on the blackboard, to exercise the geography class upon. But he
+made a sad business of it with his unsteady hand, and a smothered
+titter rippled over the house. He knew what the matter was, and set
+himself to right it. He sponged out lines and remade them; but he only
+distorted them more than ever, and the tittering was more pronounced.
+He threw his entire attention upon his work, now, as if determined not
+to be put down by the mirth. He felt that all eyes were fastened upon
+him; he imagined he was succeeding, and yet the tittering continued; it
+even manifestly increased. And well it might. There was a garret above,
+pierced with a scuttle over his head; and down through this scuttle
+came a cat, suspended around the haunches by a string; she had a rag
+tied about her head and jaws to keep her from mewing; as she slowly
+descended she curved upward and clawed at the string, she swung
+downward and clawed at the intangible air. The tittering rose higher
+and higher--the cat was within six inches of the absorbed teacher's
+head--down, down, a little lower, and she grabbed his wig with her
+desperate claws, clung to it, and was snatched up into the garret in an
+instant with her trophy still in her possession! And how the light did
+blaze abroad from the master's bald pate--for the sign-painter's boy
+had GILDED it!
+
+That broke up the meeting. The boys were avenged. Vacation had come.
+
+   NOTE:--The pretended "compositions" quoted in
+   this chapter are taken without alteration from a
+   volume entitled "Prose and Poetry, by a Western
+   Lady"--but they are exactly and precisely after
+   the schoolgirl pattern, and hence are much
+   happier than any mere imitations could be.
+
+
+
+CHAPTER XXII
+
+TOM joined the new order of Cadets of Temperance, being attracted by
+the showy character of their "regalia." He promised to abstain from
+smoking, chewing, and profanity as long as he remained a member. Now he
+found out a new thing--namely, that to promise not to do a thing is the
+surest way in the world to make a body want to go and do that very
+thing. Tom soon found himself tormented with a desire to drink and
+swear; the desire grew to be so intense that nothing but the hope of a
+chance to display himself in his red sash kept him from withdrawing
+from the order. Fourth of July was coming; but he soon gave that up
+--gave it up before he had worn his shackles over forty-eight hours--and
+fixed his hopes upon old Judge Frazer, justice of the peace, who was
+apparently on his deathbed and would have a big public funeral, since
+he was so high an official. During three days Tom was deeply concerned
+about the Judge's condition and hungry for news of it. Sometimes his
+hopes ran high--so high that he would venture to get out his regalia
+and practise before the looking-glass. But the Judge had a most
+discouraging way of fluctuating. At last he was pronounced upon the
+mend--and then convalescent. Tom was disgusted; and felt a sense of
+injury, too. He handed in his resignation at once--and that night the
+Judge suffered a relapse and died. Tom resolved that he would never
+trust a man like that again.
+
+The funeral was a fine thing. The Cadets paraded in a style calculated
+to kill the late member with envy. Tom was a free boy again, however
+--there was something in that. He could drink and swear, now--but found
+to his surprise that he did not want to. The simple fact that he could,
+took the desire away, and the charm of it.
+
+Tom presently wondered to find that his coveted vacation was beginning
+to hang a little heavily on his hands.
+
+He attempted a diary--but nothing happened during three days, and so
+he abandoned it.
+
+The first of all the negro minstrel shows came to town, and made a
+sensation. Tom and Joe Harper got up a band of performers and were
+happy for two days.
+
+Even the Glorious Fourth was in some sense a failure, for it rained
+hard, there was no procession in consequence, and the greatest man in
+the world (as Tom supposed), Mr. Benton, an actual United States
+Senator, proved an overwhelming disappointment--for he was not
+twenty-five feet high, nor even anywhere in the neighborhood of it.
+
+A circus came. The boys played circus for three days afterward in
+tents made of rag carpeting--admission, three pins for boys, two for
+girls--and then circusing was abandoned.
+
+A phrenologist and a mesmerizer came--and went again and left the
+village duller and drearier than ever.
+
+There were some boys-and-girls' parties, but they were so few and so
+delightful that they only made the aching voids between ache the harder.
+
+Becky Thatcher was gone to her Constantinople home to stay with her
+parents during vacation--so there was no bright side to life anywhere.
+
+The dreadful secret of the murder was a chronic misery. It was a very
+cancer for permanency and pain.
+
+Then came the measles.
+
+During two long weeks Tom lay a prisoner, dead to the world and its
+happenings. He was very ill, he was interested in nothing. When he got
+upon his feet at last and moved feebly down-town, a melancholy change
+had come over everything and every creature. There had been a
+"revival," and everybody had "got religion," not only the adults, but
+even the boys and girls. Tom went about, hoping against hope for the
+sight of one blessed sinful face, but disappointment crossed him
+everywhere. He found Joe Harper studying a Testament, and turned sadly
+away from the depressing spectacle. He sought Ben Rogers, and found him
+visiting the poor with a basket of tracts. He hunted up Jim Hollis, who
+called his attention to the precious blessing of his late measles as a
+warning. Every boy he encountered added another ton to his depression;
+and when, in desperation, he flew for refuge at last to the bosom of
+Huckleberry Finn and was received with a Scriptural quotation, his
+heart broke and he crept home and to bed realizing that he alone of all
+the town was lost, forever and forever.
+
+And that night there came on a terrific storm, with driving rain,
+awful claps of thunder and blinding sheets of lightning. He covered his
+head with the bedclothes and waited in a horror of suspense for his
+doom; for he had not the shadow of a doubt that all this hubbub was
+about him. He believed he had taxed the forbearance of the powers above
+to the extremity of endurance and that this was the result. It might
+have seemed to him a waste of pomp and ammunition to kill a bug with a
+battery of artillery, but there seemed nothing incongruous about the
+getting up such an expensive thunderstorm as this to knock the turf
+from under an insect like himself.
+
+By and by the tempest spent itself and died without accomplishing its
+object. The boy's first impulse was to be grateful, and reform. His
+second was to wait--for there might not be any more storms.
+
+The next day the doctors were back; Tom had relapsed. The three weeks
+he spent on his back this time seemed an entire age. When he got abroad
+at last he was hardly grateful that he had been spared, remembering how
+lonely was his estate, how companionless and forlorn he was. He drifted
+listlessly down the street and found Jim Hollis acting as judge in a
+juvenile court that was trying a cat for murder, in the presence of her
+victim, a bird. He found Joe Harper and Huck Finn up an alley eating a
+stolen melon. Poor lads! they--like Tom--had suffered a relapse.
+
+
+
+CHAPTER XXIII
+
+AT last the sleepy atmosphere was stirred--and vigorously: the murder
+trial came on in the court. It became the absorbing topic of village
+talk immediately. Tom could not get away from it. Every reference to
+the murder sent a shudder to his heart, for his troubled conscience and
+fears almost persuaded him that these remarks were put forth in his
+hearing as "feelers"; he did not see how he could be suspected of
+knowing anything about the murder, but still he could not be
+comfortable in the midst of this gossip. It kept him in a cold shiver
+all the time. He took Huck to a lonely place to have a talk with him.
+It would be some relief to unseal his tongue for a little while; to
+divide his burden of distress with another sufferer. Moreover, he
+wanted to assure himself that Huck had remained discreet.
+
+"Huck, have you ever told anybody about--that?"
+
+"'Bout what?"
+
+"You know what."
+
+"Oh--'course I haven't."
+
+"Never a word?"
+
+"Never a solitary word, so help me. What makes you ask?"
+
+"Well, I was afeard."
+
+"Why, Tom Sawyer, we wouldn't be alive two days if that got found out.
+YOU know that."
+
+Tom felt more comfortable. After a pause:
+
+"Huck, they couldn't anybody get you to tell, could they?"
+
+"Get me to tell? Why, if I wanted that half-breed devil to drownd me
+they could get me to tell. They ain't no different way."
+
+"Well, that's all right, then. I reckon we're safe as long as we keep
+mum. But let's swear again, anyway. It's more surer."
+
+"I'm agreed."
+
+So they swore again with dread solemnities.
+
+"What is the talk around, Huck? I've heard a power of it."
+
+"Talk? Well, it's just Muff Potter, Muff Potter, Muff Potter all the
+time. It keeps me in a sweat, constant, so's I want to hide som'ers."
+
+"That's just the same way they go on round me. I reckon he's a goner.
+Don't you feel sorry for him, sometimes?"
+
+"Most always--most always. He ain't no account; but then he hain't
+ever done anything to hurt anybody. Just fishes a little, to get money
+to get drunk on--and loafs around considerable; but lord, we all do
+that--leastways most of us--preachers and such like. But he's kind of
+good--he give me half a fish, once, when there warn't enough for two;
+and lots of times he's kind of stood by me when I was out of luck."
+
+"Well, he's mended kites for me, Huck, and knitted hooks on to my
+line. I wish we could get him out of there."
+
+"My! we couldn't get him out, Tom. And besides, 'twouldn't do any
+good; they'd ketch him again."
+
+"Yes--so they would. But I hate to hear 'em abuse him so like the
+dickens when he never done--that."
+
+"I do too, Tom. Lord, I hear 'em say he's the bloodiest looking
+villain in this country, and they wonder he wasn't ever hung before."
+
+"Yes, they talk like that, all the time. I've heard 'em say that if he
+was to get free they'd lynch him."
+
+"And they'd do it, too."
+
+The boys had a long talk, but it brought them little comfort. As the
+twilight drew on, they found themselves hanging about the neighborhood
+of the little isolated jail, perhaps with an undefined hope that
+something would happen that might clear away their difficulties. But
+nothing happened; there seemed to be no angels or fairies interested in
+this luckless captive.
+
+The boys did as they had often done before--went to the cell grating
+and gave Potter some tobacco and matches. He was on the ground floor
+and there were no guards.
+
+His gratitude for their gifts had always smote their consciences
+before--it cut deeper than ever, this time. They felt cowardly and
+treacherous to the last degree when Potter said:
+
+"You've been mighty good to me, boys--better'n anybody else in this
+town. And I don't forget it, I don't. Often I says to myself, says I,
+'I used to mend all the boys' kites and things, and show 'em where the
+good fishin' places was, and befriend 'em what I could, and now they've
+all forgot old Muff when he's in trouble; but Tom don't, and Huck
+don't--THEY don't forget him, says I, 'and I don't forget them.' Well,
+boys, I done an awful thing--drunk and crazy at the time--that's the
+only way I account for it--and now I got to swing for it, and it's
+right. Right, and BEST, too, I reckon--hope so, anyway. Well, we won't
+talk about that. I don't want to make YOU feel bad; you've befriended
+me. But what I want to say, is, don't YOU ever get drunk--then you won't
+ever get here. Stand a litter furder west--so--that's it; it's a prime
+comfort to see faces that's friendly when a body's in such a muck of
+trouble, and there don't none come here but yourn. Good friendly
+faces--good friendly faces. Git up on one another's backs and let me
+touch 'em. That's it. Shake hands--yourn'll come through the bars, but
+mine's too big. Little hands, and weak--but they've helped Muff Potter
+a power, and they'd help him more if they could."
+
+Tom went home miserable, and his dreams that night were full of
+horrors. The next day and the day after, he hung about the court-room,
+drawn by an almost irresistible impulse to go in, but forcing himself
+to stay out. Huck was having the same experience. They studiously
+avoided each other. Each wandered away, from time to time, but the same
+dismal fascination always brought them back presently. Tom kept his
+ears open when idlers sauntered out of the court-room, but invariably
+heard distressing news--the toils were closing more and more
+relentlessly around poor Potter. At the end of the second day the
+village talk was to the effect that Injun Joe's evidence stood firm and
+unshaken, and that there was not the slightest question as to what the
+jury's verdict would be.
+
+Tom was out late, that night, and came to bed through the window. He
+was in a tremendous state of excitement. It was hours before he got to
+sleep. All the village flocked to the court-house the next morning, for
+this was to be the great day. Both sexes were about equally represented
+in the packed audience. After a long wait the jury filed in and took
+their places; shortly afterward, Potter, pale and haggard, timid and
+hopeless, was brought in, with chains upon him, and seated where all
+the curious eyes could stare at him; no less conspicuous was Injun Joe,
+stolid as ever. There was another pause, and then the judge arrived and
+the sheriff proclaimed the opening of the court. The usual whisperings
+among the lawyers and gathering together of papers followed. These
+details and accompanying delays worked up an atmosphere of preparation
+that was as impressive as it was fascinating.
+
+Now a witness was called who testified that he found Muff Potter
+washing in the brook, at an early hour of the morning that the murder
+was discovered, and that he immediately sneaked away. After some
+further questioning, counsel for the prosecution said:
+
+"Take the witness."
+
+The prisoner raised his eyes for a moment, but dropped them again when
+his own counsel said:
+
+"I have no questions to ask him."
+
+The next witness proved the finding of the knife near the corpse.
+Counsel for the prosecution said:
+
+"Take the witness."
+
+"I have no questions to ask him," Potter's lawyer replied.
+
+A third witness swore he had often seen the knife in Potter's
+possession.
+
+"Take the witness."
+
+Counsel for Potter declined to question him. The faces of the audience
+began to betray annoyance. Did this attorney mean to throw away his
+client's life without an effort?
+
+Several witnesses deposed concerning Potter's guilty behavior when
+brought to the scene of the murder. They were allowed to leave the
+stand without being cross-questioned.
+
+Every detail of the damaging circumstances that occurred in the
+graveyard upon that morning which all present remembered so well was
+brought out by credible witnesses, but none of them were cross-examined
+by Potter's lawyer. The perplexity and dissatisfaction of the house
+expressed itself in murmurs and provoked a reproof from the bench.
+Counsel for the prosecution now said:
+
+"By the oaths of citizens whose simple word is above suspicion, we
+have fastened this awful crime, beyond all possibility of question,
+upon the unhappy prisoner at the bar. We rest our case here."
+
+A groan escaped from poor Potter, and he put his face in his hands and
+rocked his body softly to and fro, while a painful silence reigned in
+the court-room. Many men were moved, and many women's compassion
+testified itself in tears. Counsel for the defence rose and said:
+
+"Your honor, in our remarks at the opening of this trial, we
+foreshadowed our purpose to prove that our client did this fearful deed
+while under the influence of a blind and irresponsible delirium
+produced by drink. We have changed our mind. We shall not offer that
+plea." [Then to the clerk:] "Call Thomas Sawyer!"
+
+A puzzled amazement awoke in every face in the house, not even
+excepting Potter's. Every eye fastened itself with wondering interest
+upon Tom as he rose and took his place upon the stand. The boy looked
+wild enough, for he was badly scared. The oath was administered.
+
+"Thomas Sawyer, where were you on the seventeenth of June, about the
+hour of midnight?"
+
+Tom glanced at Injun Joe's iron face and his tongue failed him. The
+audience listened breathless, but the words refused to come. After a
+few moments, however, the boy got a little of his strength back, and
+managed to put enough of it into his voice to make part of the house
+hear:
+
+"In the graveyard!"
+
+"A little bit louder, please. Don't be afraid. You were--"
+
+"In the graveyard."
+
+A contemptuous smile flitted across Injun Joe's face.
+
+"Were you anywhere near Horse Williams' grave?"
+
+"Yes, sir."
+
+"Speak up--just a trifle louder. How near were you?"
+
+"Near as I am to you."
+
+"Were you hidden, or not?"
+
+"I was hid."
+
+"Where?"
+
+"Behind the elms that's on the edge of the grave."
+
+Injun Joe gave a barely perceptible start.
+
+"Any one with you?"
+
+"Yes, sir. I went there with--"
+
+"Wait--wait a moment. Never mind mentioning your companion's name. We
+will produce him at the proper time. Did you carry anything there with
+you."
+
+Tom hesitated and looked confused.
+
+"Speak out, my boy--don't be diffident. The truth is always
+respectable. What did you take there?"
+
+"Only a--a--dead cat."
+
+There was a ripple of mirth, which the court checked.
+
+"We will produce the skeleton of that cat. Now, my boy, tell us
+everything that occurred--tell it in your own way--don't skip anything,
+and don't be afraid."
+
+Tom began--hesitatingly at first, but as he warmed to his subject his
+words flowed more and more easily; in a little while every sound ceased
+but his own voice; every eye fixed itself upon him; with parted lips
+and bated breath the audience hung upon his words, taking no note of
+time, rapt in the ghastly fascinations of the tale. The strain upon
+pent emotion reached its climax when the boy said:
+
+"--and as the doctor fetched the board around and Muff Potter fell,
+Injun Joe jumped with the knife and--"
+
+Crash! Quick as lightning the half-breed sprang for a window, tore his
+way through all opposers, and was gone!
+
+
+
+CHAPTER XXIV
+
+TOM was a glittering hero once more--the pet of the old, the envy of
+the young. His name even went into immortal print, for the village
+paper magnified him. There were some that believed he would be
+President, yet, if he escaped hanging.
+
+As usual, the fickle, unreasoning world took Muff Potter to its bosom
+and fondled him as lavishly as it had abused him before. But that sort
+of conduct is to the world's credit; therefore it is not well to find
+fault with it.
+
+Tom's days were days of splendor and exultation to him, but his nights
+were seasons of horror. Injun Joe infested all his dreams, and always
+with doom in his eye. Hardly any temptation could persuade the boy to
+stir abroad after nightfall. Poor Huck was in the same state of
+wretchedness and terror, for Tom had told the whole story to the lawyer
+the night before the great day of the trial, and Huck was sore afraid
+that his share in the business might leak out, yet, notwithstanding
+Injun Joe's flight had saved him the suffering of testifying in court.
+The poor fellow had got the attorney to promise secrecy, but what of
+that? Since Tom's harassed conscience had managed to drive him to the
+lawyer's house by night and wring a dread tale from lips that had been
+sealed with the dismalest and most formidable of oaths, Huck's
+confidence in the human race was well-nigh obliterated.
+
+Daily Muff Potter's gratitude made Tom glad he had spoken; but nightly
+he wished he had sealed up his tongue.
+
+Half the time Tom was afraid Injun Joe would never be captured; the
+other half he was afraid he would be. He felt sure he never could draw
+a safe breath again until that man was dead and he had seen the corpse.
+
+Rewards had been offered, the country had been scoured, but no Injun
+Joe was found. One of those omniscient and awe-inspiring marvels, a
+detective, came up from St. Louis, moused around, shook his head,
+looked wise, and made that sort of astounding success which members of
+that craft usually achieve. That is to say, he "found a clew." But you
+can't hang a "clew" for murder, and so after that detective had got
+through and gone home, Tom felt just as insecure as he was before.
+
+The slow days drifted on, and each left behind it a slightly lightened
+weight of apprehension.
+
+
+
+CHAPTER XXV
+
+THERE comes a time in every rightly-constructed boy's life when he has
+a raging desire to go somewhere and dig for hidden treasure. This
+desire suddenly came upon Tom one day. He sallied out to find Joe
+Harper, but failed of success. Next he sought Ben Rogers; he had gone
+fishing. Presently he stumbled upon Huck Finn the Red-Handed. Huck
+would answer. Tom took him to a private place and opened the matter to
+him confidentially. Huck was willing. Huck was always willing to take a
+hand in any enterprise that offered entertainment and required no
+capital, for he had a troublesome superabundance of that sort of time
+which is not money. "Where'll we dig?" said Huck.
+
+"Oh, most anywhere."
+
+"Why, is it hid all around?"
+
+"No, indeed it ain't. It's hid in mighty particular places, Huck
+--sometimes on islands, sometimes in rotten chests under the end of a
+limb of an old dead tree, just where the shadow falls at midnight; but
+mostly under the floor in ha'nted houses."
+
+"Who hides it?"
+
+"Why, robbers, of course--who'd you reckon? Sunday-school
+sup'rintendents?"
+
+"I don't know. If 'twas mine I wouldn't hide it; I'd spend it and have
+a good time."
+
+"So would I. But robbers don't do that way. They always hide it and
+leave it there."
+
+"Don't they come after it any more?"
+
+"No, they think they will, but they generally forget the marks, or
+else they die. Anyway, it lays there a long time and gets rusty; and by
+and by somebody finds an old yellow paper that tells how to find the
+marks--a paper that's got to be ciphered over about a week because it's
+mostly signs and hy'roglyphics."
+
+"Hyro--which?"
+
+"Hy'roglyphics--pictures and things, you know, that don't seem to mean
+anything."
+
+"Have you got one of them papers, Tom?"
+
+"No."
+
+"Well then, how you going to find the marks?"
+
+"I don't want any marks. They always bury it under a ha'nted house or
+on an island, or under a dead tree that's got one limb sticking out.
+Well, we've tried Jackson's Island a little, and we can try it again
+some time; and there's the old ha'nted house up the Still-House branch,
+and there's lots of dead-limb trees--dead loads of 'em."
+
+"Is it under all of them?"
+
+"How you talk! No!"
+
+"Then how you going to know which one to go for?"
+
+"Go for all of 'em!"
+
+"Why, Tom, it'll take all summer."
+
+"Well, what of that? Suppose you find a brass pot with a hundred
+dollars in it, all rusty and gray, or rotten chest full of di'monds.
+How's that?"
+
+Huck's eyes glowed.
+
+"That's bully. Plenty bully enough for me. Just you gimme the hundred
+dollars and I don't want no di'monds."
+
+"All right. But I bet you I ain't going to throw off on di'monds. Some
+of 'em's worth twenty dollars apiece--there ain't any, hardly, but's
+worth six bits or a dollar."
+
+"No! Is that so?"
+
+"Cert'nly--anybody'll tell you so. Hain't you ever seen one, Huck?"
+
+"Not as I remember."
+
+"Oh, kings have slathers of them."
+
+"Well, I don' know no kings, Tom."
+
+"I reckon you don't. But if you was to go to Europe you'd see a raft
+of 'em hopping around."
+
+"Do they hop?"
+
+"Hop?--your granny! No!"
+
+"Well, what did you say they did, for?"
+
+"Shucks, I only meant you'd SEE 'em--not hopping, of course--what do
+they want to hop for?--but I mean you'd just see 'em--scattered around,
+you know, in a kind of a general way. Like that old humpbacked Richard."
+
+"Richard? What's his other name?"
+
+"He didn't have any other name. Kings don't have any but a given name."
+
+"No?"
+
+"But they don't."
+
+"Well, if they like it, Tom, all right; but I don't want to be a king
+and have only just a given name, like a nigger. But say--where you
+going to dig first?"
+
+"Well, I don't know. S'pose we tackle that old dead-limb tree on the
+hill t'other side of Still-House branch?"
+
+"I'm agreed."
+
+So they got a crippled pick and a shovel, and set out on their
+three-mile tramp. They arrived hot and panting, and threw themselves
+down in the shade of a neighboring elm to rest and have a smoke.
+
+"I like this," said Tom.
+
+"So do I."
+
+"Say, Huck, if we find a treasure here, what you going to do with your
+share?"
+
+"Well, I'll have pie and a glass of soda every day, and I'll go to
+every circus that comes along. I bet I'll have a gay time."
+
+"Well, ain't you going to save any of it?"
+
+"Save it? What for?"
+
+"Why, so as to have something to live on, by and by."
+
+"Oh, that ain't any use. Pap would come back to thish-yer town some
+day and get his claws on it if I didn't hurry up, and I tell you he'd
+clean it out pretty quick. What you going to do with yourn, Tom?"
+
+"I'm going to buy a new drum, and a sure-'nough sword, and a red
+necktie and a bull pup, and get married."
+
+"Married!"
+
+"That's it."
+
+"Tom, you--why, you ain't in your right mind."
+
+"Wait--you'll see."
+
+"Well, that's the foolishest thing you could do. Look at pap and my
+mother. Fight! Why, they used to fight all the time. I remember, mighty
+well."
+
+"That ain't anything. The girl I'm going to marry won't fight."
+
+"Tom, I reckon they're all alike. They'll all comb a body. Now you
+better think 'bout this awhile. I tell you you better. What's the name
+of the gal?"
+
+"It ain't a gal at all--it's a girl."
+
+"It's all the same, I reckon; some says gal, some says girl--both's
+right, like enough. Anyway, what's her name, Tom?"
+
+"I'll tell you some time--not now."
+
+"All right--that'll do. Only if you get married I'll be more lonesomer
+than ever."
+
+"No you won't. You'll come and live with me. Now stir out of this and
+we'll go to digging."
+
+They worked and sweated for half an hour. No result. They toiled
+another half-hour. Still no result. Huck said:
+
+"Do they always bury it as deep as this?"
+
+"Sometimes--not always. Not generally. I reckon we haven't got the
+right place."
+
+So they chose a new spot and began again. The labor dragged a little,
+but still they made progress. They pegged away in silence for some
+time. Finally Huck leaned on his shovel, swabbed the beaded drops from
+his brow with his sleeve, and said:
+
+"Where you going to dig next, after we get this one?"
+
+"I reckon maybe we'll tackle the old tree that's over yonder on
+Cardiff Hill back of the widow's."
+
+"I reckon that'll be a good one. But won't the widow take it away from
+us, Tom? It's on her land."
+
+"SHE take it away! Maybe she'd like to try it once. Whoever finds one
+of these hid treasures, it belongs to him. It don't make any difference
+whose land it's on."
+
+That was satisfactory. The work went on. By and by Huck said:
+
+"Blame it, we must be in the wrong place again. What do you think?"
+
+"It is mighty curious, Huck. I don't understand it. Sometimes witches
+interfere. I reckon maybe that's what's the trouble now."
+
+"Shucks! Witches ain't got no power in the daytime."
+
+"Well, that's so. I didn't think of that. Oh, I know what the matter
+is! What a blamed lot of fools we are! You got to find out where the
+shadow of the limb falls at midnight, and that's where you dig!"
+
+"Then consound it, we've fooled away all this work for nothing. Now
+hang it all, we got to come back in the night. It's an awful long way.
+Can you get out?"
+
+"I bet I will. We've got to do it to-night, too, because if somebody
+sees these holes they'll know in a minute what's here and they'll go
+for it."
+
+"Well, I'll come around and maow to-night."
+
+"All right. Let's hide the tools in the bushes."
+
+The boys were there that night, about the appointed time. They sat in
+the shadow waiting. It was a lonely place, and an hour made solemn by
+old traditions. Spirits whispered in the rustling leaves, ghosts lurked
+in the murky nooks, the deep baying of a hound floated up out of the
+distance, an owl answered with his sepulchral note. The boys were
+subdued by these solemnities, and talked little. By and by they judged
+that twelve had come; they marked where the shadow fell, and began to
+dig. Their hopes commenced to rise. Their interest grew stronger, and
+their industry kept pace with it. The hole deepened and still deepened,
+but every time their hearts jumped to hear the pick strike upon
+something, they only suffered a new disappointment. It was only a stone
+or a chunk. At last Tom said:
+
+"It ain't any use, Huck, we're wrong again."
+
+"Well, but we CAN'T be wrong. We spotted the shadder to a dot."
+
+"I know it, but then there's another thing."
+
+"What's that?".
+
+"Why, we only guessed at the time. Like enough it was too late or too
+early."
+
+Huck dropped his shovel.
+
+"That's it," said he. "That's the very trouble. We got to give this
+one up. We can't ever tell the right time, and besides this kind of
+thing's too awful, here this time of night with witches and ghosts
+a-fluttering around so. I feel as if something's behind me all the time;
+and I'm afeard to turn around, becuz maybe there's others in front
+a-waiting for a chance. I been creeping all over, ever since I got here."
+
+"Well, I've been pretty much so, too, Huck. They most always put in a
+dead man when they bury a treasure under a tree, to look out for it."
+
+"Lordy!"
+
+"Yes, they do. I've always heard that."
+
+"Tom, I don't like to fool around much where there's dead people. A
+body's bound to get into trouble with 'em, sure."
+
+"I don't like to stir 'em up, either. S'pose this one here was to
+stick his skull out and say something!"
+
+"Don't Tom! It's awful."
+
+"Well, it just is. Huck, I don't feel comfortable a bit."
+
+"Say, Tom, let's give this place up, and try somewheres else."
+
+"All right, I reckon we better."
+
+"What'll it be?"
+
+Tom considered awhile; and then said:
+
+"The ha'nted house. That's it!"
+
+"Blame it, I don't like ha'nted houses, Tom. Why, they're a dern sight
+worse'n dead people. Dead people might talk, maybe, but they don't come
+sliding around in a shroud, when you ain't noticing, and peep over your
+shoulder all of a sudden and grit their teeth, the way a ghost does. I
+couldn't stand such a thing as that, Tom--nobody could."
+
+"Yes, but, Huck, ghosts don't travel around only at night. They won't
+hender us from digging there in the daytime."
+
+"Well, that's so. But you know mighty well people don't go about that
+ha'nted house in the day nor the night."
+
+"Well, that's mostly because they don't like to go where a man's been
+murdered, anyway--but nothing's ever been seen around that house except
+in the night--just some blue lights slipping by the windows--no regular
+ghosts."
+
+"Well, where you see one of them blue lights flickering around, Tom,
+you can bet there's a ghost mighty close behind it. It stands to
+reason. Becuz you know that they don't anybody but ghosts use 'em."
+
+"Yes, that's so. But anyway they don't come around in the daytime, so
+what's the use of our being afeard?"
+
+"Well, all right. We'll tackle the ha'nted house if you say so--but I
+reckon it's taking chances."
+
+They had started down the hill by this time. There in the middle of
+the moonlit valley below them stood the "ha'nted" house, utterly
+isolated, its fences gone long ago, rank weeds smothering the very
+doorsteps, the chimney crumbled to ruin, the window-sashes vacant, a
+corner of the roof caved in. The boys gazed awhile, half expecting to
+see a blue light flit past a window; then talking in a low tone, as
+befitted the time and the circumstances, they struck far off to the
+right, to give the haunted house a wide berth, and took their way
+homeward through the woods that adorned the rearward side of Cardiff
+Hill.
+
+
+
+CHAPTER XXVI
+
+ABOUT noon the next day the boys arrived at the dead tree; they had
+come for their tools. Tom was impatient to go to the haunted house;
+Huck was measurably so, also--but suddenly said:
+
+"Lookyhere, Tom, do you know what day it is?"
+
+Tom mentally ran over the days of the week, and then quickly lifted
+his eyes with a startled look in them--
+
+"My! I never once thought of it, Huck!"
+
+"Well, I didn't neither, but all at once it popped onto me that it was
+Friday."
+
+"Blame it, a body can't be too careful, Huck. We might 'a' got into an
+awful scrape, tackling such a thing on a Friday."
+
+"MIGHT! Better say we WOULD! There's some lucky days, maybe, but
+Friday ain't."
+
+"Any fool knows that. I don't reckon YOU was the first that found it
+out, Huck."
+
+"Well, I never said I was, did I? And Friday ain't all, neither. I had
+a rotten bad dream last night--dreampt about rats."
+
+"No! Sure sign of trouble. Did they fight?"
+
+"No."
+
+"Well, that's good, Huck. When they don't fight it's only a sign that
+there's trouble around, you know. All we got to do is to look mighty
+sharp and keep out of it. We'll drop this thing for to-day, and play.
+Do you know Robin Hood, Huck?"
+
+"No. Who's Robin Hood?"
+
+"Why, he was one of the greatest men that was ever in England--and the
+best. He was a robber."
+
+"Cracky, I wisht I was. Who did he rob?"
+
+"Only sheriffs and bishops and rich people and kings, and such like.
+But he never bothered the poor. He loved 'em. He always divided up with
+'em perfectly square."
+
+"Well, he must 'a' been a brick."
+
+"I bet you he was, Huck. Oh, he was the noblest man that ever was.
+They ain't any such men now, I can tell you. He could lick any man in
+England, with one hand tied behind him; and he could take his yew bow
+and plug a ten-cent piece every time, a mile and a half."
+
+"What's a YEW bow?"
+
+"I don't know. It's some kind of a bow, of course. And if he hit that
+dime only on the edge he would set down and cry--and curse. But we'll
+play Robin Hood--it's nobby fun. I'll learn you."
+
+"I'm agreed."
+
+So they played Robin Hood all the afternoon, now and then casting a
+yearning eye down upon the haunted house and passing a remark about the
+morrow's prospects and possibilities there. As the sun began to sink
+into the west they took their way homeward athwart the long shadows of
+the trees and soon were buried from sight in the forests of Cardiff
+Hill.
+
+On Saturday, shortly after noon, the boys were at the dead tree again.
+They had a smoke and a chat in the shade, and then dug a little in
+their last hole, not with great hope, but merely because Tom said there
+were so many cases where people had given up a treasure after getting
+down within six inches of it, and then somebody else had come along and
+turned it up with a single thrust of a shovel. The thing failed this
+time, however, so the boys shouldered their tools and went away feeling
+that they had not trifled with fortune, but had fulfilled all the
+requirements that belong to the business of treasure-hunting.
+
+When they reached the haunted house there was something so weird and
+grisly about the dead silence that reigned there under the baking sun,
+and something so depressing about the loneliness and desolation of the
+place, that they were afraid, for a moment, to venture in. Then they
+crept to the door and took a trembling peep. They saw a weed-grown,
+floorless room, unplastered, an ancient fireplace, vacant windows, a
+ruinous staircase; and here, there, and everywhere hung ragged and
+abandoned cobwebs. They presently entered, softly, with quickened
+pulses, talking in whispers, ears alert to catch the slightest sound,
+and muscles tense and ready for instant retreat.
+
+In a little while familiarity modified their fears and they gave the
+place a critical and interested examination, rather admiring their own
+boldness, and wondering at it, too. Next they wanted to look up-stairs.
+This was something like cutting off retreat, but they got to daring
+each other, and of course there could be but one result--they threw
+their tools into a corner and made the ascent. Up there were the same
+signs of decay. In one corner they found a closet that promised
+mystery, but the promise was a fraud--there was nothing in it. Their
+courage was up now and well in hand. They were about to go down and
+begin work when--
+
+"Sh!" said Tom.
+
+"What is it?" whispered Huck, blanching with fright.
+
+"Sh!... There!... Hear it?"
+
+"Yes!... Oh, my! Let's run!"
+
+"Keep still! Don't you budge! They're coming right toward the door."
+
+The boys stretched themselves upon the floor with their eyes to
+knot-holes in the planking, and lay waiting, in a misery of fear.
+
+"They've stopped.... No--coming.... Here they are. Don't whisper
+another word, Huck. My goodness, I wish I was out of this!"
+
+Two men entered. Each boy said to himself: "There's the old deaf and
+dumb Spaniard that's been about town once or twice lately--never saw
+t'other man before."
+
+"T'other" was a ragged, unkempt creature, with nothing very pleasant
+in his face. The Spaniard was wrapped in a serape; he had bushy white
+whiskers; long white hair flowed from under his sombrero, and he wore
+green goggles. When they came in, "t'other" was talking in a low voice;
+they sat down on the ground, facing the door, with their backs to the
+wall, and the speaker continued his remarks. His manner became less
+guarded and his words more distinct as he proceeded:
+
+"No," said he, "I've thought it all over, and I don't like it. It's
+dangerous."
+
+"Dangerous!" grunted the "deaf and dumb" Spaniard--to the vast
+surprise of the boys. "Milksop!"
+
+This voice made the boys gasp and quake. It was Injun Joe's! There was
+silence for some time. Then Joe said:
+
+"What's any more dangerous than that job up yonder--but nothing's come
+of it."
+
+"That's different. Away up the river so, and not another house about.
+'Twon't ever be known that we tried, anyway, long as we didn't succeed."
+
+"Well, what's more dangerous than coming here in the daytime!--anybody
+would suspicion us that saw us."
+
+"I know that. But there warn't any other place as handy after that
+fool of a job. I want to quit this shanty. I wanted to yesterday, only
+it warn't any use trying to stir out of here, with those infernal boys
+playing over there on the hill right in full view."
+
+"Those infernal boys" quaked again under the inspiration of this
+remark, and thought how lucky it was that they had remembered it was
+Friday and concluded to wait a day. They wished in their hearts they
+had waited a year.
+
+The two men got out some food and made a luncheon. After a long and
+thoughtful silence, Injun Joe said:
+
+"Look here, lad--you go back up the river where you belong. Wait there
+till you hear from me. I'll take the chances on dropping into this town
+just once more, for a look. We'll do that 'dangerous' job after I've
+spied around a little and think things look well for it. Then for
+Texas! We'll leg it together!"
+
+This was satisfactory. Both men presently fell to yawning, and Injun
+Joe said:
+
+"I'm dead for sleep! It's your turn to watch."
+
+He curled down in the weeds and soon began to snore. His comrade
+stirred him once or twice and he became quiet. Presently the watcher
+began to nod; his head drooped lower and lower, both men began to snore
+now.
+
+The boys drew a long, grateful breath. Tom whispered:
+
+"Now's our chance--come!"
+
+Huck said:
+
+"I can't--I'd die if they was to wake."
+
+Tom urged--Huck held back. At last Tom rose slowly and softly, and
+started alone. But the first step he made wrung such a hideous creak
+from the crazy floor that he sank down almost dead with fright. He
+never made a second attempt. The boys lay there counting the dragging
+moments till it seemed to them that time must be done and eternity
+growing gray; and then they were grateful to note that at last the sun
+was setting.
+
+Now one snore ceased. Injun Joe sat up, stared around--smiled grimly
+upon his comrade, whose head was drooping upon his knees--stirred him
+up with his foot and said:
+
+"Here! YOU'RE a watchman, ain't you! All right, though--nothing's
+happened."
+
+"My! have I been asleep?"
+
+"Oh, partly, partly. Nearly time for us to be moving, pard. What'll we
+do with what little swag we've got left?"
+
+"I don't know--leave it here as we've always done, I reckon. No use to
+take it away till we start south. Six hundred and fifty in silver's
+something to carry."
+
+"Well--all right--it won't matter to come here once more."
+
+"No--but I'd say come in the night as we used to do--it's better."
+
+"Yes: but look here; it may be a good while before I get the right
+chance at that job; accidents might happen; 'tain't in such a very good
+place; we'll just regularly bury it--and bury it deep."
+
+"Good idea," said the comrade, who walked across the room, knelt down,
+raised one of the rearward hearth-stones and took out a bag that
+jingled pleasantly. He subtracted from it twenty or thirty dollars for
+himself and as much for Injun Joe, and passed the bag to the latter,
+who was on his knees in the corner, now, digging with his bowie-knife.
+
+The boys forgot all their fears, all their miseries in an instant.
+With gloating eyes they watched every movement. Luck!--the splendor of
+it was beyond all imagination! Six hundred dollars was money enough to
+make half a dozen boys rich! Here was treasure-hunting under the
+happiest auspices--there would not be any bothersome uncertainty as to
+where to dig. They nudged each other every moment--eloquent nudges and
+easily understood, for they simply meant--"Oh, but ain't you glad NOW
+we're here!"
+
+Joe's knife struck upon something.
+
+"Hello!" said he.
+
+"What is it?" said his comrade.
+
+"Half-rotten plank--no, it's a box, I believe. Here--bear a hand and
+we'll see what it's here for. Never mind, I've broke a hole."
+
+He reached his hand in and drew it out--
+
+"Man, it's money!"
+
+The two men examined the handful of coins. They were gold. The boys
+above were as excited as themselves, and as delighted.
+
+Joe's comrade said:
+
+"We'll make quick work of this. There's an old rusty pick over amongst
+the weeds in the corner the other side of the fireplace--I saw it a
+minute ago."
+
+He ran and brought the boys' pick and shovel. Injun Joe took the pick,
+looked it over critically, shook his head, muttered something to
+himself, and then began to use it. The box was soon unearthed. It was
+not very large; it was iron bound and had been very strong before the
+slow years had injured it. The men contemplated the treasure awhile in
+blissful silence.
+
+"Pard, there's thousands of dollars here," said Injun Joe.
+
+"'Twas always said that Murrel's gang used to be around here one
+summer," the stranger observed.
+
+"I know it," said Injun Joe; "and this looks like it, I should say."
+
+"Now you won't need to do that job."
+
+The half-breed frowned. Said he:
+
+"You don't know me. Least you don't know all about that thing. 'Tain't
+robbery altogether--it's REVENGE!" and a wicked light flamed in his
+eyes. "I'll need your help in it. When it's finished--then Texas. Go
+home to your Nance and your kids, and stand by till you hear from me."
+
+"Well--if you say so; what'll we do with this--bury it again?"
+
+"Yes. [Ravishing delight overhead.] NO! by the great Sachem, no!
+[Profound distress overhead.] I'd nearly forgot. That pick had fresh
+earth on it! [The boys were sick with terror in a moment.] What
+business has a pick and a shovel here? What business with fresh earth
+on them? Who brought them here--and where are they gone? Have you heard
+anybody?--seen anybody? What! bury it again and leave them to come and
+see the ground disturbed? Not exactly--not exactly. We'll take it to my
+den."
+
+"Why, of course! Might have thought of that before. You mean Number
+One?"
+
+"No--Number Two--under the cross. The other place is bad--too common."
+
+"All right. It's nearly dark enough to start."
+
+Injun Joe got up and went about from window to window cautiously
+peeping out. Presently he said:
+
+"Who could have brought those tools here? Do you reckon they can be
+up-stairs?"
+
+The boys' breath forsook them. Injun Joe put his hand on his knife,
+halted a moment, undecided, and then turned toward the stairway. The
+boys thought of the closet, but their strength was gone. The steps came
+creaking up the stairs--the intolerable distress of the situation woke
+the stricken resolution of the lads--they were about to spring for the
+closet, when there was a crash of rotten timbers and Injun Joe landed
+on the ground amid the debris of the ruined stairway. He gathered
+himself up cursing, and his comrade said:
+
+"Now what's the use of all that? If it's anybody, and they're up
+there, let them STAY there--who cares? If they want to jump down, now,
+and get into trouble, who objects? It will be dark in fifteen minutes
+--and then let them follow us if they want to. I'm willing. In my
+opinion, whoever hove those things in here caught a sight of us and
+took us for ghosts or devils or something. I'll bet they're running
+yet."
+
+Joe grumbled awhile; then he agreed with his friend that what daylight
+was left ought to be economized in getting things ready for leaving.
+Shortly afterward they slipped out of the house in the deepening
+twilight, and moved toward the river with their precious box.
+
+Tom and Huck rose up, weak but vastly relieved, and stared after them
+through the chinks between the logs of the house. Follow? Not they.
+They were content to reach ground again without broken necks, and take
+the townward track over the hill. They did not talk much. They were too
+much absorbed in hating themselves--hating the ill luck that made them
+take the spade and the pick there. But for that, Injun Joe never would
+have suspected. He would have hidden the silver with the gold to wait
+there till his "revenge" was satisfied, and then he would have had the
+misfortune to find that money turn up missing. Bitter, bitter luck that
+the tools were ever brought there!
+
+They resolved to keep a lookout for that Spaniard when he should come
+to town spying out for chances to do his revengeful job, and follow him
+to "Number Two," wherever that might be. Then a ghastly thought
+occurred to Tom.
+
+"Revenge? What if he means US, Huck!"
+
+"Oh, don't!" said Huck, nearly fainting.
+
+They talked it all over, and as they entered town they agreed to
+believe that he might possibly mean somebody else--at least that he
+might at least mean nobody but Tom, since only Tom had testified.
+
+Very, very small comfort it was to Tom to be alone in danger! Company
+would be a palpable improvement, he thought.
+
+
+
+CHAPTER XXVII
+
+THE adventure of the day mightily tormented Tom's dreams that night.
+Four times he had his hands on that rich treasure and four times it
+wasted to nothingness in his fingers as sleep forsook him and
+wakefulness brought back the hard reality of his misfortune. As he lay
+in the early morning recalling the incidents of his great adventure, he
+noticed that they seemed curiously subdued and far away--somewhat as if
+they had happened in another world, or in a time long gone by. Then it
+occurred to him that the great adventure itself must be a dream! There
+was one very strong argument in favor of this idea--namely, that the
+quantity of coin he had seen was too vast to be real. He had never seen
+as much as fifty dollars in one mass before, and he was like all boys
+of his age and station in life, in that he imagined that all references
+to "hundreds" and "thousands" were mere fanciful forms of speech, and
+that no such sums really existed in the world. He never had supposed
+for a moment that so large a sum as a hundred dollars was to be found
+in actual money in any one's possession. If his notions of hidden
+treasure had been analyzed, they would have been found to consist of a
+handful of real dimes and a bushel of vague, splendid, ungraspable
+dollars.
+
+But the incidents of his adventure grew sensibly sharper and clearer
+under the attrition of thinking them over, and so he presently found
+himself leaning to the impression that the thing might not have been a
+dream, after all. This uncertainty must be swept away. He would snatch
+a hurried breakfast and go and find Huck. Huck was sitting on the
+gunwale of a flatboat, listlessly dangling his feet in the water and
+looking very melancholy. Tom concluded to let Huck lead up to the
+subject. If he did not do it, then the adventure would be proved to
+have been only a dream.
+
+"Hello, Huck!"
+
+"Hello, yourself."
+
+Silence, for a minute.
+
+"Tom, if we'd 'a' left the blame tools at the dead tree, we'd 'a' got
+the money. Oh, ain't it awful!"
+
+"'Tain't a dream, then, 'tain't a dream! Somehow I most wish it was.
+Dog'd if I don't, Huck."
+
+"What ain't a dream?"
+
+"Oh, that thing yesterday. I been half thinking it was."
+
+"Dream! If them stairs hadn't broke down you'd 'a' seen how much dream
+it was! I've had dreams enough all night--with that patch-eyed Spanish
+devil going for me all through 'em--rot him!"
+
+"No, not rot him. FIND him! Track the money!"
+
+"Tom, we'll never find him. A feller don't have only one chance for
+such a pile--and that one's lost. I'd feel mighty shaky if I was to see
+him, anyway."
+
+"Well, so'd I; but I'd like to see him, anyway--and track him out--to
+his Number Two."
+
+"Number Two--yes, that's it. I been thinking 'bout that. But I can't
+make nothing out of it. What do you reckon it is?"
+
+"I dono. It's too deep. Say, Huck--maybe it's the number of a house!"
+
+"Goody!... No, Tom, that ain't it. If it is, it ain't in this
+one-horse town. They ain't no numbers here."
+
+"Well, that's so. Lemme think a minute. Here--it's the number of a
+room--in a tavern, you know!"
+
+"Oh, that's the trick! They ain't only two taverns. We can find out
+quick."
+
+"You stay here, Huck, till I come."
+
+Tom was off at once. He did not care to have Huck's company in public
+places. He was gone half an hour. He found that in the best tavern, No.
+2 had long been occupied by a young lawyer, and was still so occupied.
+In the less ostentatious house, No. 2 was a mystery. The
+tavern-keeper's young son said it was kept locked all the time, and he
+never saw anybody go into it or come out of it except at night; he did
+not know any particular reason for this state of things; had had some
+little curiosity, but it was rather feeble; had made the most of the
+mystery by entertaining himself with the idea that that room was
+"ha'nted"; had noticed that there was a light in there the night before.
+
+"That's what I've found out, Huck. I reckon that's the very No. 2
+we're after."
+
+"I reckon it is, Tom. Now what you going to do?"
+
+"Lemme think."
+
+Tom thought a long time. Then he said:
+
+"I'll tell you. The back door of that No. 2 is the door that comes out
+into that little close alley between the tavern and the old rattle trap
+of a brick store. Now you get hold of all the door-keys you can find,
+and I'll nip all of auntie's, and the first dark night we'll go there
+and try 'em. And mind you, keep a lookout for Injun Joe, because he
+said he was going to drop into town and spy around once more for a
+chance to get his revenge. If you see him, you just follow him; and if
+he don't go to that No. 2, that ain't the place."
+
+"Lordy, I don't want to foller him by myself!"
+
+"Why, it'll be night, sure. He mightn't ever see you--and if he did,
+maybe he'd never think anything."
+
+"Well, if it's pretty dark I reckon I'll track him. I dono--I dono.
+I'll try."
+
+"You bet I'll follow him, if it's dark, Huck. Why, he might 'a' found
+out he couldn't get his revenge, and be going right after that money."
+
+"It's so, Tom, it's so. I'll foller him; I will, by jingoes!"
+
+"Now you're TALKING! Don't you ever weaken, Huck, and I won't."
+
+
+
+CHAPTER XXVIII
+
+THAT night Tom and Huck were ready for their adventure. They hung
+about the neighborhood of the tavern until after nine, one watching the
+alley at a distance and the other the tavern door. Nobody entered the
+alley or left it; nobody resembling the Spaniard entered or left the
+tavern door. The night promised to be a fair one; so Tom went home with
+the understanding that if a considerable degree of darkness came on,
+Huck was to come and "maow," whereupon he would slip out and try the
+keys. But the night remained clear, and Huck closed his watch and
+retired to bed in an empty sugar hogshead about twelve.
+
+Tuesday the boys had the same ill luck. Also Wednesday. But Thursday
+night promised better. Tom slipped out in good season with his aunt's
+old tin lantern, and a large towel to blindfold it with. He hid the
+lantern in Huck's sugar hogshead and the watch began. An hour before
+midnight the tavern closed up and its lights (the only ones
+thereabouts) were put out. No Spaniard had been seen. Nobody had
+entered or left the alley. Everything was auspicious. The blackness of
+darkness reigned, the perfect stillness was interrupted only by
+occasional mutterings of distant thunder.
+
+Tom got his lantern, lit it in the hogshead, wrapped it closely in the
+towel, and the two adventurers crept in the gloom toward the tavern.
+Huck stood sentry and Tom felt his way into the alley. Then there was a
+season of waiting anxiety that weighed upon Huck's spirits like a
+mountain. He began to wish he could see a flash from the lantern--it
+would frighten him, but it would at least tell him that Tom was alive
+yet. It seemed hours since Tom had disappeared. Surely he must have
+fainted; maybe he was dead; maybe his heart had burst under terror and
+excitement. In his uneasiness Huck found himself drawing closer and
+closer to the alley; fearing all sorts of dreadful things, and
+momentarily expecting some catastrophe to happen that would take away
+his breath. There was not much to take away, for he seemed only able to
+inhale it by thimblefuls, and his heart would soon wear itself out, the
+way it was beating. Suddenly there was a flash of light and Tom came
+tearing by him: "Run!" said he; "run, for your life!"
+
+He needn't have repeated it; once was enough; Huck was making thirty
+or forty miles an hour before the repetition was uttered. The boys
+never stopped till they reached the shed of a deserted slaughter-house
+at the lower end of the village. Just as they got within its shelter
+the storm burst and the rain poured down. As soon as Tom got his breath
+he said:
+
+"Huck, it was awful! I tried two of the keys, just as soft as I could;
+but they seemed to make such a power of racket that I couldn't hardly
+get my breath I was so scared. They wouldn't turn in the lock, either.
+Well, without noticing what I was doing, I took hold of the knob, and
+open comes the door! It warn't locked! I hopped in, and shook off the
+towel, and, GREAT CAESAR'S GHOST!"
+
+"What!--what'd you see, Tom?"
+
+"Huck, I most stepped onto Injun Joe's hand!"
+
+"No!"
+
+"Yes! He was lying there, sound asleep on the floor, with his old
+patch on his eye and his arms spread out."
+
+"Lordy, what did you do? Did he wake up?"
+
+"No, never budged. Drunk, I reckon. I just grabbed that towel and
+started!"
+
+"I'd never 'a' thought of the towel, I bet!"
+
+"Well, I would. My aunt would make me mighty sick if I lost it."
+
+"Say, Tom, did you see that box?"
+
+"Huck, I didn't wait to look around. I didn't see the box, I didn't
+see the cross. I didn't see anything but a bottle and a tin cup on the
+floor by Injun Joe; yes, I saw two barrels and lots more bottles in the
+room. Don't you see, now, what's the matter with that ha'nted room?"
+
+"How?"
+
+"Why, it's ha'nted with whiskey! Maybe ALL the Temperance Taverns have
+got a ha'nted room, hey, Huck?"
+
+"Well, I reckon maybe that's so. Who'd 'a' thought such a thing? But
+say, Tom, now's a mighty good time to get that box, if Injun Joe's
+drunk."
+
+"It is, that! You try it!"
+
+Huck shuddered.
+
+"Well, no--I reckon not."
+
+"And I reckon not, Huck. Only one bottle alongside of Injun Joe ain't
+enough. If there'd been three, he'd be drunk enough and I'd do it."
+
+There was a long pause for reflection, and then Tom said:
+
+"Lookyhere, Huck, less not try that thing any more till we know Injun
+Joe's not in there. It's too scary. Now, if we watch every night, we'll
+be dead sure to see him go out, some time or other, and then we'll
+snatch that box quicker'n lightning."
+
+"Well, I'm agreed. I'll watch the whole night long, and I'll do it
+every night, too, if you'll do the other part of the job."
+
+"All right, I will. All you got to do is to trot up Hooper Street a
+block and maow--and if I'm asleep, you throw some gravel at the window
+and that'll fetch me."
+
+"Agreed, and good as wheat!"
+
+"Now, Huck, the storm's over, and I'll go home. It'll begin to be
+daylight in a couple of hours. You go back and watch that long, will
+you?"
+
+"I said I would, Tom, and I will. I'll ha'nt that tavern every night
+for a year! I'll sleep all day and I'll stand watch all night."
+
+"That's all right. Now, where you going to sleep?"
+
+"In Ben Rogers' hayloft. He lets me, and so does his pap's nigger man,
+Uncle Jake. I tote water for Uncle Jake whenever he wants me to, and
+any time I ask him he gives me a little something to eat if he can
+spare it. That's a mighty good nigger, Tom. He likes me, becuz I don't
+ever act as if I was above him. Sometime I've set right down and eat
+WITH him. But you needn't tell that. A body's got to do things when
+he's awful hungry he wouldn't want to do as a steady thing."
+
+"Well, if I don't want you in the daytime, I'll let you sleep. I won't
+come bothering around. Any time you see something's up, in the night,
+just skip right around and maow."
+
+
+
+CHAPTER XXIX
+
+THE first thing Tom heard on Friday morning was a glad piece of news
+--Judge Thatcher's family had come back to town the night before. Both
+Injun Joe and the treasure sunk into secondary importance for a moment,
+and Becky took the chief place in the boy's interest. He saw her and
+they had an exhausting good time playing "hi-spy" and "gully-keeper"
+with a crowd of their school-mates. The day was completed and crowned
+in a peculiarly satisfactory way: Becky teased her mother to appoint
+the next day for the long-promised and long-delayed picnic, and she
+consented. The child's delight was boundless; and Tom's not more
+moderate. The invitations were sent out before sunset, and straightway
+the young folks of the village were thrown into a fever of preparation
+and pleasurable anticipation. Tom's excitement enabled him to keep
+awake until a pretty late hour, and he had good hopes of hearing Huck's
+"maow," and of having his treasure to astonish Becky and the picnickers
+with, next day; but he was disappointed. No signal came that night.
+
+Morning came, eventually, and by ten or eleven o'clock a giddy and
+rollicking company were gathered at Judge Thatcher's, and everything
+was ready for a start. It was not the custom for elderly people to mar
+the picnics with their presence. The children were considered safe
+enough under the wings of a few young ladies of eighteen and a few
+young gentlemen of twenty-three or thereabouts. The old steam ferryboat
+was chartered for the occasion; presently the gay throng filed up the
+main street laden with provision-baskets. Sid was sick and had to miss
+the fun; Mary remained at home to entertain him. The last thing Mrs.
+Thatcher said to Becky, was:
+
+"You'll not get back till late. Perhaps you'd better stay all night
+with some of the girls that live near the ferry-landing, child."
+
+"Then I'll stay with Susy Harper, mamma."
+
+"Very well. And mind and behave yourself and don't be any trouble."
+
+Presently, as they tripped along, Tom said to Becky:
+
+"Say--I'll tell you what we'll do. 'Stead of going to Joe Harper's
+we'll climb right up the hill and stop at the Widow Douglas'. She'll
+have ice-cream! She has it most every day--dead loads of it. And she'll
+be awful glad to have us."
+
+"Oh, that will be fun!"
+
+Then Becky reflected a moment and said:
+
+"But what will mamma say?"
+
+"How'll she ever know?"
+
+The girl turned the idea over in her mind, and said reluctantly:
+
+"I reckon it's wrong--but--"
+
+"But shucks! Your mother won't know, and so what's the harm? All she
+wants is that you'll be safe; and I bet you she'd 'a' said go there if
+she'd 'a' thought of it. I know she would!"
+
+The Widow Douglas' splendid hospitality was a tempting bait. It and
+Tom's persuasions presently carried the day. So it was decided to say
+nothing anybody about the night's programme. Presently it occurred to
+Tom that maybe Huck might come this very night and give the signal. The
+thought took a deal of the spirit out of his anticipations. Still he
+could not bear to give up the fun at Widow Douglas'. And why should he
+give it up, he reasoned--the signal did not come the night before, so
+why should it be any more likely to come to-night? The sure fun of the
+evening outweighed the uncertain treasure; and, boy-like, he determined
+to yield to the stronger inclination and not allow himself to think of
+the box of money another time that day.
+
+Three miles below town the ferryboat stopped at the mouth of a woody
+hollow and tied up. The crowd swarmed ashore and soon the forest
+distances and craggy heights echoed far and near with shoutings and
+laughter. All the different ways of getting hot and tired were gone
+through with, and by-and-by the rovers straggled back to camp fortified
+with responsible appetites, and then the destruction of the good things
+began. After the feast there was a refreshing season of rest and chat
+in the shade of spreading oaks. By-and-by somebody shouted:
+
+"Who's ready for the cave?"
+
+Everybody was. Bundles of candles were procured, and straightway there
+was a general scamper up the hill. The mouth of the cave was up the
+hillside--an opening shaped like a letter A. Its massive oaken door
+stood unbarred. Within was a small chamber, chilly as an ice-house, and
+walled by Nature with solid limestone that was dewy with a cold sweat.
+It was romantic and mysterious to stand here in the deep gloom and look
+out upon the green valley shining in the sun. But the impressiveness of
+the situation quickly wore off, and the romping began again. The moment
+a candle was lighted there was a general rush upon the owner of it; a
+struggle and a gallant defence followed, but the candle was soon
+knocked down or blown out, and then there was a glad clamor of laughter
+and a new chase. But all things have an end. By-and-by the procession
+went filing down the steep descent of the main avenue, the flickering
+rank of lights dimly revealing the lofty walls of rock almost to their
+point of junction sixty feet overhead. This main avenue was not more
+than eight or ten feet wide. Every few steps other lofty and still
+narrower crevices branched from it on either hand--for McDougal's cave
+was but a vast labyrinth of crooked aisles that ran into each other and
+out again and led nowhere. It was said that one might wander days and
+nights together through its intricate tangle of rifts and chasms, and
+never find the end of the cave; and that he might go down, and down,
+and still down, into the earth, and it was just the same--labyrinth
+under labyrinth, and no end to any of them. No man "knew" the cave.
+That was an impossible thing. Most of the young men knew a portion of
+it, and it was not customary to venture much beyond this known portion.
+Tom Sawyer knew as much of the cave as any one.
+
+The procession moved along the main avenue some three-quarters of a
+mile, and then groups and couples began to slip aside into branch
+avenues, fly along the dismal corridors, and take each other by
+surprise at points where the corridors joined again. Parties were able
+to elude each other for the space of half an hour without going beyond
+the "known" ground.
+
+By-and-by, one group after another came straggling back to the mouth
+of the cave, panting, hilarious, smeared from head to foot with tallow
+drippings, daubed with clay, and entirely delighted with the success of
+the day. Then they were astonished to find that they had been taking no
+note of time and that night was about at hand. The clanging bell had
+been calling for half an hour. However, this sort of close to the day's
+adventures was romantic and therefore satisfactory. When the ferryboat
+with her wild freight pushed into the stream, nobody cared sixpence for
+the wasted time but the captain of the craft.
+
+Huck was already upon his watch when the ferryboat's lights went
+glinting past the wharf. He heard no noise on board, for the young
+people were as subdued and still as people usually are who are nearly
+tired to death. He wondered what boat it was, and why she did not stop
+at the wharf--and then he dropped her out of his mind and put his
+attention upon his business. The night was growing cloudy and dark. Ten
+o'clock came, and the noise of vehicles ceased, scattered lights began
+to wink out, all straggling foot-passengers disappeared, the village
+betook itself to its slumbers and left the small watcher alone with the
+silence and the ghosts. Eleven o'clock came, and the tavern lights were
+put out; darkness everywhere, now. Huck waited what seemed a weary long
+time, but nothing happened. His faith was weakening. Was there any use?
+Was there really any use? Why not give it up and turn in?
+
+A noise fell upon his ear. He was all attention in an instant. The
+alley door closed softly. He sprang to the corner of the brick store.
+The next moment two men brushed by him, and one seemed to have
+something under his arm. It must be that box! So they were going to
+remove the treasure. Why call Tom now? It would be absurd--the men
+would get away with the box and never be found again. No, he would
+stick to their wake and follow them; he would trust to the darkness for
+security from discovery. So communing with himself, Huck stepped out
+and glided along behind the men, cat-like, with bare feet, allowing
+them to keep just far enough ahead not to be invisible.
+
+They moved up the river street three blocks, then turned to the left
+up a cross-street. They went straight ahead, then, until they came to
+the path that led up Cardiff Hill; this they took. They passed by the
+old Welshman's house, half-way up the hill, without hesitating, and
+still climbed upward. Good, thought Huck, they will bury it in the old
+quarry. But they never stopped at the quarry. They passed on, up the
+summit. They plunged into the narrow path between the tall sumach
+bushes, and were at once hidden in the gloom. Huck closed up and
+shortened his distance, now, for they would never be able to see him.
+He trotted along awhile; then slackened his pace, fearing he was
+gaining too fast; moved on a piece, then stopped altogether; listened;
+no sound; none, save that he seemed to hear the beating of his own
+heart. The hooting of an owl came over the hill--ominous sound! But no
+footsteps. Heavens, was everything lost! He was about to spring with
+winged feet, when a man cleared his throat not four feet from him!
+Huck's heart shot into his throat, but he swallowed it again; and then
+he stood there shaking as if a dozen agues had taken charge of him at
+once, and so weak that he thought he must surely fall to the ground. He
+knew where he was. He knew he was within five steps of the stile
+leading into Widow Douglas' grounds. Very well, he thought, let them
+bury it there; it won't be hard to find.
+
+Now there was a voice--a very low voice--Injun Joe's:
+
+"Damn her, maybe she's got company--there's lights, late as it is."
+
+"I can't see any."
+
+This was that stranger's voice--the stranger of the haunted house. A
+deadly chill went to Huck's heart--this, then, was the "revenge" job!
+His thought was, to fly. Then he remembered that the Widow Douglas had
+been kind to him more than once, and maybe these men were going to
+murder her. He wished he dared venture to warn her; but he knew he
+didn't dare--they might come and catch him. He thought all this and
+more in the moment that elapsed between the stranger's remark and Injun
+Joe's next--which was--
+
+"Because the bush is in your way. Now--this way--now you see, don't
+you?"
+
+"Yes. Well, there IS company there, I reckon. Better give it up."
+
+"Give it up, and I just leaving this country forever! Give it up and
+maybe never have another chance. I tell you again, as I've told you
+before, I don't care for her swag--you may have it. But her husband was
+rough on me--many times he was rough on me--and mainly he was the
+justice of the peace that jugged me for a vagrant. And that ain't all.
+It ain't a millionth part of it! He had me HORSEWHIPPED!--horsewhipped
+in front of the jail, like a nigger!--with all the town looking on!
+HORSEWHIPPED!--do you understand? He took advantage of me and died. But
+I'll take it out of HER."
+
+"Oh, don't kill her! Don't do that!"
+
+"Kill? Who said anything about killing? I would kill HIM if he was
+here; but not her. When you want to get revenge on a woman you don't
+kill her--bosh! you go for her looks. You slit her nostrils--you notch
+her ears like a sow!"
+
+"By God, that's--"
+
+"Keep your opinion to yourself! It will be safest for you. I'll tie
+her to the bed. If she bleeds to death, is that my fault? I'll not cry,
+if she does. My friend, you'll help me in this thing--for MY sake
+--that's why you're here--I mightn't be able alone. If you flinch, I'll
+kill you. Do you understand that? And if I have to kill you, I'll kill
+her--and then I reckon nobody'll ever know much about who done this
+business."
+
+"Well, if it's got to be done, let's get at it. The quicker the
+better--I'm all in a shiver."
+
+"Do it NOW? And company there? Look here--I'll get suspicious of you,
+first thing you know. No--we'll wait till the lights are out--there's
+no hurry."
+
+Huck felt that a silence was going to ensue--a thing still more awful
+than any amount of murderous talk; so he held his breath and stepped
+gingerly back; planted his foot carefully and firmly, after balancing,
+one-legged, in a precarious way and almost toppling over, first on one
+side and then on the other. He took another step back, with the same
+elaboration and the same risks; then another and another, and--a twig
+snapped under his foot! His breath stopped and he listened. There was
+no sound--the stillness was perfect. His gratitude was measureless. Now
+he turned in his tracks, between the walls of sumach bushes--turned
+himself as carefully as if he were a ship--and then stepped quickly but
+cautiously along. When he emerged at the quarry he felt secure, and so
+he picked up his nimble heels and flew. Down, down he sped, till he
+reached the Welshman's. He banged at the door, and presently the heads
+of the old man and his two stalwart sons were thrust from windows.
+
+"What's the row there? Who's banging? What do you want?"
+
+"Let me in--quick! I'll tell everything."
+
+"Why, who are you?"
+
+"Huckleberry Finn--quick, let me in!"
+
+"Huckleberry Finn, indeed! It ain't a name to open many doors, I
+judge! But let him in, lads, and let's see what's the trouble."
+
+"Please don't ever tell I told you," were Huck's first words when he
+got in. "Please don't--I'd be killed, sure--but the widow's been good
+friends to me sometimes, and I want to tell--I WILL tell if you'll
+promise you won't ever say it was me."
+
+"By George, he HAS got something to tell, or he wouldn't act so!"
+exclaimed the old man; "out with it and nobody here'll ever tell, lad."
+
+Three minutes later the old man and his sons, well armed, were up the
+hill, and just entering the sumach path on tiptoe, their weapons in
+their hands. Huck accompanied them no further. He hid behind a great
+bowlder and fell to listening. There was a lagging, anxious silence,
+and then all of a sudden there was an explosion of firearms and a cry.
+
+Huck waited for no particulars. He sprang away and sped down the hill
+as fast as his legs could carry him.
+
+
+
+CHAPTER XXX
+
+AS the earliest suspicion of dawn appeared on Sunday morning, Huck
+came groping up the hill and rapped gently at the old Welshman's door.
+The inmates were asleep, but it was a sleep that was set on a
+hair-trigger, on account of the exciting episode of the night. A call
+came from a window:
+
+"Who's there!"
+
+Huck's scared voice answered in a low tone:
+
+"Please let me in! It's only Huck Finn!"
+
+"It's a name that can open this door night or day, lad!--and welcome!"
+
+These were strange words to the vagabond boy's ears, and the
+pleasantest he had ever heard. He could not recollect that the closing
+word had ever been applied in his case before. The door was quickly
+unlocked, and he entered. Huck was given a seat and the old man and his
+brace of tall sons speedily dressed themselves.
+
+"Now, my boy, I hope you're good and hungry, because breakfast will be
+ready as soon as the sun's up, and we'll have a piping hot one, too
+--make yourself easy about that! I and the boys hoped you'd turn up and
+stop here last night."
+
+"I was awful scared," said Huck, "and I run. I took out when the
+pistols went off, and I didn't stop for three mile. I've come now becuz
+I wanted to know about it, you know; and I come before daylight becuz I
+didn't want to run across them devils, even if they was dead."
+
+"Well, poor chap, you do look as if you'd had a hard night of it--but
+there's a bed here for you when you've had your breakfast. No, they
+ain't dead, lad--we are sorry enough for that. You see we knew right
+where to put our hands on them, by your description; so we crept along
+on tiptoe till we got within fifteen feet of them--dark as a cellar
+that sumach path was--and just then I found I was going to sneeze. It
+was the meanest kind of luck! I tried to keep it back, but no use
+--'twas bound to come, and it did come! I was in the lead with my pistol
+raised, and when the sneeze started those scoundrels a-rustling to get
+out of the path, I sung out, 'Fire boys!' and blazed away at the place
+where the rustling was. So did the boys. But they were off in a jiffy,
+those villains, and we after them, down through the woods. I judge we
+never touched them. They fired a shot apiece as they started, but their
+bullets whizzed by and didn't do us any harm. As soon as we lost the
+sound of their feet we quit chasing, and went down and stirred up the
+constables. They got a posse together, and went off to guard the river
+bank, and as soon as it is light the sheriff and a gang are going to
+beat up the woods. My boys will be with them presently. I wish we had
+some sort of description of those rascals--'twould help a good deal.
+But you couldn't see what they were like, in the dark, lad, I suppose?"
+
+"Oh yes; I saw them down-town and follered them."
+
+"Splendid! Describe them--describe them, my boy!"
+
+"One's the old deaf and dumb Spaniard that's ben around here once or
+twice, and t'other's a mean-looking, ragged--"
+
+"That's enough, lad, we know the men! Happened on them in the woods
+back of the widow's one day, and they slunk away. Off with you, boys,
+and tell the sheriff--get your breakfast to-morrow morning!"
+
+The Welshman's sons departed at once. As they were leaving the room
+Huck sprang up and exclaimed:
+
+"Oh, please don't tell ANYbody it was me that blowed on them! Oh,
+please!"
+
+"All right if you say it, Huck, but you ought to have the credit of
+what you did."
+
+"Oh no, no! Please don't tell!"
+
+When the young men were gone, the old Welshman said:
+
+"They won't tell--and I won't. But why don't you want it known?"
+
+Huck would not explain, further than to say that he already knew too
+much about one of those men and would not have the man know that he
+knew anything against him for the whole world--he would be killed for
+knowing it, sure.
+
+The old man promised secrecy once more, and said:
+
+"How did you come to follow these fellows, lad? Were they looking
+suspicious?"
+
+Huck was silent while he framed a duly cautious reply. Then he said:
+
+"Well, you see, I'm a kind of a hard lot,--least everybody says so,
+and I don't see nothing agin it--and sometimes I can't sleep much, on
+account of thinking about it and sort of trying to strike out a new way
+of doing. That was the way of it last night. I couldn't sleep, and so I
+come along up-street 'bout midnight, a-turning it all over, and when I
+got to that old shackly brick store by the Temperance Tavern, I backed
+up agin the wall to have another think. Well, just then along comes
+these two chaps slipping along close by me, with something under their
+arm, and I reckoned they'd stole it. One was a-smoking, and t'other one
+wanted a light; so they stopped right before me and the cigars lit up
+their faces and I see that the big one was the deaf and dumb Spaniard,
+by his white whiskers and the patch on his eye, and t'other one was a
+rusty, ragged-looking devil."
+
+"Could you see the rags by the light of the cigars?"
+
+This staggered Huck for a moment. Then he said:
+
+"Well, I don't know--but somehow it seems as if I did."
+
+"Then they went on, and you--"
+
+"Follered 'em--yes. That was it. I wanted to see what was up--they
+sneaked along so. I dogged 'em to the widder's stile, and stood in the
+dark and heard the ragged one beg for the widder, and the Spaniard
+swear he'd spile her looks just as I told you and your two--"
+
+"What! The DEAF AND DUMB man said all that!"
+
+Huck had made another terrible mistake! He was trying his best to keep
+the old man from getting the faintest hint of who the Spaniard might
+be, and yet his tongue seemed determined to get him into trouble in
+spite of all he could do. He made several efforts to creep out of his
+scrape, but the old man's eye was upon him and he made blunder after
+blunder. Presently the Welshman said:
+
+"My boy, don't be afraid of me. I wouldn't hurt a hair of your head
+for all the world. No--I'd protect you--I'd protect you. This Spaniard
+is not deaf and dumb; you've let that slip without intending it; you
+can't cover that up now. You know something about that Spaniard that
+you want to keep dark. Now trust me--tell me what it is, and trust me
+--I won't betray you."
+
+Huck looked into the old man's honest eyes a moment, then bent over
+and whispered in his ear:
+
+"'Tain't a Spaniard--it's Injun Joe!"
+
+The Welshman almost jumped out of his chair. In a moment he said:
+
+"It's all plain enough, now. When you talked about notching ears and
+slitting noses I judged that that was your own embellishment, because
+white men don't take that sort of revenge. But an Injun! That's a
+different matter altogether."
+
+During breakfast the talk went on, and in the course of it the old man
+said that the last thing which he and his sons had done, before going
+to bed, was to get a lantern and examine the stile and its vicinity for
+marks of blood. They found none, but captured a bulky bundle of--
+
+"Of WHAT?"
+
+If the words had been lightning they could not have leaped with a more
+stunning suddenness from Huck's blanched lips. His eyes were staring
+wide, now, and his breath suspended--waiting for the answer. The
+Welshman started--stared in return--three seconds--five seconds--ten
+--then replied:
+
+"Of burglar's tools. Why, what's the MATTER with you?"
+
+Huck sank back, panting gently, but deeply, unutterably grateful. The
+Welshman eyed him gravely, curiously--and presently said:
+
+"Yes, burglar's tools. That appears to relieve you a good deal. But
+what did give you that turn? What were YOU expecting we'd found?"
+
+Huck was in a close place--the inquiring eye was upon him--he would
+have given anything for material for a plausible answer--nothing
+suggested itself--the inquiring eye was boring deeper and deeper--a
+senseless reply offered--there was no time to weigh it, so at a venture
+he uttered it--feebly:
+
+"Sunday-school books, maybe."
+
+Poor Huck was too distressed to smile, but the old man laughed loud
+and joyously, shook up the details of his anatomy from head to foot,
+and ended by saying that such a laugh was money in a-man's pocket,
+because it cut down the doctor's bill like everything. Then he added:
+
+"Poor old chap, you're white and jaded--you ain't well a bit--no
+wonder you're a little flighty and off your balance. But you'll come
+out of it. Rest and sleep will fetch you out all right, I hope."
+
+Huck was irritated to think he had been such a goose and betrayed such
+a suspicious excitement, for he had dropped the idea that the parcel
+brought from the tavern was the treasure, as soon as he had heard the
+talk at the widow's stile. He had only thought it was not the treasure,
+however--he had not known that it wasn't--and so the suggestion of a
+captured bundle was too much for his self-possession. But on the whole
+he felt glad the little episode had happened, for now he knew beyond
+all question that that bundle was not THE bundle, and so his mind was
+at rest and exceedingly comfortable. In fact, everything seemed to be
+drifting just in the right direction, now; the treasure must be still
+in No. 2, the men would be captured and jailed that day, and he and Tom
+could seize the gold that night without any trouble or any fear of
+interruption.
+
+Just as breakfast was completed there was a knock at the door. Huck
+jumped for a hiding-place, for he had no mind to be connected even
+remotely with the late event. The Welshman admitted several ladies and
+gentlemen, among them the Widow Douglas, and noticed that groups of
+citizens were climbing up the hill--to stare at the stile. So the news
+had spread. The Welshman had to tell the story of the night to the
+visitors. The widow's gratitude for her preservation was outspoken.
+
+"Don't say a word about it, madam. There's another that you're more
+beholden to than you are to me and my boys, maybe, but he don't allow
+me to tell his name. We wouldn't have been there but for him."
+
+Of course this excited a curiosity so vast that it almost belittled
+the main matter--but the Welshman allowed it to eat into the vitals of
+his visitors, and through them be transmitted to the whole town, for he
+refused to part with his secret. When all else had been learned, the
+widow said:
+
+"I went to sleep reading in bed and slept straight through all that
+noise. Why didn't you come and wake me?"
+
+"We judged it warn't worth while. Those fellows warn't likely to come
+again--they hadn't any tools left to work with, and what was the use of
+waking you up and scaring you to death? My three negro men stood guard
+at your house all the rest of the night. They've just come back."
+
+More visitors came, and the story had to be told and retold for a
+couple of hours more.
+
+There was no Sabbath-school during day-school vacation, but everybody
+was early at church. The stirring event was well canvassed. News came
+that not a sign of the two villains had been yet discovered. When the
+sermon was finished, Judge Thatcher's wife dropped alongside of Mrs.
+Harper as she moved down the aisle with the crowd and said:
+
+"Is my Becky going to sleep all day? I just expected she would be
+tired to death."
+
+"Your Becky?"
+
+"Yes," with a startled look--"didn't she stay with you last night?"
+
+"Why, no."
+
+Mrs. Thatcher turned pale, and sank into a pew, just as Aunt Polly,
+talking briskly with a friend, passed by. Aunt Polly said:
+
+"Good-morning, Mrs. Thatcher. Good-morning, Mrs. Harper. I've got a
+boy that's turned up missing. I reckon my Tom stayed at your house last
+night--one of you. And now he's afraid to come to church. I've got to
+settle with him."
+
+Mrs. Thatcher shook her head feebly and turned paler than ever.
+
+"He didn't stay with us," said Mrs. Harper, beginning to look uneasy.
+A marked anxiety came into Aunt Polly's face.
+
+"Joe Harper, have you seen my Tom this morning?"
+
+"No'm."
+
+"When did you see him last?"
+
+Joe tried to remember, but was not sure he could say. The people had
+stopped moving out of church. Whispers passed along, and a boding
+uneasiness took possession of every countenance. Children were
+anxiously questioned, and young teachers. They all said they had not
+noticed whether Tom and Becky were on board the ferryboat on the
+homeward trip; it was dark; no one thought of inquiring if any one was
+missing. One young man finally blurted out his fear that they were
+still in the cave! Mrs. Thatcher swooned away. Aunt Polly fell to
+crying and wringing her hands.
+
+The alarm swept from lip to lip, from group to group, from street to
+street, and within five minutes the bells were wildly clanging and the
+whole town was up! The Cardiff Hill episode sank into instant
+insignificance, the burglars were forgotten, horses were saddled,
+skiffs were manned, the ferryboat ordered out, and before the horror
+was half an hour old, two hundred men were pouring down highroad and
+river toward the cave.
+
+All the long afternoon the village seemed empty and dead. Many women
+visited Aunt Polly and Mrs. Thatcher and tried to comfort them. They
+cried with them, too, and that was still better than words. All the
+tedious night the town waited for news; but when the morning dawned at
+last, all the word that came was, "Send more candles--and send food."
+Mrs. Thatcher was almost crazed; and Aunt Polly, also. Judge Thatcher
+sent messages of hope and encouragement from the cave, but they
+conveyed no real cheer.
+
+The old Welshman came home toward daylight, spattered with
+candle-grease, smeared with clay, and almost worn out. He found Huck
+still in the bed that had been provided for him, and delirious with
+fever. The physicians were all at the cave, so the Widow Douglas came
+and took charge of the patient. She said she would do her best by him,
+because, whether he was good, bad, or indifferent, he was the Lord's,
+and nothing that was the Lord's was a thing to be neglected. The
+Welshman said Huck had good spots in him, and the widow said:
+
+"You can depend on it. That's the Lord's mark. He don't leave it off.
+He never does. Puts it somewhere on every creature that comes from his
+hands."
+
+Early in the forenoon parties of jaded men began to straggle into the
+village, but the strongest of the citizens continued searching. All the
+news that could be gained was that remotenesses of the cavern were
+being ransacked that had never been visited before; that every corner
+and crevice was going to be thoroughly searched; that wherever one
+wandered through the maze of passages, lights were to be seen flitting
+hither and thither in the distance, and shoutings and pistol-shots sent
+their hollow reverberations to the ear down the sombre aisles. In one
+place, far from the section usually traversed by tourists, the names
+"BECKY & TOM" had been found traced upon the rocky wall with
+candle-smoke, and near at hand a grease-soiled bit of ribbon. Mrs.
+Thatcher recognized the ribbon and cried over it. She said it was the
+last relic she should ever have of her child; and that no other memorial
+of her could ever be so precious, because this one parted latest from
+the living body before the awful death came. Some said that now and
+then, in the cave, a far-away speck of light would glimmer, and then a
+glorious shout would burst forth and a score of men go trooping down the
+echoing aisle--and then a sickening disappointment always followed; the
+children were not there; it was only a searcher's light.
+
+Three dreadful days and nights dragged their tedious hours along, and
+the village sank into a hopeless stupor. No one had heart for anything.
+The accidental discovery, just made, that the proprietor of the
+Temperance Tavern kept liquor on his premises, scarcely fluttered the
+public pulse, tremendous as the fact was. In a lucid interval, Huck
+feebly led up to the subject of taverns, and finally asked--dimly
+dreading the worst--if anything had been discovered at the Temperance
+Tavern since he had been ill.
+
+"Yes," said the widow.
+
+Huck started up in bed, wild-eyed:
+
+"What? What was it?"
+
+"Liquor!--and the place has been shut up. Lie down, child--what a turn
+you did give me!"
+
+"Only tell me just one thing--only just one--please! Was it Tom Sawyer
+that found it?"
+
+The widow burst into tears. "Hush, hush, child, hush! I've told you
+before, you must NOT talk. You are very, very sick!"
+
+Then nothing but liquor had been found; there would have been a great
+powwow if it had been the gold. So the treasure was gone forever--gone
+forever! But what could she be crying about? Curious that she should
+cry.
+
+These thoughts worked their dim way through Huck's mind, and under the
+weariness they gave him he fell asleep. The widow said to herself:
+
+"There--he's asleep, poor wreck. Tom Sawyer find it! Pity but somebody
+could find Tom Sawyer! Ah, there ain't many left, now, that's got hope
+enough, or strength enough, either, to go on searching."
+
+
+
+CHAPTER XXXI
+
+NOW to return to Tom and Becky's share in the picnic. They tripped
+along the murky aisles with the rest of the company, visiting the
+familiar wonders of the cave--wonders dubbed with rather
+over-descriptive names, such as "The Drawing-Room," "The Cathedral,"
+"Aladdin's Palace," and so on. Presently the hide-and-seek frolicking
+began, and Tom and Becky engaged in it with zeal until the exertion
+began to grow a trifle wearisome; then they wandered down a sinuous
+avenue holding their candles aloft and reading the tangled web-work of
+names, dates, post-office addresses, and mottoes with which the rocky
+walls had been frescoed (in candle-smoke). Still drifting along and
+talking, they scarcely noticed that they were now in a part of the cave
+whose walls were not frescoed. They smoked their own names under an
+overhanging shelf and moved on. Presently they came to a place where a
+little stream of water, trickling over a ledge and carrying a limestone
+sediment with it, had, in the slow-dragging ages, formed a laced and
+ruffled Niagara in gleaming and imperishable stone. Tom squeezed his
+small body behind it in order to illuminate it for Becky's
+gratification. He found that it curtained a sort of steep natural
+stairway which was enclosed between narrow walls, and at once the
+ambition to be a discoverer seized him. Becky responded to his call,
+and they made a smoke-mark for future guidance, and started upon their
+quest. They wound this way and that, far down into the secret depths of
+the cave, made another mark, and branched off in search of novelties to
+tell the upper world about. In one place they found a spacious cavern,
+from whose ceiling depended a multitude of shining stalactites of the
+length and circumference of a man's leg; they walked all about it,
+wondering and admiring, and presently left it by one of the numerous
+passages that opened into it. This shortly brought them to a bewitching
+spring, whose basin was incrusted with a frostwork of glittering
+crystals; it was in the midst of a cavern whose walls were supported by
+many fantastic pillars which had been formed by the joining of great
+stalactites and stalagmites together, the result of the ceaseless
+water-drip of centuries. Under the roof vast knots of bats had packed
+themselves together, thousands in a bunch; the lights disturbed the
+creatures and they came flocking down by hundreds, squeaking and
+darting furiously at the candles. Tom knew their ways and the danger of
+this sort of conduct. He seized Becky's hand and hurried her into the
+first corridor that offered; and none too soon, for a bat struck
+Becky's light out with its wing while she was passing out of the
+cavern. The bats chased the children a good distance; but the fugitives
+plunged into every new passage that offered, and at last got rid of the
+perilous things. Tom found a subterranean lake, shortly, which
+stretched its dim length away until its shape was lost in the shadows.
+He wanted to explore its borders, but concluded that it would be best
+to sit down and rest awhile, first. Now, for the first time, the deep
+stillness of the place laid a clammy hand upon the spirits of the
+children. Becky said:
+
+"Why, I didn't notice, but it seems ever so long since I heard any of
+the others."
+
+"Come to think, Becky, we are away down below them--and I don't know
+how far away north, or south, or east, or whichever it is. We couldn't
+hear them here."
+
+Becky grew apprehensive.
+
+"I wonder how long we've been down here, Tom? We better start back."
+
+"Yes, I reckon we better. P'raps we better."
+
+"Can you find the way, Tom? It's all a mixed-up crookedness to me."
+
+"I reckon I could find it--but then the bats. If they put our candles
+out it will be an awful fix. Let's try some other way, so as not to go
+through there."
+
+"Well. But I hope we won't get lost. It would be so awful!" and the
+girl shuddered at the thought of the dreadful possibilities.
+
+They started through a corridor, and traversed it in silence a long
+way, glancing at each new opening, to see if there was anything
+familiar about the look of it; but they were all strange. Every time
+Tom made an examination, Becky would watch his face for an encouraging
+sign, and he would say cheerily:
+
+"Oh, it's all right. This ain't the one, but we'll come to it right
+away!"
+
+But he felt less and less hopeful with each failure, and presently
+began to turn off into diverging avenues at sheer random, in desperate
+hope of finding the one that was wanted. He still said it was "all
+right," but there was such a leaden dread at his heart that the words
+had lost their ring and sounded just as if he had said, "All is lost!"
+Becky clung to his side in an anguish of fear, and tried hard to keep
+back the tears, but they would come. At last she said:
+
+"Oh, Tom, never mind the bats, let's go back that way! We seem to get
+worse and worse off all the time."
+
+"Listen!" said he.
+
+Profound silence; silence so deep that even their breathings were
+conspicuous in the hush. Tom shouted. The call went echoing down the
+empty aisles and died out in the distance in a faint sound that
+resembled a ripple of mocking laughter.
+
+"Oh, don't do it again, Tom, it is too horrid," said Becky.
+
+"It is horrid, but I better, Becky; they might hear us, you know," and
+he shouted again.
+
+The "might" was even a chillier horror than the ghostly laughter, it
+so confessed a perishing hope. The children stood still and listened;
+but there was no result. Tom turned upon the back track at once, and
+hurried his steps. It was but a little while before a certain
+indecision in his manner revealed another fearful fact to Becky--he
+could not find his way back!
+
+"Oh, Tom, you didn't make any marks!"
+
+"Becky, I was such a fool! Such a fool! I never thought we might want
+to come back! No--I can't find the way. It's all mixed up."
+
+"Tom, Tom, we're lost! we're lost! We never can get out of this awful
+place! Oh, why DID we ever leave the others!"
+
+She sank to the ground and burst into such a frenzy of crying that Tom
+was appalled with the idea that she might die, or lose her reason. He
+sat down by her and put his arms around her; she buried her face in his
+bosom, she clung to him, she poured out her terrors, her unavailing
+regrets, and the far echoes turned them all to jeering laughter. Tom
+begged her to pluck up hope again, and she said she could not. He fell
+to blaming and abusing himself for getting her into this miserable
+situation; this had a better effect. She said she would try to hope
+again, she would get up and follow wherever he might lead if only he
+would not talk like that any more. For he was no more to blame than
+she, she said.
+
+So they moved on again--aimlessly--simply at random--all they could do
+was to move, keep moving. For a little while, hope made a show of
+reviving--not with any reason to back it, but only because it is its
+nature to revive when the spring has not been taken out of it by age
+and familiarity with failure.
+
+By-and-by Tom took Becky's candle and blew it out. This economy meant
+so much! Words were not needed. Becky understood, and her hope died
+again. She knew that Tom had a whole candle and three or four pieces in
+his pockets--yet he must economize.
+
+By-and-by, fatigue began to assert its claims; the children tried to
+pay attention, for it was dreadful to think of sitting down when time
+was grown to be so precious, moving, in some direction, in any
+direction, was at least progress and might bear fruit; but to sit down
+was to invite death and shorten its pursuit.
+
+At last Becky's frail limbs refused to carry her farther. She sat
+down. Tom rested with her, and they talked of home, and the friends
+there, and the comfortable beds and, above all, the light! Becky cried,
+and Tom tried to think of some way of comforting her, but all his
+encouragements were grown threadbare with use, and sounded like
+sarcasms. Fatigue bore so heavily upon Becky that she drowsed off to
+sleep. Tom was grateful. He sat looking into her drawn face and saw it
+grow smooth and natural under the influence of pleasant dreams; and
+by-and-by a smile dawned and rested there. The peaceful face reflected
+somewhat of peace and healing into his own spirit, and his thoughts
+wandered away to bygone times and dreamy memories. While he was deep in
+his musings, Becky woke up with a breezy little laugh--but it was
+stricken dead upon her lips, and a groan followed it.
+
+"Oh, how COULD I sleep! I wish I never, never had waked! No! No, I
+don't, Tom! Don't look so! I won't say it again."
+
+"I'm glad you've slept, Becky; you'll feel rested, now, and we'll find
+the way out."
+
+"We can try, Tom; but I've seen such a beautiful country in my dream.
+I reckon we are going there."
+
+"Maybe not, maybe not. Cheer up, Becky, and let's go on trying."
+
+They rose up and wandered along, hand in hand and hopeless. They tried
+to estimate how long they had been in the cave, but all they knew was
+that it seemed days and weeks, and yet it was plain that this could not
+be, for their candles were not gone yet. A long time after this--they
+could not tell how long--Tom said they must go softly and listen for
+dripping water--they must find a spring. They found one presently, and
+Tom said it was time to rest again. Both were cruelly tired, yet Becky
+said she thought she could go a little farther. She was surprised to
+hear Tom dissent. She could not understand it. They sat down, and Tom
+fastened his candle to the wall in front of them with some clay.
+Thought was soon busy; nothing was said for some time. Then Becky broke
+the silence:
+
+"Tom, I am so hungry!"
+
+Tom took something out of his pocket.
+
+"Do you remember this?" said he.
+
+Becky almost smiled.
+
+"It's our wedding-cake, Tom."
+
+"Yes--I wish it was as big as a barrel, for it's all we've got."
+
+"I saved it from the picnic for us to dream on, Tom, the way grown-up
+people do with wedding-cake--but it'll be our--"
+
+She dropped the sentence where it was. Tom divided the cake and Becky
+ate with good appetite, while Tom nibbled at his moiety. There was
+abundance of cold water to finish the feast with. By-and-by Becky
+suggested that they move on again. Tom was silent a moment. Then he
+said:
+
+"Becky, can you bear it if I tell you something?"
+
+Becky's face paled, but she thought she could.
+
+"Well, then, Becky, we must stay here, where there's water to drink.
+That little piece is our last candle!"
+
+Becky gave loose to tears and wailings. Tom did what he could to
+comfort her, but with little effect. At length Becky said:
+
+"Tom!"
+
+"Well, Becky?"
+
+"They'll miss us and hunt for us!"
+
+"Yes, they will! Certainly they will!"
+
+"Maybe they're hunting for us now, Tom."
+
+"Why, I reckon maybe they are. I hope they are."
+
+"When would they miss us, Tom?"
+
+"When they get back to the boat, I reckon."
+
+"Tom, it might be dark then--would they notice we hadn't come?"
+
+"I don't know. But anyway, your mother would miss you as soon as they
+got home."
+
+A frightened look in Becky's face brought Tom to his senses and he saw
+that he had made a blunder. Becky was not to have gone home that night!
+The children became silent and thoughtful. In a moment a new burst of
+grief from Becky showed Tom that the thing in his mind had struck hers
+also--that the Sabbath morning might be half spent before Mrs. Thatcher
+discovered that Becky was not at Mrs. Harper's.
+
+The children fastened their eyes upon their bit of candle and watched
+it melt slowly and pitilessly away; saw the half inch of wick stand
+alone at last; saw the feeble flame rise and fall, climb the thin
+column of smoke, linger at its top a moment, and then--the horror of
+utter darkness reigned!
+
+How long afterward it was that Becky came to a slow consciousness that
+she was crying in Tom's arms, neither could tell. All that they knew
+was, that after what seemed a mighty stretch of time, both awoke out of
+a dead stupor of sleep and resumed their miseries once more. Tom said
+it might be Sunday, now--maybe Monday. He tried to get Becky to talk,
+but her sorrows were too oppressive, all her hopes were gone. Tom said
+that they must have been missed long ago, and no doubt the search was
+going on. He would shout and maybe some one would come. He tried it;
+but in the darkness the distant echoes sounded so hideously that he
+tried it no more.
+
+The hours wasted away, and hunger came to torment the captives again.
+A portion of Tom's half of the cake was left; they divided and ate it.
+But they seemed hungrier than before. The poor morsel of food only
+whetted desire.
+
+By-and-by Tom said:
+
+"SH! Did you hear that?"
+
+Both held their breath and listened. There was a sound like the
+faintest, far-off shout. Instantly Tom answered it, and leading Becky
+by the hand, started groping down the corridor in its direction.
+Presently he listened again; again the sound was heard, and apparently
+a little nearer.
+
+"It's them!" said Tom; "they're coming! Come along, Becky--we're all
+right now!"
+
+The joy of the prisoners was almost overwhelming. Their speed was
+slow, however, because pitfalls were somewhat common, and had to be
+guarded against. They shortly came to one and had to stop. It might be
+three feet deep, it might be a hundred--there was no passing it at any
+rate. Tom got down on his breast and reached as far down as he could.
+No bottom. They must stay there and wait until the searchers came. They
+listened; evidently the distant shoutings were growing more distant! a
+moment or two more and they had gone altogether. The heart-sinking
+misery of it! Tom whooped until he was hoarse, but it was of no use. He
+talked hopefully to Becky; but an age of anxious waiting passed and no
+sounds came again.
+
+The children groped their way back to the spring. The weary time
+dragged on; they slept again, and awoke famished and woe-stricken. Tom
+believed it must be Tuesday by this time.
+
+Now an idea struck him. There were some side passages near at hand. It
+would be better to explore some of these than bear the weight of the
+heavy time in idleness. He took a kite-line from his pocket, tied it to
+a projection, and he and Becky started, Tom in the lead, unwinding the
+line as he groped along. At the end of twenty steps the corridor ended
+in a "jumping-off place." Tom got down on his knees and felt below, and
+then as far around the corner as he could reach with his hands
+conveniently; he made an effort to stretch yet a little farther to the
+right, and at that moment, not twenty yards away, a human hand, holding
+a candle, appeared from behind a rock! Tom lifted up a glorious shout,
+and instantly that hand was followed by the body it belonged to--Injun
+Joe's! Tom was paralyzed; he could not move. He was vastly gratified
+the next moment, to see the "Spaniard" take to his heels and get
+himself out of sight. Tom wondered that Joe had not recognized his
+voice and come over and killed him for testifying in court. But the
+echoes must have disguised the voice. Without doubt, that was it, he
+reasoned. Tom's fright weakened every muscle in his body. He said to
+himself that if he had strength enough to get back to the spring he
+would stay there, and nothing should tempt him to run the risk of
+meeting Injun Joe again. He was careful to keep from Becky what it was
+he had seen. He told her he had only shouted "for luck."
+
+But hunger and wretchedness rise superior to fears in the long run.
+Another tedious wait at the spring and another long sleep brought
+changes. The children awoke tortured with a raging hunger. Tom believed
+that it must be Wednesday or Thursday or even Friday or Saturday, now,
+and that the search had been given over. He proposed to explore another
+passage. He felt willing to risk Injun Joe and all other terrors. But
+Becky was very weak. She had sunk into a dreary apathy and would not be
+roused. She said she would wait, now, where she was, and die--it would
+not be long. She told Tom to go with the kite-line and explore if he
+chose; but she implored him to come back every little while and speak
+to her; and she made him promise that when the awful time came, he
+would stay by her and hold her hand until all was over.
+
+Tom kissed her, with a choking sensation in his throat, and made a
+show of being confident of finding the searchers or an escape from the
+cave; then he took the kite-line in his hand and went groping down one
+of the passages on his hands and knees, distressed with hunger and sick
+with bodings of coming doom.
+
+
+
+CHAPTER XXXII
+
+TUESDAY afternoon came, and waned to the twilight. The village of St.
+Petersburg still mourned. The lost children had not been found. Public
+prayers had been offered up for them, and many and many a private
+prayer that had the petitioner's whole heart in it; but still no good
+news came from the cave. The majority of the searchers had given up the
+quest and gone back to their daily avocations, saying that it was plain
+the children could never be found. Mrs. Thatcher was very ill, and a
+great part of the time delirious. People said it was heartbreaking to
+hear her call her child, and raise her head and listen a whole minute
+at a time, then lay it wearily down again with a moan. Aunt Polly had
+drooped into a settled melancholy, and her gray hair had grown almost
+white. The village went to its rest on Tuesday night, sad and forlorn.
+
+Away in the middle of the night a wild peal burst from the village
+bells, and in a moment the streets were swarming with frantic half-clad
+people, who shouted, "Turn out! turn out! they're found! they're
+found!" Tin pans and horns were added to the din, the population massed
+itself and moved toward the river, met the children coming in an open
+carriage drawn by shouting citizens, thronged around it, joined its
+homeward march, and swept magnificently up the main street roaring
+huzzah after huzzah!
+
+The village was illuminated; nobody went to bed again; it was the
+greatest night the little town had ever seen. During the first half-hour
+a procession of villagers filed through Judge Thatcher's house, seized
+the saved ones and kissed them, squeezed Mrs. Thatcher's hand, tried to
+speak but couldn't--and drifted out raining tears all over the place.
+
+Aunt Polly's happiness was complete, and Mrs. Thatcher's nearly so. It
+would be complete, however, as soon as the messenger dispatched with
+the great news to the cave should get the word to her husband. Tom lay
+upon a sofa with an eager auditory about him and told the history of
+the wonderful adventure, putting in many striking additions to adorn it
+withal; and closed with a description of how he left Becky and went on
+an exploring expedition; how he followed two avenues as far as his
+kite-line would reach; how he followed a third to the fullest stretch of
+the kite-line, and was about to turn back when he glimpsed a far-off
+speck that looked like daylight; dropped the line and groped toward it,
+pushed his head and shoulders through a small hole, and saw the broad
+Mississippi rolling by! And if it had only happened to be night he would
+not have seen that speck of daylight and would not have explored that
+passage any more! He told how he went back for Becky and broke the good
+news and she told him not to fret her with such stuff, for she was
+tired, and knew she was going to die, and wanted to. He described how he
+labored with her and convinced her; and how she almost died for joy when
+she had groped to where she actually saw the blue speck of daylight; how
+he pushed his way out at the hole and then helped her out; how they sat
+there and cried for gladness; how some men came along in a skiff and Tom
+hailed them and told them their situation and their famished condition;
+how the men didn't believe the wild tale at first, "because," said they,
+"you are five miles down the river below the valley the cave is in"
+--then took them aboard, rowed to a house, gave them supper, made them
+rest till two or three hours after dark and then brought them home.
+
+Before day-dawn, Judge Thatcher and the handful of searchers with him
+were tracked out, in the cave, by the twine clews they had strung
+behind them, and informed of the great news.
+
+Three days and nights of toil and hunger in the cave were not to be
+shaken off at once, as Tom and Becky soon discovered. They were
+bedridden all of Wednesday and Thursday, and seemed to grow more and
+more tired and worn, all the time. Tom got about, a little, on
+Thursday, was down-town Friday, and nearly as whole as ever Saturday;
+but Becky did not leave her room until Sunday, and then she looked as
+if she had passed through a wasting illness.
+
+Tom learned of Huck's sickness and went to see him on Friday, but
+could not be admitted to the bedroom; neither could he on Saturday or
+Sunday. He was admitted daily after that, but was warned to keep still
+about his adventure and introduce no exciting topic. The Widow Douglas
+stayed by to see that he obeyed. At home Tom learned of the Cardiff
+Hill event; also that the "ragged man's" body had eventually been found
+in the river near the ferry-landing; he had been drowned while trying
+to escape, perhaps.
+
+About a fortnight after Tom's rescue from the cave, he started off to
+visit Huck, who had grown plenty strong enough, now, to hear exciting
+talk, and Tom had some that would interest him, he thought. Judge
+Thatcher's house was on Tom's way, and he stopped to see Becky. The
+Judge and some friends set Tom to talking, and some one asked him
+ironically if he wouldn't like to go to the cave again. Tom said he
+thought he wouldn't mind it. The Judge said:
+
+"Well, there are others just like you, Tom, I've not the least doubt.
+But we have taken care of that. Nobody will get lost in that cave any
+more."
+
+"Why?"
+
+"Because I had its big door sheathed with boiler iron two weeks ago,
+and triple-locked--and I've got the keys."
+
+Tom turned as white as a sheet.
+
+"What's the matter, boy! Here, run, somebody! Fetch a glass of water!"
+
+The water was brought and thrown into Tom's face.
+
+"Ah, now you're all right. What was the matter with you, Tom?"
+
+"Oh, Judge, Injun Joe's in the cave!"
+
+
+
+CHAPTER XXXIII
+
+WITHIN a few minutes the news had spread, and a dozen skiff-loads of
+men were on their way to McDougal's cave, and the ferryboat, well
+filled with passengers, soon followed. Tom Sawyer was in the skiff that
+bore Judge Thatcher.
+
+When the cave door was unlocked, a sorrowful sight presented itself in
+the dim twilight of the place. Injun Joe lay stretched upon the ground,
+dead, with his face close to the crack of the door, as if his longing
+eyes had been fixed, to the latest moment, upon the light and the cheer
+of the free world outside. Tom was touched, for he knew by his own
+experience how this wretch had suffered. His pity was moved, but
+nevertheless he felt an abounding sense of relief and security, now,
+which revealed to him in a degree which he had not fully appreciated
+before how vast a weight of dread had been lying upon him since the day
+he lifted his voice against this bloody-minded outcast.
+
+Injun Joe's bowie-knife lay close by, its blade broken in two. The
+great foundation-beam of the door had been chipped and hacked through,
+with tedious labor; useless labor, too, it was, for the native rock
+formed a sill outside it, and upon that stubborn material the knife had
+wrought no effect; the only damage done was to the knife itself. But if
+there had been no stony obstruction there the labor would have been
+useless still, for if the beam had been wholly cut away Injun Joe could
+not have squeezed his body under the door, and he knew it. So he had
+only hacked that place in order to be doing something--in order to pass
+the weary time--in order to employ his tortured faculties. Ordinarily
+one could find half a dozen bits of candle stuck around in the crevices
+of this vestibule, left there by tourists; but there were none now. The
+prisoner had searched them out and eaten them. He had also contrived to
+catch a few bats, and these, also, he had eaten, leaving only their
+claws. The poor unfortunate had starved to death. In one place, near at
+hand, a stalagmite had been slowly growing up from the ground for ages,
+builded by the water-drip from a stalactite overhead. The captive had
+broken off the stalagmite, and upon the stump had placed a stone,
+wherein he had scooped a shallow hollow to catch the precious drop
+that fell once in every three minutes with the dreary regularity of a
+clock-tick--a dessertspoonful once in four and twenty hours. That drop
+was falling when the Pyramids were new; when Troy fell; when the
+foundations of Rome were laid; when Christ was crucified; when the
+Conqueror created the British empire; when Columbus sailed; when the
+massacre at Lexington was "news." It is falling now; it will still be
+falling when all these things shall have sunk down the afternoon of
+history, and the twilight of tradition, and been swallowed up in the
+thick night of oblivion. Has everything a purpose and a mission? Did
+this drop fall patiently during five thousand years to be ready for
+this flitting human insect's need? and has it another important object
+to accomplish ten thousand years to come? No matter. It is many and
+many a year since the hapless half-breed scooped out the stone to catch
+the priceless drops, but to this day the tourist stares longest at that
+pathetic stone and that slow-dropping water when he comes to see the
+wonders of McDougal's cave. Injun Joe's cup stands first in the list of
+the cavern's marvels; even "Aladdin's Palace" cannot rival it.
+
+Injun Joe was buried near the mouth of the cave; and people flocked
+there in boats and wagons from the towns and from all the farms and
+hamlets for seven miles around; they brought their children, and all
+sorts of provisions, and confessed that they had had almost as
+satisfactory a time at the funeral as they could have had at the
+hanging.
+
+This funeral stopped the further growth of one thing--the petition to
+the governor for Injun Joe's pardon. The petition had been largely
+signed; many tearful and eloquent meetings had been held, and a
+committee of sappy women been appointed to go in deep mourning and wail
+around the governor, and implore him to be a merciful ass and trample
+his duty under foot. Injun Joe was believed to have killed five
+citizens of the village, but what of that? If he had been Satan himself
+there would have been plenty of weaklings ready to scribble their names
+to a pardon-petition, and drip a tear on it from their permanently
+impaired and leaky water-works.
+
+The morning after the funeral Tom took Huck to a private place to have
+an important talk. Huck had learned all about Tom's adventure from the
+Welshman and the Widow Douglas, by this time, but Tom said he reckoned
+there was one thing they had not told him; that thing was what he
+wanted to talk about now. Huck's face saddened. He said:
+
+"I know what it is. You got into No. 2 and never found anything but
+whiskey. Nobody told me it was you; but I just knowed it must 'a' ben
+you, soon as I heard 'bout that whiskey business; and I knowed you
+hadn't got the money becuz you'd 'a' got at me some way or other and
+told me even if you was mum to everybody else. Tom, something's always
+told me we'd never get holt of that swag."
+
+"Why, Huck, I never told on that tavern-keeper. YOU know his tavern
+was all right the Saturday I went to the picnic. Don't you remember you
+was to watch there that night?"
+
+"Oh yes! Why, it seems 'bout a year ago. It was that very night that I
+follered Injun Joe to the widder's."
+
+"YOU followed him?"
+
+"Yes--but you keep mum. I reckon Injun Joe's left friends behind him,
+and I don't want 'em souring on me and doing me mean tricks. If it
+hadn't ben for me he'd be down in Texas now, all right."
+
+Then Huck told his entire adventure in confidence to Tom, who had only
+heard of the Welshman's part of it before.
+
+"Well," said Huck, presently, coming back to the main question,
+"whoever nipped the whiskey in No. 2, nipped the money, too, I reckon
+--anyways it's a goner for us, Tom."
+
+"Huck, that money wasn't ever in No. 2!"
+
+"What!" Huck searched his comrade's face keenly. "Tom, have you got on
+the track of that money again?"
+
+"Huck, it's in the cave!"
+
+Huck's eyes blazed.
+
+"Say it again, Tom."
+
+"The money's in the cave!"
+
+"Tom--honest injun, now--is it fun, or earnest?"
+
+"Earnest, Huck--just as earnest as ever I was in my life. Will you go
+in there with me and help get it out?"
+
+"I bet I will! I will if it's where we can blaze our way to it and not
+get lost."
+
+"Huck, we can do that without the least little bit of trouble in the
+world."
+
+"Good as wheat! What makes you think the money's--"
+
+"Huck, you just wait till we get in there. If we don't find it I'll
+agree to give you my drum and every thing I've got in the world. I
+will, by jings."
+
+"All right--it's a whiz. When do you say?"
+
+"Right now, if you say it. Are you strong enough?"
+
+"Is it far in the cave? I ben on my pins a little, three or four days,
+now, but I can't walk more'n a mile, Tom--least I don't think I could."
+
+"It's about five mile into there the way anybody but me would go,
+Huck, but there's a mighty short cut that they don't anybody but me
+know about. Huck, I'll take you right to it in a skiff. I'll float the
+skiff down there, and I'll pull it back again all by myself. You
+needn't ever turn your hand over."
+
+"Less start right off, Tom."
+
+"All right. We want some bread and meat, and our pipes, and a little
+bag or two, and two or three kite-strings, and some of these
+new-fangled things they call lucifer matches. I tell you, many's
+the time I wished I had some when I was in there before."
+
+A trifle after noon the boys borrowed a small skiff from a citizen who
+was absent, and got under way at once. When they were several miles
+below "Cave Hollow," Tom said:
+
+"Now you see this bluff here looks all alike all the way down from the
+cave hollow--no houses, no wood-yards, bushes all alike. But do you see
+that white place up yonder where there's been a landslide? Well, that's
+one of my marks. We'll get ashore, now."
+
+They landed.
+
+"Now, Huck, where we're a-standing you could touch that hole I got out
+of with a fishing-pole. See if you can find it."
+
+Huck searched all the place about, and found nothing. Tom proudly
+marched into a thick clump of sumach bushes and said:
+
+"Here you are! Look at it, Huck; it's the snuggest hole in this
+country. You just keep mum about it. All along I've been wanting to be
+a robber, but I knew I'd got to have a thing like this, and where to
+run across it was the bother. We've got it now, and we'll keep it
+quiet, only we'll let Joe Harper and Ben Rogers in--because of course
+there's got to be a Gang, or else there wouldn't be any style about it.
+Tom Sawyer's Gang--it sounds splendid, don't it, Huck?"
+
+"Well, it just does, Tom. And who'll we rob?"
+
+"Oh, most anybody. Waylay people--that's mostly the way."
+
+"And kill them?"
+
+"No, not always. Hive them in the cave till they raise a ransom."
+
+"What's a ransom?"
+
+"Money. You make them raise all they can, off'n their friends; and
+after you've kept them a year, if it ain't raised then you kill them.
+That's the general way. Only you don't kill the women. You shut up the
+women, but you don't kill them. They're always beautiful and rich, and
+awfully scared. You take their watches and things, but you always take
+your hat off and talk polite. They ain't anybody as polite as robbers
+--you'll see that in any book. Well, the women get to loving you, and
+after they've been in the cave a week or two weeks they stop crying and
+after that you couldn't get them to leave. If you drove them out they'd
+turn right around and come back. It's so in all the books."
+
+"Why, it's real bully, Tom. I believe it's better'n to be a pirate."
+
+"Yes, it's better in some ways, because it's close to home and
+circuses and all that."
+
+By this time everything was ready and the boys entered the hole, Tom
+in the lead. They toiled their way to the farther end of the tunnel,
+then made their spliced kite-strings fast and moved on. A few steps
+brought them to the spring, and Tom felt a shudder quiver all through
+him. He showed Huck the fragment of candle-wick perched on a lump of
+clay against the wall, and described how he and Becky had watched the
+flame struggle and expire.
+
+The boys began to quiet down to whispers, now, for the stillness and
+gloom of the place oppressed their spirits. They went on, and presently
+entered and followed Tom's other corridor until they reached the
+"jumping-off place." The candles revealed the fact that it was not
+really a precipice, but only a steep clay hill twenty or thirty feet
+high. Tom whispered:
+
+"Now I'll show you something, Huck."
+
+He held his candle aloft and said:
+
+"Look as far around the corner as you can. Do you see that? There--on
+the big rock over yonder--done with candle-smoke."
+
+"Tom, it's a CROSS!"
+
+"NOW where's your Number Two? 'UNDER THE CROSS,' hey? Right yonder's
+where I saw Injun Joe poke up his candle, Huck!"
+
+Huck stared at the mystic sign awhile, and then said with a shaky voice:
+
+"Tom, less git out of here!"
+
+"What! and leave the treasure?"
+
+"Yes--leave it. Injun Joe's ghost is round about there, certain."
+
+"No it ain't, Huck, no it ain't. It would ha'nt the place where he
+died--away out at the mouth of the cave--five mile from here."
+
+"No, Tom, it wouldn't. It would hang round the money. I know the ways
+of ghosts, and so do you."
+
+Tom began to fear that Huck was right. Misgivings gathered in his
+mind. But presently an idea occurred to him--
+
+"Lookyhere, Huck, what fools we're making of ourselves! Injun Joe's
+ghost ain't a going to come around where there's a cross!"
+
+The point was well taken. It had its effect.
+
+"Tom, I didn't think of that. But that's so. It's luck for us, that
+cross is. I reckon we'll climb down there and have a hunt for that box."
+
+Tom went first, cutting rude steps in the clay hill as he descended.
+Huck followed. Four avenues opened out of the small cavern which the
+great rock stood in. The boys examined three of them with no result.
+They found a small recess in the one nearest the base of the rock, with
+a pallet of blankets spread down in it; also an old suspender, some
+bacon rind, and the well-gnawed bones of two or three fowls. But there
+was no money-box. The lads searched and researched this place, but in
+vain. Tom said:
+
+"He said UNDER the cross. Well, this comes nearest to being under the
+cross. It can't be under the rock itself, because that sets solid on
+the ground."
+
+They searched everywhere once more, and then sat down discouraged.
+Huck could suggest nothing. By-and-by Tom said:
+
+"Lookyhere, Huck, there's footprints and some candle-grease on the
+clay about one side of this rock, but not on the other sides. Now,
+what's that for? I bet you the money IS under the rock. I'm going to
+dig in the clay."
+
+"That ain't no bad notion, Tom!" said Huck with animation.
+
+Tom's "real Barlow" was out at once, and he had not dug four inches
+before he struck wood.
+
+"Hey, Huck!--you hear that?"
+
+Huck began to dig and scratch now. Some boards were soon uncovered and
+removed. They had concealed a natural chasm which led under the rock.
+Tom got into this and held his candle as far under the rock as he
+could, but said he could not see to the end of the rift. He proposed to
+explore. He stooped and passed under; the narrow way descended
+gradually. He followed its winding course, first to the right, then to
+the left, Huck at his heels. Tom turned a short curve, by-and-by, and
+exclaimed:
+
+"My goodness, Huck, lookyhere!"
+
+It was the treasure-box, sure enough, occupying a snug little cavern,
+along with an empty powder-keg, a couple of guns in leather cases, two
+or three pairs of old moccasins, a leather belt, and some other rubbish
+well soaked with the water-drip.
+
+"Got it at last!" said Huck, ploughing among the tarnished coins with
+his hand. "My, but we're rich, Tom!"
+
+"Huck, I always reckoned we'd get it. It's just too good to believe,
+but we HAVE got it, sure! Say--let's not fool around here. Let's snake
+it out. Lemme see if I can lift the box."
+
+It weighed about fifty pounds. Tom could lift it, after an awkward
+fashion, but could not carry it conveniently.
+
+"I thought so," he said; "THEY carried it like it was heavy, that day
+at the ha'nted house. I noticed that. I reckon I was right to think of
+fetching the little bags along."
+
+The money was soon in the bags and the boys took it up to the cross
+rock.
+
+"Now less fetch the guns and things," said Huck.
+
+"No, Huck--leave them there. They're just the tricks to have when we
+go to robbing. We'll keep them there all the time, and we'll hold our
+orgies there, too. It's an awful snug place for orgies."
+
+"What orgies?"
+
+"I dono. But robbers always have orgies, and of course we've got to
+have them, too. Come along, Huck, we've been in here a long time. It's
+getting late, I reckon. I'm hungry, too. We'll eat and smoke when we
+get to the skiff."
+
+They presently emerged into the clump of sumach bushes, looked warily
+out, found the coast clear, and were soon lunching and smoking in the
+skiff. As the sun dipped toward the horizon they pushed out and got
+under way. Tom skimmed up the shore through the long twilight, chatting
+cheerily with Huck, and landed shortly after dark.
+
+"Now, Huck," said Tom, "we'll hide the money in the loft of the
+widow's woodshed, and I'll come up in the morning and we'll count it
+and divide, and then we'll hunt up a place out in the woods for it
+where it will be safe. Just you lay quiet here and watch the stuff till
+I run and hook Benny Taylor's little wagon; I won't be gone a minute."
+
+He disappeared, and presently returned with the wagon, put the two
+small sacks into it, threw some old rags on top of them, and started
+off, dragging his cargo behind him. When the boys reached the
+Welshman's house, they stopped to rest. Just as they were about to move
+on, the Welshman stepped out and said:
+
+"Hallo, who's that?"
+
+"Huck and Tom Sawyer."
+
+"Good! Come along with me, boys, you are keeping everybody waiting.
+Here--hurry up, trot ahead--I'll haul the wagon for you. Why, it's not
+as light as it might be. Got bricks in it?--or old metal?"
+
+"Old metal," said Tom.
+
+"I judged so; the boys in this town will take more trouble and fool
+away more time hunting up six bits' worth of old iron to sell to the
+foundry than they would to make twice the money at regular work. But
+that's human nature--hurry along, hurry along!"
+
+The boys wanted to know what the hurry was about.
+
+"Never mind; you'll see, when we get to the Widow Douglas'."
+
+Huck said with some apprehension--for he was long used to being
+falsely accused:
+
+"Mr. Jones, we haven't been doing nothing."
+
+The Welshman laughed.
+
+"Well, I don't know, Huck, my boy. I don't know about that. Ain't you
+and the widow good friends?"
+
+"Yes. Well, she's ben good friends to me, anyway."
+
+"All right, then. What do you want to be afraid for?"
+
+This question was not entirely answered in Huck's slow mind before he
+found himself pushed, along with Tom, into Mrs. Douglas' drawing-room.
+Mr. Jones left the wagon near the door and followed.
+
+The place was grandly lighted, and everybody that was of any
+consequence in the village was there. The Thatchers were there, the
+Harpers, the Rogerses, Aunt Polly, Sid, Mary, the minister, the editor,
+and a great many more, and all dressed in their best. The widow
+received the boys as heartily as any one could well receive two such
+looking beings. They were covered with clay and candle-grease. Aunt
+Polly blushed crimson with humiliation, and frowned and shook her head
+at Tom. Nobody suffered half as much as the two boys did, however. Mr.
+Jones said:
+
+"Tom wasn't at home, yet, so I gave him up; but I stumbled on him and
+Huck right at my door, and so I just brought them along in a hurry."
+
+"And you did just right," said the widow. "Come with me, boys."
+
+She took them to a bedchamber and said:
+
+"Now wash and dress yourselves. Here are two new suits of clothes
+--shirts, socks, everything complete. They're Huck's--no, no thanks,
+Huck--Mr. Jones bought one and I the other. But they'll fit both of you.
+Get into them. We'll wait--come down when you are slicked up enough."
+
+Then she left.
+
+
+
+CHAPTER XXXIV
+
+HUCK said: "Tom, we can slope, if we can find a rope. The window ain't
+high from the ground."
+
+"Shucks! what do you want to slope for?"
+
+"Well, I ain't used to that kind of a crowd. I can't stand it. I ain't
+going down there, Tom."
+
+"Oh, bother! It ain't anything. I don't mind it a bit. I'll take care
+of you."
+
+Sid appeared.
+
+"Tom," said he, "auntie has been waiting for you all the afternoon.
+Mary got your Sunday clothes ready, and everybody's been fretting about
+you. Say--ain't this grease and clay, on your clothes?"
+
+"Now, Mr. Siddy, you jist 'tend to your own business. What's all this
+blow-out about, anyway?"
+
+"It's one of the widow's parties that she's always having. This time
+it's for the Welshman and his sons, on account of that scrape they
+helped her out of the other night. And say--I can tell you something,
+if you want to know."
+
+"Well, what?"
+
+"Why, old Mr. Jones is going to try to spring something on the people
+here to-night, but I overheard him tell auntie to-day about it, as a
+secret, but I reckon it's not much of a secret now. Everybody knows
+--the widow, too, for all she tries to let on she don't. Mr. Jones was
+bound Huck should be here--couldn't get along with his grand secret
+without Huck, you know!"
+
+"Secret about what, Sid?"
+
+"About Huck tracking the robbers to the widow's. I reckon Mr. Jones
+was going to make a grand time over his surprise, but I bet you it will
+drop pretty flat."
+
+Sid chuckled in a very contented and satisfied way.
+
+"Sid, was it you that told?"
+
+"Oh, never mind who it was. SOMEBODY told--that's enough."
+
+"Sid, there's only one person in this town mean enough to do that, and
+that's you. If you had been in Huck's place you'd 'a' sneaked down the
+hill and never told anybody on the robbers. You can't do any but mean
+things, and you can't bear to see anybody praised for doing good ones.
+There--no thanks, as the widow says"--and Tom cuffed Sid's ears and
+helped him to the door with several kicks. "Now go and tell auntie if
+you dare--and to-morrow you'll catch it!"
+
+Some minutes later the widow's guests were at the supper-table, and a
+dozen children were propped up at little side-tables in the same room,
+after the fashion of that country and that day. At the proper time Mr.
+Jones made his little speech, in which he thanked the widow for the
+honor she was doing himself and his sons, but said that there was
+another person whose modesty--
+
+And so forth and so on. He sprung his secret about Huck's share in the
+adventure in the finest dramatic manner he was master of, but the
+surprise it occasioned was largely counterfeit and not as clamorous and
+effusive as it might have been under happier circumstances. However,
+the widow made a pretty fair show of astonishment, and heaped so many
+compliments and so much gratitude upon Huck that he almost forgot the
+nearly intolerable discomfort of his new clothes in the entirely
+intolerable discomfort of being set up as a target for everybody's gaze
+and everybody's laudations.
+
+The widow said she meant to give Huck a home under her roof and have
+him educated; and that when she could spare the money she would start
+him in business in a modest way. Tom's chance was come. He said:
+
+"Huck don't need it. Huck's rich."
+
+Nothing but a heavy strain upon the good manners of the company kept
+back the due and proper complimentary laugh at this pleasant joke. But
+the silence was a little awkward. Tom broke it:
+
+"Huck's got money. Maybe you don't believe it, but he's got lots of
+it. Oh, you needn't smile--I reckon I can show you. You just wait a
+minute."
+
+Tom ran out of doors. The company looked at each other with a
+perplexed interest--and inquiringly at Huck, who was tongue-tied.
+
+"Sid, what ails Tom?" said Aunt Polly. "He--well, there ain't ever any
+making of that boy out. I never--"
+
+Tom entered, struggling with the weight of his sacks, and Aunt Polly
+did not finish her sentence. Tom poured the mass of yellow coin upon
+the table and said:
+
+"There--what did I tell you? Half of it's Huck's and half of it's mine!"
+
+The spectacle took the general breath away. All gazed, nobody spoke
+for a moment. Then there was a unanimous call for an explanation. Tom
+said he could furnish it, and he did. The tale was long, but brimful of
+interest. There was scarcely an interruption from any one to break the
+charm of its flow. When he had finished, Mr. Jones said:
+
+"I thought I had fixed up a little surprise for this occasion, but it
+don't amount to anything now. This one makes it sing mighty small, I'm
+willing to allow."
+
+The money was counted. The sum amounted to a little over twelve
+thousand dollars. It was more than any one present had ever seen at one
+time before, though several persons were there who were worth
+considerably more than that in property.
+
+
+
+CHAPTER XXXV
+
+THE reader may rest satisfied that Tom's and Huck's windfall made a
+mighty stir in the poor little village of St. Petersburg. So vast a
+sum, all in actual cash, seemed next to incredible. It was talked
+about, gloated over, glorified, until the reason of many of the
+citizens tottered under the strain of the unhealthy excitement. Every
+"haunted" house in St. Petersburg and the neighboring villages was
+dissected, plank by plank, and its foundations dug up and ransacked for
+hidden treasure--and not by boys, but men--pretty grave, unromantic
+men, too, some of them. Wherever Tom and Huck appeared they were
+courted, admired, stared at. The boys were not able to remember that
+their remarks had possessed weight before; but now their sayings were
+treasured and repeated; everything they did seemed somehow to be
+regarded as remarkable; they had evidently lost the power of doing and
+saying commonplace things; moreover, their past history was raked up
+and discovered to bear marks of conspicuous originality. The village
+paper published biographical sketches of the boys.
+
+The Widow Douglas put Huck's money out at six per cent., and Judge
+Thatcher did the same with Tom's at Aunt Polly's request. Each lad had
+an income, now, that was simply prodigious--a dollar for every week-day
+in the year and half of the Sundays. It was just what the minister got
+--no, it was what he was promised--he generally couldn't collect it. A
+dollar and a quarter a week would board, lodge, and school a boy in
+those old simple days--and clothe him and wash him, too, for that
+matter.
+
+Judge Thatcher had conceived a great opinion of Tom. He said that no
+commonplace boy would ever have got his daughter out of the cave. When
+Becky told her father, in strict confidence, how Tom had taken her
+whipping at school, the Judge was visibly moved; and when she pleaded
+grace for the mighty lie which Tom had told in order to shift that
+whipping from her shoulders to his own, the Judge said with a fine
+outburst that it was a noble, a generous, a magnanimous lie--a lie that
+was worthy to hold up its head and march down through history breast to
+breast with George Washington's lauded Truth about the hatchet! Becky
+thought her father had never looked so tall and so superb as when he
+walked the floor and stamped his foot and said that. She went straight
+off and told Tom about it.
+
+Judge Thatcher hoped to see Tom a great lawyer or a great soldier some
+day. He said he meant to look to it that Tom should be admitted to the
+National Military Academy and afterward trained in the best law school
+in the country, in order that he might be ready for either career or
+both.
+
+Huck Finn's wealth and the fact that he was now under the Widow
+Douglas' protection introduced him into society--no, dragged him into
+it, hurled him into it--and his sufferings were almost more than he
+could bear. The widow's servants kept him clean and neat, combed and
+brushed, and they bedded him nightly in unsympathetic sheets that had
+not one little spot or stain which he could press to his heart and know
+for a friend. He had to eat with a knife and fork; he had to use
+napkin, cup, and plate; he had to learn his book, he had to go to
+church; he had to talk so properly that speech was become insipid in
+his mouth; whithersoever he turned, the bars and shackles of
+civilization shut him in and bound him hand and foot.
+
+He bravely bore his miseries three weeks, and then one day turned up
+missing. For forty-eight hours the widow hunted for him everywhere in
+great distress. The public were profoundly concerned; they searched
+high and low, they dragged the river for his body. Early the third
+morning Tom Sawyer wisely went poking among some old empty hogsheads
+down behind the abandoned slaughter-house, and in one of them he found
+the refugee. Huck had slept there; he had just breakfasted upon some
+stolen odds and ends of food, and was lying off, now, in comfort, with
+his pipe. He was unkempt, uncombed, and clad in the same old ruin of
+rags that had made him picturesque in the days when he was free and
+happy. Tom routed him out, told him the trouble he had been causing,
+and urged him to go home. Huck's face lost its tranquil content, and
+took a melancholy cast. He said:
+
+"Don't talk about it, Tom. I've tried it, and it don't work; it don't
+work, Tom. It ain't for me; I ain't used to it. The widder's good to
+me, and friendly; but I can't stand them ways. She makes me get up just
+at the same time every morning; she makes me wash, they comb me all to
+thunder; she won't let me sleep in the woodshed; I got to wear them
+blamed clothes that just smothers me, Tom; they don't seem to any air
+git through 'em, somehow; and they're so rotten nice that I can't set
+down, nor lay down, nor roll around anywher's; I hain't slid on a
+cellar-door for--well, it 'pears to be years; I got to go to church and
+sweat and sweat--I hate them ornery sermons! I can't ketch a fly in
+there, I can't chaw. I got to wear shoes all Sunday. The widder eats by
+a bell; she goes to bed by a bell; she gits up by a bell--everything's
+so awful reg'lar a body can't stand it."
+
+"Well, everybody does that way, Huck."
+
+"Tom, it don't make no difference. I ain't everybody, and I can't
+STAND it. It's awful to be tied up so. And grub comes too easy--I don't
+take no interest in vittles, that way. I got to ask to go a-fishing; I
+got to ask to go in a-swimming--dern'd if I hain't got to ask to do
+everything. Well, I'd got to talk so nice it wasn't no comfort--I'd got
+to go up in the attic and rip out awhile, every day, to git a taste in
+my mouth, or I'd a died, Tom. The widder wouldn't let me smoke; she
+wouldn't let me yell, she wouldn't let me gape, nor stretch, nor
+scratch, before folks--" [Then with a spasm of special irritation and
+injury]--"And dad fetch it, she prayed all the time! I never see such a
+woman! I HAD to shove, Tom--I just had to. And besides, that school's
+going to open, and I'd a had to go to it--well, I wouldn't stand THAT,
+Tom. Looky here, Tom, being rich ain't what it's cracked up to be. It's
+just worry and worry, and sweat and sweat, and a-wishing you was dead
+all the time. Now these clothes suits me, and this bar'l suits me, and
+I ain't ever going to shake 'em any more. Tom, I wouldn't ever got into
+all this trouble if it hadn't 'a' ben for that money; now you just take
+my sheer of it along with your'n, and gimme a ten-center sometimes--not
+many times, becuz I don't give a dern for a thing 'thout it's tollable
+hard to git--and you go and beg off for me with the widder."
+
+"Oh, Huck, you know I can't do that. 'Tain't fair; and besides if
+you'll try this thing just a while longer you'll come to like it."
+
+"Like it! Yes--the way I'd like a hot stove if I was to set on it long
+enough. No, Tom, I won't be rich, and I won't live in them cussed
+smothery houses. I like the woods, and the river, and hogsheads, and
+I'll stick to 'em, too. Blame it all! just as we'd got guns, and a
+cave, and all just fixed to rob, here this dern foolishness has got to
+come up and spile it all!"
+
+Tom saw his opportunity--
+
+"Lookyhere, Huck, being rich ain't going to keep me back from turning
+robber."
+
+"No! Oh, good-licks; are you in real dead-wood earnest, Tom?"
+
+"Just as dead earnest as I'm sitting here. But Huck, we can't let you
+into the gang if you ain't respectable, you know."
+
+Huck's joy was quenched.
+
+"Can't let me in, Tom? Didn't you let me go for a pirate?"
+
+"Yes, but that's different. A robber is more high-toned than what a
+pirate is--as a general thing. In most countries they're awful high up
+in the nobility--dukes and such."
+
+"Now, Tom, hain't you always ben friendly to me? You wouldn't shet me
+out, would you, Tom? You wouldn't do that, now, WOULD you, Tom?"
+
+"Huck, I wouldn't want to, and I DON'T want to--but what would people
+say? Why, they'd say, 'Mph! Tom Sawyer's Gang! pretty low characters in
+it!' They'd mean you, Huck. You wouldn't like that, and I wouldn't."
+
+Huck was silent for some time, engaged in a mental struggle. Finally
+he said:
+
+"Well, I'll go back to the widder for a month and tackle it and see if
+I can come to stand it, if you'll let me b'long to the gang, Tom."
+
+"All right, Huck, it's a whiz! Come along, old chap, and I'll ask the
+widow to let up on you a little, Huck."
+
+"Will you, Tom--now will you? That's good. If she'll let up on some of
+the roughest things, I'll smoke private and cuss private, and crowd
+through or bust. When you going to start the gang and turn robbers?"
+
+"Oh, right off. We'll get the boys together and have the initiation
+to-night, maybe."
+
+"Have the which?"
+
+"Have the initiation."
+
+"What's that?"
+
+"It's to swear to stand by one another, and never tell the gang's
+secrets, even if you're chopped all to flinders, and kill anybody and
+all his family that hurts one of the gang."
+
+"That's gay--that's mighty gay, Tom, I tell you."
+
+"Well, I bet it is. And all that swearing's got to be done at
+midnight, in the lonesomest, awfulest place you can find--a ha'nted
+house is the best, but they're all ripped up now."
+
+"Well, midnight's good, anyway, Tom."
+
+"Yes, so it is. And you've got to swear on a coffin, and sign it with
+blood."
+
+"Now, that's something LIKE! Why, it's a million times bullier than
+pirating. I'll stick to the widder till I rot, Tom; and if I git to be
+a reg'lar ripper of a robber, and everybody talking 'bout it, I reckon
+she'll be proud she snaked me in out of the wet."
+
+
+
+CONCLUSION
+
+SO endeth this chronicle. It being strictly a history of a BOY, it
+must stop here; the story could not go much further without becoming
+the history of a MAN. When one writes a novel about grown people, he
+knows exactly where to stop--that is, with a marriage; but when he
+writes of juveniles, he must stop where he best can.
+
+Most of the characters that perform in this book still live, and are
+prosperous and happy. Some day it may seem worth while to take up the
+story of the younger ones again and see what sort of men and women they
+turned out to be; therefore it will be wisest not to reveal any of that
+part of their lives at present.
-- 
cgit v1.3


From 1650ced98f4f6c5f0783f78cb9d0ffd3a6d1768f Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Tue, 12 Apr 2016 22:27:33 +0000
Subject: net: skip failing or flaky TestInterfaces on freebsd-arm

Updates #15262

Change-Id: I3eb1f6f71d6285d039f11ba6a34b8a599a33bf49
Reviewed-on: https://go-review.googlesource.com/21909
Run-TryBot: Brad Fitzpatrick 
Reviewed-by: Matthew Dempsky 
---
 src/net/interface_test.go | 5 +++++
 1 file changed, 5 insertions(+)

(limited to 'src')

diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index 1487acf601..60225506b4 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -5,6 +5,7 @@
 package net
 
 import (
+	"internal/testenv"
 	"reflect"
 	"runtime"
 	"testing"
@@ -56,6 +57,10 @@ type routeStats struct {
 }
 
 func TestInterfaces(t *testing.T) {
+	if runtime.GOOS == "freebsd" && runtime.GOARCH == "arm" {
+		// 100% flaky, actually, at least on some FreeBSD versions
+		testenv.SkipFlaky(t, 15262)
+	}
 	ift, err := Interfaces()
 	if err != nil {
 		t.Fatal(err)
-- 
cgit v1.3


From f0c5b8b9c9c7900033ddb11b584da6198d599454 Mon Sep 17 00:00:00 2001
From: Martin Möhrmann 
Date: Tue, 12 Apr 2016 21:16:27 +0200
Subject: image/color: optimize YCbCrToRGB
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Use one comparison to detect underflow and overflow simultaneously.
Use a shift, bitwise complement and uint8 type conversion to handle
clamping to upper and lower bound without additional branching.

Overall the new code is faster for a mix of
common case, underflow and overflow.

name     old time/op  new time/op  delta
YCbCr-2  1.12ms ± 0%  0.64ms ± 0%  -43.01%  (p=0.000 n=48+47)

name              old time/op  new time/op  delta
YCbCrToRGB/0-2    5.52ns ± 0%  5.77ns ± 0%  +4.48%  (p=0.000 n=50+49)
YCbCrToRGB/128-2  6.05ns ± 0%  5.52ns ± 0%  -8.69%  (p=0.000 n=39+50)
YCbCrToRGB/255-2  5.80ns ± 0%  5.77ns ± 0%  -0.58%  (p=0.000 n=50+49)

Found in collaboration with Josh Bleecher Snyder and Ralph Corderoy.

Change-Id: Ic5020320f704966f545fdc1ae6bc24ddb5d3d09a
Reviewed-on: https://go-review.googlesource.com/21910
Reviewed-by: Josh Bleecher Snyder 
Run-TryBot: Josh Bleecher Snyder 
TryBot-Result: Gobot Gobot 
---
 src/image/color/ycbcr.go             |  49 ++++++---
 src/image/internal/imageutil/gen.go  |  48 ++++++---
 src/image/internal/imageutil/impl.go | 192 +++++++++++++++++++++++------------
 3 files changed, 193 insertions(+), 96 deletions(-)

(limited to 'src')

diff --git a/src/image/color/ycbcr.go b/src/image/color/ycbcr.go
index 904434f6a3..d2c5b569a7 100644
--- a/src/image/color/ycbcr.go
+++ b/src/image/color/ycbcr.go
@@ -44,27 +44,44 @@ func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) {
 	//	B = Y' + 1.77200*(Cb-128)
 	// http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
 
-	yy1 := int32(y) * 0x10100 // Convert 0x12 to 0x121200.
+	yy1 := int32(y) * 0x010100 // Convert 0x12 to 0x121200.
 	cb1 := int32(cb) - 128
 	cr1 := int32(cr) - 128
-	r := (yy1 + 91881*cr1) >> 16
-	g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
-	b := (yy1 + 116130*cb1) >> 16
-	if r < 0 {
-		r = 0
-	} else if r > 0xff {
-		r = 0xff
+
+	// The bit twiddling below is equivalent to
+	//
+	// r := (yy1 + 91881*cr1) >> 16
+	// if r < 0 {
+	//     r = 0
+	// } else if r > 0xff {
+	//     r = ^int32(0)
+	// }
+	//
+	// but uses fewer branches and is faster.
+	// Note that the uint8 type conversion in the return
+	// statement will convert ^int32(0) to 0xff.
+	// The code below to compute b and g uses a similar pattern.
+	r := yy1 + 91881*cr1
+	if uint32(r)&0xff000000 == 0 {
+		r >>= 16
+	} else {
+		r = ^(r >> 31)
 	}
-	if g < 0 {
-		g = 0
-	} else if g > 0xff {
-		g = 0xff
+
+	b := yy1 + 116130*cb1
+	if uint32(b)&0xff000000 == 0 {
+		b >>= 16
+	} else {
+		b = ^(b >> 31)
 	}
-	if b < 0 {
-		b = 0
-	} else if b > 0xff {
-		b = 0xff
+
+	g := yy1 - 22554*cb1 - 46802*cr1
+	if uint32(g)&0xff000000 == 0 {
+		g >>= 16
+	} else {
+		g = ^(g >> 31)
 	}
+
 	return uint8(r), uint8(g), uint8(b)
 }
 
diff --git a/src/image/internal/imageutil/gen.go b/src/image/internal/imageutil/gen.go
index fc1e707f0f..6f8d2b2f5d 100644
--- a/src/image/internal/imageutil/gen.go
+++ b/src/image/internal/imageutil/gen.go
@@ -95,26 +95,42 @@ const sratioCase = `
 			%s
 
 				// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
-				yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+				yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
 				cb1 := int32(src.Cb[ci]) - 128
 				cr1 := int32(src.Cr[ci]) - 128
-				r := (yy1 + 91881*cr1) >> 16
-				g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
-				b := (yy1 + 116130*cb1) >> 16
-				if r < 0 {
-					r = 0
-				} else if r > 255 {
-					r = 255
+
+				// The bit twiddling below is equivalent to
+				//
+				// r := (yy1 + 91881*cr1) >> 16
+				// if r < 0 {
+				//     r = 0
+				// } else if r > 0xff {
+				//     r = ^int32(0)
+				// }
+				//
+				// but uses fewer branches and is faster.
+				// Note that the uint8 type conversion in the return
+				// statement will convert ^int32(0) to 0xff.
+				// The code below to compute b and g uses a similar pattern.
+				r := yy1 + 91881*cr1
+				if uint32(r)&0xff000000 == 0 {
+					r >>= 16
+				} else {
+					r = ^(r >> 31)
 				}
-				if g < 0 {
-					g = 0
-				} else if g > 255 {
-					g = 255
+
+				b := yy1 + 116130*cb1
+				if uint32(b)&0xff000000 == 0 {
+					b >>= 16
+				} else {
+					b = ^(b >> 31)
 				}
-				if b < 0 {
-					b = 0
-				} else if b > 255 {
-					b = 255
+
+				g := yy1 - 22554*cb1 - 46802*cr1
+				if uint32(g)&0xff000000 == 0 {
+					g >>= 16
+				} else {
+					g = ^(g >> 31)
 				}
 
 				dpix[x+0] = uint8(r)
diff --git a/src/image/internal/imageutil/impl.go b/src/image/internal/imageutil/impl.go
index fd7826d4a9..0993f3145c 100644
--- a/src/image/internal/imageutil/impl.go
+++ b/src/image/internal/imageutil/impl.go
@@ -44,26 +44,42 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
 			for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
 
 				// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
-				yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+				yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
 				cb1 := int32(src.Cb[ci]) - 128
 				cr1 := int32(src.Cr[ci]) - 128
-				r := (yy1 + 91881*cr1) >> 16
-				g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
-				b := (yy1 + 116130*cb1) >> 16
-				if r < 0 {
-					r = 0
-				} else if r > 255 {
-					r = 255
+
+				// The bit twiddling below is equivalent to
+				//
+				// r := (yy1 + 91881*cr1) >> 16
+				// if r < 0 {
+				//     r = 0
+				// } else if r > 0xff {
+				//     r = ^int32(0)
+				// }
+				//
+				// but uses fewer branches and is faster.
+				// Note that the uint8 type conversion in the return
+				// statement will convert ^int32(0) to 0xff.
+				// The code below to compute b and g uses a similar pattern.
+				r := yy1 + 91881*cr1
+				if uint32(r)&0xff000000 == 0 {
+					r >>= 16
+				} else {
+					r = ^(r >> 31)
 				}
-				if g < 0 {
-					g = 0
-				} else if g > 255 {
-					g = 255
+
+				b := yy1 + 116130*cb1
+				if uint32(b)&0xff000000 == 0 {
+					b >>= 16
+				} else {
+					b = ^(b >> 31)
 				}
-				if b < 0 {
-					b = 0
-				} else if b > 255 {
-					b = 255
+
+				g := yy1 - 22554*cb1 - 46802*cr1
+				if uint32(g)&0xff000000 == 0 {
+					g >>= 16
+				} else {
+					g = ^(g >> 31)
 				}
 
 				dpix[x+0] = uint8(r)
@@ -83,26 +99,42 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
 				ci := ciBase + sx/2
 
 				// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
-				yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+				yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
 				cb1 := int32(src.Cb[ci]) - 128
 				cr1 := int32(src.Cr[ci]) - 128
-				r := (yy1 + 91881*cr1) >> 16
-				g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
-				b := (yy1 + 116130*cb1) >> 16
-				if r < 0 {
-					r = 0
-				} else if r > 255 {
-					r = 255
+
+				// The bit twiddling below is equivalent to
+				//
+				// r := (yy1 + 91881*cr1) >> 16
+				// if r < 0 {
+				//     r = 0
+				// } else if r > 0xff {
+				//     r = ^int32(0)
+				// }
+				//
+				// but uses fewer branches and is faster.
+				// Note that the uint8 type conversion in the return
+				// statement will convert ^int32(0) to 0xff.
+				// The code below to compute b and g uses a similar pattern.
+				r := yy1 + 91881*cr1
+				if uint32(r)&0xff000000 == 0 {
+					r >>= 16
+				} else {
+					r = ^(r >> 31)
 				}
-				if g < 0 {
-					g = 0
-				} else if g > 255 {
-					g = 255
+
+				b := yy1 + 116130*cb1
+				if uint32(b)&0xff000000 == 0 {
+					b >>= 16
+				} else {
+					b = ^(b >> 31)
 				}
-				if b < 0 {
-					b = 0
-				} else if b > 255 {
-					b = 255
+
+				g := yy1 - 22554*cb1 - 46802*cr1
+				if uint32(g)&0xff000000 == 0 {
+					g >>= 16
+				} else {
+					g = ^(g >> 31)
 				}
 
 				dpix[x+0] = uint8(r)
@@ -122,26 +154,42 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
 				ci := ciBase + sx/2
 
 				// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
-				yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+				yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
 				cb1 := int32(src.Cb[ci]) - 128
 				cr1 := int32(src.Cr[ci]) - 128
-				r := (yy1 + 91881*cr1) >> 16
-				g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
-				b := (yy1 + 116130*cb1) >> 16
-				if r < 0 {
-					r = 0
-				} else if r > 255 {
-					r = 255
+
+				// The bit twiddling below is equivalent to
+				//
+				// r := (yy1 + 91881*cr1) >> 16
+				// if r < 0 {
+				//     r = 0
+				// } else if r > 0xff {
+				//     r = ^int32(0)
+				// }
+				//
+				// but uses fewer branches and is faster.
+				// Note that the uint8 type conversion in the return
+				// statement will convert ^int32(0) to 0xff.
+				// The code below to compute b and g uses a similar pattern.
+				r := yy1 + 91881*cr1
+				if uint32(r)&0xff000000 == 0 {
+					r >>= 16
+				} else {
+					r = ^(r >> 31)
 				}
-				if g < 0 {
-					g = 0
-				} else if g > 255 {
-					g = 255
+
+				b := yy1 + 116130*cb1
+				if uint32(b)&0xff000000 == 0 {
+					b >>= 16
+				} else {
+					b = ^(b >> 31)
 				}
-				if b < 0 {
-					b = 0
-				} else if b > 255 {
-					b = 255
+
+				g := yy1 - 22554*cb1 - 46802*cr1
+				if uint32(g)&0xff000000 == 0 {
+					g >>= 16
+				} else {
+					g = ^(g >> 31)
 				}
 
 				dpix[x+0] = uint8(r)
@@ -160,26 +208,42 @@ func DrawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po
 			for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 {
 
 				// This is an inline version of image/color/ycbcr.go's func YCbCrToRGB.
-				yy1 := int32(src.Y[yi]) * 0x10100 // Convert 0x12 to 0x121200.
+				yy1 := int32(src.Y[yi]) * 0x010100 // Convert 0x12 to 0x121200.
 				cb1 := int32(src.Cb[ci]) - 128
 				cr1 := int32(src.Cr[ci]) - 128
-				r := (yy1 + 91881*cr1) >> 16
-				g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
-				b := (yy1 + 116130*cb1) >> 16
-				if r < 0 {
-					r = 0
-				} else if r > 255 {
-					r = 255
+
+				// The bit twiddling below is equivalent to
+				//
+				// r := (yy1 + 91881*cr1) >> 16
+				// if r < 0 {
+				//     r = 0
+				// } else if r > 0xff {
+				//     r = ^int32(0)
+				// }
+				//
+				// but uses fewer branches and is faster.
+				// Note that the uint8 type conversion in the return
+				// statement will convert ^int32(0) to 0xff.
+				// The code below to compute b and g uses a similar pattern.
+				r := yy1 + 91881*cr1
+				if uint32(r)&0xff000000 == 0 {
+					r >>= 16
+				} else {
+					r = ^(r >> 31)
 				}
-				if g < 0 {
-					g = 0
-				} else if g > 255 {
-					g = 255
+
+				b := yy1 + 116130*cb1
+				if uint32(b)&0xff000000 == 0 {
+					b >>= 16
+				} else {
+					b = ^(b >> 31)
 				}
-				if b < 0 {
-					b = 0
-				} else if b > 255 {
-					b = 255
+
+				g := yy1 - 22554*cb1 - 46802*cr1
+				if uint32(g)&0xff000000 == 0 {
+					g >>= 16
+				} else {
+					g = ^(g >> 31)
 				}
 
 				dpix[x+0] = uint8(r)
-- 
cgit v1.3


From 982274c96d6c9ad88a9deb07583d3b74ec2df357 Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor 
Date: Tue, 12 Apr 2016 15:47:17 -0700
Subject: reflect: test that Call results are not addressable

Gccgo was erroneously marking Call results as addressable, which led to
an obscure bug using text/template, as text/template calls CanAddr to
check whether to take the address of a value when looking up methods.
When a function returned a pointer, and CanAddr was true, the result was
a pointer to a pointer that had no methods.

Fixed in gccgo by https://golang.org/cl/21908.  Adding the test here so
that it doesn't regress.

Change-Id: I1d25b868e1b8e2348b21cbac6404a636376d1a4a
Reviewed-on: https://go-review.googlesource.com/21930
Run-TryBot: Ian Lance Taylor 
Reviewed-by: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
---
 src/reflect/all_test.go | 6 ++++++
 1 file changed, 6 insertions(+)

(limited to 'src')

diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 4a76ef8608..e1b26d9c68 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -1476,6 +1476,12 @@ func TestFunc(t *testing.T) {
 	if i != 10 || j != 20 || k != 30 || l != (two{40, 50}) || m != 60 || n != 70 || o != 80 {
 		t.Errorf("Call returned %d, %d, %d, %v, %d, %g, %d; want 10, 20, 30, [40, 50], 60, 70, 80", i, j, k, l, m, n, o)
 	}
+
+	for i, v := range ret {
+		if v.CanAddr() {
+			t.Errorf("result %d is addressable", i)
+		}
+	}
 }
 
 type emptyStruct struct{}
-- 
cgit v1.3


From a0ab6cd6852cec430e280217a9516d6be3c1ef5f Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Tue, 12 Apr 2016 00:22:39 +0000
Subject: net/http: add test that panic in a handler signals an error to the
 client

Change-Id: Iba40edc9ddad62534b06c5af20bbc3dd3dc14d0a
Reviewed-on: https://go-review.googlesource.com/21881
Reviewed-by: Andrew Gerrand 
Run-TryBot: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
---
 src/net/http/clientserver_test.go | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

(limited to 'src')

diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go
index c2bab378e3..f721382365 100644
--- a/src/net/http/clientserver_test.go
+++ b/src/net/http/clientserver_test.go
@@ -1123,6 +1123,34 @@ func testBogusStatusWorks(t *testing.T, h2 bool) {
 	}
 }
 
+func TestInterruptWithPanic_h1(t *testing.T) { testInterruptWithPanic(t, h1Mode) }
+func TestInterruptWithPanic_h2(t *testing.T) { testInterruptWithPanic(t, h2Mode) }
+func testInterruptWithPanic(t *testing.T, h2 bool) {
+	log.SetOutput(ioutil.Discard) // is noisy otherwise
+	defer log.SetOutput(os.Stderr)
+
+	const msg = "hello"
+	defer afterTest(t)
+	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+		io.WriteString(w, msg)
+		w.(Flusher).Flush()
+		panic("no more")
+	}))
+	defer cst.close()
+	res, err := cst.c.Get(cst.ts.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	slurp, err := ioutil.ReadAll(res.Body)
+	if string(slurp) != msg {
+		t.Errorf("client read %q; want %q", slurp, msg)
+	}
+	if err == nil {
+		t.Errorf("client read all successfully; want some error")
+	}
+}
+
 type noteCloseConn struct {
 	net.Conn
 	closeFunc func()
-- 
cgit v1.3


From b0cbe158da10aac1876680e825a902d58a9d1bac Mon Sep 17 00:00:00 2001
From: Shahar Kohanim 
Date: Mon, 11 Apr 2016 22:19:34 +0300
Subject: cmd/link: move function only lsym fields to pcln struct
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

name       old secs    new secs    delta
LinkCmdGo   0.53 ± 9%   0.53 ±10%  -1.30%  (p=0.022 n=100+99)

name       old MaxRSS  new MaxRSS  delta
LinkCmdGo   151k ± 4%   142k ± 6%  -5.92%  (p=0.000 n=98+100)

Change-Id: Ic30e63a948f8e626b3396f458a0163f7234810c1
Reviewed-on: https://go-review.googlesource.com/21920
Run-TryBot: Ian Lance Taylor 
TryBot-Result: Gobot Gobot 
Reviewed-by: Ian Lance Taylor 
---
 src/cmd/link/internal/ld/deadcode.go |  7 +++++--
 src/cmd/link/internal/ld/dwarf.go    |  2 +-
 src/cmd/link/internal/ld/lib.go      | 17 ++++++++++++++---
 src/cmd/link/internal/ld/link.go     |  6 +++---
 src/cmd/link/internal/ld/objfile.go  | 13 +++++++------
 src/cmd/link/internal/ld/pcln.go     |  6 +++++-
 6 files changed, 35 insertions(+), 16 deletions(-)

(limited to 'src')

diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 8b2d0d447e..83e4cdc077 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -272,9 +272,12 @@ func (d *deadcodepass) flood() {
 			if Debug['v'] > 1 {
 				fmt.Fprintf(d.ctxt.Bso, "marktext %s\n", s.Name)
 			}
-			for _, a := range s.Autom {
-				d.mark(a.Gotype, s)
+			if s.Pcln != nil {
+				for _, a := range s.Pcln.Autom {
+					d.mark(a.Gotype, s)
+				}
 			}
+
 		}
 
 		if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' {
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index a3a931f94c..82689988c5 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1556,7 +1556,7 @@ func writelines(prev *LSym) *LSym {
 			dt, da int
 			offs   int64
 		)
-		for _, a := range s.Autom {
+		for _, a := range s.Pcln.Autom {
 			switch a.Name {
 			case obj.A_AUTO:
 				dt = DW_ABRV_AUTO
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 1f2df8b9c5..db34e68404 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1747,7 +1747,11 @@ func stkcheck(up *Chain, depth int) int {
 			return 0
 		}
 		// Raise limit to allow frame.
-		limit = int(obj.StackLimit+s.Locals) + int(Ctxt.FixedFrameSize())
+		locals := int32(0)
+		if s.Pcln != nil {
+			locals = s.Pcln.Locals
+		}
+		limit = int(obj.StackLimit+locals) + int(Ctxt.FixedFrameSize())
 	}
 
 	// Walk through sp adjustments in function, consuming relocs.
@@ -1978,10 +1982,17 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
 	for s := Ctxt.Textp; s != nil; s = s.Next {
 		put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype)
 
+		locals := int32(0)
+		if s.Pcln != nil {
+			locals = s.Pcln.Locals
+		}
 		// NOTE(ality): acid can't produce a stack trace without .frame symbols
-		put(nil, ".frame", 'm', int64(s.Locals)+int64(SysArch.PtrSize), 0, 0, nil)
+		put(nil, ".frame", 'm', int64(locals)+int64(SysArch.PtrSize), 0, 0, nil)
 
-		for _, a := range s.Autom {
+		if s.Pcln == nil {
+			continue
+		}
+		for _, a := range s.Pcln.Autom {
 			// Emit a or p according to actual offset, even if label is wrong.
 			// This avoids negative offsets, which cannot be encoded.
 			if a.Name != obj.A_AUTO && a.Name != obj.A_PARAM {
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 52b52f1cc0..93454fb4b2 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -50,8 +50,6 @@ type LSym struct {
 	Align       int32
 	Elfsym      int32
 	LocalElfsym int32
-	Args        int32
-	Locals      int32
 	Value       int64
 	Size        int64
 	// ElfType is set for symbols read from shared libraries by ldshlibsyms. It
@@ -67,7 +65,6 @@ type LSym struct {
 	Dynimplib   string
 	Dynimpvers  string
 	Sect        *Section
-	Autom       []Auto
 	Pcln        *Pcln
 	P           []byte
 	R           []Reloc
@@ -221,6 +218,9 @@ type Library struct {
 }
 
 type Pcln struct {
+	Args        int32
+	Locals      int32
+	Autom       []Auto
 	Pcsp        Pcdata
 	Pcfile      Pcdata
 	Pcline      Pcdata
diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
index 578afd4c74..eacccb59fb 100644
--- a/src/cmd/link/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -331,8 +331,11 @@ overwrite:
 	}
 
 	if s.Type == obj.STEXT {
-		s.Args = r.readInt32()
-		s.Locals = r.readInt32()
+		s.Pcln = new(Pcln)
+		pc := s.Pcln
+
+		pc.Args = r.readInt32()
+		pc.Locals = r.readInt32()
 		if r.readUint8() != 0 {
 			s.Attr |= AttrNoSplit
 		}
@@ -341,13 +344,13 @@ overwrite:
 			s.Attr |= AttrReflectMethod
 		}
 		n := r.readInt()
-		s.Autom = r.autom[:n:n]
+		pc.Autom = r.autom[:n:n]
 		if !isdup {
 			r.autom = r.autom[n:]
 		}
 
 		for i := 0; i < n; i++ {
-			s.Autom[i] = Auto{
+			pc.Autom[i] = Auto{
 				Asym:    r.readSymIndex(),
 				Aoffset: r.readInt32(),
 				Name:    r.readInt16(),
@@ -355,8 +358,6 @@ overwrite:
 			}
 		}
 
-		s.Pcln = new(Pcln)
-		pc := s.Pcln
 		pc.Pcsp.P = r.readData()
 		pc.Pcfile.P = r.readData()
 		pc.Pcline.P = r.readData()
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 9a947c7c07..3ef52444af 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -293,7 +293,11 @@ func pclntab() {
 
 		// args int32
 		// TODO: Move into funcinfo.
-		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args)))
+		args := uint32(0)
+		if Ctxt.Cursym.Pcln != nil {
+			args = uint32(Ctxt.Cursym.Pcln.Args)
+		}
+		off = int32(setuint32(Ctxt, ftab, int64(off), args))
 
 		// frame int32
 		// This has been removed (it was never set quite correctly anyway).
-- 
cgit v1.3


From 260b7daf0a3fa1548d976f2484325240d4bdb73a Mon Sep 17 00:00:00 2001
From: Keith Randall 
Date: Tue, 12 Apr 2016 16:25:48 -0700
Subject: cmd/compile: fix arg to getcallerpc

getcallerpc's arg needs to point to the first argument slot.
I believe this bug was introduced by Michel's itab changes
(specifically https://go-review.googlesource.com/c/20902).

Fixes #15145

Change-Id: Ifb2e17f3658e2136c7950dfc789b4d5706320683
Reviewed-on: https://go-review.googlesource.com/21931
Reviewed-by: Michel Lespinasse 
---
 src/runtime/iface.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index 3ce1e237d3..a4c962fb7a 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -173,7 +173,7 @@ func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e eface) {
 func convT2I(tab *itab, elem unsafe.Pointer, x unsafe.Pointer) (i iface) {
 	t := tab._type
 	if raceenabled {
-		raceReadObjectPC(t, elem, getcallerpc(unsafe.Pointer(&t)), funcPC(convT2I))
+		raceReadObjectPC(t, elem, getcallerpc(unsafe.Pointer(&tab)), funcPC(convT2I))
 	}
 	if msanenabled {
 		msanread(elem, t.size)
-- 
cgit v1.3


From db5338f87982086a19310ad6e25c046280644b98 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Tue, 12 Apr 2016 17:12:26 -0700
Subject: cmd/compile: teach CSE that new objects are bespoke
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

runtime.newobject never returns the same thing twice,
so the resulting value will never be a common subexpression.

This helps when compiling large static data structures
that include pointers, such as maps and slices.
No clear performance impact on other code. (See below.)

For the code in issue #15112:

Before:
  real	1m14.238s
  user	1m18.985s
  sys	0m0.787s

After:
  real	0m47.172s
  user	0m52.248s
  sys	0m0.767s

For the code in issue #15235, size 10k:

Before:
  real	0m44.916s
  user	0m46.577s
  sys	0m0.304s

After:
  real	0m7.703s
  user	0m9.041s
  sys	0m0.316s

Still more work to be done, particularly for #15112.

Updates #15112
Updates #15235


name       old time/op      new time/op      delta
Template        330ms ±11%       333ms ±13%    ~           (p=0.749 n=20+19)
Unicode         148ms ± 6%       152ms ± 8%    ~           (p=0.072 n=18+20)
GoTypes         1.01s ± 7%       1.01s ± 3%    ~           (p=0.583 n=20+20)
Compiler        5.04s ± 2%       5.06s ± 2%    ~           (p=0.314 n=20+20)

name       old user-ns/op   new user-ns/op   delta
Template   444user-ms ±11%  445user-ms ±10%    ~           (p=0.738 n=20+20)
Unicode    215user-ms ± 5%  218user-ms ± 5%    ~           (p=0.239 n=18+18)
GoTypes    1.45user-s ± 3%  1.45user-s ± 4%    ~           (p=0.620 n=20+20)
Compiler   7.23user-s ± 2%  7.22user-s ± 2%    ~           (p=0.901 n=20+19)

name       old alloc/op     new alloc/op     delta
Template       55.0MB ± 0%      55.0MB ± 0%    ~           (p=0.547 n=20+20)
Unicode        37.6MB ± 0%      37.6MB ± 0%    ~           (p=0.301 n=20+20)
GoTypes         177MB ± 0%       177MB ± 0%    ~           (p=0.065 n=20+19)
Compiler        798MB ± 0%       797MB ± 0%  -0.05%        (p=0.000 n=19+20)

name       old allocs/op    new allocs/op    delta
Template         492k ± 0%        493k ± 0%  +0.03%        (p=0.030 n=20+20)
Unicode          377k ± 0%        377k ± 0%    ~           (p=0.423 n=20+19)
GoTypes         1.40M ± 0%       1.40M ± 0%    ~           (p=0.102 n=20+20)
Compiler        5.53M ± 0%       5.53M ± 0%    ~           (p=0.094 n=17+18)

name       old text-bytes   new text-bytes   delta
HelloSize        561k ± 0%        561k ± 0%    ~     (all samples are equal)
CmdGoSize       6.13M ± 0%       6.13M ± 0%    ~     (all samples are equal)

name       old data-bytes   new data-bytes   delta
HelloSize        128k ± 0%        128k ± 0%    ~     (all samples are equal)
CmdGoSize        306k ± 0%        306k ± 0%    ~     (all samples are equal)

name       old exe-bytes    new exe-bytes    delta
HelloSize        905k ± 0%        905k ± 0%    ~     (all samples are equal)
CmdGoSize       9.64M ± 0%       9.64M ± 0%    ~     (all samples are equal)

Change-Id: Id774e2901d7701a3ec7a1c1d1cf1d9327a4107fc
Reviewed-on: https://go-review.googlesource.com/21937
Run-TryBot: Josh Bleecher Snyder 
TryBot-Result: Gobot Gobot 
Reviewed-by: Todd Neal 
---
 src/cmd/compile/internal/gc/subr.go    | 4 ++++
 src/cmd/compile/internal/ssa/config.go | 6 ++++++
 src/cmd/compile/internal/ssa/cse.go    | 8 ++++++++
 3 files changed, 18 insertions(+)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 035bd815c2..091762f496 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -1081,6 +1081,10 @@ func syslook(name string) *Node {
 	return s.Def
 }
 
+func (s *Sym) IsRuntimeCall(name string) bool {
+	return s.Pkg == Runtimepkg && s.Name == name
+}
+
 // typehash computes a hash value for type t to use in type switch
 // statements.
 func typehash(t *Type) uint32 {
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 2a676e39b3..a60291ea53 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -116,6 +116,12 @@ type GCNode interface {
 	String() string
 }
 
+// GCSym is an interface that *gc.Sym implements.
+// Using *gc.Sym directly would lead to import cycles.
+type GCSym interface {
+	IsRuntimeCall(name string) bool
+}
+
 // NewConfig returns a new configuration object for the given architecture.
 func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config {
 	c := &Config{arch: arch, fe: fe}
diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
index 1ec5712be0..9853ff06d0 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -255,6 +255,14 @@ func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp {
 		return lt2Cmp(v.Block.ID < w.Block.ID)
 	}
 
+	switch v.Op {
+	case OpStaticCall, OpAMD64CALLstatic, OpARMCALLstatic:
+		sym := v.Aux.(GCSym)
+		if sym.IsRuntimeCall("newobject") {
+			return lt2Cmp(v.ID < w.ID)
+		}
+	}
+
 	if tc := v.Type.Compare(w.Type); tc != CMPeq {
 		return tc
 	}
-- 
cgit v1.3


From 3f66d8c84b4b3d685db1031954d3343a7a8c9d0f Mon Sep 17 00:00:00 2001
From: Emmanuel Odeke 
Date: Tue, 12 Apr 2016 01:55:14 -0700
Subject: html/template: add examples of loading templates from files

Adds examples showing loading templates from files and
executing them.

Shows examples:
- Using ParseGlob.
- Using ParseFiles.
- Using helper functions to share and use templates
in different contexts by adding them to an existing
bundle of templates.
- Using a group of driver templates with distinct sets
of helper templates.

Almost all of the code was directly copied from text/template.

Fixes #8500

Change-Id: Ic3d91d5232afc5a1cd2d8cd3d9a5f3b754c64225
Reviewed-on: https://go-review.googlesource.com/21854
Reviewed-by: Andrew Gerrand 
---
 src/html/template/examplefiles_test.go | 226 +++++++++++++++++++++++++++++++++
 1 file changed, 226 insertions(+)
 create mode 100644 src/html/template/examplefiles_test.go

(limited to 'src')

diff --git a/src/html/template/examplefiles_test.go b/src/html/template/examplefiles_test.go
new file mode 100644
index 0000000000..60518aee9e
--- /dev/null
+++ b/src/html/template/examplefiles_test.go
@@ -0,0 +1,226 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package template_test
+
+import (
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"text/template"
+)
+
+// templateFile defines the contents of a template to be stored in a file, for testing.
+type templateFile struct {
+	name     string
+	contents string
+}
+
+func createTestDir(files []templateFile) string {
+	dir, err := ioutil.TempDir("", "template")
+	if err != nil {
+		log.Fatal(err)
+	}
+	for _, file := range files {
+		f, err := os.Create(filepath.Join(dir, file.name))
+		if err != nil {
+			log.Fatal(err)
+		}
+		defer f.Close()
+		_, err = io.WriteString(f, file.contents)
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+	return dir
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// Here we demonstrate loading a set of templates from a directory.
+func ExampleTemplate_glob() {
+	// Here we create a temporary directory and populate it with our sample
+	// template definition files; usually the template files would already
+	// exist in some location known to the program.
+	dir := createTestDir([]templateFile{
+		// T0.tmpl is a plain template file that just invokes T1.
+		{"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
+		// T1.tmpl defines a template, T1 that invokes T2.
+		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+		// T2.tmpl defines a template T2.
+		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+	})
+	// Clean up after the test; another quirk of running as an example.
+	defer os.RemoveAll(dir)
+
+	// pattern is the glob pattern used to find all the template files.
+	pattern := filepath.Join(dir, "*.tmpl")
+
+	// Here starts the example proper.
+	// T0.tmpl is the first name matched, so it becomes the starting template,
+	// the value returned by ParseGlob.
+	tmpl := template.Must(template.ParseGlob(pattern))
+
+	err := tmpl.Execute(os.Stdout, nil)
+	if err != nil {
+		log.Fatalf("template execution: %s", err)
+	}
+	// Output:
+	// T0 invokes T1: (T1 invokes T2: (This is T2))
+}
+
+// Here we demonstrate loading a set of templates from files in different directories
+func ExampleTemplate_parsefiles() {
+	// Here we create different temporary directories and populate them with our sample
+	// template definition files; usually the template files would already
+	// exist in some location known to the program.
+	dir1 := createTestDir([]templateFile{
+		// T1.tmpl is a plain template file that just invokes T2.
+		{"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`},
+	})
+
+	dir2 := createTestDir([]templateFile{
+		// T2.tmpl defines a template T2.
+		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+	})
+
+	// Clean up after the test; another quirk of running as an example.
+	defer func(dirs ...string) {
+		for _, dir := range dirs {
+			os.RemoveAll(dir)
+		}
+	}(dir1, dir2)
+
+	// Here starts the example proper.
+	// Let's just parse only dir1/T0 and dir2/T2
+	paths := []string{
+		filepath.Join(dir1, "T1.tmpl"),
+		filepath.Join(dir2, "T2.tmpl"),
+	}
+	tmpl := template.Must(template.ParseFiles(paths...))
+
+	err := tmpl.Execute(os.Stdout, nil)
+	if err != nil {
+		log.Fatalf("template execution: %s", err)
+	}
+	// Output:
+	// T1 invokes T2: (This is T2)
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// This example demonstrates one way to share some templates
+// and use them in different contexts. In this variant we add multiple driver
+// templates by hand to an existing bundle of templates.
+func ExampleTemplate_helpers() {
+	// Here we create a temporary directory and populate it with our sample
+	// template definition files; usually the template files would already
+	// exist in some location known to the program.
+	dir := createTestDir([]templateFile{
+		// T1.tmpl defines a template, T1 that invokes T2.
+		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+		// T2.tmpl defines a template T2.
+		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+	})
+	// Clean up after the test; another quirk of running as an example.
+	defer os.RemoveAll(dir)
+
+	// pattern is the glob pattern used to find all the template files.
+	pattern := filepath.Join(dir, "*.tmpl")
+
+	// Here starts the example proper.
+	// Load the helpers.
+	templates := template.Must(template.ParseGlob(pattern))
+	// Add one driver template to the bunch; we do this with an explicit template definition.
+	_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
+	if err != nil {
+		log.Fatal("parsing driver1: ", err)
+	}
+	// Add another driver template.
+	_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
+	if err != nil {
+		log.Fatal("parsing driver2: ", err)
+	}
+	// We load all the templates before execution. This package does not require
+	// that behavior but html/template's escaping does, so it's a good habit.
+	err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
+	if err != nil {
+		log.Fatalf("driver1 execution: %s", err)
+	}
+	err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
+	if err != nil {
+		log.Fatalf("driver2 execution: %s", err)
+	}
+	// Output:
+	// Driver 1 calls T1: (T1 invokes T2: (This is T2))
+	// Driver 2 calls T2: (This is T2)
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// This example demonstrates how to use one group of driver
+// templates with distinct sets of helper templates.
+func ExampleTemplate_share() {
+	// Here we create a temporary directory and populate it with our sample
+	// template definition files; usually the template files would already
+	// exist in some location known to the program.
+	dir := createTestDir([]templateFile{
+		// T0.tmpl is a plain template file that just invokes T1.
+		{"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
+		// T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
+		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+	})
+	// Clean up after the test; another quirk of running as an example.
+	defer os.RemoveAll(dir)
+
+	// pattern is the glob pattern used to find all the template files.
+	pattern := filepath.Join(dir, "*.tmpl")
+
+	// Here starts the example proper.
+	// Load the drivers.
+	drivers := template.Must(template.ParseGlob(pattern))
+
+	// We must define an implementation of the T2 template. First we clone
+	// the drivers, then add a definition of T2 to the template name space.
+
+	// 1. Clone the helper set to create a new name space from which to run them.
+	first, err := drivers.Clone()
+	if err != nil {
+		log.Fatal("cloning helpers: ", err)
+	}
+	// 2. Define T2, version A, and parse it.
+	_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
+	if err != nil {
+		log.Fatal("parsing T2: ", err)
+	}
+
+	// Now repeat the whole thing, using a different version of T2.
+	// 1. Clone the drivers.
+	second, err := drivers.Clone()
+	if err != nil {
+		log.Fatal("cloning drivers: ", err)
+	}
+	// 2. Define T2, version B, and parse it.
+	_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
+	if err != nil {
+		log.Fatal("parsing T2: ", err)
+	}
+
+	// Execute the templates in the reverse order to verify the
+	// first is unaffected by the second.
+	err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
+	if err != nil {
+		log.Fatalf("second execution: %s", err)
+	}
+	err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
+	if err != nil {
+		log.Fatalf("first: execution: %s", err)
+	}
+
+	// Output:
+	// T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
+	// T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
+}
-- 
cgit v1.3


From 819e0b29bbbc07022e7b94c12b55860466a02e5b Mon Sep 17 00:00:00 2001
From: Martin Möhrmann 
Date: Sat, 26 Mar 2016 00:04:48 +0100
Subject: strings: improve explode and correct comment
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Merges explodetests into splittests which already contain
some of the tests that cover explode.

Adds a test to cover the utf8.RuneError branch in explode.

name      old time/op  new time/op  delta
Split1-2  14.9ms ± 0%  14.2ms ± 0%  -4.06%  (p=0.000 n=47+49)

Change-Id: I00f796bd2edab70e926ea9e65439d820c6a28254
Reviewed-on: https://go-review.googlesource.com/21609
Run-TryBot: Ian Lance Taylor 
TryBot-Result: Gobot Gobot 
Reviewed-by: Ian Lance Taylor 
---
 src/strings/strings.go      | 27 ++++++++++-----------------
 src/strings/strings_test.go | 37 ++++++++-----------------------------
 2 files changed, 18 insertions(+), 46 deletions(-)

(limited to 'src')

diff --git a/src/strings/strings.go b/src/strings/strings.go
index c24c77b9dd..919e8c8354 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -12,32 +12,25 @@ import (
 	"unicode/utf8"
 )
 
-// explode splits s into an array of UTF-8 sequences, one per Unicode character (still strings) up to a maximum of n (n < 0 means no limit).
-// Invalid UTF-8 sequences become correct encodings of U+FFF8.
+// explode splits s into a slice of UTF-8 strings,
+// one string per Unicode character up to a maximum of n (n < 0 means no limit).
+// Invalid UTF-8 sequences become correct encodings of U+FFFD.
 func explode(s string, n int) []string {
-	if n == 0 {
-		return nil
-	}
 	l := utf8.RuneCountInString(s)
-	if n <= 0 || n > l {
+	if n < 0 || n > l {
 		n = l
 	}
 	a := make([]string, n)
-	var size int
-	var ch rune
-	i, cur := 0, 0
-	for ; i+1 < n; i++ {
-		ch, size = utf8.DecodeRuneInString(s[cur:])
+	for i := 0; i < n-1; i++ {
+		ch, size := utf8.DecodeRuneInString(s)
+		a[i] = s[:size]
+		s = s[size:]
 		if ch == utf8.RuneError {
 			a[i] = string(utf8.RuneError)
-		} else {
-			a[i] = s[cur : cur+size]
 		}
-		cur += size
 	}
-	// add the rest, if there is any
-	if cur < len(s) {
-		a[i] = s[cur:]
+	if n > 0 {
+		a[n-1] = s
 	}
 	return a
 }
diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go
index 0572adbdd9..1ed803bf85 100644
--- a/src/strings/strings_test.go
+++ b/src/strings/strings_test.go
@@ -256,31 +256,6 @@ func BenchmarkIndexByte(b *testing.B) {
 	}
 }
 
-var explodetests = []struct {
-	s string
-	n int
-	a []string
-}{
-	{"", -1, []string{}},
-	{abcd, 4, []string{"a", "b", "c", "d"}},
-	{faces, 3, []string{"☺", "☻", "☹"}},
-	{abcd, 2, []string{"a", "bcd"}},
-}
-
-func TestExplode(t *testing.T) {
-	for _, tt := range explodetests {
-		a := SplitN(tt.s, "", tt.n)
-		if !eq(a, tt.a) {
-			t.Errorf("explode(%q, %d) = %v; want %v", tt.s, tt.n, a, tt.a)
-			continue
-		}
-		s := Join(a, "")
-		if s != tt.s {
-			t.Errorf(`Join(explode(%q, %d), "") = %q`, tt.s, tt.n, s)
-		}
-	}
-}
-
 type SplitTest struct {
 	s   string
 	sep string
@@ -289,19 +264,23 @@ type SplitTest struct {
 }
 
 var splittests = []SplitTest{
+	{"", "", -1, []string{}},
+	{abcd, "", 2, []string{"a", "bcd"}},
+	{abcd, "", 4, []string{"a", "b", "c", "d"}},
+	{abcd, "", -1, []string{"a", "b", "c", "d"}},
+	{faces, "", -1, []string{"☺", "☻", "☹"}},
+	{faces, "", 3, []string{"☺", "☻", "☹"}},
+	{faces, "", 17, []string{"☺", "☻", "☹"}},
+	{"☺�☹", "", -1, []string{"☺", "�", "☹"}},
 	{abcd, "a", 0, nil},
 	{abcd, "a", -1, []string{"", "bcd"}},
 	{abcd, "z", -1, []string{"abcd"}},
-	{abcd, "", -1, []string{"a", "b", "c", "d"}},
 	{commas, ",", -1, []string{"1", "2", "3", "4"}},
 	{dots, "...", -1, []string{"1", ".2", ".3", ".4"}},
 	{faces, "☹", -1, []string{"☺☻", ""}},
 	{faces, "~", -1, []string{faces}},
-	{faces, "", -1, []string{"☺", "☻", "☹"}},
 	{"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}},
 	{"1 2", " ", 3, []string{"1", "2"}},
-	{"123", "", 2, []string{"1", "23"}},
-	{"123", "", 17, []string{"1", "2", "3"}},
 }
 
 func TestSplit(t *testing.T) {
-- 
cgit v1.3


From 381e5eee39edfb3a43e294023957aff05e9ff349 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Wed, 13 Apr 2016 04:35:37 +0000
Subject: all: use new io.SeekFoo constants instead of os.SEEK_FOO

Automated change.

Fixes #15269

Change-Id: I8deb2ac0101d3f7c390467ceb0a1561b72edbb2f
Reviewed-on: https://go-review.googlesource.com/21962
Run-TryBot: Brad Fitzpatrick 
Reviewed-by: Andrew Gerrand 
TryBot-Result: Gobot Gobot 
---
 src/archive/tar/reader.go     |  5 ++---
 src/archive/zip/reader.go     |  2 +-
 src/bytes/reader_test.go      | 21 ++++++++++-----------
 src/debug/elf/file.go         |  8 ++++----
 src/debug/pe/file.go          |  8 ++++----
 src/net/http/fs.go            | 10 +++++-----
 src/net/sendfile_dragonfly.go |  2 +-
 src/net/sendfile_freebsd.go   |  2 +-
 src/net/sendfile_solaris.go   |  2 +-
 src/os/exec/exec_test.go      |  2 +-
 src/strings/reader_test.go    | 21 ++++++++++-----------
 11 files changed, 40 insertions(+), 43 deletions(-)

(limited to 'src')

diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index c8cb69a178..741fe0152b 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -13,7 +13,6 @@ import (
 	"io"
 	"io/ioutil"
 	"math"
-	"os"
 	"strconv"
 	"strings"
 	"time"
@@ -523,10 +522,10 @@ func (tr *Reader) skipUnread() error {
 		// io.Seeker, but calling Seek always returns an error and performs
 		// no action. Thus, we try an innocent seek to the current position
 		// to see if Seek is really supported.
-		pos1, err := sr.Seek(0, os.SEEK_CUR)
+		pos1, err := sr.Seek(0, io.SeekCurrent)
 		if err == nil {
 			// Seek seems supported, so perform the real Seek.
-			pos2, err := sr.Seek(dataSkip-1, os.SEEK_CUR)
+			pos2, err := sr.Seek(dataSkip-1, io.SeekCurrent)
 			if err != nil {
 				tr.err = err
 				return tr.err
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index d741d105dc..f6c3ead3be 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -87,7 +87,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
 	z.File = make([]*File, 0, end.directoryRecords)
 	z.Comment = end.comment
 	rs := io.NewSectionReader(r, 0, size)
-	if _, err = rs.Seek(int64(end.directoryOffset), os.SEEK_SET); err != nil {
+	if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil {
 		return err
 	}
 	buf := bufio.NewReader(rs)
diff --git a/src/bytes/reader_test.go b/src/bytes/reader_test.go
index add985d57e..9341cd5b45 100644
--- a/src/bytes/reader_test.go
+++ b/src/bytes/reader_test.go
@@ -9,7 +9,6 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"os"
 	"sync"
 	"testing"
 )
@@ -24,15 +23,15 @@ func TestReader(t *testing.T) {
 		wantpos int64
 		seekerr string
 	}{
-		{seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
-		{seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
-		{seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-		{seek: os.SEEK_SET, off: -1, seekerr: "bytes.Reader.Seek: negative position"},
-		{seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
-		{seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
-		{seek: os.SEEK_SET, n: 5, want: "01234"},
-		{seek: os.SEEK_CUR, n: 5, want: "56789"},
-		{seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"},
+		{seek: io.SeekStart, off: 0, n: 20, want: "0123456789"},
+		{seek: io.SeekStart, off: 1, n: 1, want: "1"},
+		{seek: io.SeekCurrent, off: 1, wantpos: 3, n: 2, want: "34"},
+		{seek: io.SeekStart, off: -1, seekerr: "bytes.Reader.Seek: negative position"},
+		{seek: io.SeekStart, off: 1 << 33, wantpos: 1 << 33},
+		{seek: io.SeekCurrent, off: 1, wantpos: 1<<33 + 1},
+		{seek: io.SeekStart, n: 5, want: "01234"},
+		{seek: io.SeekCurrent, n: 5, want: "56789"},
+		{seek: io.SeekEnd, off: -1, n: 1, wantpos: 9, want: "9"},
 	}
 
 	for i, tt := range tests {
@@ -63,7 +62,7 @@ func TestReader(t *testing.T) {
 
 func TestReadAfterBigSeek(t *testing.T) {
 	r := NewReader([]byte("0123456789"))
-	if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil {
+	if _, err := r.Seek(1<<31+5, io.SeekStart); err != nil {
 		t.Fatal(err)
 	}
 	if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF {
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go
index c28a964b73..8fbf23fe5a 100644
--- a/src/debug/elf/file.go
+++ b/src/debug/elf/file.go
@@ -269,7 +269,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	switch f.Class {
 	case ELFCLASS32:
 		hdr := new(Header32)
-		sr.Seek(0, os.SEEK_SET)
+		sr.Seek(0, io.SeekStart)
 		if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
 			return nil, err
 		}
@@ -288,7 +288,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 		shstrndx = int(hdr.Shstrndx)
 	case ELFCLASS64:
 		hdr := new(Header64)
-		sr.Seek(0, os.SEEK_SET)
+		sr.Seek(0, io.SeekStart)
 		if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
 			return nil, err
 		}
@@ -315,7 +315,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	f.Progs = make([]*Prog, phnum)
 	for i := 0; i < phnum; i++ {
 		off := phoff + int64(i)*int64(phentsize)
-		sr.Seek(off, os.SEEK_SET)
+		sr.Seek(off, io.SeekStart)
 		p := new(Prog)
 		switch f.Class {
 		case ELFCLASS32:
@@ -359,7 +359,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	names := make([]uint32, shnum)
 	for i := 0; i < shnum; i++ {
 		off := shoff + int64(i)*int64(shentsize)
-		sr.Seek(off, os.SEEK_SET)
+		sr.Seek(off, io.SeekStart)
 		s := new(Section)
 		switch f.Class {
 		case ELFCLASS32:
diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go
index 1acc368e1b..c68ff1bdce 100644
--- a/src/debug/pe/file.go
+++ b/src/debug/pe/file.go
@@ -150,7 +150,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	} else {
 		base = int64(0)
 	}
-	sr.Seek(base, os.SEEK_SET)
+	sr.Seek(base, io.SeekStart)
 	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
 		return nil, err
 	}
@@ -161,7 +161,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	var ss []byte
 	if f.FileHeader.NumberOfSymbols > 0 {
 		// Get COFF string table, which is located at the end of the COFF symbol table.
-		sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
+		sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), io.SeekStart)
 		var l uint32
 		if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
 			return nil, err
@@ -172,7 +172,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 		}
 
 		// Process COFF symbol table.
-		sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
+		sr.Seek(int64(f.FileHeader.PointerToSymbolTable), io.SeekStart)
 		aux := uint8(0)
 		for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
 			cs := new(COFFSymbol)
@@ -203,7 +203,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	}
 
 	// Read optional header.
-	sr.Seek(base, os.SEEK_SET)
+	sr.Seek(base, io.SeekStart)
 	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
 		return nil, err
 	}
diff --git a/src/net/http/fs.go b/src/net/http/fs.go
index 5546d37516..c7a58a61df 100644
--- a/src/net/http/fs.go
+++ b/src/net/http/fs.go
@@ -121,11 +121,11 @@ func dirList(w ResponseWriter, f File) {
 // Note that *os.File implements the io.ReadSeeker interface.
 func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
 	sizeFunc := func() (int64, error) {
-		size, err := content.Seek(0, os.SEEK_END)
+		size, err := content.Seek(0, io.SeekEnd)
 		if err != nil {
 			return 0, errSeeker
 		}
-		_, err = content.Seek(0, os.SEEK_SET)
+		_, err = content.Seek(0, io.SeekStart)
 		if err != nil {
 			return 0, errSeeker
 		}
@@ -166,7 +166,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
 			var buf [sniffLen]byte
 			n, _ := io.ReadFull(content, buf[:])
 			ctype = DetectContentType(buf[:n])
-			_, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file
+			_, err := content.Seek(0, io.SeekStart) // rewind to output whole file
 			if err != nil {
 				Error(w, "seeker can't seek", StatusInternalServerError)
 				return
@@ -213,7 +213,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
 			// A response to a request for a single range MUST NOT
 			// be sent using the multipart/byteranges media type."
 			ra := ranges[0]
-			if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
+			if _, err := content.Seek(ra.start, io.SeekStart); err != nil {
 				Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
 				return
 			}
@@ -236,7 +236,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
 						pw.CloseWithError(err)
 						return
 					}
-					if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil {
+					if _, err := content.Seek(ra.start, io.SeekStart); err != nil {
 						pw.CloseWithError(err)
 						return
 					}
diff --git a/src/net/sendfile_dragonfly.go b/src/net/sendfile_dragonfly.go
index 17021c3801..d4b825c370 100644
--- a/src/net/sendfile_dragonfly.go
+++ b/src/net/sendfile_dragonfly.go
@@ -53,7 +53,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
 	// use the current position of the file -- if you pass it offset 0, it starts
 	// from offset 0. There's no way to tell it "start from current position", so
 	// we have to manage that explicitly.
-	pos, err := f.Seek(0, os.SEEK_CUR)
+	pos, err := f.Seek(0, io.SeekCurrent)
 	if err != nil {
 		return 0, err, false
 	}
diff --git a/src/net/sendfile_freebsd.go b/src/net/sendfile_freebsd.go
index f7a8529560..18cbb27b53 100644
--- a/src/net/sendfile_freebsd.go
+++ b/src/net/sendfile_freebsd.go
@@ -53,7 +53,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
 	// use the current position of the file -- if you pass it offset 0, it starts
 	// from offset 0. There's no way to tell it "start from current position", so
 	// we have to manage that explicitly.
-	pos, err := f.Seek(0, os.SEEK_CUR)
+	pos, err := f.Seek(0, io.SeekCurrent)
 	if err != nil {
 		return 0, err, false
 	}
diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go
index 20d2cddeea..add70c3147 100644
--- a/src/net/sendfile_solaris.go
+++ b/src/net/sendfile_solaris.go
@@ -57,7 +57,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
 	// use the current position of the file -- if you pass it offset 0, it starts
 	// from offset 0. There's no way to tell it "start from current position", so
 	// we have to manage that explicitly.
-	pos, err := f.Seek(0, os.SEEK_CUR)
+	pos, err := f.Seek(0, io.SeekCurrent)
 	if err != nil {
 		return 0, err, false
 	}
diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go
index 1f2fd12add..ed2721bb5e 100644
--- a/src/os/exec/exec_test.go
+++ b/src/os/exec/exec_test.go
@@ -479,7 +479,7 @@ func TestExtraFiles(t *testing.T) {
 	if err != nil {
 		t.Fatalf("Write: %v", err)
 	}
-	_, err = tf.Seek(0, os.SEEK_SET)
+	_, err = tf.Seek(0, io.SeekStart)
 	if err != nil {
 		t.Fatalf("Seek: %v", err)
 	}
diff --git a/src/strings/reader_test.go b/src/strings/reader_test.go
index 7bca2e89a1..6e9d904b9d 100644
--- a/src/strings/reader_test.go
+++ b/src/strings/reader_test.go
@@ -9,7 +9,6 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"os"
 	"strings"
 	"sync"
 	"testing"
@@ -25,15 +24,15 @@ func TestReader(t *testing.T) {
 		wantpos int64
 		seekerr string
 	}{
-		{seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
-		{seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
-		{seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-		{seek: os.SEEK_SET, off: -1, seekerr: "strings.Reader.Seek: negative position"},
-		{seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
-		{seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
-		{seek: os.SEEK_SET, n: 5, want: "01234"},
-		{seek: os.SEEK_CUR, n: 5, want: "56789"},
-		{seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"},
+		{seek: io.SeekStart, off: 0, n: 20, want: "0123456789"},
+		{seek: io.SeekStart, off: 1, n: 1, want: "1"},
+		{seek: io.SeekCurrent, off: 1, wantpos: 3, n: 2, want: "34"},
+		{seek: io.SeekStart, off: -1, seekerr: "strings.Reader.Seek: negative position"},
+		{seek: io.SeekStart, off: 1 << 33, wantpos: 1 << 33},
+		{seek: io.SeekCurrent, off: 1, wantpos: 1<<33 + 1},
+		{seek: io.SeekStart, n: 5, want: "01234"},
+		{seek: io.SeekCurrent, n: 5, want: "56789"},
+		{seek: io.SeekEnd, off: -1, n: 1, wantpos: 9, want: "9"},
 	}
 
 	for i, tt := range tests {
@@ -64,7 +63,7 @@ func TestReader(t *testing.T) {
 
 func TestReadAfterBigSeek(t *testing.T) {
 	r := strings.NewReader("0123456789")
-	if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil {
+	if _, err := r.Seek(1<<31+5, io.SeekStart); err != nil {
 		t.Fatal(err)
 	}
 	if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF {
-- 
cgit v1.3


From 6af4e996e2f0408f159a8553d11122b9fe052ffb Mon Sep 17 00:00:00 2001
From: Matthew Dempsky 
Date: Tue, 12 Apr 2016 15:51:24 -0700
Subject: runtime: simplify setPanicOnFault slightly

No need to acquire the M just to change G's paniconfault flag, and the
original C implementation of SetPanicOnFault did not. The M
acquisition logic is an artifact of golang.org/cl/131010044, which was
started before golang.org/cl/123640043 (which introduced the current
"getg" function) was submitted.

Change-Id: I6d1939008660210be46904395cf5f5bbc2c8f754
Reviewed-on: https://go-review.googlesource.com/21935
Run-TryBot: Matthew Dempsky 
TryBot-Result: Gobot Gobot 
Reviewed-by: Ian Lance Taylor 
---
 src/runtime/rdebug.go | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/runtime/rdebug.go b/src/runtime/rdebug.go
index d966734813..1b213f1934 100644
--- a/src/runtime/rdebug.go
+++ b/src/runtime/rdebug.go
@@ -15,9 +15,8 @@ func setMaxStack(in int) (out int) {
 
 //go:linkname setPanicOnFault runtime/debug.setPanicOnFault
 func setPanicOnFault(new bool) (old bool) {
-	mp := acquirem()
-	old = mp.curg.paniconfault
-	mp.curg.paniconfault = new
-	releasem(mp)
+	_g_ := getg()
+	old = _g_.paniconfault
+	_g_.paniconfault = new
 	return old
 }
-- 
cgit v1.3


From 6531fab06fc4667b7d167c7e3ee936f28bac68e2 Mon Sep 17 00:00:00 2001
From: Tal Shprecher 
Date: Tue, 12 Apr 2016 22:29:34 -0700
Subject: cmd/compile: remove unnecessary assignments while type checking.

Change-Id: Ica0ec84714d7f01d800d62fa10cdb08321d43cf3
Reviewed-on: https://go-review.googlesource.com/21967
Reviewed-by: Brad Fitzpatrick 
Run-TryBot: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
Reviewed-by: Matthew Dempsky 
---
 src/cmd/compile/internal/gc/typecheck.go | 6 ------
 1 file changed, 6 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index a20f87d940..f676b9dd09 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -338,7 +338,6 @@ OpSwitch:
 		ok |= Etype
 
 		if n.Type == nil {
-			n.Type = nil
 			return n
 		}
 
@@ -449,7 +448,6 @@ OpSwitch:
 		n.Op = OTYPE
 		n.Type = tointerface(n.List.Slice())
 		if n.Type == nil {
-			n.Type = nil
 			return n
 		}
 
@@ -458,7 +456,6 @@ OpSwitch:
 		n.Op = OTYPE
 		n.Type = functype(n.Left, n.List.Slice(), n.Rlist.Slice())
 		if n.Type == nil {
-			n.Type = nil
 			return n
 		}
 		n.Left = nil
@@ -822,7 +819,6 @@ OpSwitch:
 		ok |= Erv
 		n = typecheckcomplit(n)
 		if n.Type == nil {
-			n.Type = nil
 			return n
 		}
 		break OpSwitch
@@ -864,7 +860,6 @@ OpSwitch:
 			if n.Type.Etype != TFUNC || n.Type.Recv() == nil {
 				Yyerror("type %v has no method %v", n.Left.Type, Sconv(n.Right.Sym, FmtShort))
 				n.Type = nil
-				n.Type = nil
 				return n
 			}
 
@@ -1961,7 +1956,6 @@ OpSwitch:
 		ok |= Erv
 		typecheckclosure(n, top)
 		if n.Type == nil {
-			n.Type = nil
 			return n
 		}
 		break OpSwitch
-- 
cgit v1.3


From 0e01db4b8d6ac64e6661508bc6876fa41c799208 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky 
Date: Tue, 12 Apr 2016 17:46:41 -0700
Subject: cmd/compile: fix crash on bare package name in constant declarations

Fixes #11361.

Change-Id: I70b8808f97f0e07de680e7e6ede1322ea0fdbbc0
Reviewed-on: https://go-review.googlesource.com/21936
Reviewed-by: Brad Fitzpatrick 
---
 src/cmd/compile/internal/gc/subr.go |  7 +++++++
 test/fixedbugs/issue11361.go        | 11 +++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 test/fixedbugs/issue11361.go

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 091762f496..ea2db8721a 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -540,8 +540,15 @@ func treecopy(n *Node, lineno int32) *Node {
 		}
 		return n
 
+	case OPACK:
+		// OPACK nodes are never valid in const value declarations,
+		// but allow them like any other declared symbol to avoid
+		// crashing (golang.org/issue/11361).
+		fallthrough
+
 	case ONAME, OLITERAL, OTYPE:
 		return n
+
 	}
 }
 
diff --git a/test/fixedbugs/issue11361.go b/test/fixedbugs/issue11361.go
new file mode 100644
index 0000000000..d01776b47c
--- /dev/null
+++ b/test/fixedbugs/issue11361.go
@@ -0,0 +1,11 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+import "fmt"  // ERROR "imported and not used"
+
+const n = fmt // ERROR "fmt without selector" "fmt is not a constant"
-- 
cgit v1.3


From 24967ec122710e73b35893925fd9a8390d7524ab Mon Sep 17 00:00:00 2001
From: Tal Shprecher 
Date: Sun, 10 Apr 2016 18:12:41 -0700
Subject: cmd/compile: make enqueued map keys fail validation on forward types

Map keys are currently validated in multiple locations but share
a common validation routine. The problem is that early validations
should be lenient enough to allow for forward types while the final
validations should not. The final validations should fail on forward
types since they've already settled.

This change also separates the key type checking from the creation
of the map via typMap. Instead of the mapqueue being populated in
copytype() by checking the map line number, it's populated in the
same block that validates the key type. This isolates key validation
logic while type checking.

Fixes #14988

Change-Id: Ia47cf6213585d6c63b3a35249104c0439feae658
Reviewed-on: https://go-review.googlesource.com/21830
Reviewed-by: Matthew Dempsky 
Run-TryBot: Matthew Dempsky 
TryBot-Result: Gobot Gobot 
---
 src/cmd/compile/internal/gc/subr.go      | 24 ------------------
 src/cmd/compile/internal/gc/type.go      |  4 ---
 src/cmd/compile/internal/gc/typecheck.go | 42 +++++++++++++++++++-------------
 test/fixedbugs/issue14988.go             | 13 ++++++++++
 4 files changed, 38 insertions(+), 45 deletions(-)
 create mode 100644 test/fixedbugs/issue14988.go

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index ea2db8721a..776eb9c64e 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -372,30 +372,6 @@ func saveorignode(n *Node) {
 	n.Orig = norig
 }
 
-// checkMapKeyType checks that Type key is valid for use as a map key.
-func checkMapKeyType(key *Type) {
-	alg, bad := algtype1(key)
-	if alg != ANOEQ {
-		return
-	}
-	switch bad.Etype {
-	default:
-		Yyerror("invalid map key type %v", key)
-	case TANY:
-		// Will be resolved later.
-	case TFORW:
-		// map[key] used during definition of key.
-		// postpone check until key is fully defined.
-		// if there are multiple uses of map[key]
-		// before key is fully defined, the error
-		// will only be printed for the first one.
-		// good enough.
-		if maplineno[key] == 0 {
-			maplineno[key] = lineno
-		}
-	}
-}
-
 // methcmp sorts by symbol, then by package path for unexported symbols.
 type methcmp []*Field
 
diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go
index 25c1bcc203..a44a85bed8 100644
--- a/src/cmd/compile/internal/gc/type.go
+++ b/src/cmd/compile/internal/gc/type.go
@@ -429,10 +429,6 @@ func typChan(elem *Type, dir ChanDir) *Type {
 
 // typMap returns a new map Type with key type k and element (aka value) type v.
 func typMap(k, v *Type) *Type {
-	if k != nil {
-		checkMapKeyType(k)
-	}
-
 	t := typ(TMAP)
 	mt := t.MapType()
 	mt.Key = k
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index f676b9dd09..7089d7de72 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -416,6 +416,18 @@ OpSwitch:
 		}
 		n.Op = OTYPE
 		n.Type = typMap(l.Type, r.Type)
+
+		// map key validation
+		alg, bad := algtype1(l.Type)
+		if alg == ANOEQ {
+			if bad.Etype == TFORW {
+				// queue check for map until all the types are done settling.
+				mapqueue = append(mapqueue, mapqueueval{l, n.Lineno})
+			} else if bad.Etype != TANY {
+				// no need to queue, key is already bad
+				Yyerror("invalid map key type %v", l.Type)
+			}
+		}
 		n.Left = nil
 		n.Right = nil
 
@@ -3507,11 +3519,13 @@ func domethod(n *Node) {
 	checkwidth(n.Type)
 }
 
-var (
-	mapqueue []*Node
-	// maplineno tracks the line numbers at which types are first used as map keys
-	maplineno = map[*Type]int32{}
-)
+type mapqueueval struct {
+	n   *Node
+	lno int32
+}
+
+// tracks the line numbers at which forward types are first used as map keys
+var mapqueue []mapqueueval
 
 func copytype(n *Node, t *Type) {
 	if t.Etype == TFORW {
@@ -3520,7 +3534,6 @@ func copytype(n *Node, t *Type) {
 		return
 	}
 
-	mapline := maplineno[n.Type]
 	embedlineno := n.Type.ForwardType().Embedlineno
 	l := n.Type.ForwardType().Copyto
 
@@ -3555,12 +3568,6 @@ func copytype(n *Node, t *Type) {
 	}
 
 	lineno = lno
-
-	// Queue check for map until all the types are done settling.
-	if mapline != 0 {
-		maplineno[t] = mapline
-		mapqueue = append(mapqueue, n)
-	}
 }
 
 func typecheckdeftype(n *Node) {
@@ -3605,12 +3612,13 @@ ret:
 				domethod(n)
 			}
 		}
-
-		for _, n := range mapqueue {
-			lineno = maplineno[n.Type]
-			checkMapKeyType(n.Type)
+		for _, e := range mapqueue {
+			lineno = e.lno
+			if !e.n.Type.IsComparable() {
+				Yyerror("invalid map key type %v", e.n.Type)
+			}
 		}
-
+		mapqueue = nil
 		lineno = lno
 	}
 
diff --git a/test/fixedbugs/issue14988.go b/test/fixedbugs/issue14988.go
new file mode 100644
index 0000000000..4ddc7e728f
--- /dev/null
+++ b/test/fixedbugs/issue14988.go
@@ -0,0 +1,13 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 14988: defining a map with an invalid forward declaration array
+//              key doesn't cause a fatal.
+
+package main
+
+type m map[k]int // ERROR "invalid map key type"
+type k [1]m
-- 
cgit v1.3


From 61b7a9c57bb6b9c259360239001b2d5be4876abd Mon Sep 17 00:00:00 2001
From: Shahar Kohanim 
Date: Tue, 12 Apr 2016 23:18:47 +0300
Subject: cmd/link: rename Pcln to FuncInfo

After non pcln fields were added to it in a previous commit.

Change-Id: Icf92c0774d157c61399a6fc2a3c4d2cd47a634d2
Reviewed-on: https://go-review.googlesource.com/21921
Run-TryBot: Shahar Kohanim 
TryBot-Result: Gobot Gobot 
Reviewed-by: David Crawshaw 
---
 src/cmd/link/internal/ld/deadcode.go | 10 +++++-----
 src/cmd/link/internal/ld/dwarf.go    | 16 ++++++++--------
 src/cmd/link/internal/ld/lib.go      | 16 ++++++++--------
 src/cmd/link/internal/ld/link.go     |  4 ++--
 src/cmd/link/internal/ld/objfile.go  |  4 ++--
 src/cmd/link/internal/ld/pcln.go     | 10 +++++-----
 6 files changed, 30 insertions(+), 30 deletions(-)

(limited to 'src')

diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 83e4cdc077..51fae02ef0 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -272,8 +272,8 @@ func (d *deadcodepass) flood() {
 			if Debug['v'] > 1 {
 				fmt.Fprintf(d.ctxt.Bso, "marktext %s\n", s.Name)
 			}
-			if s.Pcln != nil {
-				for _, a := range s.Pcln.Autom {
+			if s.FuncInfo != nil {
+				for _, a := range s.FuncInfo.Autom {
 					d.mark(a.Gotype, s)
 				}
 			}
@@ -335,9 +335,9 @@ func (d *deadcodepass) flood() {
 			d.markableMethods = append(d.markableMethods, methods...)
 		}
 
-		if s.Pcln != nil {
-			for i := range s.Pcln.Funcdata {
-				d.mark(s.Pcln.Funcdata[i], s)
+		if s.FuncInfo != nil {
+			for i := range s.FuncInfo.Funcdata {
+				d.mark(s.FuncInfo.Funcdata[i], s)
 			}
 		}
 		d.mark(s.Gotype, s)
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 82689988c5..b1208b63a8 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1345,8 +1345,8 @@ func finddebugruntimepath(s *LSym) {
 		return
 	}
 
-	for i := range s.Pcln.File {
-		f := s.Pcln.File[i]
+	for i := range s.FuncInfo.File {
+		f := s.FuncInfo.File[i]
 		if i := strings.Index(f.Name, "runtime/runtime.go"); i >= 0 {
 			gdbscript = f.Name[:i] + "runtime/runtime-gdb.py"
 			break
@@ -1514,14 +1514,14 @@ func writelines(prev *LSym) *LSym {
 			newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0)
 		}
 
-		if s.Pcln == nil {
+		if s.FuncInfo == nil {
 			continue
 		}
 
 		finddebugruntimepath(s)
 
-		pciterinit(Ctxt, &pcfile, &s.Pcln.Pcfile)
-		pciterinit(Ctxt, &pcline, &s.Pcln.Pcline)
+		pciterinit(Ctxt, &pcfile, &s.FuncInfo.Pcfile)
+		pciterinit(Ctxt, &pcline, &s.FuncInfo.Pcline)
 		epc = pc
 		for pcfile.done == 0 && pcline.done == 0 {
 			if epc-s.Value >= int64(pcfile.nextpc) {
@@ -1556,7 +1556,7 @@ func writelines(prev *LSym) *LSym {
 			dt, da int
 			offs   int64
 		)
-		for _, a := range s.Pcln.Autom {
+		for _, a := range s.FuncInfo.Autom {
 			switch a.Name {
 			case obj.A_AUTO:
 				dt = DW_ABRV_AUTO
@@ -1698,14 +1698,14 @@ func writeframes(prev *LSym) *LSym {
 	var pcsp Pciter
 	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
 		s := Ctxt.Cursym
-		if s.Pcln == nil {
+		if s.FuncInfo == nil {
 			continue
 		}
 
 		// Emit a FDE, Section 6.4.1.
 		// First build the section contents into a byte buffer.
 		deltaBuf = deltaBuf[:0]
-		for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
+		for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
 			nextpc := pcsp.nextpc
 
 			// pciterinit goes up to the end of the function,
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index db34e68404..bdcc84a129 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1710,7 +1710,7 @@ func stkcheck(up *Chain, depth int) int {
 		return -1
 	}
 
-	if s.Attr.External() || s.Pcln == nil {
+	if s.Attr.External() || s.FuncInfo == nil {
 		// external function.
 		// should never be called directly.
 		// only diagnose the direct caller.
@@ -1748,8 +1748,8 @@ func stkcheck(up *Chain, depth int) int {
 		}
 		// Raise limit to allow frame.
 		locals := int32(0)
-		if s.Pcln != nil {
-			locals = s.Pcln.Locals
+		if s.FuncInfo != nil {
+			locals = s.FuncInfo.Locals
 		}
 		limit = int(obj.StackLimit+locals) + int(Ctxt.FixedFrameSize())
 	}
@@ -1761,7 +1761,7 @@ func stkcheck(up *Chain, depth int) int {
 	var ch1 Chain
 	var pcsp Pciter
 	var r *Reloc
-	for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
+	for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
 		// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
 
 		// Check stack size in effect for this span.
@@ -1983,16 +1983,16 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
 		put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype)
 
 		locals := int32(0)
-		if s.Pcln != nil {
-			locals = s.Pcln.Locals
+		if s.FuncInfo != nil {
+			locals = s.FuncInfo.Locals
 		}
 		// NOTE(ality): acid can't produce a stack trace without .frame symbols
 		put(nil, ".frame", 'm', int64(locals)+int64(SysArch.PtrSize), 0, 0, nil)
 
-		if s.Pcln == nil {
+		if s.FuncInfo == nil {
 			continue
 		}
-		for _, a := range s.Pcln.Autom {
+		for _, a := range s.FuncInfo.Autom {
 			// Emit a or p according to actual offset, even if label is wrong.
 			// This avoids negative offsets, which cannot be encoded.
 			if a.Name != obj.A_AUTO && a.Name != obj.A_PARAM {
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 93454fb4b2..b0bca4300f 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -65,7 +65,7 @@ type LSym struct {
 	Dynimplib   string
 	Dynimpvers  string
 	Sect        *Section
-	Pcln        *Pcln
+	FuncInfo    *FuncInfo
 	P           []byte
 	R           []Reloc
 }
@@ -217,7 +217,7 @@ type Library struct {
 	hash   []byte
 }
 
-type Pcln struct {
+type FuncInfo struct {
 	Args        int32
 	Locals      int32
 	Autom       []Auto
diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
index eacccb59fb..6826737cae 100644
--- a/src/cmd/link/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -331,8 +331,8 @@ overwrite:
 	}
 
 	if s.Type == obj.STEXT {
-		s.Pcln = new(Pcln)
-		pc := s.Pcln
+		s.FuncInfo = new(FuncInfo)
+		pc := s.FuncInfo
 
 		pc.Args = r.readInt32()
 		pc.Locals = r.readInt32()
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 3ef52444af..74ef8c2929 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -204,7 +204,7 @@ func container(s *LSym) int {
 // pclntab initializes the pclntab symbol with
 // runtime function and file name information.
 
-var pclntab_zpcln Pcln
+var pclntab_zpcln FuncInfo
 
 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
 var pclntabNfunc int32
@@ -255,13 +255,13 @@ func pclntab() {
 	var i int32
 	var it Pciter
 	var off int32
-	var pcln *Pcln
+	var pcln *FuncInfo
 	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
 		last = Ctxt.Cursym
 		if container(Ctxt.Cursym) != 0 {
 			continue
 		}
-		pcln = Ctxt.Cursym.Pcln
+		pcln = Ctxt.Cursym.FuncInfo
 		if pcln == nil {
 			pcln = &pclntab_zpcln
 		}
@@ -294,8 +294,8 @@ func pclntab() {
 		// args int32
 		// TODO: Move into funcinfo.
 		args := uint32(0)
-		if Ctxt.Cursym.Pcln != nil {
-			args = uint32(Ctxt.Cursym.Pcln.Args)
+		if Ctxt.Cursym.FuncInfo != nil {
+			args = uint32(Ctxt.Cursym.FuncInfo.Args)
 		}
 		off = int32(setuint32(Ctxt, ftab, int64(off), args))
 
-- 
cgit v1.3


From e0611b16645dba6768cab405f1ec1b3fce83334a Mon Sep 17 00:00:00 2001
From: Alexandru Moșoi 
Date: Wed, 13 Apr 2016 10:58:38 +0200
Subject: cmd/compile: use shared dom tree for cse, too
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Missed this in the previous CL where the shared
dom tree was introduced.

Change-Id: If0bd85d4b4567d7e87814ed511603b1303ab3903
Reviewed-on: https://go-review.googlesource.com/21970
Run-TryBot: Alexandru Moșoi 
TryBot-Result: Gobot Gobot 
Reviewed-by: David Chase 
---
 src/cmd/compile/internal/ssa/compile.go  | 5 +++--
 src/cmd/compile/internal/ssa/cse.go      | 8 +++-----
 src/cmd/compile/internal/ssa/cse_test.go | 1 +
 3 files changed, 7 insertions(+), 7 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index f4f0d8cab2..a0b5ff71cf 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -233,8 +233,8 @@ var passes = [...]pass{
 	{name: "opt", fn: opt, required: true},               // TODO: split required rules and optimizing rules
 	{name: "zero arg cse", fn: zcse, required: true},     // required to merge OpSB values
 	{name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt
-	{name: "generic cse", fn: cse},
 	{name: "generic domtree", fn: domTree},
+	{name: "generic cse", fn: cse},
 	{name: "phiopt", fn: phiopt},
 	{name: "nilcheckelim", fn: nilcheckelim},
 	{name: "prove", fn: prove},
@@ -289,7 +289,8 @@ var passOrder = [...]constraint{
 	{"opt", "nilcheckelim"},
 	// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
 	{"tighten", "lower"},
-	// nilcheckelim, prove and loopbce share idom.
+	// cse, nilcheckelim, prove and loopbce share idom.
+	{"generic domtree", "generic cse"},
 	{"generic domtree", "nilcheckelim"},
 	{"generic domtree", "prove"},
 	{"generic domtree", "loopbce"},
diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
index 9853ff06d0..c12d51e50c 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -131,9 +131,7 @@ func cse(f *Func) {
 		}
 	}
 
-	// Compute dominator tree
-	idom := dominators(f)
-	sdom := newSparseTree(f, idom)
+	// Dominator tree (f.sdom) is computed by the generic domtree pass.
 
 	// Compute substitutions we would like to do. We substitute v for w
 	// if v and w are in the same equivalence class and v dominates w.
@@ -143,7 +141,7 @@ func cse(f *Func) {
 			// Find a maximal dominant element in e
 			v := e[0]
 			for _, w := range e[1:] {
-				if sdom.isAncestorEq(w.Block, v.Block) {
+				if f.sdom.isAncestorEq(w.Block, v.Block) {
 					v = w
 				}
 			}
@@ -153,7 +151,7 @@ func cse(f *Func) {
 				w := e[i]
 				if w == v {
 					e, e[i] = e[:len(e)-1], e[len(e)-1]
-				} else if sdom.isAncestorEq(v.Block, w.Block) {
+				} else if f.sdom.isAncestorEq(v.Block, w.Block) {
 					rewrite[w.ID] = v
 					e, e[i] = e[:len(e)-1], e[len(e)-1]
 				} else {
diff --git a/src/cmd/compile/internal/ssa/cse_test.go b/src/cmd/compile/internal/ssa/cse_test.go
index 905939fc32..d5be2b52ec 100644
--- a/src/cmd/compile/internal/ssa/cse_test.go
+++ b/src/cmd/compile/internal/ssa/cse_test.go
@@ -44,6 +44,7 @@ func TestCSEAuxPartitionBug(t *testing.T) {
 			Exit("rstore")))
 
 	CheckFunc(fun.f)
+	domTree(fun.f)
 	cse(fun.f)
 	deadcode(fun.f)
 	CheckFunc(fun.f)
-- 
cgit v1.3


From 7d469179e6e3dafe16700b7fc1cf8683ad9453fa Mon Sep 17 00:00:00 2001
From: David Crawshaw 
Date: Mon, 28 Mar 2016 10:32:27 -0400
Subject: cmd/compile, etc: store method tables as offsets

This CL introduces the typeOff type and a lookup method of the same
name that can turn a typeOff offset into an *rtype.

In a typical Go binary (built with buildmode=exe, pie, c-archive, or
c-shared), there is one moduledata and all typeOff values are offsets
relative to firstmoduledata.types. This makes computing the pointer
cheap in typical programs.

With buildmode=shared (and one day, buildmode=plugin) there are
multiple modules whose relative offset is determined at runtime.
We identify a type in the general case by the pair of the original
*rtype that references it and its typeOff value. We determine
the module from the original pointer, and then use the typeOff from
there to compute the final *rtype.

To ensure there is only one *rtype representing each type, the
runtime initializes a typemap for each module, using any identical
type from an earlier module when resolving that offset. This means
that types computed from an offset match the type mapped by the
pointer dynamic relocations.

A series of followup CLs will replace other *rtype values with typeOff
(and name/*string with nameOff).

For types created at runtime by reflect, type offsets are treated as
global IDs and reference into a reflect offset map kept by the runtime.

darwin/amd64:
	cmd/go:  -57KB (0.6%)
	jujud:  -557KB (0.8%)

linux/amd64 PIE:
	cmd/go: -361KB (3.0%)
	jujud:  -3.5MB (4.2%)

For #6853.

Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96
Reviewed-on: https://go-review.googlesource.com/21285
Reviewed-by: Ian Lance Taylor 
Run-TryBot: David Crawshaw 
TryBot-Result: Gobot Gobot 
---
 src/cmd/compile/internal/gc/reflect.go |  75 +++++---
 src/cmd/internal/obj/link.go           |  15 +-
 src/cmd/link/internal/ld/deadcode.go   |  14 +-
 src/cmd/link/internal/ld/decodesym.go  |  22 +--
 src/reflect/export_test.go             |   2 +-
 src/reflect/type.go                    | 267 +++++++++++++++++++++-------
 src/reflect/value.go                   |  15 +-
 src/runtime/iface.go                   |  10 +-
 src/runtime/proc.go                    |   3 +-
 src/runtime/runtime1.go                |  33 ++++
 src/runtime/symtab.go                  |   2 +
 src/runtime/type.go                    | 307 ++++++++++++++++++++++++++++++++-
 12 files changed, 637 insertions(+), 128 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index ea67634260..2bd50b4665 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -75,7 +75,7 @@ func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{})
 	if t.Sym == nil && len(methods(t)) == 0 {
 		return 0
 	}
-	return 2*Widthptr + 2*Widthint
+	return 2 * Widthptr
 }
 
 func makefield(name string, t *Type) *Field {
@@ -580,13 +580,23 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int {
 
 	ot = dgopkgpath(s, ot, typePkg(t))
 
-	// slice header
-	ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+dataAdd)
-
-	n := len(m)
-	ot = duintxx(s, ot, uint64(n), Widthint)
-	ot = duintxx(s, ot, uint64(n), Widthint)
+	dataAdd += Widthptr + 2 + 2
+	if Widthptr == 8 {
+		dataAdd += 4
+	}
+	mcount := len(m)
+	if mcount != int(uint16(mcount)) {
+		Fatalf("too many methods on %s: %d", t, mcount)
+	}
+	if dataAdd != int(uint16(dataAdd)) {
+		Fatalf("methods are too far away on %s: %d", t, dataAdd)
+	}
 
+	ot = duint16(s, ot, uint16(mcount))
+	ot = duint16(s, ot, uint16(dataAdd))
+	if Widthptr == 8 {
+		ot = duint32(s, ot, 0) // align for following pointers
+	}
 	return ot
 }
 
@@ -609,6 +619,7 @@ func typePkg(t *Type) *Pkg {
 // dextratypeData dumps the backing array for the []method field of
 // runtime.uncommontype.
 func dextratypeData(s *Sym, ot int, t *Type) int {
+	lsym := Linksym(s)
 	for _, a := range methods(t) {
 		// ../../../../runtime/type.go:/method
 		exported := exportname(a.name)
@@ -617,21 +628,24 @@ func dextratypeData(s *Sym, ot int, t *Type) int {
 			pkg = a.pkg
 		}
 		ot = dname(s, ot, a.name, "", pkg, exported)
-		ot = dmethodptr(s, ot, dtypesym(a.mtype))
-		ot = dmethodptr(s, ot, a.isym)
-		ot = dmethodptr(s, ot, a.tsym)
+		ot = dmethodptrOffLSym(lsym, ot, Linksym(dtypesym(a.mtype)))
+		ot = dmethodptrOffLSym(lsym, ot, Linksym(a.isym))
+		ot = dmethodptrOffLSym(lsym, ot, Linksym(a.tsym))
+		if Widthptr == 8 {
+			ot = duintxxLSym(lsym, ot, 0, 4) // pad to reflect.method size
+		}
 	}
 	return ot
 }
 
-func dmethodptr(s *Sym, off int, x *Sym) int {
-	duintptr(s, off, 0)
-	r := obj.Addrel(Linksym(s))
-	r.Off = int32(off)
-	r.Siz = uint8(Widthptr)
-	r.Sym = Linksym(x)
-	r.Type = obj.R_METHOD
-	return off + Widthptr
+func dmethodptrOffLSym(s *obj.LSym, ot int, x *obj.LSym) int {
+	duintxxLSym(s, ot, 0, 4)
+	r := obj.Addrel(s)
+	r.Off = int32(ot)
+	r.Siz = 4
+	r.Sym = x
+	r.Type = obj.R_METHODOFF
+	return ot + 4
 }
 
 var kinds = []int{
@@ -1286,18 +1300,29 @@ ok:
 	ggloblsym(s, int32(ot), int16(dupok|obj.RODATA))
 
 	// generate typelink.foo pointing at s = type.foo.
+	//
 	// The linker will leave a table of all the typelinks for
-	// types in the binary, so reflect can find them.
-	// We only need the link for unnamed composites that
-	// we want be able to find.
-	if t.Sym == nil {
+	// types in the binary, so the runtime can find them.
+	//
+	// When buildmode=shared, all types are in typelinks so the
+	// runtime can deduplicate type pointers.
+	keep := Ctxt.Flag_dynlink
+	if !keep && t.Sym == nil {
+		// For an unnamed type, we only need the link if the type can
+		// be created at run time by reflect.PtrTo and similar
+		// functions. If the type exists in the program, those
+		// functions must return the existing type structure rather
+		// than creating a new one.
 		switch t.Etype {
 		case TPTR32, TPTR64, TARRAY, TCHAN, TFUNC, TMAP, TSTRUCT:
-			slink := typelinkLSym(t)
-			dsymptrOffLSym(slink, 0, Linksym(s), 0)
-			ggloblLSym(slink, 4, int16(dupok|obj.RODATA))
+			keep = true
 		}
 	}
+	if keep {
+		slink := typelinkLSym(t)
+		dsymptrOffLSym(slink, 0, Linksym(s), 0)
+		ggloblLSym(slink, 4, int16(dupok|obj.RODATA))
+	}
 
 	return s
 }
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 42aaa5f4f0..55c9f4f9e2 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -457,8 +457,8 @@ const (
 	// R_ADDRMIPS (only used on mips64) resolves to a 32-bit external address,
 	// by loading the address into a register with two instructions (lui, ori).
 	R_ADDRMIPS
-	// R_ADDROFF resolves to an offset from the beginning of the section holding
-	// the data being relocated to the referenced symbol.
+	// R_ADDROFF resolves to a 32-bit offset from the beginning of the section
+	// holding the data being relocated to the referenced symbol.
 	R_ADDROFF
 	R_SIZE
 	R_CALL
@@ -492,11 +492,12 @@ const (
 	// should be linked into the final binary, even if there are no other
 	// direct references. (This is used for types reachable by reflection.)
 	R_USETYPE
-	// R_METHOD resolves to an *rtype for a method.
-	// It is used when linking from the uncommonType of another *rtype, and
-	// may be set to zero by the linker if it determines the method text is
-	// unreachable by the linked program.
-	R_METHOD
+	// R_METHODOFF resolves to a 32-bit offset from the beginning of the section
+	// holding the data being relocated to the referenced symbol.
+	// It is a variant of R_ADDROFF used when linking from the uncommonType of a
+	// *rtype, and may be set to zero by the linker if it determines the method
+	// text is unreachable by the linked program.
+	R_METHODOFF
 	R_POWER_TOC
 	R_GOTPCREL
 	// R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 51fae02ef0..c83a104a54 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -19,7 +19,7 @@ import (
 //
 // This flood fill is wrapped in logic for pruning unused methods.
 // All methods are mentioned by relocations on their receiver's *rtype.
-// These relocations are specially defined as R_METHOD by the compiler
+// These relocations are specially defined as R_METHODOFF by the compiler
 // so we can detect and manipulated them here.
 //
 // There are three ways a method of a reachable type can be invoked:
@@ -100,7 +100,7 @@ func deadcode(ctxt *Link) {
 		d.flood()
 	}
 
-	// Remove all remaining unreached R_METHOD relocations.
+	// Remove all remaining unreached R_METHODOFF relocations.
 	for _, m := range d.markableMethods {
 		for _, r := range m.r {
 			d.cleanupReloc(r)
@@ -167,7 +167,7 @@ var markextra = []string{
 type methodref struct {
 	m   methodsig
 	src *LSym     // receiver type symbol
-	r   [3]*Reloc // R_METHOD relocations to fields of runtime.method
+	r   [3]*Reloc // R_METHODOFF relocations to fields of runtime.method
 }
 
 func (m methodref) ifn() *LSym { return m.r[1].Sym }
@@ -190,7 +190,7 @@ type deadcodepass struct {
 
 func (d *deadcodepass) cleanupReloc(r *Reloc) {
 	if r.Sym.Attr.Reachable() {
-		r.Type = obj.R_ADDR
+		r.Type = obj.R_ADDROFF
 	} else {
 		if Debug['v'] > 1 {
 			fmt.Fprintf(d.ctxt.Bso, "removing method %s\n", r.Sym.Name)
@@ -217,7 +217,7 @@ func (d *deadcodepass) mark(s, parent *LSym) {
 func (d *deadcodepass) markMethod(m methodref) {
 	for _, r := range m.r {
 		d.mark(r.Sym, m.src)
-		r.Type = obj.R_ADDR
+		r.Type = obj.R_ADDROFF
 	}
 }
 
@@ -291,14 +291,14 @@ func (d *deadcodepass) flood() {
 			}
 		}
 
-		mpos := 0 // 0-3, the R_METHOD relocs of runtime.uncommontype
+		mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype
 		var methods []methodref
 		for i := 0; i < len(s.R); i++ {
 			r := &s.R[i]
 			if r.Sym == nil {
 				continue
 			}
-			if r.Type != obj.R_METHOD {
+			if r.Type != obj.R_METHODOFF {
 				d.mark(r.Sym, s)
 				continue
 			}
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 7daa8bc812..5fa8b4c81f 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -47,9 +47,9 @@ func decode_inuxi(p []byte, sz int) uint64 {
 	}
 }
 
-func commonsize() int      { return 6*SysArch.PtrSize + 8 }                 // runtime._type
-func structfieldSize() int { return 3 * SysArch.PtrSize }                   // runtime.structfield
-func uncommonSize() int    { return 2*SysArch.PtrSize + 2*SysArch.IntSize } // runtime.uncommontype
+func commonsize() int      { return 6*SysArch.PtrSize + 8 } // runtime._type
+func structfieldSize() int { return 3 * SysArch.PtrSize }   // runtime.structfield
+func uncommonSize() int    { return 2 * SysArch.PtrSize }   // runtime.uncommontype
 
 // Type.commonType.kind
 func decodetype_kind(s *LSym) uint8 {
@@ -341,12 +341,14 @@ func decodetype_methods(s *LSym) []methodsig {
 		// just Sizeof(rtype)
 	}
 
-	numMethods := int(decode_inuxi(s.P[off+2*SysArch.PtrSize:], SysArch.IntSize))
-	r := decode_reloc(s, int32(off+SysArch.PtrSize))
-	if r.Sym != s {
-		panic(fmt.Sprintf("method slice pointer in %s leads to a different symbol %s", s, r.Sym))
+	mcount := int(decode_inuxi(s.P[off+SysArch.PtrSize:], 2))
+	moff := int(decode_inuxi(s.P[off+SysArch.PtrSize+2:], 2))
+	off += moff          // offset to array of reflect.method values
+	var sizeofMethod int // sizeof reflect.method in program
+	if SysArch.PtrSize == 4 {
+		sizeofMethod = 4 * SysArch.PtrSize
+	} else {
+		sizeofMethod = 3 * SysArch.PtrSize
 	}
-	off = int(r.Add)                    // array of reflect.method values
-	sizeofMethod := 4 * SysArch.PtrSize // sizeof reflect.method in program
-	return decode_methodsig(s, off, sizeofMethod, numMethods)
+	return decode_methodsig(s, off, sizeofMethod, mcount)
 }
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index 037c953718..2769e0db40 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -90,7 +90,7 @@ func FirstMethodNameBytes(t Type) *byte {
 	if ut == nil {
 		panic("type has no methods")
 	}
-	m := ut.methods[0]
+	m := ut.methods()[0]
 	if *m.name.data(0)&(1<<2) == 0 {
 		panic("method name does not have pkgPath *string")
 	}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 7104fde60a..c7ed402be2 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -288,10 +288,10 @@ type typeAlg struct {
 
 // Method on non-interface type
 type method struct {
-	name name           // name of method
-	mtyp *rtype         // method type (without receiver)
-	ifn  unsafe.Pointer // fn used in interface call (one-word receiver)
-	tfn  unsafe.Pointer // fn used for normal method call
+	name name    // name of method
+	mtyp typeOff // method type (without receiver)
+	ifn  textOff // fn used in interface call (one-word receiver)
+	tfn  textOff // fn used for normal method call
 }
 
 // uncommonType is present only for types with names or methods
@@ -299,8 +299,9 @@ type method struct {
 // Using a pointer to this struct reduces the overall size required
 // to describe an unnamed type with no methods.
 type uncommonType struct {
-	pkgPath *string  // import path; nil for built-in types like int, string
-	methods []method // methods associated with type
+	pkgPath *string // import path; nil for built-in types like int, string
+	mcount  uint16  // number of methods
+	moff    uint16  // offset from this uncommontype to [mcount]method
 }
 
 // ChanDir represents a channel type's direction.
@@ -589,6 +590,10 @@ var kindNames = []string{
 	UnsafePointer: "unsafe.Pointer",
 }
 
+func (t *uncommonType) methods() []method {
+	return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff)))[:t.mcount:t.mcount]
+}
+
 func (t *uncommonType) PkgPath() string {
 	if t == nil || t.pkgPath == nil {
 		return ""
@@ -596,13 +601,55 @@ func (t *uncommonType) PkgPath() string {
 	return *t.pkgPath
 }
 
+// resolveTypeOff resolves an *rtype offset from a base type.
+// The (*rtype).typeOff method is a convenience wrapper for this function.
+// Implemented in the runtime package.
+func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
+
+// resolveTextOff resolves an function pointer offset from a base type.
+// The (*rtype).textOff method is a convenience wrapper for this function.
+// Implemented in the runtime package.
+func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
+
+// addReflectOff adds a pointer to the reflection lookup map in the runtime.
+// It returns a new ID that can be used as a typeOff or textOff, and will
+// be resolved correctly. Implemented in the runtime package.
+func addReflectOff(ptr unsafe.Pointer) int32
+
+// resolveReflectType adds a *rtype to the reflection lookup map in the runtime.
+// It returns a new typeOff that can be used to refer to the pointer.
+func resolveReflectType(t *rtype) typeOff {
+	return typeOff(addReflectOff(unsafe.Pointer(t)))
+}
+
+// resolveReflectText adds a function pointer to the reflection lookup map in
+// the runtime. It returns a new textOff that can be used to refer to the
+// pointer.
+func resolveReflectText(ptr unsafe.Pointer) textOff {
+	return textOff(addReflectOff(ptr))
+}
+
+type typeOff int32 // offset to an *rtype
+type textOff int32 // offset from top of text section
+
+func (t *rtype) typeOff(off typeOff) *rtype {
+	if off == 0 {
+		return nil
+	}
+	return (*rtype)(resolveTypeOff(unsafe.Pointer(t), int32(off)))
+}
+
+func (t *rtype) textOff(off textOff) unsafe.Pointer {
+	return resolveTextOff(unsafe.Pointer(t), int32(off))
+}
+
 func (t *rtype) uncommon() *uncommonType {
 	if t.tflag&tflagUncommon == 0 {
 		return nil
 	}
 	switch t.Kind() {
 	case Struct:
-		return &(*structTypeWithMethods)(unsafe.Pointer(t)).u
+		return &(*structTypeUncommon)(unsafe.Pointer(t)).u
 	case Ptr:
 		type u struct {
 			ptrType
@@ -688,7 +735,7 @@ func (t *rtype) NumMethod() int {
 	if ut == nil {
 		return 0
 	}
-	return len(ut.methods)
+	return int(ut.mcount)
 }
 
 func (t *rtype) Method(i int) (m Method) {
@@ -698,10 +745,10 @@ func (t *rtype) Method(i int) (m Method) {
 	}
 	ut := t.uncommon()
 
-	if ut == nil || i < 0 || i >= len(ut.methods) {
+	if ut == nil || i < 0 || i >= int(ut.mcount) {
 		panic("reflect: Method index out of range")
 	}
-	p := &ut.methods[i]
+	p := ut.methods()[i]
 	m.Name = p.name.name()
 	fl := flag(Func)
 	if !p.name.isExported() {
@@ -712,8 +759,9 @@ func (t *rtype) Method(i int) (m Method) {
 		m.PkgPath = *pkgPath
 		fl |= flagStickyRO
 	}
-	if p.mtyp != nil {
-		ft := (*funcType)(unsafe.Pointer(p.mtyp))
+	if p.mtyp != 0 {
+		mtyp := t.typeOff(p.mtyp)
+		ft := (*funcType)(unsafe.Pointer(mtyp))
 		in := make([]Type, 0, 1+len(ft.in()))
 		in = append(in, t)
 		for _, arg := range ft.in() {
@@ -723,9 +771,10 @@ func (t *rtype) Method(i int) (m Method) {
 		for _, ret := range ft.out() {
 			out = append(out, ret)
 		}
-		mt := FuncOf(in, out, p.mtyp.IsVariadic())
+		mt := FuncOf(in, out, ft.IsVariadic())
 		m.Type = mt
-		fn := unsafe.Pointer(&p.tfn)
+		tfn := t.textOff(p.tfn)
+		fn := unsafe.Pointer(&tfn)
 		m.Func = Value{mt.(*rtype), fn, fl}
 	}
 	m.Index = i
@@ -741,8 +790,9 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
 	if ut == nil {
 		return Method{}, false
 	}
-	for i := range ut.methods {
-		p := &ut.methods[i]
+	utmethods := ut.methods()
+	for i := 0; i < int(ut.mcount); i++ {
+		p := utmethods[i]
 		if p.name.name() == name {
 			return t.Method(i), true
 		}
@@ -1430,10 +1480,11 @@ func implements(T, V *rtype) bool {
 		return false
 	}
 	i := 0
-	for j := 0; j < len(v.methods); j++ {
+	vmethods := v.methods()
+	for j := 0; j < int(v.mcount); j++ {
 		tm := &t.methods[i]
-		vm := &v.methods[j]
-		if vm.name.name() == tm.name.name() && vm.mtyp == tm.typ {
+		vm := vmethods[j]
+		if vm.name.name() == tm.name.name() && V.typeOff(vm.mtyp) == tm.typ {
 			if i++; i >= len(t.methods) {
 				return true
 			}
@@ -2161,21 +2212,55 @@ func SliceOf(t Type) Type {
 	return cachePut(ckey, &slice.rtype)
 }
 
-// structTypeWithMethods is a structType created at runtime with StructOf.
-// It is needed to pin the []method slice from its associated uncommonType struct.
-// Keep in sync with the memory layout of structType.
-type structTypeWithMethods struct {
-	structType
-	u uncommonType
-}
-
 // The structLookupCache caches StructOf lookups.
 // StructOf does not share the common lookupCache since we need to pin
-// the *structType and its associated *uncommonType (especially the
-// []method slice field of that uncommonType.)
+// the memory associated with *structTypeFixedN.
 var structLookupCache struct {
 	sync.RWMutex
-	m map[uint32][]*structTypeWithMethods // keyed by hash calculated in StructOf
+	m map[uint32][]interface {
+		common() *rtype
+	} // keyed by hash calculated in StructOf
+}
+
+type structTypeUncommon struct {
+	structType
+	u uncommonType
+}
+
+// A *rtype representing a struct is followed directly in memory by an
+// array of method objects representing the methods attached to the
+// struct. To get the same layout for a run time generated type, we
+// need an array directly following the uncommonType memory. The types
+// structTypeFixed4, ...structTypeFixedN are used to do this.
+//
+// A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN.
+
+// TODO(crawshaw): as these structTypeFixedN and funcTypeFixedN structs
+// have no methods, they could be defined at runtime using the StructOf
+// function.
+
+type structTypeFixed4 struct {
+	structType
+	u uncommonType
+	m [4]method
+}
+
+type structTypeFixed8 struct {
+	structType
+	u uncommonType
+	m [8]method
+}
+
+type structTypeFixed16 struct {
+	structType
+	u uncommonType
+	m [16]method
+}
+
+type structTypeFixed32 struct {
+	structType
+	u uncommonType
+	m [32]method
 }
 
 // StructOf returns the struct type containing fields.
@@ -2192,7 +2277,7 @@ func StructOf(fields []StructField) Type {
 		typalign   uint8
 		comparable = true
 		hashable   = true
-		typ        = new(structTypeWithMethods)
+		methods    []method
 
 		fs   = make([]structField, len(fields))
 		repr = make([]byte, 0, 64)
@@ -2269,7 +2354,6 @@ func StructOf(fields []StructField) Type {
 							}
 							return recv.Field(ifield).Method(imethod).Call(args)
 						})
-
 					} else {
 						tfn = MakeFunc(m.typ, func(in []Value) []Value {
 							var args []Value
@@ -2287,47 +2371,59 @@ func StructOf(fields []StructField) Type {
 							}
 							return recv.Field(ifield).Method(imethod).Call(args)
 						})
-
 					}
 
-					typ.u.methods = append(
-						typ.u.methods,
-						method{
-							name: m.name,
-							mtyp: m.typ,
-							ifn:  unsafe.Pointer(&ifn),
-							tfn:  unsafe.Pointer(&tfn),
-						},
-					)
+					methods = append(methods, method{
+						name: m.name,
+						mtyp: resolveReflectType(m.typ),
+						ifn:  resolveReflectText(unsafe.Pointer(&ifn)),
+						tfn:  resolveReflectText(unsafe.Pointer(&tfn)),
+					})
 				}
 			case Ptr:
 				ptr := (*ptrType)(unsafe.Pointer(ft))
 				if unt := ptr.uncommon(); unt != nil {
-					for _, m := range unt.methods {
+					for _, m := range unt.methods() {
 						if m.name.pkgPath() != nil {
 							// TODO(sbinet)
 							panic("reflect: embedded interface with unexported method(s) not implemented")
 						}
-						typ.u.methods = append(typ.u.methods, m)
+						methods = append(methods, method{
+							name: m.name,
+							mtyp: resolveReflectType(ptr.typeOff(m.mtyp)),
+							ifn:  resolveReflectText(ptr.textOff(m.ifn)),
+							tfn:  resolveReflectText(ptr.textOff(m.tfn)),
+						})
 					}
 				}
 				if unt := ptr.elem.uncommon(); unt != nil {
-					for _, m := range unt.methods {
+					for _, m := range unt.methods() {
 						if m.name.pkgPath() != nil {
 							// TODO(sbinet)
 							panic("reflect: embedded interface with unexported method(s) not implemented")
 						}
-						typ.u.methods = append(typ.u.methods, m)
+						methods = append(methods, method{
+							name: m.name,
+							mtyp: resolveReflectType(ptr.elem.typeOff(m.mtyp)),
+							ifn:  resolveReflectText(ptr.elem.textOff(m.ifn)),
+							tfn:  resolveReflectText(ptr.elem.textOff(m.tfn)),
+						})
 					}
 				}
 			default:
 				if unt := ft.uncommon(); unt != nil {
-					for _, m := range unt.methods {
+					for _, m := range unt.methods() {
 						if m.name.pkgPath() != nil {
 							// TODO(sbinet)
 							panic("reflect: embedded interface with unexported method(s) not implemented")
 						}
-						typ.u.methods = append(typ.u.methods, m)
+						methods = append(methods, method{
+							name: m.name,
+							mtyp: resolveReflectType(ft.typeOff(m.mtyp)),
+							ifn:  resolveReflectText(ft.textOff(m.ifn)),
+							tfn:  resolveReflectText(ft.textOff(m.tfn)),
+						})
+
 					}
 				}
 			}
@@ -2359,6 +2455,49 @@ func StructOf(fields []StructField) Type {
 
 		fs[i] = f
 	}
+
+	var typ *structType
+	var ut *uncommonType
+	var typPin interface {
+		common() *rtype
+	} // structTypeFixedN
+
+	switch {
+	case len(methods) == 0:
+		t := new(structTypeUncommon)
+		typ = &t.structType
+		ut = &t.u
+		typPin = t
+	case len(methods) <= 4:
+		t := new(structTypeFixed4)
+		typ = &t.structType
+		ut = &t.u
+		copy(t.m[:], methods)
+		typPin = t
+	case len(methods) <= 8:
+		t := new(structTypeFixed8)
+		typ = &t.structType
+		ut = &t.u
+		copy(t.m[:], methods)
+		typPin = t
+	case len(methods) <= 16:
+		t := new(structTypeFixed16)
+		typ = &t.structType
+		ut = &t.u
+		copy(t.m[:], methods)
+		typPin = t
+	case len(methods) <= 32:
+		t := new(structTypeFixed32)
+		typ = &t.structType
+		ut = &t.u
+		copy(t.m[:], methods)
+		typPin = t
+	default:
+		panic("reflect.StructOf: too many methods")
+	}
+	ut.mcount = uint16(len(methods))
+	ut.moff = uint16(unsafe.Sizeof(uncommonType{}))
+
 	if len(fs) > 0 {
 		repr = append(repr, ' ')
 	}
@@ -2372,15 +2511,16 @@ func StructOf(fields []StructField) Type {
 	// Make the struct type.
 	var istruct interface{} = struct{}{}
 	prototype := *(**structType)(unsafe.Pointer(&istruct))
-	typ.structType = *prototype
-	typ.structType.fields = fs
+	*typ = *prototype
+	typ.fields = fs
 
 	// Look in cache
 	structLookupCache.RLock()
-	for _, t := range structLookupCache.m[hash] {
-		if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) {
+	for _, st := range structLookupCache.m[hash] {
+		t := st.common()
+		if haveIdenticalUnderlyingType(&typ.rtype, t) {
 			structLookupCache.RUnlock()
-			return &t.rtype
+			return t
 		}
 	}
 	structLookupCache.RUnlock()
@@ -2389,11 +2529,14 @@ func StructOf(fields []StructField) Type {
 	structLookupCache.Lock()
 	defer structLookupCache.Unlock()
 	if structLookupCache.m == nil {
-		structLookupCache.m = make(map[uint32][]*structTypeWithMethods)
+		structLookupCache.m = make(map[uint32][]interface {
+			common() *rtype
+		})
 	}
-	for _, t := range structLookupCache.m[hash] {
-		if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) {
-			return &t.rtype
+	for _, st := range structLookupCache.m[hash] {
+		t := st.common()
+		if haveIdenticalUnderlyingType(&typ.rtype, t) {
+			return t
 		}
 	}
 
@@ -2403,9 +2546,8 @@ func StructOf(fields []StructField) Type {
 			// even if 't' wasn't a structType with methods, we should be ok
 			// as the 'u uncommonType' field won't be accessed except when
 			// tflag&tflagUncommon is set.
-			tt := (*structTypeWithMethods)(unsafe.Pointer(t))
-			structLookupCache.m[hash] = append(structLookupCache.m[hash], tt)
-			return &tt.rtype
+			structLookupCache.m[hash] = append(structLookupCache.m[hash], t)
+			return t
 		}
 	}
 
@@ -2414,7 +2556,7 @@ func StructOf(fields []StructField) Type {
 	typ.size = size
 	typ.align = typalign
 	typ.fieldAlign = typalign
-	if len(typ.u.methods) > 0 {
+	if len(methods) > 0 {
 		typ.tflag |= tflagUncommon
 	}
 	if !hasPtr {
@@ -2514,7 +2656,7 @@ func StructOf(fields []StructField) Type {
 		typ.kind &^= kindDirectIface
 	}
 
-	structLookupCache.m[hash] = append(structLookupCache.m[hash], typ)
+	structLookupCache.m[hash] = append(structLookupCache.m[hash], typPin)
 	return &typ.rtype
 }
 
@@ -2533,6 +2675,7 @@ func runtimeStructField(field StructField) structField {
 		}
 	}
 
+	_ = resolveReflectType(field.Type.common())
 	return structField{
 		name:   newName(field.Name, string(field.Tag), field.PkgPath, exported),
 		typ:    field.Type.common(),
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 262545d973..d72c14e9e1 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -566,15 +566,16 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn
 	} else {
 		rcvrtype = v.typ
 		ut := v.typ.uncommon()
-		if ut == nil || uint(i) >= uint(len(ut.methods)) {
+		if ut == nil || uint(i) >= uint(ut.mcount) {
 			panic("reflect: internal error: invalid method index")
 		}
-		m := &ut.methods[i]
+		m := ut.methods()[i]
 		if !m.name.isExported() {
 			panic("reflect: " + op + " of unexported method")
 		}
-		fn = unsafe.Pointer(&m.ifn)
-		t = m.mtyp
+		ifn := v.typ.textOff(m.ifn)
+		fn = unsafe.Pointer(&ifn)
+		t = v.typ.typeOff(m.mtyp)
 	}
 	return
 }
@@ -1687,11 +1688,11 @@ func (v Value) Type() Type {
 	}
 	// Method on concrete type.
 	ut := v.typ.uncommon()
-	if ut == nil || uint(i) >= uint(len(ut.methods)) {
+	if ut == nil || uint(i) >= uint(ut.mcount) {
 		panic("reflect: internal error: invalid method index")
 	}
-	m := &ut.methods[i]
-	return m.mtyp
+	m := ut.methods()[i]
+	return v.typ.typeOff(m.mtyp)
 }
 
 // Uint returns v's underlying value, as a uint64.
diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index a4c962fb7a..700bdc2f48 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -93,7 +93,8 @@ func additab(m *itab, locked, canfail bool) {
 	// so can iterate over both in lock step;
 	// the loop is O(ni+nt) not O(ni*nt).
 	ni := len(inter.mhdr)
-	nt := len(x.mhdr)
+	nt := int(x.mcount)
+	xmhdr := (*[1 << 16]method)(add(unsafe.Pointer(x), uintptr(x.moff)))[:nt:nt]
 	j := 0
 	for k := 0; k < ni; k++ {
 		i := &inter.mhdr[k]
@@ -104,15 +105,16 @@ func additab(m *itab, locked, canfail bool) {
 			ipkg = inter.pkgpath
 		}
 		for ; j < nt; j++ {
-			t := &x.mhdr[j]
-			if t.mtyp == itype && t.name.name() == iname {
+			t := &xmhdr[j]
+			if typ.typeOff(t.mtyp) == itype && t.name.name() == iname {
 				pkgPath := t.name.pkgPath()
 				if pkgPath == nil {
 					pkgPath = x.pkgpath
 				}
 				if t.name.isExported() || pkgPath == ipkg {
 					if m != nil {
-						*(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = t.ifn
+						ifn := typ.textOff(t.ifn)
+						*(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = ifn
 					}
 					goto nextimethod
 				}
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 1a9dbd6c53..98a986cd63 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -435,9 +435,10 @@ func schedinit() {
 	tracebackinit()
 	moduledataverify()
 	stackinit()
-	itabsinit()
 	mallocinit()
 	mcommoninit(_g_.m)
+	typelinksinit()
+	itabsinit()
 
 	msigsave(_g_.m)
 	initSigmask = _g_.m.sigmask
diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go
index e1956569fd..02aeedaf75 100644
--- a/src/runtime/runtime1.go
+++ b/src/runtime/runtime1.go
@@ -486,3 +486,36 @@ func reflect_typelinks() ([]unsafe.Pointer, [][]int32) {
 	}
 	return sections, ret
 }
+
+// reflect_resolveTypeOff resolves an *rtype offset from a base type.
+//go:linkname reflect_resolveTypeOff reflect.resolveTypeOff
+func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
+	return unsafe.Pointer((*_type)(rtype).typeOff(typeOff(off)))
+}
+
+// reflect_resolveTextOff resolves an function pointer offset from a base type.
+//go:linkname reflect_resolveTextOff reflect.resolveTextOff
+func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
+	return (*_type)(rtype).textOff(textOff(off))
+
+}
+
+// reflect_addReflectOff adds a pointer to the reflection offset lookup map.
+//go:linkname reflect_addReflectOff reflect.addReflectOff
+func reflect_addReflectOff(ptr unsafe.Pointer) int32 {
+	lock(&reflectOffs.lock)
+	if reflectOffs.m == nil {
+		reflectOffs.m = make(map[int32]unsafe.Pointer)
+		reflectOffs.minv = make(map[unsafe.Pointer]int32)
+		reflectOffs.next = -1
+	}
+	id, found := reflectOffs.minv[ptr]
+	if !found {
+		id = reflectOffs.next
+		reflectOffs.next-- // use negative offsets as IDs to aid debugging
+		reflectOffs.m[id] = ptr
+		reflectOffs.minv[ptr] = id
+	}
+	unlock(&reflectOffs.lock)
+	return id
+}
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 8c70f22c1f..2df390253a 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -137,6 +137,8 @@ type moduledata struct {
 
 	gcdatamask, gcbssmask bitvector
 
+	typemap map[typeOff]*_type // offset to *_rtype in previous module
+
 	next *moduledata
 }
 
diff --git a/src/runtime/type.go b/src/runtime/type.go
index fbf6f9973c..86131d3ff3 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -131,6 +131,92 @@ func (t *_type) name() string {
 	return t._string[i+1:]
 }
 
+// reflectOffs holds type offsets defined at run time by the reflect package.
+//
+// When a type is defined at run time, its *rtype data lives on the heap.
+// There are a wide range of possible addresses the heap may use, that
+// may not be representable as a 32-bit offset. Moreover the GC may
+// one day start moving heap memory, in which case there is no stable
+// offset that can be defined.
+//
+// To provide stable offsets, we add pin *rtype objects in a global map
+// and treat the offset as an identifier. We use negative offsets that
+// do not overlap with any compile-time module offsets.
+//
+// Entries are created by reflect.addReflectOff.
+var reflectOffs struct {
+	lock mutex
+	next int32
+	m    map[int32]unsafe.Pointer
+	minv map[unsafe.Pointer]int32
+}
+
+func (t *_type) typeOff(off typeOff) *_type {
+	if off == 0 {
+		return nil
+	}
+	base := uintptr(unsafe.Pointer(t))
+	var md *moduledata
+	for next := &firstmoduledata; next != nil; next = next.next {
+		if base >= next.types && base < next.etypes {
+			md = next
+			break
+		}
+	}
+	if md == nil {
+		lock(&reflectOffs.lock)
+		res := reflectOffs.m[int32(off)]
+		unlock(&reflectOffs.lock)
+		if res == nil {
+			println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:")
+			for next := &firstmoduledata; next != nil; next = next.next {
+				println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+			}
+			throw("runtime: type offset base pointer out of range")
+		}
+		return (*_type)(res)
+	}
+	if t := md.typemap[off]; t != nil {
+		return t
+	}
+	res := md.types + uintptr(off)
+	if res > md.etypes {
+		println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
+		throw("runtime: type offset out of range")
+	}
+	return (*_type)(unsafe.Pointer(res))
+}
+
+func (t *_type) textOff(off textOff) unsafe.Pointer {
+	base := uintptr(unsafe.Pointer(t))
+	var md *moduledata
+	for next := &firstmoduledata; next != nil; next = next.next {
+		if base >= next.types && base < next.etypes {
+			md = next
+			break
+		}
+	}
+	if md == nil {
+		lock(&reflectOffs.lock)
+		res := reflectOffs.m[int32(off)]
+		unlock(&reflectOffs.lock)
+		if res == nil {
+			println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:")
+			for next := &firstmoduledata; next != nil; next = next.next {
+				println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+			}
+			throw("runtime: text offset base pointer out of range")
+		}
+		return res
+	}
+	res := md.text + uintptr(off)
+	if res > md.etext {
+		println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext))
+		throw("runtime: text offset out of range")
+	}
+	return unsafe.Pointer(res)
+}
+
 func (t *functype) in() []*_type {
 	// See funcType in reflect/type.go for details on data layout.
 	uadd := uintptr(unsafe.Sizeof(functype{}))
@@ -154,16 +240,20 @@ func (t *functype) dotdotdot() bool {
 	return t.outCount&(1<<15) != 0
 }
 
+type typeOff int32
+type textOff int32
+
 type method struct {
 	name name
-	mtyp *_type
-	ifn  unsafe.Pointer
-	tfn  unsafe.Pointer
+	mtyp typeOff
+	ifn  textOff
+	tfn  textOff
 }
 
 type uncommontype struct {
 	pkgpath *string
-	mhdr    []method
+	mcount  uint16 // number of methods
+	moff    uint16 // offset from this uncommontype to [mcount]method
 }
 
 type imethod struct {
@@ -270,6 +360,18 @@ func (n *name) name() (s string) {
 	return s
 }
 
+func (n *name) tag() (s string) {
+	tl := n.tagLen()
+	if tl == 0 {
+		return ""
+	}
+	nl := n.nameLen()
+	hdr := (*stringStruct)(unsafe.Pointer(&s))
+	hdr.str = unsafe.Pointer(n.data(3 + nl + 2))
+	hdr.len = tl
+	return s
+}
+
 func (n *name) pkgPath() *string {
 	if *n.data(0)&(1<<2) == 0 {
 		return nil
@@ -281,3 +383,200 @@ func (n *name) pkgPath() *string {
 	off = int(round(uintptr(off), sys.PtrSize))
 	return *(**string)(unsafe.Pointer(n.data(off)))
 }
+
+// typelinksinit scans the types from extra modules and builds the
+// moduledata typemap used to de-duplicate type pointers.
+func typelinksinit() {
+	if firstmoduledata.next == nil {
+		return
+	}
+	typehash := make(map[uint32][]*_type)
+
+	modules := []*moduledata{}
+	for md := &firstmoduledata; md != nil; md = md.next {
+		modules = append(modules, md)
+	}
+	prev, modules := modules[len(modules)-1], modules[:len(modules)-1]
+	for len(modules) > 0 {
+		// Collect types from the previous module into typehash.
+	collect:
+		for _, tl := range prev.typelinks {
+			var t *_type
+			if prev.typemap == nil {
+				t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl)))
+			} else {
+				t = prev.typemap[typeOff(tl)]
+			}
+			// Add to typehash if not seen before.
+			tlist := typehash[t.hash]
+			for _, tcur := range tlist {
+				if tcur == t {
+					continue collect
+				}
+			}
+			typehash[t.hash] = append(tlist, t)
+		}
+
+		// If any of this module's typelinks match a type from a
+		// prior module, prefer that prior type by adding the offset
+		// to this module's typemap.
+		md := modules[len(modules)-1]
+		md.typemap = make(map[typeOff]*_type, len(md.typelinks))
+		for _, tl := range md.typelinks {
+			t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
+			for _, candidate := range typehash[t.hash] {
+				if typesEqual(t, candidate) {
+					t = candidate
+					break
+				}
+			}
+			md.typemap[typeOff(tl)] = t
+		}
+
+		prev, modules = md, modules[:len(modules)-1]
+	}
+}
+
+// typesEqual reports whether two types are equal.
+//
+// Everywhere in the runtime and reflect packages, it is assumed that
+// there is exactly one *_type per Go type, so that pointer equality
+// can be used to test if types are equal. There is one place that
+// breaks this assumption: buildmode=shared. In this case a type can
+// appear as two different pieces of memory. This is hidden from the
+// runtime and reflect package by the per-module typemap built in
+// typelinksinit. It uses typesEqual to map types from later modules
+// back into earlier ones.
+//
+// Only typelinksinit needs this function.
+func typesEqual(t, v *_type) bool {
+	if t == v {
+		return true
+	}
+	kind := t.kind & kindMask
+	if kind != v.kind&kindMask {
+		return false
+	}
+	if t._string != v._string {
+		return false
+	}
+	ut := t.uncommon()
+	uv := v.uncommon()
+	if ut != nil || uv != nil {
+		if ut == nil || uv == nil {
+			return false
+		}
+		if !pkgPathEqual(ut.pkgpath, uv.pkgpath) {
+			return false
+		}
+	}
+	if kindBool <= kind && kind <= kindComplex128 {
+		return true
+	}
+	switch kind {
+	case kindString, kindUnsafePointer:
+		return true
+	case kindArray:
+		at := (*arraytype)(unsafe.Pointer(t))
+		av := (*arraytype)(unsafe.Pointer(v))
+		return typesEqual(at.elem, av.elem) && at.len == av.len
+	case kindChan:
+		ct := (*chantype)(unsafe.Pointer(t))
+		cv := (*chantype)(unsafe.Pointer(v))
+		return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem)
+	case kindFunc:
+		ft := (*functype)(unsafe.Pointer(t))
+		fv := (*functype)(unsafe.Pointer(v))
+		if ft.outCount != fv.outCount || ft.inCount != fv.inCount {
+			return false
+		}
+		tin, vin := ft.in(), fv.in()
+		for i := 0; i < len(tin); i++ {
+			if !typesEqual(tin[i], vin[i]) {
+				return false
+			}
+		}
+		tout, vout := ft.out(), fv.out()
+		for i := 0; i < len(tout); i++ {
+			if !typesEqual(tout[i], vout[i]) {
+				return false
+			}
+		}
+		return true
+	case kindInterface:
+		it := (*interfacetype)(unsafe.Pointer(t))
+		iv := (*interfacetype)(unsafe.Pointer(v))
+		if !pkgPathEqual(it.pkgpath, iv.pkgpath) {
+			return false
+		}
+		if len(it.mhdr) != len(iv.mhdr) {
+			return false
+		}
+		for i := range it.mhdr {
+			tm := &it.mhdr[i]
+			vm := &iv.mhdr[i]
+			if tm.name.name() != vm.name.name() {
+				return false
+			}
+			if !pkgPathEqual(tm.name.pkgPath(), vm.name.pkgPath()) {
+				return false
+			}
+			if !typesEqual(tm._type, vm._type) {
+				return false
+			}
+		}
+		return true
+	case kindMap:
+		mt := (*maptype)(unsafe.Pointer(t))
+		mv := (*maptype)(unsafe.Pointer(v))
+		return typesEqual(mt.key, mv.key) && typesEqual(mt.elem, mv.elem)
+	case kindPtr:
+		pt := (*ptrtype)(unsafe.Pointer(t))
+		pv := (*ptrtype)(unsafe.Pointer(v))
+		return typesEqual(pt.elem, pv.elem)
+	case kindSlice:
+		st := (*slicetype)(unsafe.Pointer(t))
+		sv := (*slicetype)(unsafe.Pointer(v))
+		return typesEqual(st.elem, sv.elem)
+	case kindStruct:
+		st := (*structtype)(unsafe.Pointer(t))
+		sv := (*structtype)(unsafe.Pointer(v))
+		if len(st.fields) != len(sv.fields) {
+			return false
+		}
+		for i := range st.fields {
+			tf := &st.fields[i]
+			vf := &sv.fields[i]
+			if tf.name.name() != vf.name.name() {
+				return false
+			}
+			if !pkgPathEqual(tf.name.pkgPath(), vf.name.pkgPath()) {
+				return false
+			}
+			if !typesEqual(tf.typ, vf.typ) {
+				return false
+			}
+			if tf.name.tag() != vf.name.tag() {
+				return false
+			}
+			if tf.offset != vf.offset {
+				return false
+			}
+		}
+		return true
+	default:
+		println("runtime: impossible type kind", kind)
+		throw("runtime: impossible type kind")
+		return false
+	}
+}
+
+func pkgPathEqual(p, q *string) bool {
+	if p == q {
+		return true
+	}
+	if p == nil || q == nil {
+		return false
+	}
+	return *p == *q
+}
-- 
cgit v1.3


From 66afbf1010fa492fb9a266f9019f707bd09f066d Mon Sep 17 00:00:00 2001
From: David Crawshaw 
Date: Wed, 13 Apr 2016 10:06:12 -0400
Subject: cmd/link: use a switch for name prefix switching

Minor cleanup.

Change-Id: I7574f58a7e55c2bb798ebe9c7c98d36b8c258fb8
Reviewed-on: https://go-review.googlesource.com/21982
Run-TryBot: David Crawshaw 
TryBot-Result: Gobot Gobot 
Reviewed-by: Brad Fitzpatrick 
---
 src/cmd/link/internal/ld/symtab.go | 61 +++++++++++++-------------------------
 1 file changed, 21 insertions(+), 40 deletions(-)

(limited to 'src')

diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 678ed38730..60bec0d6c9 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -384,33 +384,19 @@ func symtab() {
 		symtyperel = s
 	}
 
-	s = Linklookup(Ctxt, "go.string.*", 0)
-	s.Type = obj.SGOSTRING
-	s.Attr |= AttrLocal
-	s.Size = 0
-	s.Attr |= AttrReachable
-	symgostring := s
-
-	s = Linklookup(Ctxt, "go.string.hdr.*", 0)
-	s.Type = obj.SGOSTRINGHDR
-	s.Attr |= AttrLocal
-	s.Size = 0
-	s.Attr |= AttrReachable
-	symgostringhdr := s
-
-	s = Linklookup(Ctxt, "go.func.*", 0)
-	s.Type = obj.SGOFUNC
-	s.Attr |= AttrLocal
-	s.Size = 0
-	s.Attr |= AttrReachable
-	symgofunc := s
-
-	s = Linklookup(Ctxt, "runtime.gcbits.*", 0)
-	s.Type = obj.SGCBITS
-	s.Attr |= AttrLocal
-	s.Size = 0
-	s.Attr |= AttrReachable
-	symgcbits := s
+	groupSym := func(name string, t int16) *LSym {
+		s := Linklookup(Ctxt, name, 0)
+		s.Type = t
+		s.Size = 0
+		s.Attr |= AttrLocal | AttrReachable
+		return s
+	}
+	var (
+		symgostring    = groupSym("go.string.*", obj.SGOSTRING)
+		symgostringhdr = groupSym("go.string.hdr.*", obj.SGOSTRINGHDR)
+		symgofunc      = groupSym("go.func.*", obj.SGOFUNC)
+		symgcbits      = groupSym("runtime.gcbits.*", obj.SGCBITS)
+	)
 
 	symtypelink := Linklookup(Ctxt, "runtime.typelink", 0)
 	symtypelink.Type = obj.STYPELINK
@@ -436,7 +422,8 @@ func symtab() {
 			continue
 		}
 
-		if strings.HasPrefix(s.Name, "type.") {
+		switch {
+		case strings.HasPrefix(s.Name, "type."):
 			if !DynlinkingGo() {
 				s.Attr |= AttrHidden
 			}
@@ -447,23 +434,20 @@ func symtab() {
 				s.Type = obj.STYPE
 				s.Outer = symtype
 			}
-		}
 
-		if strings.HasPrefix(s.Name, "go.typelink.") {
+		case strings.HasPrefix(s.Name, "go.typelink."):
 			ntypelinks++
 			s.Type = obj.STYPELINK
 			s.Attr |= AttrHidden
 			s.Outer = symtypelink
-		}
 
-		if strings.HasPrefix(s.Name, "go.itablink.") {
+		case strings.HasPrefix(s.Name, "go.itablink."):
 			nitablinks++
 			s.Type = obj.SITABLINK
 			s.Attr |= AttrHidden
 			s.Outer = symitablink
-		}
 
-		if strings.HasPrefix(s.Name, "go.string.") {
+		case strings.HasPrefix(s.Name, "go.string."):
 			s.Type = obj.SGOSTRING
 			s.Attr |= AttrHidden
 			s.Outer = symgostring
@@ -471,21 +455,18 @@ func symtab() {
 				s.Type = obj.SGOSTRINGHDR
 				s.Outer = symgostringhdr
 			}
-		}
 
-		if strings.HasPrefix(s.Name, "runtime.gcbits.") {
+		case strings.HasPrefix(s.Name, "runtime.gcbits."):
 			s.Type = obj.SGCBITS
 			s.Attr |= AttrHidden
 			s.Outer = symgcbits
-		}
 
-		if strings.HasPrefix(s.Name, "go.func.") {
+		case strings.HasPrefix(s.Name, "go.func."):
 			s.Type = obj.SGOFUNC
 			s.Attr |= AttrHidden
 			s.Outer = symgofunc
-		}
 
-		if strings.HasPrefix(s.Name, "gcargs.") || strings.HasPrefix(s.Name, "gclocals.") || strings.HasPrefix(s.Name, "gclocals·") {
+		case strings.HasPrefix(s.Name, "gcargs."), strings.HasPrefix(s.Name, "gclocals."), strings.HasPrefix(s.Name, "gclocals·"):
 			s.Type = obj.SGOFUNC
 			s.Attr |= AttrHidden
 			s.Outer = symgofunc
-- 
cgit v1.3


From c4807d4cc759025854e354fee99ac20d125f0d79 Mon Sep 17 00:00:00 2001
From: Lynn Boger 
Date: Wed, 13 Apr 2016 08:58:10 -0500
Subject: runtime: improve memmove performance ppc64,ppc64le

This change improves the performance of memmove
on ppc64 & ppc64le mainly for moves >=32 bytes.
In addition, the test to detect backward moves
 was enhanced to avoid backward moves if source
and dest were in different types of storage, since
backward moves might not always be efficient.

Fixes #14507

The following shows some of the improvements from the test
in the runtime package:

BenchmarkMemmove32                   4229.56      4717.13      1.12x
BenchmarkMemmove64                   6156.03      7810.42      1.27x
BenchmarkMemmove128                  7521.69      12468.54     1.66x
BenchmarkMemmove256                  6729.90      18260.33     2.71x
BenchmarkMemmove512                  8521.59      18033.81     2.12x
BenchmarkMemmove1024                 9760.92      25762.61     2.64x
BenchmarkMemmove2048                 10241.00     29584.94     2.89x
BenchmarkMemmove4096                 10399.37     31882.31     3.07x

BenchmarkMemmoveUnalignedDst16       1943.69      2258.33      1.16x
BenchmarkMemmoveUnalignedDst32       3885.08      3965.81      1.02x
BenchmarkMemmoveUnalignedDst64       5121.63      6965.54      1.36x
BenchmarkMemmoveUnalignedDst128      7212.34      11372.68     1.58x
BenchmarkMemmoveUnalignedDst256      6564.52      16913.59     2.58x
BenchmarkMemmoveUnalignedDst512      8364.35      17782.57     2.13x
BenchmarkMemmoveUnalignedDst1024     9539.87      24914.72     2.61x
BenchmarkMemmoveUnalignedDst2048     9199.23      21235.11     2.31x
BenchmarkMemmoveUnalignedDst4096     10077.39     25231.99     2.50x

BenchmarkMemmoveUnalignedSrc32       3249.83      3742.52      1.15x
BenchmarkMemmoveUnalignedSrc64       5562.35      6627.96      1.19x
BenchmarkMemmoveUnalignedSrc128      6023.98      10200.84     1.69x
BenchmarkMemmoveUnalignedSrc256      6921.83      15258.43     2.20x
BenchmarkMemmoveUnalignedSrc512      8593.13      16541.97     1.93x
BenchmarkMemmoveUnalignedSrc1024     9730.95      22927.84     2.36x
BenchmarkMemmoveUnalignedSrc2048     9793.28      21537.73     2.20x
BenchmarkMemmoveUnalignedSrc4096     10132.96     26295.06     2.60x

Change-Id: I73af59970d4c97c728deabb9708b31ec7e01bdf2
Reviewed-on: https://go-review.googlesource.com/21990
Reviewed-by: Bill O'Farrell 
Reviewed-by: Brad Fitzpatrick 
---
 src/runtime/memmove_ppc64x.s | 117 +++++++++++++++++++++++++++----------------
 1 file changed, 74 insertions(+), 43 deletions(-)

(limited to 'src')

diff --git a/src/runtime/memmove_ppc64x.s b/src/runtime/memmove_ppc64x.s
index ea73b455b4..26dabd9e69 100644
--- a/src/runtime/memmove_ppc64x.s
+++ b/src/runtime/memmove_ppc64x.s
@@ -11,78 +11,109 @@ TEXT runtime·memmove(SB), NOSPLIT|NOFRAME, $0-24
 	MOVD	to+0(FP), R3
 	MOVD	from+8(FP), R4
 	MOVD	n+16(FP), R5
-	CMP	R5, $0
-	BNE	check
-	RET
 
+	// Determine if there are doublewords to
+	// copy so a more efficient move can be done
 check:
-	ANDCC	$7, R5, R7	// R7 is the number of bytes to copy and CR0[EQ] is set if there are none.
-	SRAD	$3, R5, R6	// R6 is the number of words to copy
-	CMP	R6, $0, CR1	// CR1[EQ] is set if there are no words to copy.
-
-	CMP	R3, R4, CR2
-	BC	12, 9, backward	// I think you should be able to write this as "BGT CR2, backward"
+	ANDCC	$7, R5, R7	// R7: bytes to copy
+	SRAD	$3, R5, R6	// R6: double words to copy
+	CMP	R6, $0, CR1	// CR1[EQ] set if no double words to copy
 
-	// Copying forward proceeds by copying R6 words then copying R7 bytes.
-	// R3 and R4 are advanced as we copy. Because PPC64 lacks post-increment
-	// load/store, R3 and R4 point before the bytes that are to be copied.
+	// Determine overlap by subtracting dest - src and comparing against the
+	// length.  The catches the cases where src and dest are in different types
+	// of storage such as stack and static to avoid doing backward move when not
+	// necessary.
 
-	BC	12, 6, noforwardlarge	// "BEQ CR1, noforwardlarge"
-
-	MOVD	R6, CTR
+	SUB	R4, R3, R8	// dest - src
+	CMPU	R8, R5, CR2	// < len?
+	BC	12, 8, backward // BLT CR2 backward
 
-	SUB	$8, R3
-	SUB	$8, R4
+	// Copying forward if no overlap.
 
-forwardlargeloop:
-	MOVDU	8(R4), R8
-	MOVDU	R8, 8(R3)
-	BC	16, 0, forwardlargeloop // "BDNZ"
-
-	ADD	$8, R3
-	ADD	$8, R4
+	BC	12, 6, noforwardlarge	// "BEQ CR1, noforwardlarge"
+	MOVD	R6,CTR			// R6 = number of double words
+	SRADCC	$2,R6,R8		// 32 byte chunks?
+	BNE	forward32setup		//
+
+	// Move double words
+
+forward8:
+	MOVD    0(R4), R8		// double word
+	ADD     $8,R4
+	MOVD    R8, 0(R3)		//
+	ADD     $8,R3
+	BC      16, 0, forward8
+	BR	noforwardlarge		// handle remainder
+
+	// Prepare for moves of 32 bytes at a time.
+
+forward32setup:
+	DCBTST	(R3)			// prepare data cache
+	DCBT	(R4)
+	MOVD	R8, CTR			// double work count
+
+forward32:
+	MOVD	0(R4), R8		// load 4 double words
+	MOVD	8(R4), R9
+	MOVD	16(R4), R14
+	MOVD	24(R4), R15
+	ADD	$32,R4
+	MOVD	R8, 0(R3)		// store those 4
+	MOVD	R9, 8(R3)
+	MOVD	R14,16(R3)
+	MOVD	R15,24(R3)
+	ADD	$32,R3			// bump up for next set
+	BC	16, 0, forward32	// continue
+	RLDCLCC	$61,R5,$3,R6		// remaining doublewords
+	BEQ	noforwardlarge
+	MOVD	R6,CTR			// set up the CTR
+	BR	forward8
 
 noforwardlarge:
-	BNE	forwardtail	// Tests the bit set by ANDCC above
-	RET
+	CMP	R7,$0			// any remaining bytes
+	BC	4, 1, LR
 
 forwardtail:
-	SUB	$1, R3
-	SUB	$1, R4
-	MOVD	R7, CTR
+	MOVD	R7, CTR			// move tail bytes
 
 forwardtailloop:
-	MOVBZU	1(R4), R8
-	MOVBZU	R8, 1(R3)
+	MOVBZ	0(R4), R8		// move single bytes
+	ADD	$1,R4
+	MOVBZ	R8, 0(R3)
+	ADD	$1,R3
 	BC	16, 0, forwardtailloop
 	RET
 
 backward:
-	// Copying backwards proceeds by copying R7 bytes then copying R6 words.
+	// Copying backwards proceeds by copying R7 bytes then copying R6 double words.
 	// R3 and R4 are advanced to the end of the destination/source buffers
 	// respectively and moved back as we copy.
 
-	ADD	R5, R4, R4
-	ADD	R3, R5, R3
+	ADD	R5, R4, R4		// end of source
+	ADD	R3, R5, R3		// end of dest
 
-	BEQ	nobackwardtail
+	BEQ	nobackwardtail		// earlier condition
 
-	MOVD	R7, CTR
+	MOVD	R7, CTR			// bytes to move
 
 backwardtailloop:
-	MOVBZU	-1(R4), R8
-	MOVBZU	R8, -1(R3)
+	MOVBZ 	-1(R4), R8		// point to last byte
+	SUB	$1,R4
+	MOVBZ 	R8, -1(R3)
+	SUB	$1,R3
 	BC	16, 0, backwardtailloop
 
 nobackwardtail:
-	BC	4, 6, backwardlarge		// "BNE CR1"
-	RET
+	CMP	R6,$0
+	BC	4, 5, LR
 
 backwardlarge:
 	MOVD	R6, CTR
 
 backwardlargeloop:
-	MOVDU	-8(R4), R8
-	MOVDU	R8, -8(R3)
-	BC	16, 0, backwardlargeloop	// "BDNZ"
+	MOVD 	-8(R4), R8
+	SUB	$8,R4
+	MOVD 	R8, -8(R3)
+	SUB	$8,R3
+	BC	16, 0, backwardlargeloop	//
 	RET
-- 
cgit v1.3


From 6b85a45edc94786c7669823ee47a6ce1156d6a9a Mon Sep 17 00:00:00 2001
From: David Chase 
Date: Mon, 21 Mar 2016 11:32:04 -0400
Subject: cmd/compile: move spills to loop exits when easy.

For call-free inner loops.

Revised statistics:
  85 inner loop spills sunk
 341 inner loop spills remaining
1162 inner loop spills that were candidates for sinking
     ended up completely register allocated
 119 inner loop spills could have been sunk were used in
     "shuffling" at the bottom of the loop.
   1 inner loop spill not sunk because the register assigned
     changed between def and exit,

 Understanding how to make an inner loop definition not be
 a candidate for from-memory shuffling (to force the shuffle
 code to choose some other value) should pick up some of the
 119 other spills disqualified for this reason.

 Modified the stats printing based on feedback from Austin.

Change-Id: If3fb9b5d5a028f42ccc36c4e3d9e0da39db5ca60
Reviewed-on: https://go-review.googlesource.com/21037
Reviewed-by: Keith Randall 
Run-TryBot: David Chase 
TryBot-Result: Gobot Gobot 
---
 src/cmd/compile/internal/ssa/likelyadjust.go | 142 +++++++++++++-
 src/cmd/compile/internal/ssa/regalloc.go     | 284 ++++++++++++++++++++++++++-
 src/cmd/compile/internal/ssa/sparsemap.go    |  16 ++
 3 files changed, 435 insertions(+), 7 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/ssa/likelyadjust.go b/src/cmd/compile/internal/ssa/likelyadjust.go
index 76251bdd14..2f52c4c6e6 100644
--- a/src/cmd/compile/internal/ssa/likelyadjust.go
+++ b/src/cmd/compile/internal/ssa/likelyadjust.go
@@ -11,11 +11,24 @@ import (
 type loop struct {
 	header *Block // The header node of this (reducible) loop
 	outer  *loop  // loop containing this loop
-	// Next two fields not currently used, but cheap to maintain,
-	// and aid in computation of inner-ness and list of blocks.
-	nBlocks      int32 // Number of blocks in this loop but not within inner loops
-	isInner      bool  // True if never discovered to contain a loop
-	containsCall bool  // if any block in this loop or any loop it contains is a BlockCall or BlockDefer
+
+	// By default, children exits, and depth are not initialized.
+	children []*loop  // loops nested directly within this loop. Initialized by assembleChildren().
+	exits    []*Block // exits records blocks reached by exits from this loop. Initialized by findExits().
+
+	// Loops aren't that common, so rather than force regalloc to keep
+	// a map or slice for its data, just put it here.
+	spills  []*Value
+	scratch int32
+
+	// Next three fields used by regalloc and/or
+	// aid in computation of inner-ness and list of blocks.
+	nBlocks int32 // Number of blocks in this loop but not within inner loops
+	depth   int16 // Nesting depth of the loop; 1 is outermost. Initialized by calculateDepths().
+	isInner bool  // True if never discovered to contain a loop
+
+	// register allocation uses this.
+	containsCall bool // if any block in this loop or any loop it contains is a BlockCall or BlockDefer
 }
 
 // outerinner records that outer contains inner
@@ -48,6 +61,9 @@ type loopnest struct {
 	po    []*Block
 	sdom  sparseTree
 	loops []*loop
+
+	// Record which of the lazily initialized fields have actually been initialized.
+	initializedChildren, initializedDepth, initializedExits bool
 }
 
 func min8(a, b int8) int8 {
@@ -295,6 +311,35 @@ func loopnestfor(f *Func) *loopnest {
 			innermost.nBlocks++
 		}
 	}
+
+	ln := &loopnest{f: f, b2l: b2l, po: po, sdom: sdom, loops: loops}
+
+	// Curious about the loopiness? "-d=ssa/likelyadjust/stats"
+	if f.pass.stats > 0 && len(loops) > 0 {
+		ln.assembleChildren()
+		ln.calculateDepths()
+		ln.findExits()
+
+		// Note stats for non-innermost loops are slightly flawed because
+		// they don't account for inner loop exits that span multiple levels.
+
+		for _, l := range loops {
+			x := len(l.exits)
+			cf := 0
+			if !l.containsCall {
+				cf = 1
+			}
+			inner := 0
+			if l.isInner {
+				inner++
+			}
+
+			f.logStat("loopstats:",
+				l.depth, "depth", x, "exits",
+				inner, "is_inner", cf, "is_callfree", l.nBlocks, "n_blocks")
+		}
+	}
+
 	if f.pass.debug > 1 && len(loops) > 0 {
 		fmt.Printf("Loops in %s:\n", f.Name)
 		for _, l := range loops {
@@ -314,5 +359,90 @@ func loopnestfor(f *Func) *loopnest {
 		}
 		fmt.Print("\n")
 	}
-	return &loopnest{f, b2l, po, sdom, loops}
+	return ln
+}
+
+// assembleChildren initializes the children field of each
+// loop in the nest.  Loop A is a child of loop B if A is
+// directly nested within B (based on the reducible-loops
+// detection above)
+func (ln *loopnest) assembleChildren() {
+	if ln.initializedChildren {
+		return
+	}
+	for _, l := range ln.loops {
+		if l.outer != nil {
+			l.outer.children = append(l.outer.children, l)
+		}
+	}
+	ln.initializedChildren = true
+}
+
+// calculateDepths uses the children field of loops
+// to determine the nesting depth (outer=1) of each
+// loop.  This is helpful for finding exit edges.
+func (ln *loopnest) calculateDepths() {
+	if ln.initializedDepth {
+		return
+	}
+	ln.assembleChildren()
+	for _, l := range ln.loops {
+		if l.outer == nil {
+			l.setDepth(1)
+		}
+	}
+	ln.initializedDepth = true
+}
+
+// findExits uses loop depth information to find the
+// exits from a loop.
+func (ln *loopnest) findExits() {
+	if ln.initializedExits {
+		return
+	}
+	ln.calculateDepths()
+	b2l := ln.b2l
+	for _, b := range ln.po {
+		l := b2l[b.ID]
+		if l != nil && len(b.Succs) == 2 {
+			sl := b2l[b.Succs[0].ID]
+			if recordIfExit(l, sl, b.Succs[0]) {
+				continue
+			}
+			sl = b2l[b.Succs[1].ID]
+			if recordIfExit(l, sl, b.Succs[1]) {
+				continue
+			}
+		}
+	}
+	ln.initializedExits = true
+}
+
+// recordIfExit checks sl (the loop containing b) to see if it
+// is outside of loop l, and if so, records b as an exit block
+// from l and returns true.
+func recordIfExit(l, sl *loop, b *Block) bool {
+	if sl != l {
+		if sl == nil || sl.depth <= l.depth {
+			l.exits = append(l.exits, b)
+			return true
+		}
+		// sl is not nil, and is deeper than l
+		// it's possible for this to be a goto into an irreducible loop made from gotos.
+		for sl.depth > l.depth {
+			sl = sl.outer
+		}
+		if sl != l {
+			l.exits = append(l.exits, b)
+			return true
+		}
+	}
+	return false
+}
+
+func (l *loop) setDepth(d int16) {
+	l.depth = d
+	for _, c := range l.children {
+		c.setDepth(d + 1)
+	}
 }
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index dfae8612d6..d1de3646d9 100644
--- a/src/cmd/compile/internal/ssa/regalloc.go
+++ b/src/cmd/compile/internal/ssa/regalloc.go
@@ -91,6 +91,13 @@
 // will have no use (so don't run deadcode after regalloc!).
 // TODO: maybe we should introduce these extra phis?
 
+// Additional not-quite-SSA output occurs when spills are sunk out
+// of loops to the targets of exit edges from the loop.  Before sinking,
+// there is one spill site (one StoreReg) targeting stack slot X, after
+// sinking there may be multiple spill sites targeting stack slot X,
+// with no phi functions at any join points reachable by the multiple
+// spill sites.
+
 package ssa
 
 import (
@@ -100,7 +107,8 @@ import (
 )
 
 const (
-	logSpills = iota
+	moveSpills = iota
+	logSpills
 	regDebug
 	stackDebug
 )
@@ -176,6 +184,7 @@ type valState struct {
 	uses              *use    // list of uses in this block
 	spill             *Value  // spilled copy of the Value
 	spillUsed         bool
+	spillUsedShuffle  bool     // true if used in shuffling, after ordinary uses
 	needReg           bool     // cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !.v.Type.IsFlags()
 	rematerializeable bool     // cached value of v.rematerializeable()
 	desired           register // register we want value to be in, if any
@@ -243,6 +252,15 @@ type regAllocState struct {
 	loopnest *loopnest
 }
 
+type spillToSink struct {
+	spill *Value // Spill instruction to move (a StoreReg)
+	dests int32  // Bitmask indicating exit blocks from loop in which spill/val is defined. 1<= 0; i-- {
+							v := ss.Values[i]
+							entryCandidates.remove(v.ID) // Cannot be an issue, only keeps the sets smaller.
+							for _, a := range v.Args {
+								if s.isLoopSpillCandidate(loop, a) {
+									entryCandidates.setBit(a.ID, uint(whichExit))
+								}
+							}
+						}
+					}
+
+					for _, e := range loop.spills {
+						whichblocks := entryCandidates.get(e.ID)
+						oldSpill := s.values[e.ID].spill
+						if whichblocks != 0 && whichblocks != -1 { // -1 = not in map.
+							toSink = append(toSink, spillToSink{spill: oldSpill, dests: whichblocks})
+						}
+					}
+
+				} // loop is inner etc
+				loop.scratch = 0 // Don't leave a mess, just in case.
+				loop.spills = nil
+			} // if scratch == nBlocks
+		} // if loop is not nil
+
 		// Clear any final uses.
 		// All that is left should be the pseudo-uses added for values which
 		// are live at the end of b.
@@ -1110,9 +1241,16 @@ func (s *regAllocState) regalloc(f *Func) {
 			// Constants, SP, SB, ...
 			continue
 		}
+		loop := s.loopForBlock(spill.Block)
+		if loop != nil {
+			nSpillsInner--
+		}
+
 		spill.Args[0].Uses--
 		f.freeValue(spill)
+		nSpills--
 	}
+
 	for _, b := range f.Blocks {
 		i := 0
 		for _, v := range b.Values {
@@ -1127,12 +1265,153 @@ func (s *regAllocState) regalloc(f *Func) {
 		// Not important now because this is the last phase that manipulates Values
 	}
 
+	// Must clear these out before any potential recycling, though that's
+	// not currently implemented.
+	for i, ts := range toSink {
+		vsp := ts.spill
+		if vsp.Op == OpInvalid { // This spill was completely eliminated
+			toSink[i].spill = nil
+		}
+	}
+
 	// Anything that didn't get a register gets a stack location here.
 	// (StoreReg, stack-based phis, inputs, ...)
 	stacklive := stackalloc(s.f, s.spillLive)
 
 	// Fix up all merge edges.
 	s.shuffle(stacklive)
+
+	// Insert moved spills (that have not been marked invalid above)
+	// at start of appropriate block and remove the originals from their
+	// location within loops.  Notice that this can break SSA form;
+	// if a spill is sunk to multiple exits, there will be no phi for that
+	// spill at a join point downstream of those two exits, though the
+	// two spills will target the same stack slot.  Notice also that this
+	// takes place after stack allocation, so the stack allocator does
+	// not need to process these malformed flow graphs.
+sinking:
+	for _, ts := range toSink {
+		vsp := ts.spill
+		if vsp == nil { // This spill was completely eliminated
+			nSpillsSunkUnused++
+			continue sinking
+		}
+		e := ts.spilledValue()
+		if s.values[e.ID].spillUsedShuffle {
+			nSpillsNotSunkLateUse++
+			continue sinking
+		}
+
+		// move spills to a better (outside of loop) block.
+		// This would be costly if it occurred very often, but it doesn't.
+		b := vsp.Block
+		loop := s.loopnest.b2l[b.ID]
+		dests := ts.dests
+
+		// Pre-check to be sure that spilled value is still in expected register on all exits where live.
+	check_val_still_in_reg:
+		for i := uint(0); i < 32 && dests != 0; i++ {
+
+			if dests&(1< 1 {
+				panic("Should be impossible given critical edges removed")
+			}
+			p := d.Preds[0] // block in loop exiting to d.
+
+			endregs := s.endRegs[p.ID]
+			for _, regrec := range endregs {
+				if regrec.v == e && regrec.r != noRegister && regrec.c == e { // TODO: regrec.c != e implies different spill possible.
+					continue check_val_still_in_reg
+				}
+			}
+			// If here, the register assignment was lost down at least one exit and it can't be sunk
+			if s.f.pass.debug > moveSpills {
+				s.f.Config.Warnl(e.Line, "lost register assignment for spill %v in %v at exit %v to %v",
+					vsp, b, p, d)
+			}
+			nSpillsChanged++
+			continue sinking
+		}
+
+		nSpillsSunk++
+		nSpillsInner--
+		// don't update nSpills, since spill is only moved, and if it is duplicated, the spills-on-a-path is not increased.
+
+		dests = ts.dests
+
+		// remove vsp from b.Values
+		i := 0
+		for _, w := range b.Values {
+			if vsp == w {
+				continue
+			}
+			b.Values[i] = w
+			i++
+		}
+		b.Values = b.Values[:i]
+
+		for i := uint(0); i < 32 && dests != 0; i++ {
+
+			if dests&(1< moveSpills {
+				s.f.Config.Warnl(e.Line, "moved spill %v in %v for %v to %v in %v",
+					vsp, b, e, vspnew, d)
+			}
+
+			f.setHome(vspnew, f.getHome(vsp.ID)) // copy stack home
+
+			// shuffle vspnew to the beginning of its block
+			copy(d.Values[1:], d.Values[0:len(d.Values)-1])
+			d.Values[0] = vspnew
+		}
+	}
+
+	if f.pass.stats > 0 {
+		f.logStat("spills_info",
+			nSpills, "spills", nSpillsInner, "inner_spills_remaining", nSpillsSunk, "inner_spills_sunk", nSpillsSunkUnused, "inner_spills_unused", nSpillsNotSunkLateUse, "inner_spills_shuffled", nSpillsChanged, "inner_spills_changed")
+	}
+}
+
+// isLoopSpillCandidate indicates whether the spill for v satisfies preliminary
+// spill-sinking conditions just after the last block of loop has been processed.
+// In particular:
+//   v needs a register.
+//   v's spill is not (YET) used.
+//   v's definition is within loop.
+// The spill may be used in the future, either by an outright use
+// in the code, or by shuffling code inserted after stack allocation.
+// Outright uses cause sinking; shuffling (within the loop) inhibits it.
+func (s *regAllocState) isLoopSpillCandidate(loop *loop, v *Value) bool {
+	return s.values[v.ID].needReg && !s.values[v.ID].spillUsed && s.loopnest.b2l[v.Block.ID] == loop
+}
+
+// lateSpillUse notes a late (after stack allocation) use of spill c
+// This will inhibit spill sinking.
+func (s *regAllocState) lateSpillUse(c *Value) {
+	// TODO investigate why this is necessary.
+	// It appears that an outside-the-loop use of
+	// an otherwise sinkable spill makes the spill
+	// a candidate for shuffling, when it would not
+	// otherwise have been the case (spillUsed was not
+	// true when isLoopSpillCandidate was called, yet
+	// it was shuffled).  Such shuffling cuts the amount
+	// of spill sinking by more than half (in make.bash)
+	v := s.orig[c.ID]
+	if v != nil {
+		s.values[v.ID].spillUsedShuffle = true
+	}
 }
 
 // shuffle fixes up all the merge edges (those going into blocks of indegree > 1).
@@ -1307,6 +1586,7 @@ func (e *edgeState) process() {
 		if _, isReg := loc.(*Register); isReg {
 			c = e.p.NewValue1(c.Line, OpCopy, c.Type, c)
 		} else {
+			e.s.lateSpillUse(c)
 			c = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
 		}
 		e.set(r, vid, c, false)
@@ -1395,6 +1675,7 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
 			}
 		} else {
 			if dstReg {
+				e.s.lateSpillUse(c)
 				x = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
 			} else {
 				// mem->mem. Use temp register.
@@ -1412,6 +1693,7 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
 				e.erase(loc)
 
 				r := e.findRegFor(c.Type)
+				e.s.lateSpillUse(c)
 				t := e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
 				e.set(r, vid, t, false)
 				x = e.p.NewValue1(c.Line, OpStoreReg, loc.(LocalSlot).Type, t)
diff --git a/src/cmd/compile/internal/ssa/sparsemap.go b/src/cmd/compile/internal/ssa/sparsemap.go
index 6c0043b230..0211a70f09 100644
--- a/src/cmd/compile/internal/ssa/sparsemap.go
+++ b/src/cmd/compile/internal/ssa/sparsemap.go
@@ -32,6 +32,8 @@ func (s *sparseMap) contains(k ID) bool {
 	return i < len(s.dense) && s.dense[i].key == k
 }
 
+// get returns the value for key k, or -1 if k does
+// not appear in the map.
 func (s *sparseMap) get(k ID) int32 {
 	i := s.sparse[k]
 	if i < len(s.dense) && s.dense[i].key == k {
@@ -50,6 +52,20 @@ func (s *sparseMap) set(k ID, v int32) {
 	s.sparse[k] = len(s.dense) - 1
 }
 
+// setBit sets the v'th bit of k's value, where 0 <= v < 32
+func (s *sparseMap) setBit(k ID, v uint) {
+	if v >= 32 {
+		panic("bit index too large.")
+	}
+	i := s.sparse[k]
+	if i < len(s.dense) && s.dense[i].key == k {
+		s.dense[i].val |= 1 << v
+		return
+	}
+	s.dense = append(s.dense, sparseEntry{k, 1 << v})
+	s.sparse[k] = len(s.dense) - 1
+}
+
 func (s *sparseMap) remove(k ID) {
 	i := s.sparse[k]
 	if i < len(s.dense) && s.dense[i].key == k {
-- 
cgit v1.3


From d8e8fc292ace5ae59a0da44dfca1dd5b1a71ecf1 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Wed, 13 Apr 2016 11:13:39 -0400
Subject: runtime/internal/atomic: remove write barrier from Storep1 on s390x

atomic.Storep1 is not supposed to invoke a write barrier (that's what
atomicstorep is for), but currently does on s390x. This causes a panic
in runtime.mapzero when it tries to use atomic.Storep1 to store what's
actually a scalar.

Fix this by eliminating the write barrier from atomic.Storep1 on
s390x. Also add some documentation to atomicstorep to explain the
difference between these.

Fixes #15270.

Change-Id: I291846732d82f090a218df3ef6351180aff54e81
Reviewed-on: https://go-review.googlesource.com/21993
Reviewed-by: Brad Fitzpatrick 
Run-TryBot: Austin Clements 
Reviewed-by: Michael Munday 
---
 src/runtime/atomic_pointer.go               | 5 ++---
 src/runtime/internal/atomic/atomic_s390x.go | 2 +-
 2 files changed, 3 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/runtime/atomic_pointer.go b/src/runtime/atomic_pointer.go
index bd21b49945..d54f1d6eef 100644
--- a/src/runtime/atomic_pointer.go
+++ b/src/runtime/atomic_pointer.go
@@ -15,10 +15,9 @@ import (
 // escape analysis decisions about the pointer value being stored.
 // Instead, these are wrappers around the actual atomics (casp1 and so on)
 // that use noescape to convey which arguments do not escape.
-//
-// Additionally, these functions must update the shadow heap for
-// write barrier checking.
 
+// atomicstorep performs *ptr = new atomically and invokes a write barrier.
+//
 //go:nosplit
 func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) {
 	atomic.Storep1(noescape(ptr), new)
diff --git a/src/runtime/internal/atomic/atomic_s390x.go b/src/runtime/internal/atomic/atomic_s390x.go
index f31f1af444..b6d3d84bdf 100644
--- a/src/runtime/internal/atomic/atomic_s390x.go
+++ b/src/runtime/internal/atomic/atomic_s390x.go
@@ -40,7 +40,7 @@ func Store64(ptr *uint64, val uint64) {
 //go:noinline
 //go:nosplit
 func Storep1(ptr unsafe.Pointer, val unsafe.Pointer) {
-	*(*unsafe.Pointer)(ptr) = val
+	*(*uintptr)(ptr) = uintptr(val)
 }
 
 //go:noescape
-- 
cgit v1.3


From 7d0d1222477ce50736ee24adb38c1f487d0801d9 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Tue, 12 Apr 2016 18:00:04 -0700
Subject: cmd/compile: move more compiler specifics into compiler specific
 export section

Instead of indicating with each function signature if it has an inlineable
body, collect all functions in order and export function bodies with function
index in platform-specific section.

Moves this compiler specific information out of the platform-independent
export data section, and removes an int value for all functions w/o body.
Also simplifies the code a bit.

Change-Id: I8b2d7299dbe81f2706be49ecfb9d9f7da85fd854
Reviewed-on: https://go-review.googlesource.com/21939
Reviewed-by: Matthew Dempsky 
---
 src/cmd/compile/internal/gc/bexport.go |  63 +++++++++++---------
 src/cmd/compile/internal/gc/bimport.go | 104 ++++++++++++++-------------------
 src/go/internal/gcimporter/bimport.go  |   2 -
 3 files changed, 80 insertions(+), 89 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index cb438d7573..e780bcf577 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -124,10 +124,11 @@ const exportVersion = "v0"
 const exportInlined = true // default: true
 
 type exporter struct {
-	out      *bufio.Writer
-	pkgIndex map[*Pkg]int
-	typIndex map[*Type]int
-	inlined  []*Func
+	out *bufio.Writer
+
+	pkgIndex map[*Pkg]int  // pkg -> pkg index in order of appearance
+	typIndex map[*Type]int // type -> type index in order of appearance
+	funcList []*Func       // in order of appearance
 
 	// debugging support
 	written int // bytes written
@@ -322,27 +323,39 @@ func export(out *bufio.Writer, trace bool) int {
 	// --- inlined function bodies ---
 
 	if p.trace {
-		p.tracef("\n--- inlined function bodies ---\n[ ")
+		p.tracef("\n--- inlined function bodies ---\n")
 		if p.indent != 0 {
 			Fatalf("exporter: incorrect indentation")
 		}
 	}
 
-	// write inlined function bodies
-	p.int(len(p.inlined))
-	if p.trace {
-		p.tracef("]\n")
-	}
-	for _, f := range p.inlined {
-		if p.trace {
-			p.tracef("\n----\nfunc { %s }\n", Hconv(f.Inl, FmtSharp))
-		}
-		p.stmtList(f.Inl)
-		if p.trace {
-			p.tracef("\n")
+	// write inlineable function bodies
+	objcount = 0
+	for i, f := range p.funcList {
+		if f != nil {
+			// function has inlineable body:
+			// write index and body
+			if p.trace {
+				p.tracef("\n----\nfunc { %s }\n", Hconv(f.Inl, FmtSharp))
+			}
+			p.int(i)
+			p.stmtList(f.Inl)
+			if p.trace {
+				p.tracef("\n")
+			}
+			objcount++
 		}
 	}
 
+	// indicate end of list
+	if p.trace {
+		p.tracef("\n")
+	}
+	p.tag(-1) // invalid index terminates list
+
+	// for self-verification only (redundant)
+	p.int(objcount)
+
 	if p.trace {
 		p.tracef("\n--- end ---\n")
 	}
@@ -443,10 +456,9 @@ func (p *exporter) obj(sym *Sym) {
 			p.paramList(sig.Params(), inlineable)
 			p.paramList(sig.Results(), inlineable)
 
-			index := -1
+			var f *Func
 			if inlineable {
-				index = len(p.inlined)
-				p.inlined = append(p.inlined, sym.Def.Func)
+				f = sym.Def.Func
 				// TODO(gri) re-examine reexportdeplist:
 				// Because we can trivially export types
 				// in-place, we don't need to collect types
@@ -454,9 +466,9 @@ func (p *exporter) obj(sym *Sym) {
 				// With an adjusted reexportdeplist used only
 				// by the binary exporter, we can also avoid
 				// the global exportlist.
-				reexportdeplist(sym.Def.Func.Inl)
+				reexportdeplist(f.Inl)
 			}
-			p.int(index)
+			p.funcList = append(p.funcList, f)
 		} else {
 			// variable
 			p.tag(varTag)
@@ -563,13 +575,12 @@ func (p *exporter) typ(t *Type) {
 			p.paramList(sig.Params(), inlineable)
 			p.paramList(sig.Results(), inlineable)
 
-			index := -1
+			var f *Func
 			if inlineable {
-				index = len(p.inlined)
-				p.inlined = append(p.inlined, mfn.Func)
+				f = mfn.Func
 				reexportdeplist(mfn.Func.Inl)
 			}
-			p.int(index)
+			p.funcList = append(p.funcList, f)
 		}
 
 		if p.trace && len(methods) > 0 {
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 9cebafcaef..2e80b9f81d 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -23,9 +23,10 @@ type importer struct {
 	in       *bufio.Reader
 	buf      []byte   // for reading strings
 	bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
-	pkgList  []*Pkg
-	typList  []*Type
-	inlined  []*Node // functions with pending inlined function bodies
+
+	pkgList  []*Pkg  // in order of appearance
+	typList  []*Type // in order of appearance
+	funcList []*Node // in order of appearance; nil entry means already declared
 
 	// debugging support
 	debugFormat bool
@@ -107,21 +108,35 @@ func Import(in *bufio.Reader) {
 		Fatalf("importer: got %d objects; want %d", objcount, count)
 	}
 
-	// read inlined functions bodies
+	// read inlineable functions bodies
 	if dclcontext != PEXTERN {
 		Fatalf("importer: unexpected context %d", dclcontext)
 	}
 
-	bcount := p.int() // consistency check only
-	if bcount != len(p.inlined) {
-		Fatalf("importer: expected %d inlined function bodies; got %d", bcount, len(p.inlined))
-	}
-	for _, f := range p.inlined {
+	objcount = 0
+	for i0 := -1; ; {
+		i := p.int() // index of function with inlineable body
+		if i < 0 {
+			break
+		}
+
+		// don't process the same function twice
+		if i <= i0 {
+			Fatalf("importer: index not increasing: %d <= %d", i, i0)
+		}
+		i0 = i
+
 		if Funcdepth != 0 {
 			Fatalf("importer: unexpected Funcdepth %d", Funcdepth)
 		}
-		if f != nil {
-			// function body not yet imported - read body and set it
+
+		// Note: In the original code, funchdr and funcbody are called for
+		// all functions (that were not yet imported). Now, we are calling
+		// them only for functions with inlineable bodies. funchdr does
+		// parameter renaming which doesn't matter if we don't have a body.
+
+		if f := p.funcList[i]; f != nil {
+			// function not yet imported - read body and set it
 			funchdr(f)
 			f.Func.Inl.Set(p.stmtList())
 			funcbody(f)
@@ -131,6 +146,13 @@ func Import(in *bufio.Reader) {
 			p.stmtList()
 			dclcontext = PEXTERN
 		}
+
+		objcount++
+	}
+
+	// self-verification
+	if count := p.int(); count != objcount {
+		Fatalf("importer: got %d functions; want %d", objcount, count)
 	}
 
 	if dclcontext != PEXTERN {
@@ -214,47 +236,23 @@ func (p *importer) obj(tag int) {
 		sym := p.qualifiedName()
 		params := p.paramList()
 		result := p.paramList()
-		inl := p.int()
 
 		sig := functype(nil, params, result)
 		importsym(sym, ONAME)
 		if sym.Def != nil && sym.Def.Op == ONAME {
-			if Eqtype(sig, sym.Def.Type) {
-				// function was imported before (via another import)
-				dclcontext = PDISCARD // since we skip funchdr below
-			} else {
+			// function was imported before (via another import)
+			if !Eqtype(sig, sym.Def.Type) {
 				Fatalf("importer: inconsistent definition for func %v during import\n\t%v\n\t%v", sym, sym.Def.Type, sig)
 			}
-		}
-
-		var n *Node
-		if dclcontext != PDISCARD {
-			n = newfuncname(sym)
-			n.Type = sig
-			declare(n, PFUNC)
-			if inl < 0 {
-				funchdr(n)
-			}
-		}
-
-		if inl >= 0 {
-			// function has inlined body - collect for later
-			if inl != len(p.inlined) {
-				Fatalf("importer: inlined index = %d; want %d", inl, len(p.inlined))
-			}
-			p.inlined = append(p.inlined, n)
-		}
-
-		// parser.go:hidden_import
-		if dclcontext == PDISCARD {
-			dclcontext = PEXTERN // since we skip the funcbody below
+			p.funcList = append(p.funcList, nil)
 			break
 		}
 
-		if inl < 0 {
-			funcbody(n)
-		}
-		importlist = append(importlist, n) // TODO(gri) may only be needed for inlineable functions
+		n := newfuncname(sym)
+		n.Type = sig
+		declare(n, PFUNC)
+		p.funcList = append(p.funcList, n)
+		importlist = append(importlist, n)
 
 		if Debug['E'] > 0 {
 			fmt.Printf("import [%q] func %v \n", importpkg.Path, n)
@@ -316,23 +314,13 @@ func (p *importer) typ() *Type {
 			recv := p.paramList() // TODO(gri) do we need a full param list for the receiver?
 			params := p.paramList()
 			result := p.paramList()
-			inl := p.int()
 
 			n := methodname1(newname(sym), recv[0].Right)
 			n.Type = functype(recv[0], params, result)
 			checkwidth(n.Type)
 			addmethod(sym, n.Type, tsym.Pkg, false, false)
-			if inl < 0 {
-				funchdr(n)
-			}
-
-			if inl >= 0 {
-				// method has inlined body - collect for later
-				if inl != len(p.inlined) {
-					Fatalf("importer: inlined index = %d; want %d", inl, len(p.inlined))
-				}
-				p.inlined = append(p.inlined, n)
-			}
+			p.funcList = append(p.funcList, n)
+			importlist = append(importlist, n)
 
 			// (comment from parser.go)
 			// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
@@ -341,12 +329,6 @@ func (p *importer) typ() *Type {
 			// this back link here we avoid special casing there.
 			n.Type.SetNname(n)
 
-			// parser.go:hidden_import
-			if inl < 0 {
-				funcbody(n)
-			}
-			importlist = append(importlist, n) // TODO(gri) may only be needed for inlineable functions
-
 			if Debug['E'] > 0 {
 				fmt.Printf("import [%q] meth %v \n", importpkg.Path, n)
 				if Debug['m'] > 2 && len(n.Func.Inl.Slice()) != 0 {
diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go
index a9d678b021..81af064b88 100644
--- a/src/go/internal/gcimporter/bimport.go
+++ b/src/go/internal/gcimporter/bimport.go
@@ -186,7 +186,6 @@ func (p *importer) obj(tag int) {
 		params, isddd := p.paramList()
 		result, _ := p.paramList()
 		sig := types.NewSignature(nil, params, result, isddd)
-		p.int() // read and discard index of inlined function body
 		p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
 
 	default:
@@ -269,7 +268,6 @@ func (p *importer) typ(parent *types.Package) types.Type {
 			recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
 			params, isddd := p.paramList()
 			result, _ := p.paramList()
-			p.int() // read and discard index of inlined function body
 
 			sig := types.NewSignature(recv.At(0), params, result, isddd)
 			t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig))
-- 
cgit v1.3


From eb79f21c48915454b372de7fee2c6b86d52ea0bc Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Tue, 12 Apr 2016 21:58:44 -0700
Subject: cmd/compile, go/importer: minor cleanups

Change-Id: Ic7a1fb0dbbf108052c970a4a830269a5673df7df
Reviewed-on: https://go-review.googlesource.com/21963
Reviewed-by: Matthew Dempsky 
---
 src/cmd/compile/internal/gc/bexport.go |  5 ++--
 src/cmd/compile/internal/gc/bimport.go | 10 +++++---
 src/go/internal/gcimporter/bimport.go  | 46 ++++++++++++++--------------------
 3 files changed, 27 insertions(+), 34 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index e780bcf577..59a85c2f23 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -59,9 +59,8 @@ Encoding format:
 
 The export data starts with a single byte indicating the encoding format
 (compact, or with debugging information), followed by a version string
-(so we can evolve the encoding if need be), the name of the imported
-package, and a string containing platform-specific information for that
-package.
+(so we can evolve the encoding if need be), and then the package object
+for the exported package (with an empty path).
 
 After this header, two lists of objects and the list of inlined function
 bodies follows.
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 2e80b9f81d..4a93b5a91d 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -59,9 +59,6 @@ func Import(in *bufio.Reader) {
 
 	// read package data
 	p.pkg()
-	if p.pkgList[0] != importpkg {
-		Fatalf("importer: imported package not found in pkgList[0]")
-	}
 
 	// defer some type-checking until all types are read in completely
 	// (parser.go:import_package)
@@ -193,7 +190,12 @@ func (p *importer) pkg() *Pkg {
 		Fatalf("importer: bad path in import: %q", path)
 	}
 
-	// an empty path denotes the package we are currently importing
+	// an empty path denotes the package we are currently importing;
+	// it must be the first package we see
+	if (path == "") != (len(p.pkgList) == 0) {
+		panic(fmt.Sprintf("package path %q for pkg index %d", path, len(p.pkgList)))
+	}
+
 	pkg := importpkg
 	if path != "" {
 		pkg = mkpkg(path)
diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go
index 81af064b88..7a7bc871f4 100644
--- a/src/go/internal/gcimporter/bimport.go
+++ b/src/go/internal/gcimporter/bimport.go
@@ -16,12 +16,15 @@ import (
 )
 
 type importer struct {
-	imports  map[string]*types.Package
-	data     []byte
+	imports map[string]*types.Package
+	data    []byte
+	path    string
+
 	buf      []byte   // for reading strings
 	bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib
-	pkgList  []*types.Package
-	typList  []types.Type
+
+	pkgList []*types.Package
+	typList []types.Type
 
 	debugFormat bool
 	read        int // bytes read
@@ -35,6 +38,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
 	p := importer{
 		imports: imports,
 		data:    data,
+		path:    path,
 	}
 	p.buf = p.bufarray[:]
 
@@ -58,25 +62,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
 	p.typList = append(p.typList, predeclared...)
 
 	// read package data
-	// TODO(gri) clean this up
-	i := p.tagOrIndex()
-	if i != packageTag {
-		panic(fmt.Sprintf("package tag expected, got %d", i))
-	}
-	name := p.string()
-	if s := p.string(); s != "" {
-		panic(fmt.Sprintf("empty path expected, got %s", s))
-	}
-	pkg := p.imports[path]
-	if pkg == nil {
-		pkg = types.NewPackage(path, name)
-		p.imports[path] = pkg
-	}
-	p.pkgList = append(p.pkgList, pkg)
-
-	if debug && p.pkgList[0] != pkg {
-		panic("imported packaged not found in pkgList[0]")
-	}
+	pkg := p.pkg()
 
 	// read objects of phase 1 only (see cmd/compiler/internal/gc/bexport.go)
 	objcount := 0
@@ -91,7 +77,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
 
 	// self-verification
 	if count := p.int(); count != objcount {
-		panic(fmt.Sprintf("importer: got %d objects; want %d", objcount, count))
+		panic(fmt.Sprintf("got %d objects; want %d", objcount, count))
 	}
 
 	// ignore compiler-specific import data
@@ -135,16 +121,22 @@ func (p *importer) pkg() *types.Package {
 		panic("empty package name in import")
 	}
 
-	// we should never see an empty import path
-	if path == "" {
-		panic("empty import path")
+	// an empty path denotes the package we are currently importing;
+	// it must be the first package we see
+	if (path == "") != (len(p.pkgList) == 0) {
+		panic(fmt.Sprintf("package path %q for pkg index %d", path, len(p.pkgList)))
 	}
 
 	// if the package was imported before, use that one; otherwise create a new one
+	if path == "" {
+		path = p.path
+	}
 	pkg := p.imports[path]
 	if pkg == nil {
 		pkg = types.NewPackage(path, name)
 		p.imports[path] = pkg
+	} else if pkg.Name() != name {
+		panic(fmt.Sprintf("conflicting names %s and %s for package %q", pkg.Name(), name, path))
 	}
 	p.pkgList = append(p.pkgList, pkg)
 
-- 
cgit v1.3


From da6205b67e844503152b3be7bbb1a25c76cbbce2 Mon Sep 17 00:00:00 2001
From: Dmitry Vyukov 
Date: Fri, 8 Apr 2016 17:56:36 +0200
Subject: cmd/pprof/internal/profile: always subtract 1 from PCs

Go runtime never emits PCs that are not a return address
(except for cpu profiler).

Change-Id: I08d9dc5c7c71e23f34f2f0c16f8baeeb4f64fcd6
Reviewed-on: https://go-review.googlesource.com/21735
Reviewed-by: Ian Lance Taylor 
---
 src/cmd/internal/pprof/profile/legacy_profile.go | 41 ++++++++----------------
 1 file changed, 13 insertions(+), 28 deletions(-)

(limited to 'src')

diff --git a/src/cmd/internal/pprof/profile/legacy_profile.go b/src/cmd/internal/pprof/profile/legacy_profile.go
index e1f24c4c6d..3d4da6b4d7 100644
--- a/src/cmd/internal/pprof/profile/legacy_profile.go
+++ b/src/cmd/internal/pprof/profile/legacy_profile.go
@@ -110,11 +110,8 @@ func parseGoCount(b []byte) (*Profile, error) {
 			if err != nil {
 				return nil, errMalformed
 			}
-			// Adjust all frames by -1 (except the leaf) to land on top of
-			// the call instruction.
-			if len(locs) > 0 {
-				addr--
-			}
+			// Adjust all frames by -1 to land on the call instruction.
+			addr--
 			loc := locations[addr]
 			if loc == nil {
 				loc = &Location{
@@ -291,11 +288,8 @@ func ParseTracebacks(b []byte) (*Profile, error) {
 		if s, addrs := extractHexAddresses(l); len(s) > 0 {
 			for _, addr := range addrs {
 				// Addresses from stack traces point to the next instruction after
-				// each call. Adjust by -1 to land somewhere on the actual call
-				// (except for the leaf, which is not a call).
-				if len(sloc) > 0 {
-					addr--
-				}
+				// each call. Adjust by -1 to land somewhere on the actual call.
+				addr--
 				loc := locs[addr]
 				if locs[addr] == nil {
 					loc = &Location{
@@ -568,13 +562,10 @@ func parseHeap(b []byte) (p *Profile, err error) {
 			return nil, err
 		}
 		var sloc []*Location
-		for i, addr := range addrs {
+		for _, addr := range addrs {
 			// Addresses from stack traces point to the next instruction after
-			// each call. Adjust by -1 to land somewhere on the actual call
-			// (except for the leaf, which is not a call).
-			if i > 0 {
-				addr--
-			}
+			// each call. Adjust by -1 to land somewhere on the actual call.
+			addr--
 			loc := locs[addr]
 			if locs[addr] == nil {
 				loc = &Location{
@@ -776,13 +767,10 @@ func parseContention(b []byte) (p *Profile, err error) {
 			return nil, err
 		}
 		var sloc []*Location
-		for i, addr := range addrs {
+		for _, addr := range addrs {
 			// Addresses from stack traces point to the next instruction after
-			// each call. Adjust by -1 to land somewhere on the actual call
-			// (except for the leaf, which is not a call).
-			if i > 0 {
-				addr--
-			}
+			// each call. Adjust by -1 to land somewhere on the actual call.
+			addr--
 			loc := locs[addr]
 			if locs[addr] == nil {
 				loc = &Location{
@@ -919,13 +907,10 @@ func parseThread(b []byte) (*Profile, error) {
 		}
 
 		var sloc []*Location
-		for i, addr := range addrs {
+		for _, addr := range addrs {
 			// Addresses from stack traces point to the next instruction after
-			// each call. Adjust by -1 to land somewhere on the actual call
-			// (except for the leaf, which is not a call).
-			if i > 0 {
-				addr--
-			}
+			// each call. Adjust by -1 to land somewhere on the actual call.
+			addr--
 			loc := locs[addr]
 			if locs[addr] == nil {
 				loc = &Location{
-- 
cgit v1.3


From 4721ea6abcde318a2f5d61ec249cde5e9c57ebea Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Wed, 13 Apr 2016 11:22:42 -0400
Subject: runtime/internal/atomic: rename Storep1 to StorepNoWB

Make it clear that the point of this function stores a pointer
*without* a write barrier.

sed -i -e 's/Storep1/StorepNoWB/' $(git grep -l Storep1)

Updates #15270.

Change-Id: Ifad7e17815e51a738070655fe3b178afdadaecf6
Reviewed-on: https://go-review.googlesource.com/21994
Reviewed-by: Brad Fitzpatrick 
Reviewed-by: Michael Matloob 
---
 src/runtime/atomic_pointer.go                 | 4 ++--
 src/runtime/hashmap.go                        | 4 ++--
 src/runtime/internal/atomic/asm_386.s         | 2 +-
 src/runtime/internal/atomic/asm_amd64.s       | 2 +-
 src/runtime/internal/atomic/asm_amd64p32.s    | 2 +-
 src/runtime/internal/atomic/asm_mips64x.s     | 2 +-
 src/runtime/internal/atomic/asm_ppc64x.s      | 2 +-
 src/runtime/internal/atomic/atomic_386.go     | 2 +-
 src/runtime/internal/atomic/atomic_amd64x.go  | 5 ++++-
 src/runtime/internal/atomic/atomic_arm.go     | 2 +-
 src/runtime/internal/atomic/atomic_arm64.go   | 2 +-
 src/runtime/internal/atomic/atomic_arm64.s    | 2 +-
 src/runtime/internal/atomic/atomic_mips64x.go | 2 +-
 src/runtime/internal/atomic/atomic_ppc64x.go  | 2 +-
 src/runtime/internal/atomic/atomic_s390x.go   | 2 +-
 15 files changed, 20 insertions(+), 17 deletions(-)

(limited to 'src')

diff --git a/src/runtime/atomic_pointer.go b/src/runtime/atomic_pointer.go
index d54f1d6eef..e9f5d03b2b 100644
--- a/src/runtime/atomic_pointer.go
+++ b/src/runtime/atomic_pointer.go
@@ -20,7 +20,7 @@ import (
 //
 //go:nosplit
 func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) {
-	atomic.Storep1(noescape(ptr), new)
+	atomic.StorepNoWB(noescape(ptr), new)
 	writebarrierptr_nostore((*uintptr)(ptr), uintptr(new))
 }
 
@@ -44,7 +44,7 @@ func sync_atomic_StoreUintptr(ptr *uintptr, new uintptr)
 //go:nosplit
 func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) {
 	sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
-	atomic.Storep1(noescape(unsafe.Pointer(ptr)), new)
+	atomic.StorepNoWB(noescape(unsafe.Pointer(ptr)), new)
 	writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
 }
 
diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index d549ce4194..4f5d03d983 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -1075,8 +1075,8 @@ func mapzero(t *_type) {
 				throw("map element too large")
 			}
 		}
-		atomic.Storep1(unsafe.Pointer(&zeroptr), persistentalloc(cursize, 64, &memstats.other_sys))
-		atomic.Storep1(unsafe.Pointer(&zerosize), unsafe.Pointer(zerosize))
+		atomic.StorepNoWB(unsafe.Pointer(&zeroptr), persistentalloc(cursize, 64, &memstats.other_sys))
+		atomic.StorepNoWB(unsafe.Pointer(&zerosize), unsafe.Pointer(zerosize))
 	}
 	unlock(&zerolock)
 }
diff --git a/src/runtime/internal/atomic/asm_386.s b/src/runtime/internal/atomic/asm_386.s
index ce84fd83d1..ebecd0b4cb 100644
--- a/src/runtime/internal/atomic/asm_386.s
+++ b/src/runtime/internal/atomic/asm_386.s
@@ -102,7 +102,7 @@ TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-12
 	JMP	runtime∕internal∕atomic·Xchg(SB)
 
 
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-8
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-8
 	MOVL	ptr+0(FP), BX
 	MOVL	val+4(FP), AX
 	XCHGL	AX, 0(BX)
diff --git a/src/runtime/internal/atomic/asm_amd64.s b/src/runtime/internal/atomic/asm_amd64.s
index 7463fec4a1..94d4ac2698 100644
--- a/src/runtime/internal/atomic/asm_amd64.s
+++ b/src/runtime/internal/atomic/asm_amd64.s
@@ -115,7 +115,7 @@ TEXT runtime∕internal∕atomic·Xchg64(SB), NOSPLIT, $0-24
 TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-24
 	JMP	runtime∕internal∕atomic·Xchg64(SB)
 
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-16
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-16
 	MOVQ	ptr+0(FP), BX
 	MOVQ	val+8(FP), AX
 	XCHGQ	AX, 0(BX)
diff --git a/src/runtime/internal/atomic/asm_amd64p32.s b/src/runtime/internal/atomic/asm_amd64p32.s
index f1e2c3aca6..74c79d08fd 100644
--- a/src/runtime/internal/atomic/asm_amd64p32.s
+++ b/src/runtime/internal/atomic/asm_amd64p32.s
@@ -115,7 +115,7 @@ TEXT runtime∕internal∕atomic·Xchg64(SB), NOSPLIT, $0-24
 TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-12
 	JMP	runtime∕internal∕atomic·Xchg(SB)
 
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-8
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-8
 	MOVL	ptr+0(FP), BX
 	MOVL	val+4(FP), AX
 	XCHGL	AX, 0(BX)
diff --git a/src/runtime/internal/atomic/asm_mips64x.s b/src/runtime/internal/atomic/asm_mips64x.s
index a454f284ab..d0f5c7bdd3 100644
--- a/src/runtime/internal/atomic/asm_mips64x.s
+++ b/src/runtime/internal/atomic/asm_mips64x.s
@@ -155,7 +155,7 @@ TEXT ·Xchg64(SB), NOSPLIT, $0-24
 TEXT ·Xchguintptr(SB), NOSPLIT, $0-24
 	JMP	·Xchg64(SB)
 
-TEXT ·Storep1(SB), NOSPLIT, $0-16
+TEXT ·StorepNoWB(SB), NOSPLIT, $0-16
 	JMP	·Store64(SB)
 
 TEXT ·Store(SB), NOSPLIT, $0-12
diff --git a/src/runtime/internal/atomic/asm_ppc64x.s b/src/runtime/internal/atomic/asm_ppc64x.s
index 45a48b6203..4a776787a2 100644
--- a/src/runtime/internal/atomic/asm_ppc64x.s
+++ b/src/runtime/internal/atomic/asm_ppc64x.s
@@ -150,7 +150,7 @@ TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-24
 	BR	runtime∕internal∕atomic·Xchg64(SB)
 
 
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-16
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-16
 	BR	runtime∕internal∕atomic·Store64(SB)
 
 TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12
diff --git a/src/runtime/internal/atomic/atomic_386.go b/src/runtime/internal/atomic/atomic_386.go
index f4c50b0be1..23a8479515 100644
--- a/src/runtime/internal/atomic/atomic_386.go
+++ b/src/runtime/internal/atomic/atomic_386.go
@@ -73,4 +73,4 @@ func Store(ptr *uint32, val uint32)
 func Store64(ptr *uint64, val uint64)
 
 // NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_amd64x.go b/src/runtime/internal/atomic/atomic_amd64x.go
index bd40fb3ea2..54851d30f4 100644
--- a/src/runtime/internal/atomic/atomic_amd64x.go
+++ b/src/runtime/internal/atomic/atomic_amd64x.go
@@ -61,5 +61,8 @@ func Store(ptr *uint32, val uint32)
 //go:noescape
 func Store64(ptr *uint64, val uint64)
 
+// StorepNoWB performs *ptr = val atomically and without a write
+// barrier.
+//
 // NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go
index c361aef382..244237df4d 100644
--- a/src/runtime/internal/atomic/atomic_arm.go
+++ b/src/runtime/internal/atomic/atomic_arm.go
@@ -85,7 +85,7 @@ func Loadp(addr unsafe.Pointer) unsafe.Pointer {
 }
 
 //go:nosplit
-func Storep1(addr unsafe.Pointer, v unsafe.Pointer) {
+func StorepNoWB(addr unsafe.Pointer, v unsafe.Pointer) {
 	for {
 		old := *(*unsafe.Pointer)(addr)
 		if Casp1((*unsafe.Pointer)(addr), old, v) {
diff --git a/src/runtime/internal/atomic/atomic_arm64.go b/src/runtime/internal/atomic/atomic_arm64.go
index 6b32346656..dc82c3396d 100644
--- a/src/runtime/internal/atomic/atomic_arm64.go
+++ b/src/runtime/internal/atomic/atomic_arm64.go
@@ -77,4 +77,4 @@ func Store(ptr *uint32, val uint32)
 func Store64(ptr *uint64, val uint64)
 
 // NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_arm64.s b/src/runtime/internal/atomic/atomic_arm64.s
index 7b1b0efaf6..eb32f378aa 100644
--- a/src/runtime/internal/atomic/atomic_arm64.s
+++ b/src/runtime/internal/atomic/atomic_arm64.s
@@ -25,7 +25,7 @@ TEXT ·Loadp(SB),NOSPLIT,$-8-16
 	MOVD	R0, ret+8(FP)
 	RET
 
-TEXT runtime∕internal∕atomic·Storep1(SB), NOSPLIT, $0-16
+TEXT runtime∕internal∕atomic·StorepNoWB(SB), NOSPLIT, $0-16
 	B	runtime∕internal∕atomic·Store64(SB)
 
 TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12
diff --git a/src/runtime/internal/atomic/atomic_mips64x.go b/src/runtime/internal/atomic/atomic_mips64x.go
index 8094db58a0..d06ea4809a 100644
--- a/src/runtime/internal/atomic/atomic_mips64x.go
+++ b/src/runtime/internal/atomic/atomic_mips64x.go
@@ -53,4 +53,4 @@ func Store(ptr *uint32, val uint32)
 func Store64(ptr *uint64, val uint64)
 
 // NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_ppc64x.go b/src/runtime/internal/atomic/atomic_ppc64x.go
index bf82b82643..72c98eb0c5 100644
--- a/src/runtime/internal/atomic/atomic_ppc64x.go
+++ b/src/runtime/internal/atomic/atomic_ppc64x.go
@@ -53,4 +53,4 @@ func Store(ptr *uint32, val uint32)
 func Store64(ptr *uint64, val uint64)
 
 // NO go:noescape annotation; see atomic_pointer.go.
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer)
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_s390x.go b/src/runtime/internal/atomic/atomic_s390x.go
index b6d3d84bdf..9343853485 100644
--- a/src/runtime/internal/atomic/atomic_s390x.go
+++ b/src/runtime/internal/atomic/atomic_s390x.go
@@ -39,7 +39,7 @@ func Store64(ptr *uint64, val uint64) {
 // NO go:noescape annotation; see atomic_pointer.go.
 //go:noinline
 //go:nosplit
-func Storep1(ptr unsafe.Pointer, val unsafe.Pointer) {
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer) {
 	*(*uintptr)(ptr) = uintptr(val)
 }
 
-- 
cgit v1.3


From 3ea7cfabbb0549d62d524e4ad30cb464af250fde Mon Sep 17 00:00:00 2001
From: Todd Neal 
Date: Wed, 13 Apr 2016 08:51:46 -0400
Subject: cmd/compile: sort partitions by dom to speed up cse

We do two O(n) scans of all values in an eqclass when computing
substitutions for CSE.

In unfortunate cases, like those found in #15112, we can have a large
eqclass composed of values found in blocks none of whom dominate the
other.  This leads to O(n^2) behavior. The elements are removed one at a
time, with O(n) scans each time.

This CL removes the linear scan by sorting the eqclass so that dominant
values will be sorted first.  As long as we also ensure we don't disturb
the sort order, then we no longer need to scan for the maximally
dominant value.

For the code in issue #15112:

Before:
real    1m26.094s
user    1m30.776s
sys     0m1.125s

Aefter:
real    0m52.099s
user    0m56.829s
sys     0m1.092s

Updates #15112

Change-Id: Ic4f8680ed172e716232436d31963209c146ef850
Reviewed-on: https://go-review.googlesource.com/21981
Reviewed-by: Josh Bleecher Snyder 
Run-TryBot: Todd Neal 
TryBot-Result: Gobot Gobot 
---
 src/cmd/compile/internal/ssa/cse.go        | 32 ++++++++++++++++++++----------
 src/cmd/compile/internal/ssa/sparsetree.go | 12 +++++++++++
 2 files changed, 33 insertions(+), 11 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
index c12d51e50c..76db9d5467 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -137,23 +137,20 @@ func cse(f *Func) {
 	// if v and w are in the same equivalence class and v dominates w.
 	rewrite := make([]*Value, f.NumValues())
 	for _, e := range partition {
+		sort.Sort(sortbyentry{e, f.sdom})
 		for len(e) > 1 {
-			// Find a maximal dominant element in e
+			// e is sorted by entry value so maximal dominant element should be
+			// found first in the slice
 			v := e[0]
-			for _, w := range e[1:] {
-				if f.sdom.isAncestorEq(w.Block, v.Block) {
-					v = w
-				}
-			}
-
+			e = e[1:]
 			// Replace all elements of e which v dominates
 			for i := 0; i < len(e); {
 				w := e[i]
-				if w == v {
-					e, e[i] = e[:len(e)-1], e[len(e)-1]
-				} else if f.sdom.isAncestorEq(v.Block, w.Block) {
+				if f.sdom.isAncestorEq(v.Block, w.Block) {
 					rewrite[w.ID] = v
-					e, e[i] = e[:len(e)-1], e[len(e)-1]
+					// retain the sort order
+					copy(e[i:], e[i+1:])
+					e = e[:len(e)-1]
 				} else {
 					i++
 				}
@@ -308,3 +305,16 @@ func (sv sortvalues) Less(i, j int) bool {
 	// Sort by value ID last to keep the sort result deterministic.
 	return v.ID < w.ID
 }
+
+type sortbyentry struct {
+	a    []*Value // array of values
+	sdom sparseTree
+}
+
+func (sv sortbyentry) Len() int      { return len(sv.a) }
+func (sv sortbyentry) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
+func (sv sortbyentry) Less(i, j int) bool {
+	v := sv.a[i]
+	w := sv.a[j]
+	return sv.sdom.maxdomorder(v.Block) < sv.sdom.maxdomorder(w.Block)
+}
diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go
index cae91e7ddb..45c7897496 100644
--- a/src/cmd/compile/internal/ssa/sparsetree.go
+++ b/src/cmd/compile/internal/ssa/sparsetree.go
@@ -116,6 +116,9 @@ func (t sparseTree) Child(x *Block) *Block {
 
 // isAncestorEq reports whether x is an ancestor of or equal to y.
 func (t sparseTree) isAncestorEq(x, y *Block) bool {
+	if x == y {
+		return true
+	}
 	xx := &t[x.ID]
 	yy := &t[y.ID]
 	return xx.entry <= yy.entry && yy.exit <= xx.exit
@@ -123,7 +126,16 @@ func (t sparseTree) isAncestorEq(x, y *Block) bool {
 
 // isAncestor reports whether x is a strict ancestor of y.
 func (t sparseTree) isAncestor(x, y *Block) bool {
+	if x == y {
+		return false
+	}
 	xx := &t[x.ID]
 	yy := &t[y.ID]
 	return xx.entry < yy.entry && yy.exit < xx.exit
 }
+
+// maxdomorder returns a value to allow a maximal dominator first sort.  maxdomorder(x) < maxdomorder(y) is true
+// if x may dominate y, and false if x cannot dominate y.
+func (t sparseTree) maxdomorder(x *Block) int32 {
+	return t[x.ID].entry
+}
-- 
cgit v1.3


From 79048df2ccc2d4c2ccc4e15d481f7888d48cf440 Mon Sep 17 00:00:00 2001
From: David Crawshaw 
Date: Wed, 6 Apr 2016 07:11:24 -0400
Subject: cmd/link: handle long symbol names

Fixes #15104.

Change-Id: I9ddfbbf39ef0a873b703ee3e04fbb7d1192f5f39
Reviewed-on: https://go-review.googlesource.com/21581
Run-TryBot: David Crawshaw 
TryBot-Result: Gobot Gobot 
Reviewed-by: Ian Lance Taylor 
---
 src/cmd/link/internal/ld/objfile.go | 17 ++++++++++++-----
 src/cmd/link/link_test.go           | 30 ++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+), 5 deletions(-)
 create mode 100644 src/cmd/link/link_test.go

(limited to 'src')

diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
index 6826737cae..b4d2a2184f 100644
--- a/src/cmd/link/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -531,13 +531,18 @@ func (r *objReader) readSymName() string {
 		r.readInt64()
 		return ""
 	}
-	origName, err := r.rd.Peek(n)
-	if err != nil {
-		log.Fatalf("%s: unexpectedly long symbol name", r.pn)
-	}
 	if cap(r.rdBuf) < n {
 		r.rdBuf = make([]byte, 2*n)
 	}
+	origName, err := r.rd.Peek(n)
+	if err == bufio.ErrBufferFull {
+		// Long symbol names are rare but exist. One source is type
+		// symbols for types with long string forms. See #15104.
+		origName = make([]byte, n)
+		r.readFull(origName)
+	} else if err != nil {
+		log.Fatalf("%s: error reading symbol: %v", err)
+	}
 	adjName := r.rdBuf[:0]
 	for {
 		i := bytes.Index(origName, emptyPkg)
@@ -546,7 +551,9 @@ func (r *objReader) readSymName() string {
 			// Read past the peeked origName, now that we're done with it,
 			// using the rfBuf (also no longer used) as the scratch space.
 			// TODO: use bufio.Reader.Discard if available instead?
-			r.readFull(r.rdBuf[:n])
+			if err == nil {
+				r.readFull(r.rdBuf[:n])
+			}
 			r.rdBuf = adjName[:0] // in case 2*n wasn't enough
 			return s
 		}
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
new file mode 100644
index 0000000000..4ef184518e
--- /dev/null
+++ b/src/cmd/link/link_test.go
@@ -0,0 +1,30 @@
+package main
+
+import "testing"
+
+var AuthorPaidByTheColumnInch struct {
+	fog int `
+	London. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big as full-grown snowflakes—gone into mourning, one might imagine, for the death of the sun. Dogs, undistinguishable in mire. Horses, scarcely better; splashed to their very blinkers. Foot passengers, jostling one another’s umbrellas in a general infection of ill temper, and losing their foot-hold at street-corners, where tens of thousands of other foot passengers have been slipping and sliding since the day broke (if this day ever broke), adding new deposits to the crust upon crust of mud, sticking at those points tenaciously to the pavement, and accumulating at compound interest.
+
+	Fog everywhere. Fog up the river, where it flows among green aits and meadows; fog down the river, where it rolls defiled among the tiers of shipping and the waterside pollutions of a great (and dirty) city. Fog on the Essex marshes, fog on the Kentish heights. Fog creeping into the cabooses of collier-brigs; fog lying out on the yards and hovering in the rigging of great ships; fog drooping on the gunwales of barges and small boats. Fog in the eyes and throats of ancient Greenwich pensioners, wheezing by the firesides of their wards; fog in the stem and bowl of the afternoon pipe of the wrathful skipper, down in his close cabin; fog cruelly pinching the toes and fingers of his shivering little ‘prentice boy on deck. Chance people on the bridges peeping over the parapets into a nether sky of fog, with fog all round them, as if they were up in a balloon and hanging in the misty clouds.
+
+	Gas looming through the fog in divers places in the streets, much as the sun may, from the spongey fields, be seen to loom by husbandman and ploughboy. Most of the shops lighted two hours before their time—as the gas seems to know, for it has a haggard and unwilling look.
+
+	The raw afternoon is rawest, and the dense fog is densest, and the muddy streets are muddiest near that leaden-headed old obstruction, appropriate ornament for the threshold of a leaden-headed old corporation, Temple Bar. And hard by Temple Bar, in Lincoln’s Inn Hall, at the very heart of the fog, sits the Lord High Chancellor in his High Court of Chancery.`
+
+	wind int `
+	It was grand to see how the wind awoke, and bent the trees, and drove the rain before it like a cloud of smoke; and to hear the solemn thunder, and to see the lightning; and while thinking with awe of the tremendous powers by which our little lives are encompassed, to consider how beneficent they are, and how upon the smallest flower and leaf there was already a freshness poured from all this seeming rage, which seemed to make creation new again.`
+
+	jarndyce int `
+	Jarndyce and Jarndyce drones on. This scarecrow of a suit has, over the course of time, become so complicated, that no man alive knows what it means. The parties to it understand it least; but it has been observed that no two Chancery lawyers can talk about it for five minutes, without coming to a total disagreement as to all the premises. Innumerable children have been born into the cause; innumerable young people have married into it; innumerable old people have died out of it. Scores of persons have deliriously found themselves made parties in Jarndyce and Jarndyce, without knowing how or why; whole families have inherited legendary hatreds with the suit. The little plaintiff or defendant, who was promised a new rocking-horse when Jarndyce and Jarndyce should be settled, has grown up, possessed himself of a real horse, and trotted away into the other world. Fair wards of court have faded into mothers and grandmothers; a long procession of Chancellors has come in and gone out; the legion of bills in the suit have been transformed into mere bills of mortality; there are not three Jarndyces left upon the earth perhaps, since old Tom Jarndyce in despair blew his brains out at a coffee-house in Chancery Lane; but Jarndyce and Jarndyce still drags its dreary length before the Court, perennially hopeless.`
+
+	principle int `
+	The one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble.`
+}
+
+func TestLargeSymName(t *testing.T) {
+	// The compiler generates a symbol name using the string form of the
+	// type. This tests that the linker can read symbol names larger than
+	// the bufio buffer. Issue #15104.
+	_ = AuthorPaidByTheColumnInch
+}
-- 
cgit v1.3


From d9712aa82af7192469d75802c6dc1734ea9858b2 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Wed, 13 Apr 2016 11:31:24 -0700
Subject: runtime: merge the darwin os*.go files together

Merge them together into os1_darwin.go. A future CL will rename it.

Change-Id: Ia4380d3296ebd5ce210908ce3582ff184566f692
Reviewed-on: https://go-review.googlesource.com/22004
Run-TryBot: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
Reviewed-by: Ian Lance Taylor 
---
 src/runtime/os1_darwin.go | 50 ++++++++++++++++++++++++++++++++++++++++++++---
 src/runtime/os2_darwin.go | 14 -------------
 src/runtime/os_darwin.go  | 42 ---------------------------------------
 3 files changed, 47 insertions(+), 59 deletions(-)
 delete mode 100644 src/runtime/os2_darwin.go
 delete mode 100644 src/runtime/os_darwin.go

(limited to 'src')

diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go
index 01dc90f97c..a0e3d8ed6b 100644
--- a/src/runtime/os1_darwin.go
+++ b/src/runtime/os1_darwin.go
@@ -6,11 +6,23 @@ package runtime
 
 import "unsafe"
 
-//extern SigTabTT runtime·sigtab[];
+type mOS struct {
+	machport uint32 // return address for mach ipc
+	waitsema uint32 // semaphore for parking on locks
+}
 
-type sigset uint32
+func bsdthread_create(stk, arg unsafe.Pointer, fn uintptr) int32
+func bsdthread_register() int32
 
-var sigset_all = ^sigset(0)
+//go:noescape
+func mach_msg_trap(h unsafe.Pointer, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32
+
+func mach_reply_port() uint32
+func mach_task_self() uint32
+func mach_thread_self() uint32
+
+//go:noescape
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
 
 func unimplemented(name string) {
 	println(name, "not implemented")
@@ -473,6 +485,38 @@ func memlimit() uintptr {
 	return 0
 }
 
+const (
+	_NSIG        = 32
+	_SI_USER     = 0 /* empirically true, but not what headers say */
+	_SIG_BLOCK   = 1
+	_SIG_UNBLOCK = 2
+	_SIG_SETMASK = 3
+	_SS_DISABLE  = 4
+)
+
+//go:noescape
+func sigprocmask(how uint32, new, old *sigset)
+
+//go:noescape
+func sigaction(mode uint32, new *sigactiont, old *usigactiont)
+
+//go:noescape
+func sigaltstack(new, old *stackt)
+
+func sigtramp()
+
+//go:noescape
+func setitimer(mode int32, new, old *itimerval)
+
+func raise(sig int32)
+func raiseproc(int32)
+
+//extern SigTabTT runtime·sigtab[];
+
+type sigset uint32
+
+var sigset_all = ^sigset(0)
+
 //go:nosplit
 //go:nowritebarrierrec
 func setsig(i int32, fn uintptr, restart bool) {
diff --git a/src/runtime/os2_darwin.go b/src/runtime/os2_darwin.go
deleted file mode 100644
index 542bd74219..0000000000
--- a/src/runtime/os2_darwin.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2009 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 runtime
-
-const (
-	_NSIG        = 32
-	_SI_USER     = 0 /* empirically true, but not what headers say */
-	_SIG_BLOCK   = 1
-	_SIG_UNBLOCK = 2
-	_SIG_SETMASK = 3
-	_SS_DISABLE  = 4
-)
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
deleted file mode 100644
index e9b8933fb9..0000000000
--- a/src/runtime/os_darwin.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2014 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 runtime
-
-import "unsafe"
-
-type mOS struct {
-	machport uint32 // return address for mach ipc
-	waitsema uint32 // semaphore for parking on locks
-}
-
-func bsdthread_create(stk, arg unsafe.Pointer, fn uintptr) int32
-func bsdthread_register() int32
-
-//go:noescape
-func mach_msg_trap(h unsafe.Pointer, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32
-
-func mach_reply_port() uint32
-func mach_task_self() uint32
-func mach_thread_self() uint32
-
-//go:noescape
-func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
-
-//go:noescape
-func sigprocmask(how uint32, new, old *sigset)
-
-//go:noescape
-func sigaction(mode uint32, new *sigactiont, old *usigactiont)
-
-//go:noescape
-func sigaltstack(new, old *stackt)
-
-func sigtramp()
-
-//go:noescape
-func setitimer(mode int32, new, old *itimerval)
-
-func raise(sig int32)
-func raiseproc(int32)
-- 
cgit v1.3


From 73e2ad20220050f88b1ea79bf5a2e4c4fbee0533 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Wed, 13 Apr 2016 11:33:42 -0700
Subject: runtime: rename os1_darwin.go to os_darwin.go

Change-Id: If0e0bc5a85101db1e70faaab168fc2d12024eb93
Reviewed-on: https://go-review.googlesource.com/22005
Reviewed-by: Ian Lance Taylor 
---
 src/runtime/os1_darwin.go | 582 ----------------------------------------------
 src/runtime/os_darwin.go  | 582 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 582 insertions(+), 582 deletions(-)
 delete mode 100644 src/runtime/os1_darwin.go
 create mode 100644 src/runtime/os_darwin.go

(limited to 'src')

diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go
deleted file mode 100644
index a0e3d8ed6b..0000000000
--- a/src/runtime/os1_darwin.go
+++ /dev/null
@@ -1,582 +0,0 @@
-// Copyright 2009 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 runtime
-
-import "unsafe"
-
-type mOS struct {
-	machport uint32 // return address for mach ipc
-	waitsema uint32 // semaphore for parking on locks
-}
-
-func bsdthread_create(stk, arg unsafe.Pointer, fn uintptr) int32
-func bsdthread_register() int32
-
-//go:noescape
-func mach_msg_trap(h unsafe.Pointer, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32
-
-func mach_reply_port() uint32
-func mach_task_self() uint32
-func mach_thread_self() uint32
-
-//go:noescape
-func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
-
-func unimplemented(name string) {
-	println(name, "not implemented")
-	*(*int)(unsafe.Pointer(uintptr(1231))) = 1231
-}
-
-//go:nosplit
-func semawakeup(mp *m) {
-	mach_semrelease(mp.waitsema)
-}
-
-//go:nosplit
-func semacreate(mp *m) {
-	if mp.waitsema != 0 {
-		return
-	}
-	systemstack(func() {
-		mp.waitsema = mach_semcreate()
-	})
-}
-
-// BSD interface for threading.
-func osinit() {
-	// bsdthread_register delayed until end of goenvs so that we
-	// can look at the environment first.
-
-	ncpu = getncpu()
-}
-
-func getncpu() int32 {
-	// Use sysctl to fetch hw.ncpu.
-	mib := [2]uint32{6, 3}
-	out := uint32(0)
-	nout := unsafe.Sizeof(out)
-	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
-	if ret >= 0 && int32(out) > 0 {
-		return int32(out)
-	}
-	return 1
-}
-
-var urandom_dev = []byte("/dev/urandom\x00")
-
-//go:nosplit
-func getRandomData(r []byte) {
-	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
-	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
-	closefd(fd)
-	extendRandom(r, int(n))
-}
-
-func goenvs() {
-	goenvs_unix()
-
-	// Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
-	// but only if we're not using cgo. If we are using cgo we need
-	// to let the C pthread library install its own thread-creation callback.
-	if !iscgo {
-		if bsdthread_register() != 0 {
-			if gogetenv("DYLD_INSERT_LIBRARIES") != "" {
-				throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)")
-			}
-			throw("runtime: bsdthread_register error")
-		}
-	}
-}
-
-// May run with m.p==nil, so write barriers are not allowed.
-//go:nowritebarrier
-func newosproc(mp *m, stk unsafe.Pointer) {
-	if false {
-		print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
-	}
-
-	var oset sigset
-	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-	errno := bsdthread_create(stk, unsafe.Pointer(mp), funcPC(mstart))
-	sigprocmask(_SIG_SETMASK, &oset, nil)
-
-	if errno < 0 {
-		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -errno, ")\n")
-		throw("runtime.newosproc")
-	}
-}
-
-// newosproc0 is a version of newosproc that can be called before the runtime
-// is initialized.
-//
-// As Go uses bsdthread_register when running without cgo, this function is
-// not safe to use after initialization as it does not pass an M as fnarg.
-//
-//go:nosplit
-func newosproc0(stacksize uintptr, fn unsafe.Pointer, fnarg uintptr) {
-	stack := sysAlloc(stacksize, &memstats.stacks_sys)
-	if stack == nil {
-		write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
-		exit(1)
-	}
-	stk := unsafe.Pointer(uintptr(stack) + stacksize)
-
-	var oset sigset
-	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
-	errno := bsdthread_create(stk, fn, fnarg)
-	sigprocmask(_SIG_SETMASK, &oset, nil)
-
-	if errno < 0 {
-		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
-		exit(1)
-	}
-}
-
-var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
-var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
-
-// Called to do synchronous initialization of Go code built with
-// -buildmode=c-archive or -buildmode=c-shared.
-// None of the Go runtime is initialized.
-//go:nosplit
-//go:nowritebarrierrec
-func libpreinit() {
-	initsig(true)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
-func mpreinit(mp *m) {
-	mp.gsignal = malg(32 * 1024) // OS X wants >= 8K
-	mp.gsignal.m = mp
-}
-
-//go:nosplit
-func msigsave(mp *m) {
-	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
-	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
-// Called to initialize a new m (including the bootstrap m).
-// Called on the new thread, cannot allocate memory.
-func minit() {
-	// Initialize signal handling.
-	_g_ := getg()
-
-	// The alternate signal stack is buggy on arm and arm64.
-	// The signal handler handles it directly.
-	// The sigaltstack assembly function does nothing.
-	if GOARCH != "arm" && GOARCH != "arm64" {
-		var st stackt
-		sigaltstack(nil, &st)
-		if st.ss_flags&_SS_DISABLE != 0 {
-			signalstack(&_g_.m.gsignal.stack)
-			_g_.m.newSigstack = true
-		} else {
-			// Use existing signal stack.
-			stsp := uintptr(unsafe.Pointer(st.ss_sp))
-			_g_.m.gsignal.stack.lo = stsp
-			_g_.m.gsignal.stack.hi = stsp + st.ss_size
-			_g_.m.gsignal.stackguard0 = stsp + _StackGuard
-			_g_.m.gsignal.stackguard1 = stsp + _StackGuard
-			_g_.m.gsignal.stackAlloc = st.ss_size
-			_g_.m.newSigstack = false
-		}
-	}
-
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			nmask &^= 1 << (uint32(i) - 1)
-		}
-	}
-	sigprocmask(_SIG_SETMASK, &nmask, nil)
-}
-
-// Called from dropm to undo the effect of an minit.
-//go:nosplit
-func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
-}
-
-// Mach IPC, to get at semaphores
-// Definitions are in /usr/include/mach on a Mac.
-
-func macherror(r int32, fn string) {
-	print("mach error ", fn, ": ", r, "\n")
-	throw("mach error")
-}
-
-const _DebugMach = false
-
-var zerondr machndr
-
-func mach_msgh_bits(a, b uint32) uint32 {
-	return a | b<<8
-}
-
-func mach_msg(h *machheader, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32 {
-	// TODO: Loop on interrupt.
-	return mach_msg_trap(unsafe.Pointer(h), op, send_size, rcv_size, rcv_name, timeout, notify)
-}
-
-// Mach RPC (MIG)
-const (
-	_MinMachMsg = 48
-	_MachReply  = 100
-)
-
-type codemsg struct {
-	h    machheader
-	ndr  machndr
-	code int32
-}
-
-func machcall(h *machheader, maxsize int32, rxsize int32) int32 {
-	_g_ := getg()
-	port := _g_.m.machport
-	if port == 0 {
-		port = mach_reply_port()
-		_g_.m.machport = port
-	}
-
-	h.msgh_bits |= mach_msgh_bits(_MACH_MSG_TYPE_COPY_SEND, _MACH_MSG_TYPE_MAKE_SEND_ONCE)
-	h.msgh_local_port = port
-	h.msgh_reserved = 0
-	id := h.msgh_id
-
-	if _DebugMach {
-		p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
-		print("send:\t")
-		var i uint32
-		for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
-			print(" ", p[i])
-			if i%8 == 7 {
-				print("\n\t")
-			}
-		}
-		if i%8 != 0 {
-			print("\n")
-		}
-	}
-	ret := mach_msg(h, _MACH_SEND_MSG|_MACH_RCV_MSG, h.msgh_size, uint32(maxsize), port, 0, 0)
-	if ret != 0 {
-		if _DebugMach {
-			print("mach_msg error ", ret, "\n")
-		}
-		return ret
-	}
-	if _DebugMach {
-		p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
-		var i uint32
-		for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
-			print(" ", p[i])
-			if i%8 == 7 {
-				print("\n\t")
-			}
-		}
-		if i%8 != 0 {
-			print("\n")
-		}
-	}
-	if h.msgh_id != id+_MachReply {
-		if _DebugMach {
-			print("mach_msg _MachReply id mismatch ", h.msgh_id, " != ", id+_MachReply, "\n")
-		}
-		return -303 // MIG_REPLY_MISMATCH
-	}
-	// Look for a response giving the return value.
-	// Any call can send this back with an error,
-	// and some calls only have return values so they
-	// send it back on success too. I don't quite see how
-	// you know it's one of these and not the full response
-	// format, so just look if the message is right.
-	c := (*codemsg)(unsafe.Pointer(h))
-	if uintptr(h.msgh_size) == unsafe.Sizeof(*c) && h.msgh_bits&_MACH_MSGH_BITS_COMPLEX == 0 {
-		if _DebugMach {
-			print("mig result ", c.code, "\n")
-		}
-		return c.code
-	}
-	if h.msgh_size != uint32(rxsize) {
-		if _DebugMach {
-			print("mach_msg _MachReply size mismatch ", h.msgh_size, " != ", rxsize, "\n")
-		}
-		return -307 // MIG_ARRAY_TOO_LARGE
-	}
-	return 0
-}
-
-// Semaphores!
-
-const (
-	tmach_semcreate = 3418
-	rmach_semcreate = tmach_semcreate + _MachReply
-
-	tmach_semdestroy = 3419
-	rmach_semdestroy = tmach_semdestroy + _MachReply
-
-	_KERN_ABORTED             = 14
-	_KERN_OPERATION_TIMED_OUT = 49
-)
-
-type tmach_semcreatemsg struct {
-	h      machheader
-	ndr    machndr
-	policy int32
-	value  int32
-}
-
-type rmach_semcreatemsg struct {
-	h         machheader
-	body      machbody
-	semaphore machport
-}
-
-type tmach_semdestroymsg struct {
-	h         machheader
-	body      machbody
-	semaphore machport
-}
-
-func mach_semcreate() uint32 {
-	var m [256]uint8
-	tx := (*tmach_semcreatemsg)(unsafe.Pointer(&m))
-	rx := (*rmach_semcreatemsg)(unsafe.Pointer(&m))
-
-	tx.h.msgh_bits = 0
-	tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
-	tx.h.msgh_remote_port = mach_task_self()
-	tx.h.msgh_id = tmach_semcreate
-	tx.ndr = zerondr
-
-	tx.policy = 0 // 0 = SYNC_POLICY_FIFO
-	tx.value = 0
-
-	for {
-		r := machcall(&tx.h, int32(unsafe.Sizeof(m)), int32(unsafe.Sizeof(*rx)))
-		if r == 0 {
-			break
-		}
-		if r == _KERN_ABORTED { // interrupted
-			continue
-		}
-		macherror(r, "semaphore_create")
-	}
-	if rx.body.msgh_descriptor_count != 1 {
-		unimplemented("mach_semcreate desc count")
-	}
-	return rx.semaphore.name
-}
-
-func mach_semdestroy(sem uint32) {
-	var m [256]uint8
-	tx := (*tmach_semdestroymsg)(unsafe.Pointer(&m))
-
-	tx.h.msgh_bits = _MACH_MSGH_BITS_COMPLEX
-	tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
-	tx.h.msgh_remote_port = mach_task_self()
-	tx.h.msgh_id = tmach_semdestroy
-	tx.body.msgh_descriptor_count = 1
-	tx.semaphore.name = sem
-	tx.semaphore.disposition = _MACH_MSG_TYPE_MOVE_SEND
-	tx.semaphore._type = 0
-
-	for {
-		r := machcall(&tx.h, int32(unsafe.Sizeof(m)), 0)
-		if r == 0 {
-			break
-		}
-		if r == _KERN_ABORTED { // interrupted
-			continue
-		}
-		macherror(r, "semaphore_destroy")
-	}
-}
-
-// The other calls have simple system call traps in sys_darwin_{amd64,386}.s
-
-func mach_semaphore_wait(sema uint32) int32
-func mach_semaphore_timedwait(sema, sec, nsec uint32) int32
-func mach_semaphore_signal(sema uint32) int32
-func mach_semaphore_signal_all(sema uint32) int32
-
-func semasleep1(ns int64) int32 {
-	_g_ := getg()
-
-	if ns >= 0 {
-		var nsecs int32
-		secs := timediv(ns, 1000000000, &nsecs)
-		r := mach_semaphore_timedwait(_g_.m.waitsema, uint32(secs), uint32(nsecs))
-		if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT {
-			return -1
-		}
-		if r != 0 {
-			macherror(r, "semaphore_wait")
-		}
-		return 0
-	}
-
-	for {
-		r := mach_semaphore_wait(_g_.m.waitsema)
-		if r == 0 {
-			break
-		}
-		if r == _KERN_ABORTED { // interrupted
-			continue
-		}
-		macherror(r, "semaphore_wait")
-	}
-	return 0
-}
-
-//go:nosplit
-func semasleep(ns int64) int32 {
-	var r int32
-	systemstack(func() {
-		r = semasleep1(ns)
-	})
-	return r
-}
-
-//go:nosplit
-func mach_semrelease(sem uint32) {
-	for {
-		r := mach_semaphore_signal(sem)
-		if r == 0 {
-			break
-		}
-		if r == _KERN_ABORTED { // interrupted
-			continue
-		}
-
-		// mach_semrelease must be completely nosplit,
-		// because it is called from Go code.
-		// If we're going to die, start that process on the system stack
-		// to avoid a Go stack split.
-		systemstack(func() { macherror(r, "semaphore_signal") })
-	}
-}
-
-//go:nosplit
-func osyield() {
-	usleep(1)
-}
-
-func memlimit() uintptr {
-	// NOTE(rsc): Could use getrlimit here,
-	// like on FreeBSD or Linux, but Darwin doesn't enforce
-	// ulimit -v, so it's unclear why we'd try to stay within
-	// the limit.
-	return 0
-}
-
-const (
-	_NSIG        = 32
-	_SI_USER     = 0 /* empirically true, but not what headers say */
-	_SIG_BLOCK   = 1
-	_SIG_UNBLOCK = 2
-	_SIG_SETMASK = 3
-	_SS_DISABLE  = 4
-)
-
-//go:noescape
-func sigprocmask(how uint32, new, old *sigset)
-
-//go:noescape
-func sigaction(mode uint32, new *sigactiont, old *usigactiont)
-
-//go:noescape
-func sigaltstack(new, old *stackt)
-
-func sigtramp()
-
-//go:noescape
-func setitimer(mode int32, new, old *itimerval)
-
-func raise(sig int32)
-func raiseproc(int32)
-
-//extern SigTabTT runtime·sigtab[];
-
-type sigset uint32
-
-var sigset_all = ^sigset(0)
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
-	var sa sigactiont
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
-	sa.sa_mask = ^uint32(0)
-	sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp)) // runtime·sigtramp's job is to call into real handler
-	*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn
-	sigaction(uint32(i), &sa, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func setsigstack(i int32) {
-	var osa usigactiont
-	sigaction(uint32(i), nil, &osa)
-	handler := *(*uintptr)(unsafe.Pointer(&osa.__sigaction_u))
-	if handler == 0 || handler == _SIG_DFL || handler == _SIG_IGN || osa.sa_flags&_SA_ONSTACK != 0 {
-		return
-	}
-	var sa sigactiont
-	*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = handler
-	sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp))
-	sa.sa_mask = osa.sa_mask
-	sa.sa_flags = osa.sa_flags | _SA_ONSTACK
-	sigaction(uint32(i), &sa, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func getsig(i int32) uintptr {
-	var sa usigactiont
-	sigaction(uint32(i), nil, &sa)
-	return *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))
-}
-
-//go:nosplit
-func signalstack(s *stack) {
-	var st stackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
-		st.ss_size = s.hi - s.lo
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func updatesigmask(m sigmask) {
-	s := sigset(m[0])
-	sigprocmask(_SIG_SETMASK, &s, nil)
-}
-
-func unblocksig(sig int32) {
-	mask := sigset(1) << (uint32(sig) - 1)
-	sigprocmask(_SIG_UNBLOCK, &mask, nil)
-}
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
new file mode 100644
index 0000000000..a0e3d8ed6b
--- /dev/null
+++ b/src/runtime/os_darwin.go
@@ -0,0 +1,582 @@
+// Copyright 2009 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 runtime
+
+import "unsafe"
+
+type mOS struct {
+	machport uint32 // return address for mach ipc
+	waitsema uint32 // semaphore for parking on locks
+}
+
+func bsdthread_create(stk, arg unsafe.Pointer, fn uintptr) int32
+func bsdthread_register() int32
+
+//go:noescape
+func mach_msg_trap(h unsafe.Pointer, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32
+
+func mach_reply_port() uint32
+func mach_task_self() uint32
+func mach_thread_self() uint32
+
+//go:noescape
+func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
+
+func unimplemented(name string) {
+	println(name, "not implemented")
+	*(*int)(unsafe.Pointer(uintptr(1231))) = 1231
+}
+
+//go:nosplit
+func semawakeup(mp *m) {
+	mach_semrelease(mp.waitsema)
+}
+
+//go:nosplit
+func semacreate(mp *m) {
+	if mp.waitsema != 0 {
+		return
+	}
+	systemstack(func() {
+		mp.waitsema = mach_semcreate()
+	})
+}
+
+// BSD interface for threading.
+func osinit() {
+	// bsdthread_register delayed until end of goenvs so that we
+	// can look at the environment first.
+
+	ncpu = getncpu()
+}
+
+func getncpu() int32 {
+	// Use sysctl to fetch hw.ncpu.
+	mib := [2]uint32{6, 3}
+	out := uint32(0)
+	nout := unsafe.Sizeof(out)
+	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+	if ret >= 0 && int32(out) > 0 {
+		return int32(out)
+	}
+	return 1
+}
+
+var urandom_dev = []byte("/dev/urandom\x00")
+
+//go:nosplit
+func getRandomData(r []byte) {
+	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
+	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
+	closefd(fd)
+	extendRandom(r, int(n))
+}
+
+func goenvs() {
+	goenvs_unix()
+
+	// Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
+	// but only if we're not using cgo. If we are using cgo we need
+	// to let the C pthread library install its own thread-creation callback.
+	if !iscgo {
+		if bsdthread_register() != 0 {
+			if gogetenv("DYLD_INSERT_LIBRARIES") != "" {
+				throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)")
+			}
+			throw("runtime: bsdthread_register error")
+		}
+	}
+}
+
+// May run with m.p==nil, so write barriers are not allowed.
+//go:nowritebarrier
+func newosproc(mp *m, stk unsafe.Pointer) {
+	if false {
+		print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
+	}
+
+	var oset sigset
+	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
+	errno := bsdthread_create(stk, unsafe.Pointer(mp), funcPC(mstart))
+	sigprocmask(_SIG_SETMASK, &oset, nil)
+
+	if errno < 0 {
+		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -errno, ")\n")
+		throw("runtime.newosproc")
+	}
+}
+
+// newosproc0 is a version of newosproc that can be called before the runtime
+// is initialized.
+//
+// As Go uses bsdthread_register when running without cgo, this function is
+// not safe to use after initialization as it does not pass an M as fnarg.
+//
+//go:nosplit
+func newosproc0(stacksize uintptr, fn unsafe.Pointer, fnarg uintptr) {
+	stack := sysAlloc(stacksize, &memstats.stacks_sys)
+	if stack == nil {
+		write(2, unsafe.Pointer(&failallocatestack[0]), int32(len(failallocatestack)))
+		exit(1)
+	}
+	stk := unsafe.Pointer(uintptr(stack) + stacksize)
+
+	var oset sigset
+	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
+	errno := bsdthread_create(stk, fn, fnarg)
+	sigprocmask(_SIG_SETMASK, &oset, nil)
+
+	if errno < 0 {
+		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
+		exit(1)
+	}
+}
+
+var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
+var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
+
+// Called to do synchronous initialization of Go code built with
+// -buildmode=c-archive or -buildmode=c-shared.
+// None of the Go runtime is initialized.
+//go:nosplit
+//go:nowritebarrierrec
+func libpreinit() {
+	initsig(true)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+func mpreinit(mp *m) {
+	mp.gsignal = malg(32 * 1024) // OS X wants >= 8K
+	mp.gsignal.m = mp
+}
+
+//go:nosplit
+func msigsave(mp *m) {
+	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
+}
+
+//go:nosplit
+func msigrestore(sigmask sigset) {
+	sigprocmask(_SIG_SETMASK, &sigmask, nil)
+}
+
+//go:nosplit
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, cannot allocate memory.
+func minit() {
+	// Initialize signal handling.
+	_g_ := getg()
+
+	// The alternate signal stack is buggy on arm and arm64.
+	// The signal handler handles it directly.
+	// The sigaltstack assembly function does nothing.
+	if GOARCH != "arm" && GOARCH != "arm64" {
+		var st stackt
+		sigaltstack(nil, &st)
+		if st.ss_flags&_SS_DISABLE != 0 {
+			signalstack(&_g_.m.gsignal.stack)
+			_g_.m.newSigstack = true
+		} else {
+			// Use existing signal stack.
+			stsp := uintptr(unsafe.Pointer(st.ss_sp))
+			_g_.m.gsignal.stack.lo = stsp
+			_g_.m.gsignal.stack.hi = stsp + st.ss_size
+			_g_.m.gsignal.stackguard0 = stsp + _StackGuard
+			_g_.m.gsignal.stackguard1 = stsp + _StackGuard
+			_g_.m.gsignal.stackAlloc = st.ss_size
+			_g_.m.newSigstack = false
+		}
+	}
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := _g_.m.sigmask
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask &^= 1 << (uint32(i) - 1)
+		}
+	}
+	sigprocmask(_SIG_SETMASK, &nmask, nil)
+}
+
+// Called from dropm to undo the effect of an minit.
+//go:nosplit
+func unminit() {
+	if getg().m.newSigstack {
+		signalstack(nil)
+	}
+}
+
+// Mach IPC, to get at semaphores
+// Definitions are in /usr/include/mach on a Mac.
+
+func macherror(r int32, fn string) {
+	print("mach error ", fn, ": ", r, "\n")
+	throw("mach error")
+}
+
+const _DebugMach = false
+
+var zerondr machndr
+
+func mach_msgh_bits(a, b uint32) uint32 {
+	return a | b<<8
+}
+
+func mach_msg(h *machheader, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32 {
+	// TODO: Loop on interrupt.
+	return mach_msg_trap(unsafe.Pointer(h), op, send_size, rcv_size, rcv_name, timeout, notify)
+}
+
+// Mach RPC (MIG)
+const (
+	_MinMachMsg = 48
+	_MachReply  = 100
+)
+
+type codemsg struct {
+	h    machheader
+	ndr  machndr
+	code int32
+}
+
+func machcall(h *machheader, maxsize int32, rxsize int32) int32 {
+	_g_ := getg()
+	port := _g_.m.machport
+	if port == 0 {
+		port = mach_reply_port()
+		_g_.m.machport = port
+	}
+
+	h.msgh_bits |= mach_msgh_bits(_MACH_MSG_TYPE_COPY_SEND, _MACH_MSG_TYPE_MAKE_SEND_ONCE)
+	h.msgh_local_port = port
+	h.msgh_reserved = 0
+	id := h.msgh_id
+
+	if _DebugMach {
+		p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
+		print("send:\t")
+		var i uint32
+		for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
+			print(" ", p[i])
+			if i%8 == 7 {
+				print("\n\t")
+			}
+		}
+		if i%8 != 0 {
+			print("\n")
+		}
+	}
+	ret := mach_msg(h, _MACH_SEND_MSG|_MACH_RCV_MSG, h.msgh_size, uint32(maxsize), port, 0, 0)
+	if ret != 0 {
+		if _DebugMach {
+			print("mach_msg error ", ret, "\n")
+		}
+		return ret
+	}
+	if _DebugMach {
+		p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
+		var i uint32
+		for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
+			print(" ", p[i])
+			if i%8 == 7 {
+				print("\n\t")
+			}
+		}
+		if i%8 != 0 {
+			print("\n")
+		}
+	}
+	if h.msgh_id != id+_MachReply {
+		if _DebugMach {
+			print("mach_msg _MachReply id mismatch ", h.msgh_id, " != ", id+_MachReply, "\n")
+		}
+		return -303 // MIG_REPLY_MISMATCH
+	}
+	// Look for a response giving the return value.
+	// Any call can send this back with an error,
+	// and some calls only have return values so they
+	// send it back on success too. I don't quite see how
+	// you know it's one of these and not the full response
+	// format, so just look if the message is right.
+	c := (*codemsg)(unsafe.Pointer(h))
+	if uintptr(h.msgh_size) == unsafe.Sizeof(*c) && h.msgh_bits&_MACH_MSGH_BITS_COMPLEX == 0 {
+		if _DebugMach {
+			print("mig result ", c.code, "\n")
+		}
+		return c.code
+	}
+	if h.msgh_size != uint32(rxsize) {
+		if _DebugMach {
+			print("mach_msg _MachReply size mismatch ", h.msgh_size, " != ", rxsize, "\n")
+		}
+		return -307 // MIG_ARRAY_TOO_LARGE
+	}
+	return 0
+}
+
+// Semaphores!
+
+const (
+	tmach_semcreate = 3418
+	rmach_semcreate = tmach_semcreate + _MachReply
+
+	tmach_semdestroy = 3419
+	rmach_semdestroy = tmach_semdestroy + _MachReply
+
+	_KERN_ABORTED             = 14
+	_KERN_OPERATION_TIMED_OUT = 49
+)
+
+type tmach_semcreatemsg struct {
+	h      machheader
+	ndr    machndr
+	policy int32
+	value  int32
+}
+
+type rmach_semcreatemsg struct {
+	h         machheader
+	body      machbody
+	semaphore machport
+}
+
+type tmach_semdestroymsg struct {
+	h         machheader
+	body      machbody
+	semaphore machport
+}
+
+func mach_semcreate() uint32 {
+	var m [256]uint8
+	tx := (*tmach_semcreatemsg)(unsafe.Pointer(&m))
+	rx := (*rmach_semcreatemsg)(unsafe.Pointer(&m))
+
+	tx.h.msgh_bits = 0
+	tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
+	tx.h.msgh_remote_port = mach_task_self()
+	tx.h.msgh_id = tmach_semcreate
+	tx.ndr = zerondr
+
+	tx.policy = 0 // 0 = SYNC_POLICY_FIFO
+	tx.value = 0
+
+	for {
+		r := machcall(&tx.h, int32(unsafe.Sizeof(m)), int32(unsafe.Sizeof(*rx)))
+		if r == 0 {
+			break
+		}
+		if r == _KERN_ABORTED { // interrupted
+			continue
+		}
+		macherror(r, "semaphore_create")
+	}
+	if rx.body.msgh_descriptor_count != 1 {
+		unimplemented("mach_semcreate desc count")
+	}
+	return rx.semaphore.name
+}
+
+func mach_semdestroy(sem uint32) {
+	var m [256]uint8
+	tx := (*tmach_semdestroymsg)(unsafe.Pointer(&m))
+
+	tx.h.msgh_bits = _MACH_MSGH_BITS_COMPLEX
+	tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
+	tx.h.msgh_remote_port = mach_task_self()
+	tx.h.msgh_id = tmach_semdestroy
+	tx.body.msgh_descriptor_count = 1
+	tx.semaphore.name = sem
+	tx.semaphore.disposition = _MACH_MSG_TYPE_MOVE_SEND
+	tx.semaphore._type = 0
+
+	for {
+		r := machcall(&tx.h, int32(unsafe.Sizeof(m)), 0)
+		if r == 0 {
+			break
+		}
+		if r == _KERN_ABORTED { // interrupted
+			continue
+		}
+		macherror(r, "semaphore_destroy")
+	}
+}
+
+// The other calls have simple system call traps in sys_darwin_{amd64,386}.s
+
+func mach_semaphore_wait(sema uint32) int32
+func mach_semaphore_timedwait(sema, sec, nsec uint32) int32
+func mach_semaphore_signal(sema uint32) int32
+func mach_semaphore_signal_all(sema uint32) int32
+
+func semasleep1(ns int64) int32 {
+	_g_ := getg()
+
+	if ns >= 0 {
+		var nsecs int32
+		secs := timediv(ns, 1000000000, &nsecs)
+		r := mach_semaphore_timedwait(_g_.m.waitsema, uint32(secs), uint32(nsecs))
+		if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT {
+			return -1
+		}
+		if r != 0 {
+			macherror(r, "semaphore_wait")
+		}
+		return 0
+	}
+
+	for {
+		r := mach_semaphore_wait(_g_.m.waitsema)
+		if r == 0 {
+			break
+		}
+		if r == _KERN_ABORTED { // interrupted
+			continue
+		}
+		macherror(r, "semaphore_wait")
+	}
+	return 0
+}
+
+//go:nosplit
+func semasleep(ns int64) int32 {
+	var r int32
+	systemstack(func() {
+		r = semasleep1(ns)
+	})
+	return r
+}
+
+//go:nosplit
+func mach_semrelease(sem uint32) {
+	for {
+		r := mach_semaphore_signal(sem)
+		if r == 0 {
+			break
+		}
+		if r == _KERN_ABORTED { // interrupted
+			continue
+		}
+
+		// mach_semrelease must be completely nosplit,
+		// because it is called from Go code.
+		// If we're going to die, start that process on the system stack
+		// to avoid a Go stack split.
+		systemstack(func() { macherror(r, "semaphore_signal") })
+	}
+}
+
+//go:nosplit
+func osyield() {
+	usleep(1)
+}
+
+func memlimit() uintptr {
+	// NOTE(rsc): Could use getrlimit here,
+	// like on FreeBSD or Linux, but Darwin doesn't enforce
+	// ulimit -v, so it's unclear why we'd try to stay within
+	// the limit.
+	return 0
+}
+
+const (
+	_NSIG        = 32
+	_SI_USER     = 0 /* empirically true, but not what headers say */
+	_SIG_BLOCK   = 1
+	_SIG_UNBLOCK = 2
+	_SIG_SETMASK = 3
+	_SS_DISABLE  = 4
+)
+
+//go:noescape
+func sigprocmask(how uint32, new, old *sigset)
+
+//go:noescape
+func sigaction(mode uint32, new *sigactiont, old *usigactiont)
+
+//go:noescape
+func sigaltstack(new, old *stackt)
+
+func sigtramp()
+
+//go:noescape
+func setitimer(mode int32, new, old *itimerval)
+
+func raise(sig int32)
+func raiseproc(int32)
+
+//extern SigTabTT runtime·sigtab[];
+
+type sigset uint32
+
+var sigset_all = ^sigset(0)
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsig(i int32, fn uintptr, restart bool) {
+	var sa sigactiont
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
+	if restart {
+		sa.sa_flags |= _SA_RESTART
+	}
+	sa.sa_mask = ^uint32(0)
+	sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp)) // runtime·sigtramp's job is to call into real handler
+	*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn
+	sigaction(uint32(i), &sa, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func setsigstack(i int32) {
+	var osa usigactiont
+	sigaction(uint32(i), nil, &osa)
+	handler := *(*uintptr)(unsafe.Pointer(&osa.__sigaction_u))
+	if handler == 0 || handler == _SIG_DFL || handler == _SIG_IGN || osa.sa_flags&_SA_ONSTACK != 0 {
+		return
+	}
+	var sa sigactiont
+	*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = handler
+	sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp))
+	sa.sa_mask = osa.sa_mask
+	sa.sa_flags = osa.sa_flags | _SA_ONSTACK
+	sigaction(uint32(i), &sa, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func getsig(i int32) uintptr {
+	var sa usigactiont
+	sigaction(uint32(i), nil, &sa)
+	return *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))
+}
+
+//go:nosplit
+func signalstack(s *stack) {
+	var st stackt
+	if s == nil {
+		st.ss_flags = _SS_DISABLE
+	} else {
+		st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
+		st.ss_size = s.hi - s.lo
+		st.ss_flags = 0
+	}
+	sigaltstack(&st, nil)
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func updatesigmask(m sigmask) {
+	s := sigset(m[0])
+	sigprocmask(_SIG_SETMASK, &s, nil)
+}
+
+func unblocksig(sig int32) {
+	mask := sigset(1) << (uint32(sig) - 1)
+	sigprocmask(_SIG_UNBLOCK, &mask, nil)
+}
-- 
cgit v1.3


From f120936dfffa3ac935730699587e6957f2d5ea61 Mon Sep 17 00:00:00 2001
From: David Crawshaw 
Date: Thu, 31 Mar 2016 10:02:10 -0400
Subject: cmd/compile, etc: use name for type pkgPath

By replacing the *string used to represent pkgPath with a
reflect.name everywhere, the embedded *string for package paths
inside the reflect.name can be replaced by an offset, nameOff.
This reduces the number of pointers in the type information.

This also moves all reflect.name types into the same section, making
it possible to use nameOff more widely in later CLs.

No significant binary size change for normal binaries, but:

linux/amd64 PIE:
	cmd/go: -440KB (3.7%)
	jujud:  -2.6MB (3.2%)

For #6853.

Change-Id: I3890b132a784a1090b1b72b32febfe0bea77eaee
Reviewed-on: https://go-review.googlesource.com/21395
Run-TryBot: David Crawshaw 
TryBot-Result: Gobot Gobot 
Reviewed-by: Ian Lance Taylor 
---
 src/cmd/compile/internal/gc/go.go      |   2 +-
 src/cmd/compile/internal/gc/reflect.go | 117 +++++++++++++++++++--------------
 src/cmd/internal/obj/data.go           |  13 +++-
 src/reflect/type.go                    |  64 +++++++++---------
 src/runtime/heapdump.go                |   5 +-
 src/runtime/iface.go                   |   8 +--
 src/runtime/type.go                    |  72 ++++++++++++--------
 7 files changed, 168 insertions(+), 113 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 5df49b56d6..8411d2d0ac 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -20,7 +20,7 @@ const (
 type Pkg struct {
 	Name     string // package name, e.g. "sys"
 	Path     string // string literal used in import statement, e.g. "runtime/internal/sys"
-	Pathsym  *Sym
+	Pathsym  *obj.LSym
 	Prefix   string // escaped path for use in symbol table
 	Imported bool   // export data of this package was parsed
 	Exported bool   // import line written in export data
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index 2bd50b4665..70a75f9324 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -412,8 +412,6 @@ func imethods(t *Type) []*Sig {
 	return methods
 }
 
-var dimportpath_gopkg *Pkg
-
 func dimportpath(p *Pkg) {
 	if p.Pathsym != nil {
 		return
@@ -426,27 +424,18 @@ func dimportpath(p *Pkg) {
 		return
 	}
 
-	if dimportpath_gopkg == nil {
-		dimportpath_gopkg = mkpkg("go")
-		dimportpath_gopkg.Name = "go"
-	}
-
-	nam := "importpath." + p.Prefix + "."
-
-	n := Nod(ONAME, nil, nil)
-	n.Sym = Pkglookup(nam, dimportpath_gopkg)
-
-	n.Class = PEXTERN
-	n.Xoffset = 0
-	p.Pathsym = n.Sym
-
+	var str string
 	if p == localpkg {
 		// Note: myimportpath != "", or else dgopkgpath won't call dimportpath.
-		gdatastring(n, myimportpath)
+		str = myimportpath
 	} else {
-		gdatastring(n, p.Path)
+		str = p.Path
 	}
-	ggloblsym(n.Sym, int32(Types[TSTRING].Width), obj.DUPOK|obj.RODATA)
+
+	s := obj.Linklookup(Ctxt, "go.importpath."+p.Prefix+".", 0)
+	ot := dnameData(s, 0, str, "", nil, false)
+	ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA)
+	p.Pathsym = s
 }
 
 func dgopkgpath(s *Sym, ot int, pkg *Pkg) int {
@@ -469,7 +458,23 @@ func dgopkgpathLSym(s *obj.LSym, ot int, pkg *Pkg) int {
 	}
 
 	dimportpath(pkg)
-	return dsymptrLSym(s, ot, Linksym(pkg.Pathsym), 0)
+	return dsymptrLSym(s, ot, pkg.Pathsym, 0)
+}
+
+// dgopkgpathOffLSym writes an offset relocation in s at offset ot to the pkg path symbol.
+func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int {
+	if pkg == localpkg && myimportpath == "" {
+		// If we don't know the full import path of the package being compiled
+		// (i.e. -p was not passed on the compiler command line), emit a reference to
+		// go.importpath.""., which the linker will rewrite using the correct import path.
+		// Every package that imports this one directly defines the symbol.
+		// See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
+		ns := obj.Linklookup(Ctxt, `go.importpath."".`, 0)
+		return dsymptrOffLSym(s, ot, ns, 0)
+	}
+
+	dimportpath(pkg)
+	return dsymptrOffLSym(s, ot, pkg.Pathsym, 0)
 }
 
 // isExportedField reports whether a struct field is exported.
@@ -495,13 +500,12 @@ func dnameField(s *Sym, ot int, ft *Field) int {
 	if ft.Note != nil {
 		tag = *ft.Note
 	}
-	return dname(s, ot, name, tag, nil, isExportedField(ft))
+	nsym := dname(name, tag, nil, isExportedField(ft))
+	return dsymptrLSym(Linksym(s), ot, nsym, 0)
 }
 
-var dnameCount int
-
-// dname dumps a reflect.name for a struct field or method.
-func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int {
+// dnameData writes the contents of a reflect.name into s at offset ot.
+func dnameData(s *obj.LSym, ot int, name, tag string, pkg *Pkg, exported bool) int {
 	if len(name) > 1<<16-1 {
 		Fatalf("name too long: %s", name)
 	}
@@ -534,31 +538,46 @@ func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int {
 		copy(tb[2:], tag)
 	}
 
-	// Very few names require a pkgPath *string (only those
-	// defined in a different package than their type). So if
-	// there is no pkgPath, we treat the name contents as string
-	// data that duplicates across packages.
-	var bsym *obj.LSym
+	ot = int(s.WriteBytes(Ctxt, int64(ot), b))
+
+	if pkg != nil {
+		ot = dgopkgpathOffLSym(s, ot, pkg)
+	}
+
+	return ot
+}
+
+var dnameCount int
+
+// dname creates a reflect.name for a struct field or method.
+func dname(name, tag string, pkg *Pkg, exported bool) *obj.LSym {
+	// Write out data as "type.." to signal two things to the
+	// linker, first that when dynamically linking, the symbol
+	// should be moved to a relro section, and second that the
+	// contents should not be decoded as a type.
+	sname := "type..namedata."
 	if pkg == nil {
-		_, bsym = stringsym(string(b))
+		// In the common case, share data with other packages.
+		if name == "" {
+			if exported {
+				sname += "-noname-exported." + tag
+			} else {
+				sname += "-noname-unexported." + tag
+			}
+		} else {
+			sname += name + "." + tag
+		}
 	} else {
-		// Write out data as "type.." to signal two things to the
-		// linker, first that when dynamically linking, the symbol
-		// should be moved to a relro section, and second that the
-		// contents should not be decoded as a type.
-		bsymname := fmt.Sprintf(`type..methodname."".%d`, dnameCount)
+		sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount)
 		dnameCount++
-		bsym = obj.Linklookup(Ctxt, bsymname, 0)
-		bsym.P = b
-		boff := len(b)
-		boff = int(Rnd(int64(boff), int64(Widthptr)))
-		boff = dgopkgpathLSym(bsym, boff, pkg)
-		ggloblLSym(bsym, int32(boff), obj.RODATA|obj.LOCAL)
 	}
-
-	ot = dsymptrLSym(Linksym(s), ot, bsym, 0)
-
-	return ot
+	s := obj.Linklookup(Ctxt, sname, 0)
+	if len(s.P) > 0 {
+		return s
+	}
+	ot := dnameData(s, 0, name, tag, pkg, exported)
+	ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA)
+	return s
 }
 
 // dextratype dumps the fields of a runtime.uncommontype.
@@ -627,7 +646,8 @@ func dextratypeData(s *Sym, ot int, t *Type) int {
 		if !exported && a.pkg != typePkg(t) {
 			pkg = a.pkg
 		}
-		ot = dname(s, ot, a.name, "", pkg, exported)
+		nsym := dname(a.name, "", pkg, exported)
+		ot = dsymptrLSym(lsym, ot, nsym, 0)
 		ot = dmethodptrOffLSym(lsym, ot, Linksym(dtypesym(a.mtype)))
 		ot = dmethodptrOffLSym(lsym, ot, Linksym(a.isym))
 		ot = dmethodptrOffLSym(lsym, ot, Linksym(a.tsym))
@@ -1213,7 +1233,8 @@ ok:
 			if !exported && a.pkg != tpkg {
 				pkg = a.pkg
 			}
-			ot = dname(s, ot, a.name, "", pkg, exported)
+			nsym := dname(a.name, "", pkg, exported)
+			ot = dsymptrLSym(Linksym(s), ot, nsym, 0)
 			ot = dsymptr(s, ot, dtypesym(a.type_), 0)
 		}
 
diff --git a/src/cmd/internal/obj/data.go b/src/cmd/internal/obj/data.go
index 546ff37269..d7f0840bc1 100644
--- a/src/cmd/internal/obj/data.go
+++ b/src/cmd/internal/obj/data.go
@@ -75,7 +75,11 @@ func (s *LSym) prepwrite(ctxt *Link, off int64, siz int) {
 	if s.Type == SBSS || s.Type == STLSBSS {
 		ctxt.Diag("cannot supply data for BSS var")
 	}
-	s.Grow(off + int64(siz))
+	l := off + int64(siz)
+	s.Grow(l)
+	if l > s.Size {
+		s.Size = l
+	}
 }
 
 // WriteFloat32 writes f into s at offset off.
@@ -150,6 +154,13 @@ func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) {
 	copy(s.P[off:off+int64(siz)], str)
 }
 
+// WriteBytes writes a slice of bytes into s at offset off.
+func (s *LSym) WriteBytes(ctxt *Link, off int64, b []byte) int64 {
+	s.prepwrite(ctxt, off, len(b))
+	copy(s.P[off:], b)
+	return off + int64(len(b))
+}
+
 func Addrel(s *LSym) *Reloc {
 	s.R = append(s.R, Reloc{})
 	return &s.R[len(s.R)-1]
diff --git a/src/reflect/type.go b/src/reflect/type.go
index c7ed402be2..3c7affcd7f 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -299,9 +299,9 @@ type method struct {
 // Using a pointer to this struct reduces the overall size required
 // to describe an unnamed type with no methods.
 type uncommonType struct {
-	pkgPath *string // import path; nil for built-in types like int, string
-	mcount  uint16  // number of methods
-	moff    uint16  // offset from this uncommontype to [mcount]method
+	pkgPath name   // import path; empty for built-in types like int, string
+	mcount  uint16 // number of methods
+	moff    uint16 // offset from this uncommontype to [mcount]method
 }
 
 // ChanDir represents a channel type's direction.
@@ -354,7 +354,7 @@ type imethod struct {
 // interfaceType represents an interface type.
 type interfaceType struct {
 	rtype   `reflect:"interface"`
-	pkgPath *string   // import path
+	pkgPath name      // import path
 	methods []imethod // sorted by hash
 }
 
@@ -396,7 +396,7 @@ type structField struct {
 // structType represents a struct type.
 type structType struct {
 	rtype   `reflect:"struct"`
-	pkgPath *string
+	pkgPath name
 	fields  []structField // sorted by offset
 }
 
@@ -406,7 +406,7 @@ type structType struct {
 //
 //	1<<0 the name is exported
 //	1<<1 tag data follows the name
-//	1<<2 pkgPath *string follow the name and tag
+//	1<<2 pkgPath nameOff follows the name and tag
 //
 // The next two bytes are the data length:
 //
@@ -417,10 +417,9 @@ type structType struct {
 // If tag data follows then bytes 3+l and 3+l+1 are the tag length,
 // with the data following.
 //
-// If the import path follows, then ptrSize bytes at the end of
-// the data form a *string. The pointer is aligned to its width.
-// The import path is only set for concrete methods that are defined
-// in a different package than their type.
+// If the import path follows, then 4 bytes at the end of
+// the data form a nameOff. The import path is only set for concrete
+// methods that are defined in a different package than their type.
 type name struct {
 	bytes *byte
 }
@@ -446,6 +445,9 @@ func (n *name) tagLen() int {
 }
 
 func (n *name) name() (s string) {
+	if n.bytes == nil {
+		return ""
+	}
 	nl := n.nameLen()
 	if nl == 0 {
 		return ""
@@ -468,16 +470,18 @@ func (n *name) tag() (s string) {
 	return s
 }
 
-func (n *name) pkgPath() *string {
-	if *n.data(0)&(1<<2) == 0 {
-		return nil
+func (n *name) pkgPath() string {
+	if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
+		return ""
 	}
 	off := 3 + n.nameLen()
 	if tl := n.tagLen(); tl > 0 {
 		off += 2 + tl
 	}
-	off = int(round(uintptr(off), ptrSize))
-	return *(**string)(unsafe.Pointer(n.data(off)))
+	var nameOff int32
+	copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
+	pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n), nameOff))}
+	return pkgPathName.name()
 }
 
 // round n up to a multiple of a.  a must be a power of 2.
@@ -595,10 +599,10 @@ func (t *uncommonType) methods() []method {
 }
 
 func (t *uncommonType) PkgPath() string {
-	if t == nil || t.pkgPath == nil {
+	if t == nil {
 		return ""
 	}
-	return *t.pkgPath
+	return t.pkgPath.name()
 }
 
 // resolveTypeOff resolves an *rtype offset from a base type.
@@ -752,11 +756,10 @@ func (t *rtype) Method(i int) (m Method) {
 	m.Name = p.name.name()
 	fl := flag(Func)
 	if !p.name.isExported() {
-		pkgPath := p.name.pkgPath()
-		if pkgPath == nil {
-			pkgPath = ut.pkgPath
+		m.PkgPath = p.name.pkgPath()
+		if m.PkgPath == "" {
+			m.PkgPath = ut.pkgPath.name()
 		}
-		m.PkgPath = *pkgPath
 		fl |= flagStickyRO
 	}
 	if p.mtyp != 0 {
@@ -1004,11 +1007,10 @@ func (t *interfaceType) Method(i int) (m Method) {
 	p := &t.methods[i]
 	m.Name = p.name.name()
 	if !p.name.isExported() {
-		pkgPath := p.name.pkgPath()
-		if pkgPath == nil {
-			pkgPath = t.pkgPath
+		m.PkgPath = p.name.pkgPath()
+		if m.PkgPath == "" {
+			m.PkgPath = t.pkgPath.name()
 		}
-		m.PkgPath = *pkgPath
 	}
 	m.Type = toType(p.typ)
 	m.Index = i
@@ -1146,9 +1148,9 @@ func (t *structType) Field(i int) (f StructField) {
 		f.Name = t.Name()
 		f.Anonymous = true
 	}
-	if t.pkgPath != nil && !p.name.isExported() {
+	if !p.name.isExported() {
 		// Fields never have an import path in their name.
-		f.PkgPath = *t.pkgPath
+		f.PkgPath = t.pkgPath.name()
 	}
 	if tag := p.name.tag(); tag != "" {
 		f.Tag = StructTag(tag)
@@ -2325,7 +2327,7 @@ func StructOf(fields []StructField) Type {
 			case Interface:
 				ift := (*interfaceType)(unsafe.Pointer(ft))
 				for im, m := range ift.methods {
-					if m.name.pkgPath() != nil {
+					if m.name.pkgPath() != "" {
 						// TODO(sbinet)
 						panic("reflect: embedded interface with unexported method(s) not implemented")
 					}
@@ -2384,7 +2386,7 @@ func StructOf(fields []StructField) Type {
 				ptr := (*ptrType)(unsafe.Pointer(ft))
 				if unt := ptr.uncommon(); unt != nil {
 					for _, m := range unt.methods() {
-						if m.name.pkgPath() != nil {
+						if m.name.pkgPath() != "" {
 							// TODO(sbinet)
 							panic("reflect: embedded interface with unexported method(s) not implemented")
 						}
@@ -2398,7 +2400,7 @@ func StructOf(fields []StructField) Type {
 				}
 				if unt := ptr.elem.uncommon(); unt != nil {
 					for _, m := range unt.methods() {
-						if m.name.pkgPath() != nil {
+						if m.name.pkgPath() != "" {
 							// TODO(sbinet)
 							panic("reflect: embedded interface with unexported method(s) not implemented")
 						}
@@ -2413,7 +2415,7 @@ func StructOf(fields []StructField) Type {
 			default:
 				if unt := ft.uncommon(); unt != nil {
 					for _, m := range unt.methods() {
-						if m.name.pkgPath() != nil {
+						if m.name.pkgPath() != "" {
 							// TODO(sbinet)
 							panic("reflect: embedded interface with unexported method(s) not implemented")
 						}
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index 2410b1954a..adfd660847 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -183,10 +183,11 @@ func dumptype(t *_type) {
 	dumpint(tagType)
 	dumpint(uint64(uintptr(unsafe.Pointer(t))))
 	dumpint(uint64(t.size))
-	if x := t.uncommon(); x == nil || x.pkgpath == nil {
+	if x := t.uncommon(); x == nil || x.pkgpath.name() == "" {
 		dumpstr(t._string)
 	} else {
-		pkgpath := stringStructOf(x.pkgpath)
+		pkgpathstr := x.pkgpath.name()
+		pkgpath := stringStructOf(&pkgpathstr)
 		namestr := t.name()
 		name := stringStructOf(&namestr)
 		dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len)))
diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index 700bdc2f48..84f0ee8f0c 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -101,15 +101,15 @@ func additab(m *itab, locked, canfail bool) {
 		iname := i.name.name()
 		itype := i._type
 		ipkg := i.name.pkgPath()
-		if ipkg == nil {
-			ipkg = inter.pkgpath
+		if ipkg == "" {
+			ipkg = inter.pkgpath.name()
 		}
 		for ; j < nt; j++ {
 			t := &xmhdr[j]
 			if typ.typeOff(t.mtyp) == itype && t.name.name() == iname {
 				pkgPath := t.name.pkgPath()
-				if pkgPath == nil {
-					pkgPath = x.pkgpath
+				if pkgPath == "" {
+					pkgPath = x.pkgpath.name()
 				}
 				if t.name.isExported() || pkgPath == ipkg {
 					if m != nil {
diff --git a/src/runtime/type.go b/src/runtime/type.go
index 86131d3ff3..711753bab5 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -6,10 +6,7 @@
 
 package runtime
 
-import (
-	"runtime/internal/sys"
-	"unsafe"
-)
+import "unsafe"
 
 // tflag is documented in ../reflect/type.go.
 type tflag uint8
@@ -151,6 +148,33 @@ var reflectOffs struct {
 	minv map[unsafe.Pointer]int32
 }
 
+func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
+	if off == 0 {
+		return name{}
+	}
+	base := uintptr(ptrInModule)
+	var md *moduledata
+	for next := &firstmoduledata; next != nil; next = next.next {
+		if base >= next.types && base < next.etypes {
+			md = next
+			break
+		}
+	}
+	if md == nil {
+		println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
+		for next := &firstmoduledata; next != nil; next = next.next {
+			println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+		}
+		throw("runtime: name offset base pointer out of range")
+	}
+	res := md.types + uintptr(off)
+	if res > md.etypes {
+		println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
+		throw("runtime: name offset out of range")
+	}
+	return name{(*byte)(unsafe.Pointer(res))}
+}
+
 func (t *_type) typeOff(off typeOff) *_type {
 	if off == 0 {
 		return nil
@@ -240,6 +264,7 @@ func (t *functype) dotdotdot() bool {
 	return t.outCount&(1<<15) != 0
 }
 
+type nameOff int32
 type typeOff int32
 type textOff int32
 
@@ -251,7 +276,7 @@ type method struct {
 }
 
 type uncommontype struct {
-	pkgpath *string
+	pkgpath name
 	mcount  uint16 // number of methods
 	moff    uint16 // offset from this uncommontype to [mcount]method
 }
@@ -263,7 +288,7 @@ type imethod struct {
 
 type interfacetype struct {
 	typ     _type
-	pkgpath *string
+	pkgpath name
 	mhdr    []imethod
 }
 
@@ -319,7 +344,7 @@ type structfield struct {
 
 type structtype struct {
 	typ     _type
-	pkgPath *string
+	pkgPath name
 	fields  []structfield
 }
 
@@ -350,6 +375,9 @@ func (n *name) tagLen() int {
 }
 
 func (n *name) name() (s string) {
+	if n.bytes == nil {
+		return ""
+	}
 	nl := n.nameLen()
 	if nl == 0 {
 		return ""
@@ -372,16 +400,18 @@ func (n *name) tag() (s string) {
 	return s
 }
 
-func (n *name) pkgPath() *string {
-	if *n.data(0)&(1<<2) == 0 {
-		return nil
+func (n *name) pkgPath() string {
+	if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
+		return ""
 	}
 	off := 3 + n.nameLen()
 	if tl := n.tagLen(); tl > 0 {
 		off += 2 + tl
 	}
-	off = int(round(uintptr(off), sys.PtrSize))
-	return *(**string)(unsafe.Pointer(n.data(off)))
+	var nameOff nameOff
+	copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
+	pkgPathName := resolveNameOff(unsafe.Pointer(n.bytes), nameOff)
+	return pkgPathName.name()
 }
 
 // typelinksinit scans the types from extra modules and builds the
@@ -466,7 +496,7 @@ func typesEqual(t, v *_type) bool {
 		if ut == nil || uv == nil {
 			return false
 		}
-		if !pkgPathEqual(ut.pkgpath, uv.pkgpath) {
+		if ut.pkgpath.name() != uv.pkgpath.name() {
 			return false
 		}
 	}
@@ -506,7 +536,7 @@ func typesEqual(t, v *_type) bool {
 	case kindInterface:
 		it := (*interfacetype)(unsafe.Pointer(t))
 		iv := (*interfacetype)(unsafe.Pointer(v))
-		if !pkgPathEqual(it.pkgpath, iv.pkgpath) {
+		if it.pkgpath.name() != iv.pkgpath.name() {
 			return false
 		}
 		if len(it.mhdr) != len(iv.mhdr) {
@@ -518,7 +548,7 @@ func typesEqual(t, v *_type) bool {
 			if tm.name.name() != vm.name.name() {
 				return false
 			}
-			if !pkgPathEqual(tm.name.pkgPath(), vm.name.pkgPath()) {
+			if tm.name.pkgPath() != vm.name.pkgPath() {
 				return false
 			}
 			if !typesEqual(tm._type, vm._type) {
@@ -550,7 +580,7 @@ func typesEqual(t, v *_type) bool {
 			if tf.name.name() != vf.name.name() {
 				return false
 			}
-			if !pkgPathEqual(tf.name.pkgPath(), vf.name.pkgPath()) {
+			if tf.name.pkgPath() != vf.name.pkgPath() {
 				return false
 			}
 			if !typesEqual(tf.typ, vf.typ) {
@@ -570,13 +600,3 @@ func typesEqual(t, v *_type) bool {
 		return false
 	}
 }
-
-func pkgPathEqual(p, q *string) bool {
-	if p == q {
-		return true
-	}
-	if p == nil || q == nil {
-		return false
-	}
-	return *p == *q
-}
-- 
cgit v1.3


From 44f80f6d4925ae59b519ced3a626170099258904 Mon Sep 17 00:00:00 2001
From: Lynn Boger 
Date: Wed, 6 Apr 2016 11:07:12 -0500
Subject: syscall:  fix epoll_event struct for ppc64le/ppc64

The existing epoll_event structure used by many of
the epoll_* syscalls was defined incorrectly
for use with ppc64le & ppc64 in the syscall
directory.  This resulted in the caller getting
incorrect information on return from these
syscalls.  This caused failures in fsnotify as
well as builds with upstream Docker.  The
structure is defined correctly in gccgo.

This adds a pad field that is expected for
these syscalls on ppc64le, ppc64.
Fixes #15135

Change-Id: If7e8ea9eb1d1ca5182c8dc0f935b334127341ffd
Reviewed-on: https://go-review.googlesource.com/21582
Reviewed-by: Ian Lance Taylor 
Run-TryBot: Ian Lance Taylor 
---
 src/syscall/types_linux.go          | 3 +++
 src/syscall/ztypes_linux_ppc64.go   | 7 ++++---
 src/syscall/ztypes_linux_ppc64le.go | 7 ++++---
 3 files changed, 11 insertions(+), 6 deletions(-)

(limited to 'src')

diff --git a/src/syscall/types_linux.go b/src/syscall/types_linux.go
index 9bccfcabd8..28d0225cbf 100644
--- a/src/syscall/types_linux.go
+++ b/src/syscall/types_linux.go
@@ -116,6 +116,9 @@ struct my_epoll_event {
 	// padding is not specified in linux/eventpoll.h but added to conform to the
 	// alignment requirements of EABI
 	int32_t padFd;
+#endif
+#ifdef  __powerpc64__
+	int32_t _padFd;
 #endif
 	int32_t fd;
 	int32_t pad;
diff --git a/src/syscall/ztypes_linux_ppc64.go b/src/syscall/ztypes_linux_ppc64.go
index 33d1b7f3e5..915ca95190 100644
--- a/src/syscall/ztypes_linux_ppc64.go
+++ b/src/syscall/ztypes_linux_ppc64.go
@@ -574,9 +574,10 @@ type Ustat_t struct {
 }
 
 type EpollEvent struct {
-	Events uint32
-	Fd     int32
-	Pad    int32
+	Events  uint32
+	X_padFd int32
+	Fd      int32
+	Pad     int32
 }
 
 const (
diff --git a/src/syscall/ztypes_linux_ppc64le.go b/src/syscall/ztypes_linux_ppc64le.go
index 27ca004834..a1180553ec 100644
--- a/src/syscall/ztypes_linux_ppc64le.go
+++ b/src/syscall/ztypes_linux_ppc64le.go
@@ -574,9 +574,10 @@ type Ustat_t struct {
 }
 
 type EpollEvent struct {
-	Events uint32
-	Fd     int32
-	Pad    int32
+	Events  uint32
+	X_padFd int32
+	Fd      int32
+	Pad     int32
 }
 
 const (
-- 
cgit v1.3


From 6e5027a37a851eb19dba7dad7ea5a8b43e27b842 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Wed, 13 Apr 2016 13:17:30 -0700
Subject: cmd/compile: don't export unneeded OAS, OASWB nodes

Also:
- "rewrite" node Op in exporter for some nodes instead of importer
- more comments

Change-Id: I809e6754d14987b28f1da9379951ffa2e690c2a7
Reviewed-on: https://go-review.googlesource.com/22008
Reviewed-by: Matthew Dempsky 
Run-TryBot: Robert Griesemer 
TryBot-Result: Gobot Gobot 
---
 src/cmd/compile/internal/gc/bexport.go | 27 +++++++++++++---------
 src/cmd/compile/internal/gc/bimport.go | 41 +++++++++++++++++-----------------
 2 files changed, 36 insertions(+), 32 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index 59a85c2f23..e0810f9139 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -783,7 +783,11 @@ func (p *exporter) param(q *Field, n int, numbered bool) {
 		// supply the parameter package here. We need the package
 		// when the function is inlined so we can properly resolve
 		// the name.
-		// TODO(gri) should do this only once per function/method
+		// TODO(gri) This is compiler-specific. Try using importpkg
+		// here and then update the symbols if we find an inlined
+		// body only. Otherwise, the parameter name is ignored and
+		// the package doesn't matter. This would remove an int
+		// (likely 1 byte) for each named parameter.
 		p.pkg(q.Sym.Pkg)
 	}
 	// TODO(gri) This is compiler-specific (escape info).
@@ -1266,12 +1270,11 @@ func (p *exporter) stmt(n *Node) {
 	//	unimplemented - handled by default case
 
 	case OAS, OASWB:
-		p.op(op)
 		// Don't export "v = " initializing statements, hope they're always
 		// preceded by the DCL which will be re-parsed and typecheck to reproduce
 		// the "v = " again.
-		// TODO(gri) if n.Right == nil, don't emit anything
-		if p.bool(n.Right != nil) {
+		if n.Right != nil {
+			p.op(OAS)
 			p.expr(n.Left)
 			p.expr(n.Right)
 		}
@@ -1284,16 +1287,14 @@ func (p *exporter) stmt(n *Node) {
 			p.expr(n.Right)
 		}
 
+	case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+		fallthrough
+
 	case OAS2:
 		p.op(OAS2)
 		p.exprList(n.List)
 		p.exprList(n.Rlist)
 
-	case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
-		p.op(op)
-		p.exprList(n.List)
-		p.exprList(n.Rlist)
-
 	case ORETURN:
 		p.op(ORETURN)
 		p.exprList(n.List)
@@ -1332,11 +1333,15 @@ func (p *exporter) stmt(n *Node) {
 		p.stmtList(n.List)
 
 	case OCASE, OXCASE:
-		p.op(op)
+		p.op(OXCASE)
 		p.stmtList(n.List)
 		p.stmtList(n.Nbody)
 
-	case OBREAK, OCONTINUE, OGOTO, OFALL, OXFALL:
+	case OFALL:
+		op = OXFALL
+		fallthrough
+
+	case OBREAK, OCONTINUE, OGOTO, OXFALL:
 		p.op(op)
 		p.exprsOrNil(n.Left, nil)
 
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 4a93b5a91d..223cc443aa 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -626,6 +626,10 @@ func (p *importer) float(x *Mpflt) {
 // re-establish the syntax tree's invariants. At some future point we might be
 // able to avoid this round-about way and create the rewritten nodes directly,
 // possibly avoiding a lot of duplicate work (name resolution, type checking).
+//
+// Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their
+// unrefined nodes (since this is what the importer uses). The respective case
+// entries are unreachable in the importer.
 
 func (p *importer) stmtList() []*Node {
 	var list []*Node
@@ -871,14 +875,11 @@ func (p *importer) node() *Node {
 	// case ODCLFIELD:
 	//	unimplemented
 
-	case OAS, OASWB:
-		if p.bool() {
-			lhs := p.expr()
-			rhs := p.expr()
-			return Nod(OAS, lhs, rhs)
-		}
-		// TODO(gri) we should not have emitted anything here
-		return Nod(OEMPTY, nil, nil)
+	// case OAS, OASWB:
+	// 	unreachable - mapped to OAS case below by exporter
+
+	case OAS:
+		return Nod(OAS, p.expr(), p.expr())
 
 	case OASOP:
 		n := Nod(OASOP, nil, nil)
@@ -892,15 +893,10 @@ func (p *importer) node() *Node {
 		}
 		return n
 
-	case OAS2:
-		lhs := p.exprList()
-		rhs := p.exprList()
-		n := Nod(OAS2, nil, nil)
-		n.List.Set(lhs)
-		n.Rlist.Set(rhs)
-		return n
+	// case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+	// 	unreachable - mapped to OAS2 case below by exporter
 
-	case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
+	case OAS2:
 		n := Nod(OAS2, nil, nil)
 		n.List.Set(p.exprList())
 		n.Rlist.Set(p.exprList())
@@ -954,7 +950,10 @@ func (p *importer) node() *Node {
 		popdcl()
 		return n
 
-	case OCASE, OXCASE:
+	// case OCASE, OXCASE:
+	// 	unreachable - mapped to OXCASE case below by exporter
+
+	case OXCASE:
 		markdcl()
 		n := Nod(OXCASE, nil, nil)
 		n.List.Set(p.exprList())
@@ -964,10 +963,10 @@ func (p *importer) node() *Node {
 		popdcl()
 		return n
 
-	case OBREAK, OCONTINUE, OGOTO, OFALL, OXFALL:
-		if op == OFALL {
-			op = OXFALL
-		}
+	// case OFALL:
+	// 	unreachable - mapped to OXFALL case below by exporter
+
+	case OBREAK, OCONTINUE, OGOTO, OXFALL:
 		left, _ := p.exprsOrNil()
 		return Nod(op, left, nil)
 
-- 
cgit v1.3


From 933d521a7aa5defc46d3336bcb71a2f3f2b8172d Mon Sep 17 00:00:00 2001
From: Rob Pike 
Date: Wed, 13 Apr 2016 11:47:25 -0700
Subject: fmt: clarify that for %g precision determines number of significant
 digits

Documentation change only.

Fixes #15178.

Change-Id: I3c7d80ce9e668ac7515f7ebb9da80f3bd8e534d6
Reviewed-on: https://go-review.googlesource.com/22006
Reviewed-by: Ian Lance Taylor 
---
 src/fmt/doc.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/fmt/doc.go b/src/fmt/doc.go
index 4eea48eb6b..2f2ee24207 100644
--- a/src/fmt/doc.go
+++ b/src/fmt/doc.go
@@ -95,10 +95,10 @@
 
 	For floating-point values, width sets the minimum width of the field and
 	precision sets the number of places after the decimal, if appropriate,
-	except that for %g/%G it sets the total number of digits. For example,
-	given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5.
-	The default precision for %e and %f is 6; for %g it is the smallest
-	number of digits necessary to identify the value uniquely.
+	except that for %g/%G precision sets the total number of significant
+	digits. For example, given 12.345 the format %6.3f prints 12.345 while
+	%.3g prints 12.3. The default precision for %e and %f is 6; for %g it
+	is the smallest number of digits necessary to identify the value uniquely.
 
 	For complex numbers, the width and precision apply to the two
 	components independently and the result is parenthesized, so %f applied
-- 
cgit v1.3


From ae9804595879eb07efd23b9c98eab46693573447 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Wed, 13 Apr 2016 16:57:23 -0700
Subject: cmd/compile: use correct export function (fix debugFormat)

Tested with debugFormat enabled and running
(export GO_GCFLAGS=-newexport; sh all.bash).

Change-Id: If7d43e1e594ea43c644232b89e670f7abb6b003e
Reviewed-on: https://go-review.googlesource.com/22033
Reviewed-by: Matthew Dempsky 
---
 src/cmd/compile/internal/gc/bexport.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index e0810f9139..eef2e2200d 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -350,7 +350,7 @@ func export(out *bufio.Writer, trace bool) int {
 	if p.trace {
 		p.tracef("\n")
 	}
-	p.tag(-1) // invalid index terminates list
+	p.int(-1) // invalid index terminates list
 
 	// for self-verification only (redundant)
 	p.int(objcount)
-- 
cgit v1.3


From 980ab12ade53e70d037ab2ab475148b216d84a14 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky 
Date: Wed, 13 Apr 2016 18:37:18 -0700
Subject: cmd/compile/internal/gc: change flags to bool where possible

Some of the Debug[x] flags are actually boolean too, but not all, so
they need to be handled separately.

While here, change some obj.Flagstr and obj.Flagint64 calls to
directly use flag.StringVar and flag.Int64Var instead.

Change-Id: Iccedf6fed4328240ee2257f57fe6d66688f237c4
Reviewed-on: https://go-review.googlesource.com/22052
Reviewed-by: Michael Hudson-Doyle 
---
 src/cmd/compile/internal/gc/alg.go       |  5 +-
 src/cmd/compile/internal/gc/bexport.go   |  2 +-
 src/cmd/compile/internal/gc/closure.go   |  2 +-
 src/cmd/compile/internal/gc/dcl.go       |  4 +-
 src/cmd/compile/internal/gc/export.go    |  8 ++--
 src/cmd/compile/internal/gc/gen.go       |  2 +-
 src/cmd/compile/internal/gc/go.go        | 18 ++++----
 src/cmd/compile/internal/gc/inl.go       |  4 +-
 src/cmd/compile/internal/gc/lex.go       |  6 +--
 src/cmd/compile/internal/gc/main.go      | 79 ++++++++++++++++----------------
 src/cmd/compile/internal/gc/obj.go       |  8 ++--
 src/cmd/compile/internal/gc/pgen.go      |  2 +-
 src/cmd/compile/internal/gc/racewalk.go  | 10 ++--
 src/cmd/compile/internal/gc/reflect.go   |  4 +-
 src/cmd/compile/internal/gc/ssa.go       |  4 +-
 src/cmd/compile/internal/gc/subr.go      |  2 +-
 src/cmd/compile/internal/gc/typecheck.go |  4 +-
 src/cmd/compile/internal/gc/unsafe.go    |  2 +-
 src/cmd/compile/internal/gc/walk.go      |  9 ++--
 19 files changed, 88 insertions(+), 87 deletions(-)

(limited to 'src')

diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index e9b5afe838..6e85438610 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -316,11 +316,12 @@ func genhash(sym *Sym, t *Type) {
 	// for a struct containing a reflect.Value, which itself has
 	// an unexported field of type unsafe.Pointer.
 	old_safemode := safemode
+	safemode = false
 
-	safemode = 0
 	Disable_checknil++
 	funccompile(fn)
 	Disable_checknil--
+
 	safemode = old_safemode
 }
 
@@ -509,7 +510,7 @@ func geneq(sym *Sym, t *Type) {
 	// for a struct containing a reflect.Value, which itself has
 	// an unexported field of type unsafe.Pointer.
 	old_safemode := safemode
-	safemode = 0
+	safemode = false
 
 	// Disable checknils while compiling this code.
 	// We are comparing a struct or an array,
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index eef2e2200d..e5fa3c39a6 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -261,7 +261,7 @@ func export(out *bufio.Writer, trace bool) int {
 	}
 
 	// write compiler-specific flags
-	p.bool(safemode != 0)
+	p.bool(safemode)
 	if p.trace {
 		p.tracef("\n")
 	}
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
index 80c8d309af..db4eb3f14d 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -419,7 +419,7 @@ func closuredebugruntimecheck(r *Node) {
 			Warnl(r.Lineno, "stack closure, captured vars = %v", r.Func.Cvars)
 		}
 	}
-	if compiling_runtime > 0 && r.Esc == EscHeap {
+	if compiling_runtime && r.Esc == EscHeap {
 		yyerrorl(r.Lineno, "heap-allocated closure, not allowed in runtime.")
 	}
 }
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index c652c65962..e1028f681c 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -1330,7 +1330,7 @@ func makefuncsym(s *Sym) {
 	if isblanksym(s) {
 		return
 	}
-	if compiling_runtime != 0 && s.Name == "getg" {
+	if compiling_runtime && s.Name == "getg" {
 		// runtime.getg() is not a real function and so does
 		// not get a funcsym.
 		return
@@ -1440,7 +1440,7 @@ func (c *nowritebarrierrecChecker) visitcall(n *Node) {
 	if fn == nil || fn.Op != ONAME || fn.Class != PFUNC || fn.Name.Defn == nil {
 		return
 	}
-	if (compiling_runtime != 0 || fn.Sym.Pkg == Runtimepkg) && fn.Sym.Name == "allocm" {
+	if (compiling_runtime || fn.Sym.Pkg == Runtimepkg) && fn.Sym.Name == "allocm" {
 		return
 	}
 	defn := fn.Name.Defn
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index ae36657a65..cfe192f3ba 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -15,8 +15,8 @@ import (
 )
 
 var (
-	newexport    int // if set, use new export format
-	Debug_export int // if set, print debugging information about export data
+	newexport    bool // if set, use new export format
+	Debug_export int  // if set, print debugging information about export data
 	exportsize   int
 )
 
@@ -377,7 +377,7 @@ func dumpexport() {
 	}
 
 	size := 0 // size of export section without enclosing markers
-	if forceNewExport || newexport != 0 {
+	if forceNewExport || newexport {
 		// binary export
 		// The linker also looks for the $$ marker - use char after $$ to distinguish format.
 		exportf("\n$$B\n") // indicate binary format
@@ -417,7 +417,7 @@ func dumpexport() {
 		exportf("\n$$\n") // indicate textual format
 		exportsize = 0
 		exportf("package %s", localpkg.Name)
-		if safemode != 0 {
+		if safemode {
 			exportf(" safe")
 		}
 		exportf("\n")
diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
index 7527452c93..cc624cce7a 100644
--- a/src/cmd/compile/internal/gc/gen.go
+++ b/src/cmd/compile/internal/gc/gen.go
@@ -246,7 +246,7 @@ func cgen_dcl(n *Node) {
 	if n.Class&PHEAP == 0 {
 		return
 	}
-	if compiling_runtime != 0 {
+	if compiling_runtime {
 		Yyerror("%v escapes to heap, not allowed in runtime.", n)
 	}
 	if prealloc[n] == nil {
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 8411d2d0ac..af9aaf0dae 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -144,9 +144,9 @@ var nsyntaxerrors int
 
 var decldepth int32
 
-var safemode int
+var safemode bool
 
-var nolocalimports int
+var nolocalimports bool
 
 var Debug [256]int
 
@@ -261,21 +261,21 @@ var Funcdepth int32
 
 var typecheckok bool
 
-var compiling_runtime int
+var compiling_runtime bool
 
 var compiling_wrappers int
 
-var use_writebarrier int
+var use_writebarrier bool
 
-var pure_go int
+var pure_go bool
 
 var flag_installsuffix string
 
-var flag_race int
+var flag_race bool
 
-var flag_msan int
+var flag_msan bool
 
-var flag_largemodel int
+var flag_largemodel bool
 
 // Whether we are adding any sort of code instrumentation, such as
 // when the race detector is enabled.
@@ -285,7 +285,7 @@ var debuglive int
 
 var Ctxt *obj.Link
 
-var writearchive int
+var writearchive bool
 
 var bstdout *bufio.Writer
 
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
index ea2394e7f9..f9e425618b 100644
--- a/src/cmd/compile/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -71,7 +71,7 @@ func typecheckinl(fn *Node) {
 	}
 
 	save_safemode := safemode
-	safemode = 0
+	safemode = false
 
 	savefn := Curfn
 	Curfn = fn
@@ -492,7 +492,7 @@ func mkinlcall(n *Node, fn *Node, isddd bool) *Node {
 	pkg := fnpkg(fn)
 
 	if pkg != localpkg && pkg != nil {
-		safemode = 0
+		safemode = false
 	}
 	n = mkinlcall1(n, fn, isddd)
 	safemode = save_safemode
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index 4b95bb7124..09fed98985 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -914,17 +914,17 @@ func (l *lexer) getlinepragma() rune {
 		case "go:noinline":
 			l.pragma |= Noinline
 		case "go:systemstack":
-			if compiling_runtime == 0 {
+			if !compiling_runtime {
 				Yyerror("//go:systemstack only allowed in runtime")
 			}
 			l.pragma |= Systemstack
 		case "go:nowritebarrier":
-			if compiling_runtime == 0 {
+			if !compiling_runtime {
 				Yyerror("//go:nowritebarrier only allowed in runtime")
 			}
 			l.pragma |= Nowritebarrier
 		case "go:nowritebarrierrec":
-			if compiling_runtime == 0 {
+			if !compiling_runtime {
 				Yyerror("//go:nowritebarrierrec only allowed in runtime")
 			}
 			l.pragma |= Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 45a510d577..f41097b83b 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -142,15 +142,14 @@ func Main() {
 
 	Nacl = goos == "nacl"
 	if Nacl {
-		flag_largemodel = 1
+		flag_largemodel = true
 	}
 
-	outfile = ""
-	obj.Flagcount("+", "compiling runtime", &compiling_runtime)
+	flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
 	obj.Flagcount("%", "debug non-static initializers", &Debug['%'])
 	obj.Flagcount("A", "for bootstrapping, allow 'any' type", &Debug['A'])
 	obj.Flagcount("B", "disable bounds checking", &Debug['B'])
-	obj.Flagstr("D", "set relative `path` for local imports", &localimport)
+	flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
 	obj.Flagcount("E", "debug symbol export", &Debug['E'])
 	obj.Flagfn1("I", "add `directory` to import search path", addidir)
 	obj.Flagcount("K", "debug missing line numbers", &Debug['K'])
@@ -162,57 +161,59 @@ func Main() {
 	obj.Flagcount("S", "print assembly listing", &Debug['S'])
 	obj.Flagfn0("V", "print compiler version", doversion)
 	obj.Flagcount("W", "debug parse tree after type checking", &Debug['W'])
-	obj.Flagstr("asmhdr", "write assembly header to `file`", &asmhdr)
-	obj.Flagstr("buildid", "record `id` as the build id in the export metadata", &buildid)
-	obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go)
-	obj.Flagstr("d", "print debug information about items in `list`", &debugstr)
+	flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`")
+	flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata")
+	flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
+	flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`")
 	obj.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
 	obj.Flagcount("f", "debug stack frames", &Debug['f'])
 	obj.Flagcount("g", "debug code generation", &Debug['g'])
 	obj.Flagcount("h", "halt on error", &Debug['h'])
 	obj.Flagcount("i", "debug line number stack", &Debug['i'])
 	obj.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
-	obj.Flagstr("installsuffix", "set pkg directory `suffix`", &flag_installsuffix)
+	flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
 	obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
 	obj.Flagcount("l", "disable inlining", &Debug['l'])
 	obj.Flagcount("live", "debug liveness analysis", &debuglive)
 	obj.Flagcount("m", "print optimization decisions", &Debug['m'])
-	obj.Flagcount("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan)
-	obj.Flagcount("newexport", "use new export format", &newexport) // TODO(gri) remove eventually (issue 13241)
-	obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports)
-	obj.Flagstr("o", "write output to `file`", &outfile)
-	obj.Flagstr("p", "set expected package import `path`", &myimportpath)
-	obj.Flagcount("pack", "write package file instead of object file", &writearchive)
+	flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
+	flag.BoolVar(&newexport, "newexport", false, "use new export format") // TODO(gri) remove eventually (issue 13241)
+	flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
+	flag.StringVar(&outfile, "o", "", "write output to `file`")
+	flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
+	flag.BoolVar(&writearchive, "pack", false, "write package file instead of object file")
 	obj.Flagcount("r", "debug generated wrappers", &Debug['r'])
-	obj.Flagcount("race", "enable race detector", &flag_race)
+	flag.BoolVar(&flag_race, "race", false, "enable race detector")
 	obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s'])
-	obj.Flagstr("trimpath", "remove `prefix` from recorded source file paths", &Ctxt.LineHist.TrimPathPrefix)
-	obj.Flagcount("u", "reject unsafe code", &safemode)
+	flag.StringVar(&Ctxt.LineHist.TrimPathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
+	flag.BoolVar(&safemode, "u", false, "reject unsafe code")
 	obj.Flagcount("v", "increase debug verbosity", &Debug['v'])
 	obj.Flagcount("w", "debug type checking", &Debug['w'])
-	use_writebarrier = 1
-	obj.Flagcount("wb", "enable write barrier", &use_writebarrier)
+	flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
 	obj.Flagcount("x", "debug lexer", &Debug['x'])
 	obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y'])
-	var flag_shared int
+	var flag_shared bool
 	var flag_dynlink bool
 	if supportsDynlink(Thearch.LinkArch.Arch) {
-		obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared)
+		flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
 		flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
 	}
 	if Thearch.LinkArch.Family == sys.AMD64 {
-		obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel)
+		flag.BoolVar(&flag_largemodel, "largemodel", false, "generate code that assumes a large memory model")
 	}
-	obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile)
-	obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile)
-	obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate)
+	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
+	flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
+	flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
 	flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code")
 	obj.Flagparse(usage)
 
 	if flag_dynlink {
-		flag_shared = 1
+		flag_shared = true
+	}
+	if flag_shared {
+		// TODO(mdempsky): Change Flag_shared to bool.
+		Ctxt.Flag_shared = 1
 	}
-	Ctxt.Flag_shared = int32(flag_shared)
 	Ctxt.Flag_dynlink = flag_dynlink
 	Ctxt.Flag_optimize = Debug['N'] == 0
 
@@ -225,17 +226,17 @@ func Main() {
 
 	startProfile()
 
-	if flag_race != 0 {
+	if flag_race {
 		racepkg = mkpkg("runtime/race")
 		racepkg.Name = "race"
 	}
-	if flag_msan != 0 {
+	if flag_msan {
 		msanpkg = mkpkg("runtime/msan")
 		msanpkg.Name = "msan"
 	}
-	if flag_race != 0 && flag_msan != 0 {
+	if flag_race && flag_msan {
 		log.Fatal("cannot use both -race and -msan")
-	} else if flag_race != 0 || flag_msan != 0 {
+	} else if flag_race || flag_msan {
 		instrumenting = true
 	}
 
@@ -471,7 +472,7 @@ func Main() {
 		fninit(xtop)
 	}
 
-	if compiling_runtime != 0 {
+	if compiling_runtime {
 		checknowritebarrierrec()
 	}
 
@@ -569,7 +570,7 @@ func islocalname(name string) bool {
 
 func findpkg(name string) (file string, ok bool) {
 	if islocalname(name) {
-		if safemode != 0 || nolocalimports != 0 {
+		if safemode || nolocalimports {
 			return "", false
 		}
 
@@ -612,10 +613,10 @@ func findpkg(name string) (file string, ok bool) {
 		if flag_installsuffix != "" {
 			suffixsep = "_"
 			suffix = flag_installsuffix
-		} else if flag_race != 0 {
+		} else if flag_race {
 			suffixsep = "_"
 			suffix = "race"
-		} else if flag_msan != 0 {
+		} else if flag_msan {
 			suffixsep = "_"
 			suffix = "msan"
 		}
@@ -694,7 +695,7 @@ func importfile(f *Val, indent []byte) {
 	}
 
 	if path_ == "unsafe" {
-		if safemode != 0 {
+		if safemode {
 			Yyerror("cannot import package unsafe")
 			errorexit()
 		}
@@ -818,7 +819,7 @@ func importfile(f *Val, indent []byte) {
 		errorexit()
 	}
 
-	if safemode != 0 && !importpkg.Safe {
+	if safemode && !importpkg.Safe {
 		Yyerror("cannot import unsafe package %q", importpkg.Path)
 	}
 }
@@ -896,7 +897,7 @@ func mkpackage(pkgname string) {
 			p = p[:i]
 		}
 		suffix := ".o"
-		if writearchive > 0 {
+		if writearchive {
 			suffix = ".a"
 		}
 		outfile = p + suffix
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index eed0ed6e24..59ce0547c8 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -33,7 +33,7 @@ func dumpobj() {
 
 	startobj := int64(0)
 	var arhdr [ArhdrSize]byte
-	if writearchive != 0 {
+	if writearchive {
 		bout.WriteString("!\n")
 		arhdr = [ArhdrSize]byte{}
 		bout.Write(arhdr[:])
@@ -43,7 +43,7 @@ func dumpobj() {
 	fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
 	dumpexport()
 
-	if writearchive != 0 {
+	if writearchive {
 		bout.Flush()
 		size := bout.Offset() - startobj
 		if size&1 != 0 {
@@ -62,7 +62,7 @@ func dumpobj() {
 	}
 
 	if pragcgobuf != "" {
-		if writearchive != 0 {
+		if writearchive {
 			// write empty export section; must be before cgo section
 			fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
 		}
@@ -90,7 +90,7 @@ func dumpobj() {
 	dumpdata()
 	obj.Writeobjdirect(Ctxt, bout)
 
-	if writearchive != 0 {
+	if writearchive {
 		bout.Flush()
 		size := bout.Offset() - startobj
 		if size&1 != 0 {
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index baa960bf75..7b9b91e7b0 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -364,7 +364,7 @@ func compile(fn *Node) {
 	dowidth(Curfn.Type)
 
 	if len(fn.Nbody.Slice()) == 0 {
-		if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
+		if pure_go || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
 			Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
 			return
 		}
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
index f6e65146d6..a8a5e92485 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -54,14 +54,14 @@ func instrument(fn *Node) {
 		return
 	}
 
-	if flag_race == 0 || !ispkgin(norace_inst_pkgs) {
+	if !flag_race || !ispkgin(norace_inst_pkgs) {
 		instrumentlist(fn.Nbody, nil)
 
 		// nothing interesting for race detector in fn->enter
 		instrumentlist(fn.Func.Exit, nil)
 	}
 
-	if flag_race != 0 {
+	if flag_race {
 		// nodpc is the PC of the caller as extracted by
 		// getcallerpc. We use -widthptr(FP) for x86.
 		// BUG: this will not work on arm.
@@ -503,7 +503,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
 		n = treecopy(n, 0)
 		makeaddable(n)
 		var f *Node
-		if flag_msan != 0 {
+		if flag_msan {
 			name := "msanread"
 			if wr != 0 {
 				name = "msanwrite"
@@ -515,7 +515,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
 				Fatalf("instrument: %v badwidth", t)
 			}
 			f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
-		} else if flag_race != 0 && (t.IsStruct() || t.IsArray()) {
+		} else if flag_race && (t.IsStruct() || t.IsArray()) {
 			name := "racereadrange"
 			if wr != 0 {
 				name = "racewriterange"
@@ -527,7 +527,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
 				Fatalf("instrument: %v badwidth", t)
 			}
 			f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
-		} else if flag_race != 0 {
+		} else if flag_race {
 			name := "raceread"
 			if wr != 0 {
 				name = "racewrite"
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index 70a75f9324..df68f46d4c 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -1424,10 +1424,10 @@ func dumptypestructs() {
 		// add paths for runtime and main, which 6l imports implicitly.
 		dimportpath(Runtimepkg)
 
-		if flag_race != 0 {
+		if flag_race {
 			dimportpath(racepkg)
 		}
-		if flag_msan != 0 {
+		if flag_msan {
 			dimportpath(msanpkg)
 		}
 		dimportpath(mkpkg("main"))
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index fdd14953e6..4a93dc1087 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -554,7 +554,7 @@ func (s *state) stmt(n *Node) {
 	case OCALLFUNC, OCALLMETH, OCALLINTER:
 		s.call(n, callNormal)
 		if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class == PFUNC &&
-			(compiling_runtime != 0 && n.Left.Sym.Name == "throw" ||
+			(compiling_runtime && n.Left.Sym.Name == "throw" ||
 				n.Left.Sym.Pkg == Runtimepkg && (n.Left.Sym.Name == "gopanic" || n.Left.Sym.Name == "selectgo" || n.Left.Sym.Name == "block")) {
 			m := s.mem()
 			b := s.endBlock()
@@ -579,7 +579,7 @@ func (s *state) stmt(n *Node) {
 		if n.Left.Class&PHEAP == 0 {
 			return
 		}
-		if compiling_runtime != 0 {
+		if compiling_runtime {
 			Fatalf("%v escapes to heap, not allowed in runtime.", n)
 		}
 
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 776eb9c64e..f6af11adba 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -750,7 +750,7 @@ func assignop(src *Type, dst *Type, why *string) Op {
 
 	// TODO(rsc,lvd): This behaves poorly in the presence of inlining.
 	// https://golang.org/issue/2795
-	if safemode != 0 && importpkg == nil && src != nil && src.Etype == TUNSAFEPTR {
+	if safemode && importpkg == nil && src != nil && src.Etype == TUNSAFEPTR {
 		Yyerror("cannot use unsafe.Pointer")
 		errorexit()
 	}
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 7089d7de72..6067677738 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -1354,7 +1354,7 @@ OpSwitch:
 		if t.Results().NumFields() == 1 {
 			n.Type = l.Type.Results().Field(0).Type
 
-			if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime != 0 || n.Left.Sym.Pkg == Runtimepkg) && n.Left.Sym.Name == "getg" {
+			if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime || n.Left.Sym.Pkg == Runtimepkg) && n.Left.Sym.Name == "getg" {
 				// Emit code for runtime.getg() directly instead of calling function.
 				// Most such rewrites (for example the similar one for math.Sqrt) should be done in walk,
 				// so that the ordering pass can make sure to preserve the semantics of the original code
@@ -2176,7 +2176,7 @@ OpSwitch:
 		}
 	}
 
-	if safemode != 0 && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR {
+	if safemode && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR {
 		Yyerror("cannot use unsafe.Pointer")
 	}
 
diff --git a/src/cmd/compile/internal/gc/unsafe.go b/src/cmd/compile/internal/gc/unsafe.go
index 338f3c0eae..e1d3b40098 100644
--- a/src/cmd/compile/internal/gc/unsafe.go
+++ b/src/cmd/compile/internal/gc/unsafe.go
@@ -9,7 +9,7 @@ func unsafenmagic(nn *Node) *Node {
 	fn := nn.Left
 	args := nn.List
 
-	if safemode != 0 || fn == nil || fn.Op != ONAME {
+	if safemode || fn == nil || fn.Op != ONAME {
 		return nil
 	}
 	s := fn.Sym
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 3e5f5161db..78bad8d348 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -594,8 +594,7 @@ opswitch:
 		// for a struct containing a reflect.Value, which itself has
 		// an unexported field of type unsafe.Pointer.
 		old_safemode := safemode
-
-		safemode = 0
+		safemode = false
 		n = walkcompare(n, init)
 		safemode = old_safemode
 
@@ -1938,7 +1937,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
 			on = substArgTypes(on, n.Type) // any-1
 		} else if Isint[et] {
 			if et == TUINT64 {
-				if (t.Sym.Pkg == Runtimepkg || compiling_runtime != 0) && t.Sym.Name == "hex" {
+				if (t.Sym.Pkg == Runtimepkg || compiling_runtime) && t.Sym.Name == "hex" {
 					on = syslook("printhex")
 				} else {
 					on = syslook("printuint")
@@ -2041,7 +2040,7 @@ func isglobal(n *Node) bool {
 
 // Do we need a write barrier for the assignment l = r?
 func needwritebarrier(l *Node, r *Node) bool {
-	if use_writebarrier == 0 {
+	if !use_writebarrier {
 		return false
 	}
 
@@ -2550,7 +2549,7 @@ func paramstoheap(params *Type, out bool) []*Node {
 		}
 
 		// generate allocation & copying code
-		if compiling_runtime != 0 {
+		if compiling_runtime {
 			Yyerror("%v escapes to heap, not allowed in runtime.", v)
 		}
 		if prealloc[v] == nil {
-- 
cgit v1.3


From babfb4ec3ba3e4e36b1003d6efbaeddf2e975240 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky 
Date: Wed, 13 Apr 2016 18:41:59 -0700
Subject: cmd/internal/obj: change Link.Flag_shared to bool

Change-Id: I9bda2ce6f45fb8292503f86d8f9f161601f222b7
Reviewed-on: https://go-review.googlesource.com/22053
Reviewed-by: Michael Hudson-Doyle 
---
 src/cmd/asm/main.go                      |  4 +---
 src/cmd/compile/internal/gc/cgen.go      |  2 +-
 src/cmd/compile/internal/gc/main.go      |  8 +-------
 src/cmd/compile/internal/ppc64/galign.go |  2 +-
 src/cmd/compile/internal/ppc64/gsubr.go  |  4 ++--
 src/cmd/compile/internal/ppc64/reg.go    |  2 +-
 src/cmd/compile/internal/x86/reg.go      |  2 +-
 src/cmd/internal/obj/arm/asm5.go         |  8 ++++----
 src/cmd/internal/obj/arm64/asm7.go       |  2 +-
 src/cmd/internal/obj/link.go             |  2 +-
 src/cmd/internal/obj/ppc64/asm9.go       | 10 +++++-----
 src/cmd/internal/obj/ppc64/obj9.go       |  4 ++--
 src/cmd/internal/obj/s390x/asmz.go       |  2 +-
 src/cmd/internal/obj/x86/asm6.go         | 22 +++++++++++-----------
 src/cmd/internal/obj/x86/obj6.go         |  4 ++--
 15 files changed, 35 insertions(+), 43 deletions(-)

(limited to 'src')

diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index f010ca93f1..40e1d9c4a9 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -39,9 +39,7 @@ func main() {
 	}
 	ctxt.LineHist.TrimPathPrefix = *flags.TrimPath
 	ctxt.Flag_dynlink = *flags.Dynlink
-	if *flags.Shared || *flags.Dynlink {
-		ctxt.Flag_shared = 1
-	}
+	ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
 	ctxt.Bso = bufio.NewWriter(os.Stdout)
 	defer ctxt.Bso.Flush()
 
diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go
index 9de2a19f68..32ca1ae940 100644
--- a/src/cmd/compile/internal/gc/cgen.go
+++ b/src/cmd/compile/internal/gc/cgen.go
@@ -2363,7 +2363,7 @@ func Ginscall(f *Node, proc int) {
 					// If the MOVD is not needed, insert a hardware NOP
 					// so that the same number of instructions are used
 					// on ppc64 in both shared and non-shared modes.
-					if Ctxt.Flag_shared != 0 {
+					if Ctxt.Flag_shared {
 						p := Thearch.Gins(ppc64.AMOVD, nil, nil)
 						p.From.Type = obj.TYPE_MEM
 						p.From.Offset = 24
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index f41097b83b..2baf9f6585 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -207,13 +207,7 @@ func Main() {
 	flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code")
 	obj.Flagparse(usage)
 
-	if flag_dynlink {
-		flag_shared = true
-	}
-	if flag_shared {
-		// TODO(mdempsky): Change Flag_shared to bool.
-		Ctxt.Flag_shared = 1
-	}
+	Ctxt.Flag_shared = flag_dynlink || flag_shared
 	Ctxt.Flag_dynlink = flag_dynlink
 	Ctxt.Flag_optimize = Debug['N'] == 0
 
diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go
index 04fa4cfc78..a83dff9a8b 100644
--- a/src/cmd/compile/internal/ppc64/galign.go
+++ b/src/cmd/compile/internal/ppc64/galign.go
@@ -11,7 +11,7 @@ import (
 )
 
 func betypeinit() {
-	if gc.Ctxt.Flag_shared != 0 {
+	if gc.Ctxt.Flag_shared {
 		gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, ppc64.REG_R2)
 		gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, ppc64.REG_R12)
 	}
diff --git a/src/cmd/compile/internal/ppc64/gsubr.go b/src/cmd/compile/internal/ppc64/gsubr.go
index de6e2fbe05..eb6cd2c5e9 100644
--- a/src/cmd/compile/internal/ppc64/gsubr.go
+++ b/src/cmd/compile/internal/ppc64/gsubr.go
@@ -580,7 +580,7 @@ func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
 	case obj.ACALL:
 		if p.To.Type == obj.TYPE_REG && p.To.Reg != ppc64.REG_CTR {
 			// Allow front end to emit CALL REG, and rewrite into MOV REG, CTR; CALL CTR.
-			if gc.Ctxt.Flag_shared != 0 {
+			if gc.Ctxt.Flag_shared {
 				// Make sure function pointer is in R12 as well when
 				// compiling Go into PIC.
 				// TODO(mwhudson): it would obviously be better to
@@ -602,7 +602,7 @@ func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
 			p.To.Type = obj.TYPE_REG
 			p.To.Reg = ppc64.REG_CTR
 
-			if gc.Ctxt.Flag_shared != 0 {
+			if gc.Ctxt.Flag_shared {
 				// When compiling Go into PIC, the function we just
 				// called via pointer might have been implemented in
 				// a separate module and so overwritten the TOC
diff --git a/src/cmd/compile/internal/ppc64/reg.go b/src/cmd/compile/internal/ppc64/reg.go
index 447679e207..558ba4a4f4 100644
--- a/src/cmd/compile/internal/ppc64/reg.go
+++ b/src/cmd/compile/internal/ppc64/reg.go
@@ -113,7 +113,7 @@ func excludedregs() uint64 {
 	// Exclude registers with fixed functions
 	regbits := 1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS) | RtoB(ppc64.REGTMP)
 
-	if gc.Ctxt.Flag_shared != 0 {
+	if gc.Ctxt.Flag_shared {
 		// When compiling Go into PIC, R2 is reserved to be the TOC pointer
 		// and R12 so that calls via function pointer can stomp on it.
 		regbits |= RtoB(ppc64.REG_R2)
diff --git a/src/cmd/compile/internal/x86/reg.go b/src/cmd/compile/internal/x86/reg.go
index 76d90b8e89..d49a1aed9d 100644
--- a/src/cmd/compile/internal/x86/reg.go
+++ b/src/cmd/compile/internal/x86/reg.go
@@ -62,7 +62,7 @@ func regnames(n *int) []string {
 }
 
 func excludedregs() uint64 {
-	if gc.Ctxt.Flag_shared != 0 {
+	if gc.Ctxt.Flag_shared {
 		return RtoB(x86.REG_SP) | RtoB(x86.REG_CX)
 	} else {
 		return RtoB(x86.REG_SP)
diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go
index f49ee65a04..564f96a94e 100644
--- a/src/cmd/internal/obj/arm/asm5.go
+++ b/src/cmd/internal/obj/arm/asm5.go
@@ -870,7 +870,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
 		t.To.Type = a.Type
 		t.To.Name = a.Name
 
-		if ctxt.Flag_shared != 0 && t.To.Sym != nil {
+		if ctxt.Flag_shared && t.To.Sym != nil {
 			t.Rel = p
 		}
 
@@ -1015,7 +1015,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 
 			ctxt.Instoffset = 0 // s.b. unused but just in case
 			if a.Sym.Type == obj.STLSBSS {
-				if ctxt.Flag_shared != 0 {
+				if ctxt.Flag_shared {
 					return C_TLS_IE
 				} else {
 					return C_TLS_LE
@@ -1322,7 +1322,7 @@ func buildop(ctxt *obj.Link) {
 	}
 	for n = 0; optab[n].as != obj.AXXX; n++ {
 		if optab[n].flag&LPCREL != 0 {
-			if ctxt.Flag_shared != 0 {
+			if ctxt.Flag_shared {
 				optab[n].size += int8(optab[n].pcrelsiz)
 			} else {
 				optab[n].flag &^= LPCREL
@@ -1633,7 +1633,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			rel.Sym = p.To.Sym
 			rel.Add = p.To.Offset
 
-			if ctxt.Flag_shared != 0 {
+			if ctxt.Flag_shared {
 				if p.To.Name == obj.NAME_GOTREF {
 					rel.Type = obj.R_GOTPCREL
 				} else {
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index d0ae6115cb..55397132e0 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -972,7 +972,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 			ctxt.Instoffset = a.Offset
 			if a.Sym != nil { // use relocation
 				if a.Sym.Type == obj.STLSBSS {
-					if ctxt.Flag_shared != 0 {
+					if ctxt.Flag_shared {
 						return C_TLS_IE
 					} else {
 						return C_TLS_LE
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 55c9f4f9e2..5f257f60ab 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -617,7 +617,7 @@ type Link struct {
 	Debugvlog     int32
 	Debugdivmod   int32
 	Debugpcln     int32
-	Flag_shared   int32
+	Flag_shared   bool
 	Flag_dynlink  bool
 	Flag_optimize bool
 	Bso           *bufio.Writer
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 0497d3b678..e793f26803 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -585,7 +585,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 			ctxt.Instoffset = a.Offset
 			if a.Sym != nil { // use relocation
 				if a.Sym.Type == obj.STLSBSS {
-					if ctxt.Flag_shared != 0 {
+					if ctxt.Flag_shared {
 						return C_TLS_IE
 					} else {
 						return C_TLS_LE
@@ -1413,7 +1413,7 @@ func opform(ctxt *obj.Link, insn uint32) int {
 func symbolAccess(ctxt *obj.Link, s *obj.LSym, d int64, reg int16, op uint32) (o1, o2 uint32) {
 	var base uint32
 	form := opform(ctxt, op)
-	if ctxt.Flag_shared != 0 {
+	if ctxt.Flag_shared {
 		base = REG_R2
 	} else {
 		base = REG_R0
@@ -1425,7 +1425,7 @@ func symbolAccess(ctxt *obj.Link, s *obj.LSym, d int64, reg int16, op uint32) (o
 	rel.Siz = 8
 	rel.Sym = s
 	rel.Add = d
-	if ctxt.Flag_shared != 0 {
+	if ctxt.Flag_shared {
 		switch form {
 		case D_FORM:
 			rel.Type = obj.R_ADDRPOWER_TOCREL
@@ -1646,7 +1646,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			if v != 0 {
 				ctxt.Diag("illegal indexed instruction\n%v", p)
 			}
-			if ctxt.Flag_shared != 0 && r == REG_R13 {
+			if ctxt.Flag_shared && r == REG_R13 {
 				rel := obj.Addrel(ctxt.Cursym)
 				rel.Off = int32(ctxt.Pc)
 				rel.Siz = 4
@@ -1677,7 +1677,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			if v != 0 {
 				ctxt.Diag("illegal indexed instruction\n%v", p)
 			}
-			if ctxt.Flag_shared != 0 && r == REG_R13 {
+			if ctxt.Flag_shared && r == REG_R13 {
 				rel := obj.Addrel(ctxt.Cursym)
 				rel.Off = int32(ctxt.Pc)
 				rel.Siz = 4
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index 7a24d1d1bf..4f9b3943cf 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -470,7 +470,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 
 			q = p
 
-			if ctxt.Flag_shared != 0 && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
+			if ctxt.Flag_shared && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" {
 				// When compiling Go into PIC, all functions must start
 				// with instructions to load the TOC pointer into r2:
 				//
@@ -558,7 +558,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				q.Spadj = int32(-aoffset)
 			}
 
-			if ctxt.Flag_shared != 0 {
+			if ctxt.Flag_shared {
 				q = obj.Appendp(ctxt, q)
 				q.As = AMOVD
 				q.Lineno = p.Lineno
diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go
index bae4dc3ce7..9b26580d11 100644
--- a/src/cmd/internal/obj/s390x/asmz.go
+++ b/src/cmd/internal/obj/s390x/asmz.go
@@ -473,7 +473,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 			}
 			ctxt.Instoffset = a.Offset
 			if a.Sym.Type == obj.STLSBSS {
-				if ctxt.Flag_shared != 0 {
+				if ctxt.Flag_shared {
 					return C_TLS_IE // initial exec model
 				}
 				return C_TLS_LE // local exec model
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index c563a7a48d..e806a834fd 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -2165,7 +2165,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
 					return 0x64 // FS
 				}
 
-				if ctxt.Flag_shared != 0 {
+				if ctxt.Flag_shared {
 					log.Fatalf("unknown TLS base register for linux with -shared")
 				} else {
 					return 0x64 // FS
@@ -2185,7 +2185,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
 	}
 
 	if p.Mode == 32 {
-		if a.Index == REG_TLS && ctxt.Flag_shared != 0 {
+		if a.Index == REG_TLS && ctxt.Flag_shared {
 			// When building for inclusion into a shared library, an instruction of the form
 			//     MOVL 0(CX)(TLS*1), AX
 			// becomes
@@ -2214,7 +2214,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
 		return 0x26
 
 	case REG_TLS:
-		if ctxt.Flag_shared != 0 {
+		if ctxt.Flag_shared {
 			// When building for inclusion into a shared library, an instruction of the form
 			//     MOV 0(CX)(TLS*1), AX
 			// becomes
@@ -2288,7 +2288,7 @@ func oclass(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
 
 		case obj.NAME_EXTERN,
 			obj.NAME_STATIC:
-			if a.Sym != nil && isextern(a.Sym) || (p.Mode == 32 && ctxt.Flag_shared == 0) {
+			if a.Sym != nil && isextern(a.Sym) || (p.Mode == 32 && !ctxt.Flag_shared) {
 				return Yi32
 			}
 			return Yiauto // use pc-relative addressing
@@ -2707,7 +2707,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
 		if a.Name == obj.NAME_GOTREF {
 			r.Siz = 4
 			r.Type = obj.R_GOTPCREL
-		} else if isextern(s) || (p.Mode != 64 && ctxt.Flag_shared == 0) {
+		} else if isextern(s) || (p.Mode != 64 && !ctxt.Flag_shared) {
 			r.Siz = 4
 			r.Type = obj.R_ADDR
 		} else {
@@ -2728,7 +2728,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
 			log.Fatalf("reloc")
 		}
 
-		if ctxt.Flag_shared == 0 || isAndroid {
+		if !ctxt.Flag_shared || isAndroid {
 			r.Type = obj.R_TLS_LE
 			r.Siz = 4
 			r.Off = -1 // caller must fill in
@@ -2793,7 +2793,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
 			if !isextern(a.Sym) && p.Mode == 64 {
 				goto bad
 			}
-			if p.Mode == 32 && ctxt.Flag_shared != 0 {
+			if p.Mode == 32 && ctxt.Flag_shared {
 				base = REG_CX
 			} else {
 				base = REG_NONE
@@ -2838,7 +2838,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
 		if a.Sym == nil {
 			ctxt.Diag("bad addr: %v", p)
 		}
-		if p.Mode == 32 && ctxt.Flag_shared != 0 {
+		if p.Mode == 32 && ctxt.Flag_shared {
 			base = REG_CX
 		} else {
 			base = REG_NONE
@@ -2892,7 +2892,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
 	}
 
 	if REG_AX <= base && base <= REG_R15 {
-		if a.Index == REG_TLS && ctxt.Flag_shared == 0 {
+		if a.Index == REG_TLS && !ctxt.Flag_shared {
 			rel = obj.Reloc{}
 			rel.Type = obj.R_TLS_LE
 			rel.Siz = 4
@@ -3945,7 +3945,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 
 						case obj.Hlinux,
 							obj.Hnacl:
-							if ctxt.Flag_shared != 0 {
+							if ctxt.Flag_shared {
 								// Note that this is not generating the same insns as the other cases.
 								//     MOV TLS, R_to
 								// becomes
@@ -4019,7 +4019,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 						log.Fatalf("unknown TLS base location for %s", obj.Headstr(ctxt.Headtype))
 
 					case obj.Hlinux:
-						if ctxt.Flag_shared == 0 {
+						if !ctxt.Flag_shared {
 							log.Fatalf("unknown TLS base location for linux without -shared")
 						}
 						// Note that this is not generating the same insn as the other cases.
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 302a597f4c..b638c048e8 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -66,7 +66,7 @@ func CanUse1InsnTLS(ctxt *obj.Link) bool {
 		obj.Hwindows:
 		return false
 	case obj.Hlinux:
-		return ctxt.Flag_shared == 0
+		return !ctxt.Flag_shared
 	}
 
 	return true
@@ -314,7 +314,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 		rewriteToUseGot(ctxt, p)
 	}
 
-	if ctxt.Flag_shared != 0 && p.Mode == 32 {
+	if ctxt.Flag_shared && p.Mode == 32 {
 		rewriteToPcrel(ctxt, p)
 	}
 }
-- 
cgit v1.3


From 8f64336edc7c725abcbe564d21b3d2dc5ec250ec Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Thu, 14 Apr 2016 09:57:43 +0900
Subject: net: make newLocalPacketListener handle network argument correcly

Change-Id: I41691134770d01805c19c0f84f8828b00b85de0c
Reviewed-on: https://go-review.googlesource.com/22058
Run-TryBot: Mikio Hara 
Reviewed-by: Ian Lance Taylor 
TryBot-Result: Gobot Gobot 
---
 src/net/mockserver_test.go | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index ffc6836e73..9e6907c09a 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -336,13 +336,21 @@ func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) {
 
 func newLocalPacketListener(network string) (PacketConn, error) {
 	switch network {
-	case "udp", "udp4", "udp6":
+	case "udp":
 		if supportsIPv4 {
 			return ListenPacket("udp4", "127.0.0.1:0")
 		}
 		if supportsIPv6 {
 			return ListenPacket("udp6", "[::1]:0")
 		}
+	case "udp4":
+		if supportsIPv4 {
+			return ListenPacket("udp4", "127.0.0.1:0")
+		}
+	case "udp6":
+		if supportsIPv6 {
+			return ListenPacket("udp6", "[::1]:0")
+		}
 	case "unixgram":
 		return ListenPacket(network, testUnixAddr())
 	}
-- 
cgit v1.3


From ed7cd2546eb997e976534f0542816e12448f34d5 Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Thu, 14 Apr 2016 12:17:44 +0900
Subject: net: make use of internal/testenv package

Change-Id: I6644081df495cb92b3d208f867066f9acb08946f
Reviewed-on: https://go-review.googlesource.com/22074
Run-TryBot: Mikio Hara 
Reviewed-by: Ian Lance Taylor 
---
 src/net/dial_test.go           | 29 ++++++++++++---------------
 src/net/dnsclient_unix_test.go | 21 ++++++--------------
 src/net/external_test.go       | 11 +++++------
 src/net/listen_test.go         | 19 ++++++++++++------
 src/net/lookup_test.go         | 45 +++++++++++++++++++++++++-----------------
 src/net/lookup_windows_test.go | 17 +++++-----------
 src/net/main_test.go           |  2 --
 src/net/platform_test.go       |  3 ++-
 src/net/tcpsock_test.go        | 10 ++++------
 src/net/udpsock_test.go        | 10 ++++------
 10 files changed, 79 insertions(+), 88 deletions(-)

(limited to 'src')

diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 466adf060e..d4f04e0a4f 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -24,13 +24,12 @@ var prohibitionaryDialArgTests = []struct {
 }
 
 func TestProhibitionaryDialArg(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
 	switch runtime.GOOS {
 	case "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
 	if !supportsIPv4map {
 		t.Skip("mapping ipv4 address inside ipv6 address not supported")
 	}
@@ -243,9 +242,8 @@ func dialClosedPort() (actual, expected time.Duration) {
 }
 
 func TestDialParallel(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
+
 	if !supportsIPv4 || !supportsIPv6 {
 		t.Skip("both IPv4 and IPv6 are required")
 	}
@@ -422,9 +420,8 @@ func lookupSlowFast(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, e
 }
 
 func TestDialerFallbackDelay(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
+
 	if !supportsIPv4 || !supportsIPv6 {
 		t.Skip("both IPv4 and IPv6 are required")
 	}
@@ -814,18 +811,18 @@ func TestDialerKeepAlive(t *testing.T) {
 }
 
 func TestDialCancel(t *testing.T) {
+	switch testenv.Builder() {
+	case "linux-arm64-buildlet":
+		t.Skip("skipping on linux-arm64-buildlet; incompatible network config? issue 15191")
+	case "":
+		testenv.MustHaveExternalNetwork(t)
+	}
+
 	if runtime.GOOS == "plan9" || runtime.GOOS == "nacl" {
 		// plan9 is not implemented and nacl doesn't have
 		// external network access.
 		t.Skipf("skipping on %s", runtime.GOOS)
 	}
-	onGoBuildFarm := testenv.Builder() != ""
-	if testing.Short() && !onGoBuildFarm {
-		t.Skip("skipping in short mode")
-	}
-	if testenv.Builder() == "linux-arm64-buildlet" {
-		t.Skip("skipping on linux-arm64-buildlet; incompatible network config? issue 15191")
-	}
 
 	blackholeIPPort := JoinHostPort(slowDst4, "1234")
 	if !supportsIPv4 {
diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index 6845481e17..edf7c00f72 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -8,6 +8,7 @@ package net
 
 import (
 	"fmt"
+	"internal/testenv"
 	"io/ioutil"
 	"os"
 	"path"
@@ -32,9 +33,7 @@ var dnsTransportFallbackTests = []struct {
 }
 
 func TestDNSTransportFallback(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	for _, tt := range dnsTransportFallbackTests {
 		timeout := time.Duration(tt.timeout) * time.Second
@@ -74,9 +73,7 @@ var specialDomainNameTests = []struct {
 }
 
 func TestSpecialDomainName(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	server := "8.8.8.8:53"
 	for _, tt := range specialDomainNameTests {
@@ -232,9 +229,7 @@ var updateResolvConfTests = []struct {
 }
 
 func TestUpdateResolvConf(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	conf, err := newResolvConfTest()
 	if err != nil {
@@ -389,9 +384,7 @@ var goLookupIPWithResolverConfigTests = []struct {
 }
 
 func TestGoLookupIPWithResolverConfig(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	conf, err := newResolvConfTest()
 	if err != nil {
@@ -436,9 +429,7 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
 
 // Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
 func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	// Add a config that simulates no dns servers being available.
 	conf, err := newResolvConfTest()
diff --git a/src/net/external_test.go b/src/net/external_test.go
index d5ff2be20a..e18b547cac 100644
--- a/src/net/external_test.go
+++ b/src/net/external_test.go
@@ -6,15 +6,15 @@ package net
 
 import (
 	"fmt"
+	"internal/testenv"
 	"io"
 	"strings"
 	"testing"
 )
 
 func TestResolveGoogle(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
+
 	if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
 		t.Skip("both IPv4 and IPv6 are required")
 	}
@@ -60,9 +60,8 @@ var dialGoogleTests = []struct {
 }
 
 func TestDialGoogle(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
+
 	if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
 		t.Skip("both IPv4 and IPv6 are required")
 	}
diff --git a/src/net/listen_test.go b/src/net/listen_test.go
index a4320eb5a5..6037f3600d 100644
--- a/src/net/listen_test.go
+++ b/src/net/listen_test.go
@@ -8,6 +8,7 @@ package net
 
 import (
 	"fmt"
+	"internal/testenv"
 	"os"
 	"runtime"
 	"syscall"
@@ -483,13 +484,12 @@ func checkDualStackAddrFamily(fd *netFD) error {
 }
 
 func TestWildWildcardListener(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
 	switch runtime.GOOS {
 	case "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
 
 	defer func() {
 		if p := recover(); p != nil {
@@ -527,12 +527,17 @@ var ipv4MulticastListenerTests = []struct {
 // test listener with same address family, same group address and same
 // port.
 func TestIPv4MulticastListener(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
 	switch runtime.GOOS {
 	case "android", "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	case "solaris":
 		t.Skipf("not supported on solaris, see golang.org/issue/7399")
 	}
+	if !supportsIPv4 {
+		t.Skip("IPv4 is not supported")
+	}
 
 	closer := func(cs []*UDPConn) {
 		for _, c := range cs {
@@ -548,7 +553,7 @@ func TestIPv4MulticastListener(t *testing.T) {
 		// routing stuff for finding out an appropriate
 		// nexthop containing both network and link layer
 		// adjacencies.
-		if ifi == nil && (testing.Short() || !*testExternal) {
+		if ifi == nil || !*testIPv4 {
 			continue
 		}
 		for _, tt := range ipv4MulticastListenerTests {
@@ -597,6 +602,8 @@ var ipv6MulticastListenerTests = []struct {
 // test listener with same address family, same group address and same
 // port.
 func TestIPv6MulticastListener(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
 	switch runtime.GOOS {
 	case "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
@@ -604,7 +611,7 @@ func TestIPv6MulticastListener(t *testing.T) {
 		t.Skipf("not supported on solaris, see issue 7399")
 	}
 	if !supportsIPv6 {
-		t.Skip("ipv6 is not supported")
+		t.Skip("IPv6 is not supported")
 	}
 	if os.Getuid() != 0 {
 		t.Skip("must be root")
@@ -624,7 +631,7 @@ func TestIPv6MulticastListener(t *testing.T) {
 		// routing stuff for finding out an appropriate
 		// nexthop containing both network and link layer
 		// adjacencies.
-		if ifi == nil && (testing.Short() || !*testExternal || !*testIPv6) {
+		if ifi == nil && !*testIPv6 {
 			continue
 		}
 		for _, tt := range ipv6MulticastListenerTests {
diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go
index 6307a8612d..1345751cfd 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -58,9 +58,10 @@ var lookupGoogleSRVTests = []struct {
 }
 
 func TestLookupGoogleSRV(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
@@ -92,9 +93,10 @@ var lookupGmailMXTests = []struct {
 }
 
 func TestLookupGmailMX(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
@@ -123,9 +125,10 @@ var lookupGmailNSTests = []struct {
 }
 
 func TestLookupGmailNS(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
@@ -154,9 +157,10 @@ var lookupGmailTXTTests = []struct {
 }
 
 func TestLookupGmailTXT(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
@@ -188,9 +192,10 @@ var lookupGooglePublicDNSAddrTests = []struct {
 }
 
 func TestLookupGooglePublicDNSAddr(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
 		t.Skip("both IPv4 and IPv6 are required")
 	}
@@ -243,9 +248,10 @@ var lookupIANACNAMETests = []struct {
 }
 
 func TestLookupIANACNAME(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
@@ -269,9 +275,10 @@ var lookupGoogleHostTests = []struct {
 }
 
 func TestLookupGoogleHost(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
@@ -300,9 +307,10 @@ var lookupGoogleIPTests = []struct {
 }
 
 func TestLookupGoogleIP(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
@@ -463,9 +471,10 @@ func TestLookupDotsWithLocalSource(t *testing.T) {
 }
 
 func TestLookupDotsWithRemoteSource(t *testing.T) {
-	if testing.Short() && testenv.Builder() == "" || !*testExternal {
-		t.Skip("avoid external network")
+	if testenv.Builder() == "" {
+		testenv.MustHaveExternalNetwork(t)
 	}
+
 	if !supportsIPv4 || !*testIPv4 {
 		t.Skip("IPv4 is required")
 	}
diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go
index 7ff32b809b..9af2c61b74 100644
--- a/src/net/lookup_windows_test.go
+++ b/src/net/lookup_windows_test.go
@@ -8,6 +8,7 @@ import (
 	"bytes"
 	"encoding/json"
 	"errors"
+	"internal/testenv"
 	"os/exec"
 	"reflect"
 	"regexp"
@@ -24,9 +25,7 @@ func toJson(v interface{}) string {
 }
 
 func TestLookupMX(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	for _, server := range nslookupTestServers {
 		mx, err := LookupMX(server)
@@ -51,9 +50,7 @@ func TestLookupMX(t *testing.T) {
 }
 
 func TestLookupCNAME(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	for _, server := range nslookupTestServers {
 		cname, err := LookupCNAME(server)
@@ -76,9 +73,7 @@ func TestLookupCNAME(t *testing.T) {
 }
 
 func TestLookupNS(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	for _, server := range nslookupTestServers {
 		ns, err := LookupNS(server)
@@ -104,9 +99,7 @@ func TestLookupNS(t *testing.T) {
 }
 
 func TestLookupTXT(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	for _, server := range nslookupTestServers {
 		txt, err := LookupTXT(server)
diff --git a/src/net/main_test.go b/src/net/main_test.go
index f3f8b1a900..7573ded93b 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -26,8 +26,6 @@ var (
 var (
 	testDNSFlood = flag.Bool("dnsflood", false, "whether to test DNS query flooding")
 
-	testExternal = flag.Bool("external", true, "allow use of external networks during long test")
-
 	// If external IPv4 connectivity exists, we can try dialing
 	// non-node/interface local scope IPv4 addresses.
 	// On Windows, Lookup APIs may not return IPv4-related
diff --git a/src/net/platform_test.go b/src/net/platform_test.go
index 76c53138cd..2a14095cc2 100644
--- a/src/net/platform_test.go
+++ b/src/net/platform_test.go
@@ -5,6 +5,7 @@
 package net
 
 import (
+	"internal/testenv"
 	"os"
 	"runtime"
 	"strings"
@@ -110,7 +111,7 @@ func testableListenArgs(network, address, client string) bool {
 	}
 
 	// Test wildcard IP addresses.
-	if wildcard && (testing.Short() || !*testExternal) {
+	if wildcard && !testenv.HasExternalNetwork() {
 		return false
 	}
 
diff --git a/src/net/tcpsock_test.go b/src/net/tcpsock_test.go
index 8de6ad71ce..4af47fcf1a 100644
--- a/src/net/tcpsock_test.go
+++ b/src/net/tcpsock_test.go
@@ -5,6 +5,7 @@
 package net
 
 import (
+	"internal/testenv"
 	"io"
 	"reflect"
 	"runtime"
@@ -345,9 +346,7 @@ var tcpListenerNameTests = []struct {
 }
 
 func TestTCPListenerName(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	for _, tt := range tcpListenerNameTests {
 		ln, err := ListenTCP(tt.net, tt.laddr)
@@ -363,9 +362,8 @@ func TestTCPListenerName(t *testing.T) {
 }
 
 func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
+
 	if !supportsIPv6 {
 		t.Skip("IPv6 is not supported")
 	}
diff --git a/src/net/udpsock_test.go b/src/net/udpsock_test.go
index 1404b7ce80..1da24b2cc8 100644
--- a/src/net/udpsock_test.go
+++ b/src/net/udpsock_test.go
@@ -5,6 +5,7 @@
 package net
 
 import (
+	"internal/testenv"
 	"reflect"
 	"runtime"
 	"testing"
@@ -178,9 +179,7 @@ var udpConnLocalNameTests = []struct {
 }
 
 func TestUDPConnLocalName(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
 
 	for _, tt := range udpConnLocalNameTests {
 		c, err := ListenUDP(tt.net, tt.laddr)
@@ -234,9 +233,8 @@ func TestUDPConnLocalAndRemoteNames(t *testing.T) {
 }
 
 func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
-	if testing.Short() || !*testExternal {
-		t.Skip("avoid external network")
-	}
+	testenv.MustHaveExternalNetwork(t)
+
 	if !supportsIPv6 {
 		t.Skip("IPv6 is not supported")
 	}
-- 
cgit v1.3


From 285e78609f4fd85948d056f581d3443d5f9b230a Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Thu, 14 Apr 2016 14:53:12 +0900
Subject: net: fix TestDialAddrError

Fixes #15291.

Change-Id: I563140c2acd37d4989a940488b217414cf73f6c2
Reviewed-on: https://go-review.googlesource.com/22077
Reviewed-by: Alex Brainman 
---
 src/net/error_test.go | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'src')

diff --git a/src/net/error_test.go b/src/net/error_test.go
index 40f235c924..31cc0e5055 100644
--- a/src/net/error_test.go
+++ b/src/net/error_test.go
@@ -211,6 +211,9 @@ func TestDialAddrError(t *testing.T) {
 	case "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
+	if !supportsIPv4 || !supportsIPv6 {
+		t.Skip("both IPv4 and IPv6 are required")
+	}
 
 	for _, tt := range []struct {
 		network string
-- 
cgit v1.3


From 0ec6d7c0bbfceb7b8e4857b775686ae5cf699e54 Mon Sep 17 00:00:00 2001
From: Marcel van Lohuizen 
Date: Thu, 14 Apr 2016 15:44:48 +0800
Subject: testing: removed flakey test

The synchronization in this test is a bit complicated and likely
incorrect, judging from the sporadically hanging trybots.
Most of what this is supposed to test is already tested in
TestTestContext, so I'll just remove it.

Fixes #15170

Change-Id: If54db977503caa109cec4516974eda9191051888
Reviewed-on: https://go-review.googlesource.com/22080
Run-TryBot: Marcel van Lohuizen 
TryBot-Result: Gobot Gobot 
Reviewed-by: Brad Fitzpatrick 
---
 src/testing/sub_test.go | 51 -------------------------------------------------
 1 file changed, 51 deletions(-)

(limited to 'src')

diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go
index 4f26a53ab6..2804550737 100644
--- a/src/testing/sub_test.go
+++ b/src/testing/sub_test.go
@@ -226,57 +226,6 @@ func TestTRun(t *T) {
 				t.Errorf("count was %d; want 4", count)
 			}
 		},
-	}, {
-		desc:   "run no more than *parallel tests concurrently",
-		ok:     true,
-		maxPar: 4,
-		f: func(t *T) {
-			max := 0
-			in := make(chan int)
-			out := make(chan int)
-			ctx := t.context
-			t.Run("wait", func(t *T) {
-				t.Run("controller", func(t *T) {
-					// Verify sequential tests don't skew counts.
-					t.Run("seq1", func(t *T) {})
-					t.Run("seq2", func(t *T) {})
-					t.Run("seq3", func(t *T) {})
-					t.Parallel()
-					for i := 0; i < 80; i++ {
-						ctx.mu.Lock()
-						if ctx.running > max {
-							max = ctx.running
-						}
-						ctx.mu.Unlock()
-						<-in
-						// force a minimum to avoid a race, although it works
-						// without it.
-						if i >= ctx.maxParallel-2 { // max - this - 1
-							out <- i
-						}
-					}
-					close(out)
-				})
-				// Ensure we don't exceed the maximum even with nested parallelism.
-				for i := 0; i < 2; i++ {
-					t.Run("", func(t *T) {
-						t.Parallel()
-						for j := 0; j < 40; j++ {
-							t.Run("", func(t *T) {
-								t.Run("seq1", func(t *T) {})
-								t.Run("seq2", func(t *T) {})
-								t.Parallel()
-								in <- j
-								<-out
-							})
-						}
-					})
-				}
-			})
-			if max != ctx.maxParallel {
-				realTest.Errorf("max: got %d; want: %d", max, ctx.maxParallel)
-			}
-		},
 	}, {
 		desc: "alternate sequential and parallel",
 		// Sequential tests should partake in the counting of running threads.
-- 
cgit v1.3


From 2c9d773f7411de211389b9e1da441fae68f826d8 Mon Sep 17 00:00:00 2001
From: Dmitry Vyukov 
Date: Thu, 14 Apr 2016 16:31:42 +0200
Subject: misc/trace: update trace viewer html

The old trace-viewer is broken since Chrome 49:
https://bugs.chromium.org/p/chromium/issues/detail?id=569417
It was fixed in:
https://github.com/catapult-project/catapult/commit/506457cbd726324f327b80ae11f46c1dfeb8710d

This change updates trace-viewer to the latest version
(now it is called catapult).

This version has a bug in the lean config that we use, though:
https://github.com/catapult-project/catapult/issues/2247
So use full config for now (it works, but leads to larger html).
When the bug is fixed we need to switch back to lean config (issue #15302).

Change-Id: Ifb8d782ced66e3292d81c5604039fe18eaf267c5
Reviewed-on: https://go-review.googlesource.com/22013
Reviewed-by: Brad Fitzpatrick 
---
 misc/trace/README.md              |   12 +-
 misc/trace/trace_viewer_lean.html | 8773 ++++++++++++++++++++++++++-----------
 src/cmd/trace/trace.go            |   91 +-
 3 files changed, 6332 insertions(+), 2544 deletions(-)

(limited to 'src')

diff --git a/misc/trace/README.md b/misc/trace/README.md
index 8561c79bfd..2e8bb057d4 100644
--- a/misc/trace/README.md
+++ b/misc/trace/README.md
@@ -1,12 +1,16 @@
 This directory contains helper file for trace viewer (`go tool trace`).
 
 `trace_viewer_lean.html` was generated by following
-[instructions](https://github.com/google/trace-viewer/wiki/Embedding)
-on revision `280626ef607decf36291e290d5f0322b173e8a7f` using:
+[instructions](https://github.com/catapult-project/catapult/blob/master/tracing/docs/embedding-trace-viewer.md)
+on revision `623a005a3ffa9de13c4b92bc72290e7bcd1ca591`
+of [catapult](https://github.com/catapult-project/catapult) using:
 ```
-trace-viewer$ ./vulcanize_trace_viewer --config=lean
-trace-viewer$ cp bin/trace_viewer_lean.html $GOROOT/misc/trace/
+catapult$ ./tracing/bin/vulcanize_trace_viewer --config=full
+catapult$ cp tracing/bin/trace_viewer_full.html $GOROOT/misc/trace/trace_viewer_lean.html
 ```
+We are supposed to use --config=lean (produces smaller html),
+but it is broken at the moment:
+https://github.com/catapult-project/catapult/issues/2247
 
 The license for trace-viewer is as follows:
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
diff --git a/misc/trace/trace_viewer_lean.html b/misc/trace/trace_viewer_lean.html
index 5d40bc5805..7939aae8a6 100644
--- a/misc/trace/trace_viewer_lean.html
+++ b/misc/trace/trace_viewer_lean.html
@@ -1,113 +1,9 @@
-
+
 
   
-  
-    
-
-
-
-
-
-
-
-
+  
+
+  
+
+  
+
+  
+
+  
+
+  
+  
+
+  
+
+  
 
-
-
+  
+
+  
 
-
-
-
-
-
-
-
 
-
-
 
-
-
+
+  
+
+  
+
+  
 
-    table tr > td {
-      padding: 2px 4px 2px 4px;
-      vertical-align: text-top;
-      width: 150px;
+  
+
+  
+  
+
+  
+
+  
+  
+
+  
+
+  
+
+  
+
+  
+
+  
-
-
-
+    
+  
 
-
-
+  
+
+  
+  
+
+  
+
+  
+
+  
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+    
+ -
- - - - - - - + + + + - - - - - - + +
+ + +
+ + + +
+ - - - - - - - - - - - - - - - - - - - + #icon { + display: flex; + flex: 0 0 auto; + flex: 0 0 auto; + } + dialog { + position: absolute; + padding: 0; + border: 0; + margin: 0; + } + dialog::backdrop { + background: rgba(0,0,0,.05); + } - - - - - - - - +
+ -
- - +
+ +
+
- - - - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + +