2015-02-13 14:40:36 -05:00
|
|
|
// Copyright 2012 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 (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2015-10-20 10:00:07 -07:00
|
|
|
// The instrument pass modifies the code tree for instrumentation.
|
|
|
|
|
//
|
|
|
|
|
// For flag_race it modifies the function as follows:
|
2015-02-13 14:40:36 -05:00
|
|
|
//
|
|
|
|
|
// 1. It inserts a call to racefuncenter at the beginning of each function.
|
|
|
|
|
// 2. It inserts a call to racefuncexit at the end of each function.
|
|
|
|
|
// 3. It inserts a call to raceread before each memory read.
|
|
|
|
|
// 4. It inserts a call to racewrite before each memory write.
|
|
|
|
|
//
|
2015-10-21 07:04:10 -07:00
|
|
|
// For flag_msan:
|
|
|
|
|
//
|
|
|
|
|
// 1. It inserts a call to msanread before each memory read.
|
|
|
|
|
// 2. It inserts a call to msanwrite before each memory write.
|
|
|
|
|
//
|
2015-02-13 14:40:36 -05:00
|
|
|
// The rewriting is not yet complete. Certain nodes are not rewritten
|
|
|
|
|
// but should be.
|
|
|
|
|
|
|
|
|
|
// TODO(dvyukov): do not instrument initialization as writes:
|
|
|
|
|
// a := make([]int, 10)
|
|
|
|
|
|
|
|
|
|
// Do not instrument the following packages at all,
|
|
|
|
|
// at best instrumentation would cause infinite recursion.
|
2015-11-11 12:39:30 -05:00
|
|
|
var omit_pkgs = []string{"runtime/internal/atomic", "runtime/internal/sys", "runtime", "runtime/race", "runtime/msan"}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Only insert racefuncenter/racefuncexit into the following packages.
|
|
|
|
|
// Memory accesses in the packages are either uninteresting or will cause false positives.
|
2015-10-21 07:04:10 -07:00
|
|
|
var norace_inst_pkgs = []string{"sync", "sync/atomic"}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func ispkgin(pkgs []string) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if myimportpath != "" {
|
2015-09-08 22:22:44 +02:00
|
|
|
for _, p := range pkgs {
|
|
|
|
|
if myimportpath == p {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-10-20 10:00:07 -07:00
|
|
|
func instrument(fn *Node) {
|
2015-10-19 17:01:37 -07:00
|
|
|
if ispkgin(omit_pkgs) || fn.Func.Norace {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-21 07:04:10 -07:00
|
|
|
if flag_race == 0 || !ispkgin(norace_inst_pkgs) {
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentlist(fn.Nbody, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// nothing interesting for race detector in fn->enter
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentlist(fn.Func.Exit, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-10-21 07:04:10 -07:00
|
|
|
if flag_race != 0 {
|
|
|
|
|
// nodpc is the PC of the caller as extracted by
|
|
|
|
|
// getcallerpc. We use -widthptr(FP) for x86.
|
|
|
|
|
// BUG: this will not work on arm.
|
|
|
|
|
nodpc := Nod(OXXX, nil, nil)
|
|
|
|
|
|
|
|
|
|
*nodpc = *nodfp
|
|
|
|
|
nodpc.Type = Types[TUINTPTR]
|
|
|
|
|
nodpc.Xoffset = int64(-Widthptr)
|
|
|
|
|
nd := mkcall("racefuncenter", nil, nil, nodpc)
|
|
|
|
|
fn.Func.Enter = concat(list1(nd), fn.Func.Enter)
|
|
|
|
|
nd = mkcall("racefuncexit", nil, nil)
|
|
|
|
|
fn.Func.Exit = list(fn.Func.Exit, nd)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if Debug['W'] != 0 {
|
2015-10-20 10:00:07 -07:00
|
|
|
s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
dumplist(s, fn.Nbody)
|
2015-05-27 10:42:55 -04:00
|
|
|
s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym)
|
2015-03-25 19:33:01 -07:00
|
|
|
dumplist(s, fn.Func.Enter)
|
2015-05-27 10:42:55 -04:00
|
|
|
s = fmt.Sprintf("exit %v", fn.Func.Nname.Sym)
|
2015-03-25 19:33:01 -07:00
|
|
|
dumplist(s, fn.Func.Exit)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-20 10:00:07 -07:00
|
|
|
func instrumentlist(l *NodeList, init **NodeList) {
|
2015-02-13 14:40:36 -05:00
|
|
|
var instr *NodeList
|
|
|
|
|
|
|
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
|
instr = nil
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&l.N, &instr, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
if init == nil {
|
|
|
|
|
l.N.Ninit = concat(l.N.Ninit, instr)
|
|
|
|
|
} else {
|
|
|
|
|
*init = concat(*init, instr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// walkexpr and walkstmt combined
|
|
|
|
|
// walks the tree and adds calls to the
|
|
|
|
|
// instrumentation code to top-level (statement) nodes' init
|
2015-10-20 10:00:07 -07:00
|
|
|
func instrumentnode(np **Node, init **NodeList, wr int, skip int) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if n == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if Debug['w'] > 1 {
|
2015-10-20 10:00:07 -07:00
|
|
|
Dump("instrument-before", n)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
setlineno(n)
|
|
|
|
|
if init == nil {
|
2015-10-20 10:00:07 -07:00
|
|
|
Fatalf("instrument: bad init list")
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
if init == &n.Ninit {
|
|
|
|
|
// If init == &n->ninit and n->ninit is non-nil,
|
2015-10-20 10:00:07 -07:00
|
|
|
// instrumentnode might append it to itself.
|
2015-02-13 14:40:36 -05:00
|
|
|
// nil it out and handle it separately before putting it back.
|
2015-02-23 16:07:24 -05:00
|
|
|
l := n.Ninit
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
n.Ninit = nil
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentlist(l, nil)
|
|
|
|
|
instrumentnode(&n, &l, wr, skip) // recurse with nil n->ninit
|
2015-02-13 14:40:36 -05:00
|
|
|
appendinit(&n, l)
|
|
|
|
|
*np = n
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentlist(n.Ninit, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
|
default:
|
2015-10-20 10:00:07 -07:00
|
|
|
Fatalf("instrument: unknown node type %v", Oconv(int(n.Op), 0))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
cmd/internal/gc: emit write barriers at lower level
This is primarily preparation for inlining, not an optimization by itself,
but it still helps some.
name old new delta
BenchmarkBinaryTree17 18.2s × (0.99,1.01) 17.9s × (0.99,1.01) -1.57%
BenchmarkFannkuch11 4.44s × (1.00,1.00) 4.42s × (1.00,1.00) -0.40%
BenchmarkFmtFprintfEmpty 119ns × (0.95,1.02) 118ns × (0.96,1.02) ~
BenchmarkFmtFprintfString 501ns × (0.99,1.02) 486ns × (0.99,1.01) -2.89%
BenchmarkFmtFprintfInt 474ns × (0.99,1.00) 457ns × (0.99,1.01) -3.59%
BenchmarkFmtFprintfIntInt 792ns × (1.00,1.00) 768ns × (1.00,1.01) -3.03%
BenchmarkFmtFprintfPrefixedInt 574ns × (1.00,1.01) 584ns × (0.99,1.03) +1.83%
BenchmarkFmtFprintfFloat 749ns × (1.00,1.00) 739ns × (0.99,1.00) -1.34%
BenchmarkFmtManyArgs 2.94µs × (1.00,1.01) 2.77µs × (1.00,1.00) -5.76%
BenchmarkGobDecode 39.5ms × (0.99,1.01) 39.3ms × (0.99,1.01) ~
BenchmarkGobEncode 39.4ms × (1.00,1.01) 39.4ms × (0.99,1.00) ~
BenchmarkGzip 658ms × (1.00,1.01) 661ms × (0.99,1.01) ~
BenchmarkGunzip 142ms × (1.00,1.00) 142ms × (1.00,1.00) +0.22%
BenchmarkHTTPClientServer 134µs × (0.99,1.01) 133µs × (0.98,1.01) ~
BenchmarkJSONEncode 57.1ms × (0.99,1.01) 56.5ms × (0.99,1.01) ~
BenchmarkJSONDecode 141ms × (1.00,1.00) 143ms × (1.00,1.00) +1.09%
BenchmarkMandelbrot200 6.01ms × (1.00,1.00) 6.01ms × (1.00,1.00) ~
BenchmarkGoParse 10.1ms × (0.91,1.09) 9.6ms × (0.94,1.07) ~
BenchmarkRegexpMatchEasy0_32 207ns × (1.00,1.01) 210ns × (1.00,1.00) +1.45%
BenchmarkRegexpMatchEasy0_1K 592ns × (0.99,1.00) 596ns × (0.99,1.01) +0.68%
BenchmarkRegexpMatchEasy1_32 184ns × (0.99,1.01) 184ns × (0.99,1.01) ~
BenchmarkRegexpMatchEasy1_1K 1.01µs × (1.00,1.00) 1.01µs × (0.99,1.01) ~
BenchmarkRegexpMatchMedium_32 327ns × (0.99,1.00) 327ns × (1.00,1.01) ~
BenchmarkRegexpMatchMedium_1K 92.5µs × (1.00,1.00) 93.0µs × (1.00,1.02) +0.48%
BenchmarkRegexpMatchHard_32 4.79µs × (0.95,1.00) 4.76µs × (0.95,1.01) ~
BenchmarkRegexpMatchHard_1K 136µs × (1.00,1.00) 136µs × (1.00,1.01) ~
BenchmarkRevcomp 900ms × (0.99,1.01) 892ms × (1.00,1.01) ~
BenchmarkTemplate 170ms × (0.99,1.01) 175ms × (0.99,1.00) +2.95%
BenchmarkTimeParse 645ns × (1.00,1.00) 638ns × (1.00,1.00) -1.16%
BenchmarkTimeFormat 740ns × (1.00,1.00) 772ns × (1.00,1.00) +4.39%
Change-Id: I0be905e32791e0cb70ff01f169c4b309a971d981
Reviewed-on: https://go-review.googlesource.com/9159
Reviewed-by: Rick Hudson <rlh@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-04-17 00:25:10 -04:00
|
|
|
case OAS, OASWB, OAS2FUNC:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 1, 0)
|
|
|
|
|
instrumentnode(&n.Right, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// can't matter
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCFUNC, OVARKILL:
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OBLOCK:
|
2015-06-29 15:17:14 -04:00
|
|
|
var out *NodeList
|
|
|
|
|
for l := n.List; l != nil; l = l.Next {
|
|
|
|
|
switch l.N.Op {
|
|
|
|
|
case OCALLFUNC, OCALLMETH, OCALLINTER:
|
2015-12-14 13:24:27 -05:00
|
|
|
instrumentnode(&l.N, &l.N.Ninit, 0, 0)
|
2015-06-29 15:17:14 -04:00
|
|
|
out = list(out, l.N)
|
|
|
|
|
// Scan past OAS nodes copying results off stack.
|
|
|
|
|
// Those must not be instrumented, because the
|
|
|
|
|
// instrumentation calls will smash the results.
|
|
|
|
|
// The assignments are to temporaries, so they cannot
|
|
|
|
|
// be involved in races and need not be instrumented.
|
|
|
|
|
for l.Next != nil && l.Next.N.Op == OAS && iscallret(l.Next.N.Right) {
|
|
|
|
|
l = l.Next
|
|
|
|
|
out = list(out, l.N)
|
|
|
|
|
}
|
|
|
|
|
default:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&l.N, &out, 0, 0)
|
2015-06-29 15:17:14 -04:00
|
|
|
out = list(out, l.N)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-06-29 15:17:14 -04:00
|
|
|
n.List = out
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ODEFER:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OPROC:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OCALLINTER:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// Instrument dst argument of runtime.writebarrier* calls
|
|
|
|
|
// as we do not instrument runtime code.
|
|
|
|
|
// typedslicecopy is instrumented in runtime.
|
|
|
|
|
case OCALLFUNC:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ONOT,
|
|
|
|
|
OMINUS,
|
|
|
|
|
OPLUS,
|
|
|
|
|
OREAL,
|
|
|
|
|
OIMAG,
|
2015-04-03 15:58:18 -04:00
|
|
|
OCOM,
|
|
|
|
|
OSQRT:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ODOTINTER:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ODOT:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
callinstr(&n, init, wr, skip)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
callinstr(&n, init, wr, skip)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OIND: // *p
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
callinstr(&n, init, wr, skip)
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OSPTR, OLEN, OCAP:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-17 22:13:49 -05:00
|
|
|
if Istype(n.Left.Type, TMAP) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n1 := Nod(OCONVNOP, n.Left, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
n1.Type = Ptrto(Types[TUINT8])
|
|
|
|
|
n1 = Nod(OIND, n1, nil)
|
|
|
|
|
typecheck(&n1, Erv)
|
|
|
|
|
callinstr(&n1, init, 0, skip)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OLSH,
|
|
|
|
|
ORSH,
|
|
|
|
|
OLROT,
|
|
|
|
|
OAND,
|
|
|
|
|
OANDNOT,
|
|
|
|
|
OOR,
|
|
|
|
|
OXOR,
|
|
|
|
|
OSUB,
|
|
|
|
|
OMUL,
|
|
|
|
|
OHMUL,
|
|
|
|
|
OEQ,
|
|
|
|
|
ONE,
|
|
|
|
|
OLT,
|
|
|
|
|
OLE,
|
|
|
|
|
OGE,
|
|
|
|
|
OGT,
|
|
|
|
|
OADD,
|
|
|
|
|
OCOMPLEX:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, wr, 0)
|
|
|
|
|
instrumentnode(&n.Right, init, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OANDAND, OOROR:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// walk has ensured the node has moved to a location where
|
|
|
|
|
// side effects are safe.
|
|
|
|
|
// n->right may not be executed,
|
|
|
|
|
// so instrumentation goes to n->right->ninit, not init.
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Right, &n.Right.Ninit, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ONAME:
|
|
|
|
|
callinstr(&n, init, wr, skip)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OCONV:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OCONVNOP:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case ODIV, OMOD:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, wr, 0)
|
|
|
|
|
instrumentnode(&n.Right, init, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OINDEX:
|
2015-02-17 22:13:49 -05:00
|
|
|
if !Isfixedarray(n.Left.Type) {
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if !islvalue(n.Left) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// index of unaddressable array, like Map[k][i].
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, wr, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Right, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Right, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Left.Type.Etype != TSTRING {
|
|
|
|
|
callinstr(&n, init, wr, skip)
|
|
|
|
|
}
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-06-24 19:25:51 +02:00
|
|
|
case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
|
|
|
|
instrumentnode(&n.Right, init, 0, 0)
|
2015-06-24 19:25:51 +02:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OKEY:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
|
|
|
|
instrumentnode(&n.Right, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OADDR:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// n->left is Type* which is not interesting.
|
|
|
|
|
case OEFACE:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Right, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OITAB:
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, init, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// should not appear in AST by now
|
|
|
|
|
case OSEND,
|
|
|
|
|
ORECV,
|
|
|
|
|
OCLOSE,
|
|
|
|
|
ONEW,
|
|
|
|
|
OXCASE,
|
|
|
|
|
OXFALL,
|
|
|
|
|
OCASE,
|
|
|
|
|
OPANIC,
|
|
|
|
|
ORECOVER,
|
|
|
|
|
OCONVIFACE,
|
|
|
|
|
OCMPIFACE,
|
|
|
|
|
OMAKECHAN,
|
|
|
|
|
OMAKEMAP,
|
|
|
|
|
OMAKESLICE,
|
|
|
|
|
OCALL,
|
|
|
|
|
OCOPY,
|
|
|
|
|
OAPPEND,
|
|
|
|
|
ORUNESTR,
|
|
|
|
|
OARRAYBYTESTR,
|
|
|
|
|
OARRAYRUNESTR,
|
|
|
|
|
OSTRARRAYBYTE,
|
|
|
|
|
OSTRARRAYRUNE,
|
|
|
|
|
OINDEXMAP,
|
|
|
|
|
// lowered to call
|
|
|
|
|
OCMPSTR,
|
|
|
|
|
OADDSTR,
|
|
|
|
|
ODOTTYPE,
|
|
|
|
|
ODOTTYPE2,
|
|
|
|
|
OAS2DOTTYPE,
|
|
|
|
|
OCALLPART,
|
|
|
|
|
// lowered to PTRLIT
|
|
|
|
|
OCLOSURE, // lowered to PTRLIT
|
|
|
|
|
ORANGE, // lowered to ordinary for loop
|
|
|
|
|
OARRAYLIT, // lowered to assignments
|
|
|
|
|
OMAPLIT,
|
|
|
|
|
OSTRUCTLIT,
|
|
|
|
|
OAS2,
|
|
|
|
|
OAS2RECV,
|
|
|
|
|
OAS2MAPR,
|
|
|
|
|
OASOP:
|
2015-10-20 10:00:07 -07:00
|
|
|
Yyerror("instrument: %v must be lowered by now", Oconv(int(n.Op), 0))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// impossible nodes: only appear in backend.
|
2015-04-01 09:38:44 -07:00
|
|
|
case ORROTC, OEXTEND:
|
2015-10-20 10:00:07 -07:00
|
|
|
Yyerror("instrument: %v cannot exist now", Oconv(int(n.Op), 0))
|
2015-04-09 10:08:29 +03:00
|
|
|
goto ret
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-04-09 10:08:29 +03:00
|
|
|
case OGETG:
|
2015-10-20 10:00:07 -07:00
|
|
|
Yyerror("instrument: OGETG can happen only in runtime which we don't instrument")
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
2015-05-22 01:16:52 -04:00
|
|
|
case OFOR:
|
2015-05-26 21:30:20 -04:00
|
|
|
if n.Left != nil {
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, &n.Left.Ninit, 0, 0)
|
2015-05-26 21:30:20 -04:00
|
|
|
}
|
2015-05-22 01:16:52 -04:00
|
|
|
if n.Right != nil {
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Right, &n.Right.Ninit, 0, 0)
|
2015-05-22 01:16:52 -04:00
|
|
|
}
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-05-26 21:30:20 -04:00
|
|
|
case OIF, OSWITCH:
|
|
|
|
|
if n.Left != nil {
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentnode(&n.Left, &n.Left.Ninit, 0, 0)
|
2015-05-26 21:30:20 -04:00
|
|
|
}
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// just do generic traversal
|
|
|
|
|
case OCALLMETH,
|
2015-02-13 14:40:36 -05:00
|
|
|
ORETURN,
|
|
|
|
|
ORETJMP,
|
|
|
|
|
OSELECT,
|
|
|
|
|
OEMPTY,
|
|
|
|
|
OBREAK,
|
|
|
|
|
OCONTINUE,
|
|
|
|
|
OFALL,
|
|
|
|
|
OGOTO,
|
|
|
|
|
OLABEL:
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// does not require instrumentation
|
|
|
|
|
case OPRINT, // don't bother instrumenting it
|
|
|
|
|
OPRINTN, // don't bother instrumenting it
|
|
|
|
|
OCHECKNIL, // always followed by a read.
|
|
|
|
|
OPARAM, // it appears only in fn->exit to copy heap params back
|
|
|
|
|
OCLOSUREVAR, // immutable pointer to captured variable
|
|
|
|
|
ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT)
|
|
|
|
|
OINDREG, // at this stage, only n(SP) nodes from nodarg
|
|
|
|
|
ODCL, // declarations (without value) cannot be races
|
|
|
|
|
ODCLCONST,
|
|
|
|
|
ODCLTYPE,
|
|
|
|
|
OTYPE,
|
|
|
|
|
ONONAME,
|
|
|
|
|
OLITERAL,
|
2015-06-24 19:25:51 +02:00
|
|
|
OTYPESW: // ignored by code generation, do not instrument.
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret:
|
|
|
|
|
if n.Op != OBLOCK { // OBLOCK is handled above in a special way.
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentlist(n.List, init)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-10-20 10:00:07 -07:00
|
|
|
instrumentlist(n.Nbody, nil)
|
|
|
|
|
instrumentlist(n.Rlist, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
*np = n
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func isartificial(n *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
// compiler-emitted artificial things that we do not want to instrument,
|
2015-10-21 07:04:10 -07:00
|
|
|
// can't possibly participate in a data race.
|
|
|
|
|
// can't be seen by C/C++ and therefore irrelevant for msan.
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" {
|
|
|
|
|
if n.Sym.Name == "_" {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// autotmp's are always local
|
|
|
|
|
if strings.HasPrefix(n.Sym.Name, "autotmp_") {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// statictmp's are read-only
|
|
|
|
|
if strings.HasPrefix(n.Sym.Name, "statictmp_") {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// go.itab is accessed only by the compiler and runtime (assume safe)
|
|
|
|
|
if n.Sym.Pkg != nil && n.Sym.Pkg.Name != "" && n.Sym.Pkg.Name == "go.itab" {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func callinstr(np **Node, init **NodeList, wr int, skip int) bool {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
//print("callinstr for %+N [ %O ] etype=%E class=%d\n",
|
|
|
|
|
// n, n->op, n->type ? n->type->etype : -1, n->class);
|
|
|
|
|
|
|
|
|
|
if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Type
|
2015-02-17 22:13:49 -05:00
|
|
|
if isartificial(n) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
b := outervalue(n)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// it skips e.g. stores to ... parameter array
|
2015-02-17 22:13:49 -05:00
|
|
|
if isartificial(b) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-27 12:00:07 -07:00
|
|
|
class := b.Class
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// BUG: we _may_ want to instrument PAUTO sometimes
|
|
|
|
|
// e.g. if we've got a local variable/method receiver
|
|
|
|
|
// that has got a pointer inside. Whether it points to
|
|
|
|
|
// the heap or not is impossible to know at compile time
|
|
|
|
|
if (class&PHEAP != 0) || class == PPARAMREF || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND {
|
2015-02-23 16:07:24 -05:00
|
|
|
hascalls := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
foreach(n, hascallspred, &hascalls)
|
|
|
|
|
if hascalls != 0 {
|
|
|
|
|
n = detachexpr(n, init)
|
|
|
|
|
*np = n
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-22 22:01:01 -04:00
|
|
|
n = treecopy(n, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
makeaddable(n)
|
2015-02-23 16:07:24 -05:00
|
|
|
var f *Node
|
2015-10-21 07:04:10 -07:00
|
|
|
if flag_msan != 0 {
|
|
|
|
|
name := "msanread"
|
|
|
|
|
if wr != 0 {
|
|
|
|
|
name = "msanwrite"
|
|
|
|
|
}
|
2015-10-26 13:58:23 -07:00
|
|
|
// dowidth may not have been called for PEXTERN.
|
|
|
|
|
dowidth(t)
|
|
|
|
|
w := t.Width
|
|
|
|
|
if w == BADWIDTH {
|
|
|
|
|
Fatalf("instrument: %v badwidth", t)
|
|
|
|
|
}
|
|
|
|
|
f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
|
2015-10-21 07:04:10 -07:00
|
|
|
} else if flag_race != 0 && (t.Etype == TSTRUCT || Isfixedarray(t)) {
|
2015-02-23 16:07:24 -05:00
|
|
|
name := "racereadrange"
|
2015-02-13 14:40:36 -05:00
|
|
|
if wr != 0 {
|
|
|
|
|
name = "racewriterange"
|
|
|
|
|
}
|
2015-10-26 13:58:23 -07:00
|
|
|
// dowidth may not have been called for PEXTERN.
|
|
|
|
|
dowidth(t)
|
|
|
|
|
w := t.Width
|
|
|
|
|
if w == BADWIDTH {
|
|
|
|
|
Fatalf("instrument: %v badwidth", t)
|
|
|
|
|
}
|
|
|
|
|
f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
|
2015-10-21 07:04:10 -07:00
|
|
|
} else if flag_race != 0 {
|
2015-02-23 16:07:24 -05:00
|
|
|
name := "raceread"
|
2015-02-13 14:40:36 -05:00
|
|
|
if wr != 0 {
|
|
|
|
|
name = "racewrite"
|
|
|
|
|
}
|
|
|
|
|
f = mkcall(name, nil, init, uintptraddr(n))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*init = list(*init, f)
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// makeaddable returns a node whose memory location is the
|
|
|
|
|
// same as n, but which is addressable in the Go language
|
|
|
|
|
// sense.
|
|
|
|
|
// This is different from functions like cheapexpr that may make
|
|
|
|
|
// a copy of their argument.
|
|
|
|
|
func makeaddable(n *Node) {
|
|
|
|
|
// The arguments to uintptraddr technically have an address but
|
|
|
|
|
// may not be addressable in the Go sense: for example, in the case
|
|
|
|
|
// of T(v).Field where T is a struct type and v is
|
|
|
|
|
// an addressable value.
|
|
|
|
|
switch n.Op {
|
|
|
|
|
case OINDEX:
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isfixedarray(n.Left.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
makeaddable(n.Left)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Turn T(v).Field into v.Field
|
2015-04-01 09:38:44 -07:00
|
|
|
case ODOT, OXDOT:
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Left.Op == OCONVNOP {
|
|
|
|
|
n.Left = n.Left.Left
|
|
|
|
|
}
|
|
|
|
|
makeaddable(n.Left)
|
|
|
|
|
|
|
|
|
|
// nothing to do
|
|
|
|
|
case ODOTPTR:
|
|
|
|
|
fallthrough
|
|
|
|
|
default:
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func uintptraddr(n *Node) *Node {
|
2015-02-23 16:07:24 -05:00
|
|
|
r := Nod(OADDR, n, nil)
|
2015-02-17 22:13:49 -05:00
|
|
|
r.Bounded = true
|
2015-02-13 14:40:36 -05:00
|
|
|
r = conv(r, Types[TUNSAFEPTR])
|
|
|
|
|
r = conv(r, Types[TUINTPTR])
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func detachexpr(n *Node, init **NodeList) *Node {
|
2015-02-23 16:07:24 -05:00
|
|
|
addr := Nod(OADDR, n, nil)
|
|
|
|
|
l := temp(Ptrto(n.Type))
|
|
|
|
|
as := Nod(OAS, l, addr)
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&as, Etop)
|
|
|
|
|
walkexpr(&as, init)
|
|
|
|
|
*init = list(*init, as)
|
2015-02-23 16:07:24 -05:00
|
|
|
ind := Nod(OIND, l, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&ind, Erv)
|
|
|
|
|
walkexpr(&ind, init)
|
|
|
|
|
return ind
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func foreachnode(n *Node, f func(*Node, interface{}), c interface{}) {
|
|
|
|
|
if n != nil {
|
|
|
|
|
f(n, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func foreachlist(l *NodeList, f func(*Node, interface{}), c interface{}) {
|
|
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
|
foreachnode(l.N, f, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func foreach(n *Node, f func(*Node, interface{}), c interface{}) {
|
|
|
|
|
foreachlist(n.Ninit, f, c)
|
|
|
|
|
foreachnode(n.Left, f, c)
|
|
|
|
|
foreachnode(n.Right, f, c)
|
|
|
|
|
foreachlist(n.List, f, c)
|
|
|
|
|
foreachlist(n.Nbody, f, c)
|
|
|
|
|
foreachlist(n.Rlist, f, c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func hascallspred(n *Node, c interface{}) {
|
|
|
|
|
switch n.Op {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER:
|
2015-02-13 14:40:36 -05:00
|
|
|
(*c.(*int))++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-11 12:58:47 -07:00
|
|
|
// appendinit is like addinit in subr.go
|
2015-02-13 14:40:36 -05:00
|
|
|
// but appends rather than prepends.
|
|
|
|
|
func appendinit(np **Node, init *NodeList) {
|
|
|
|
|
if init == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
switch n.Op {
|
|
|
|
|
// There may be multiple refs to this node;
|
|
|
|
|
// introduce OCONVNOP to hold init list.
|
2015-04-01 09:38:44 -07:00
|
|
|
case ONAME, OLITERAL:
|
2015-02-13 14:40:36 -05:00
|
|
|
n = Nod(OCONVNOP, n, nil)
|
|
|
|
|
|
|
|
|
|
n.Type = n.Left.Type
|
|
|
|
|
n.Typecheck = 1
|
|
|
|
|
*np = n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n.Ninit = concat(n.Ninit, init)
|
|
|
|
|
n.Ullman = UINF
|
|
|
|
|
}
|