Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1 | // Copyright 2012 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package gc |
| 6 | |
| 7 | import ( |
| 8 | "fmt" |
| 9 | "strings" |
| 10 | ) |
| 11 | |
| 12 | // The racewalk pass modifies the code tree for the function as follows: |
| 13 | // |
| 14 | // 1. It inserts a call to racefuncenter at the beginning of each function. |
| 15 | // 2. It inserts a call to racefuncexit at the end of each function. |
| 16 | // 3. It inserts a call to raceread before each memory read. |
| 17 | // 4. It inserts a call to racewrite before each memory write. |
| 18 | // |
| 19 | // The rewriting is not yet complete. Certain nodes are not rewritten |
| 20 | // but should be. |
| 21 | |
| 22 | // TODO(dvyukov): do not instrument initialization as writes: |
| 23 | // a := make([]int, 10) |
| 24 | |
| 25 | // Do not instrument the following packages at all, |
| 26 | // at best instrumentation would cause infinite recursion. |
| 27 | var omit_pkgs = []string{"runtime", "runtime/race"} |
| 28 | |
| 29 | // Only insert racefuncenter/racefuncexit into the following packages. |
| 30 | // Memory accesses in the packages are either uninteresting or will cause false positives. |
| 31 | var noinst_pkgs = []string{"sync", "sync/atomic"} |
| 32 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 33 | func ispkgin(pkgs []string) bool { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 34 | if myimportpath != "" { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 35 | for i := 0; i < len(pkgs); i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 36 | if myimportpath == pkgs[i] { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 37 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 42 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 43 | } |
| 44 | |
Russ Cox | 75d7795 | 2015-07-22 15:31:54 -0400 | [diff] [blame] | 45 | // TODO(rsc): Remove. Put //go:norace on forkAndExecInChild instead. |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 46 | func isforkfunc(fn *Node) bool { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 47 | // Special case for syscall.forkAndExecInChild. |
| 48 | // In the child, this function must not acquire any locks, because |
| 49 | // they might have been locked at the time of the fork. This means |
| 50 | // no rescheduling, no malloc calls, and no new stack segments. |
| 51 | // Race instrumentation does all of the above. |
Russ Cox | bd4fff6 | 2015-05-27 10:42:55 -0400 | [diff] [blame] | 52 | return myimportpath != "" && myimportpath == "syscall" && fn.Func.Nname.Sym.Name == "forkAndExecInChild" |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | func racewalk(fn *Node) { |
Russ Cox | 75d7795 | 2015-07-22 15:31:54 -0400 | [diff] [blame] | 56 | if ispkgin(omit_pkgs) || isforkfunc(fn) || fn.Func.Norace { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 57 | return |
| 58 | } |
| 59 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 60 | if !ispkgin(noinst_pkgs) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 61 | racewalklist(fn.Nbody, nil) |
| 62 | |
| 63 | // nothing interesting for race detector in fn->enter |
Josh Bleecher Snyder | 3ed9e4c | 2015-03-25 19:33:01 -0700 | [diff] [blame] | 64 | racewalklist(fn.Func.Exit, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | // nodpc is the PC of the caller as extracted by |
| 68 | // getcallerpc. We use -widthptr(FP) for x86. |
| 69 | // BUG: this will not work on arm. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 70 | nodpc := Nod(OXXX, nil, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 71 | |
| 72 | *nodpc = *nodfp |
| 73 | nodpc.Type = Types[TUINTPTR] |
| 74 | nodpc.Xoffset = int64(-Widthptr) |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 75 | nd := mkcall("racefuncenter", nil, nil, nodpc) |
Josh Bleecher Snyder | 3ed9e4c | 2015-03-25 19:33:01 -0700 | [diff] [blame] | 76 | fn.Func.Enter = concat(list1(nd), fn.Func.Enter) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 77 | nd = mkcall("racefuncexit", nil, nil) |
Josh Bleecher Snyder | 3ed9e4c | 2015-03-25 19:33:01 -0700 | [diff] [blame] | 78 | fn.Func.Exit = list(fn.Func.Exit, nd) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 79 | |
| 80 | if Debug['W'] != 0 { |
Russ Cox | bd4fff6 | 2015-05-27 10:42:55 -0400 | [diff] [blame] | 81 | s := fmt.Sprintf("after racewalk %v", fn.Func.Nname.Sym) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 82 | dumplist(s, fn.Nbody) |
Russ Cox | bd4fff6 | 2015-05-27 10:42:55 -0400 | [diff] [blame] | 83 | s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym) |
Josh Bleecher Snyder | 3ed9e4c | 2015-03-25 19:33:01 -0700 | [diff] [blame] | 84 | dumplist(s, fn.Func.Enter) |
Russ Cox | bd4fff6 | 2015-05-27 10:42:55 -0400 | [diff] [blame] | 85 | s = fmt.Sprintf("exit %v", fn.Func.Nname.Sym) |
Josh Bleecher Snyder | 3ed9e4c | 2015-03-25 19:33:01 -0700 | [diff] [blame] | 86 | dumplist(s, fn.Func.Exit) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 87 | } |
| 88 | } |
| 89 | |
| 90 | func racewalklist(l *NodeList, init **NodeList) { |
| 91 | var instr *NodeList |
| 92 | |
| 93 | for ; l != nil; l = l.Next { |
| 94 | instr = nil |
| 95 | racewalknode(&l.N, &instr, 0, 0) |
| 96 | if init == nil { |
| 97 | l.N.Ninit = concat(l.N.Ninit, instr) |
| 98 | } else { |
| 99 | *init = concat(*init, instr) |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | // walkexpr and walkstmt combined |
| 105 | // walks the tree and adds calls to the |
| 106 | // instrumentation code to top-level (statement) nodes' init |
| 107 | func racewalknode(np **Node, init **NodeList, wr int, skip int) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 108 | n := *np |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 109 | |
| 110 | if n == nil { |
| 111 | return |
| 112 | } |
| 113 | |
| 114 | if Debug['w'] > 1 { |
| 115 | Dump("racewalk-before", n) |
| 116 | } |
| 117 | setlineno(n) |
| 118 | if init == nil { |
Håvard Haugen | 3c9fa38 | 2015-08-30 23:10:03 +0200 | [diff] [blame^] | 119 | Fatalf("racewalk: bad init list") |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 120 | } |
| 121 | if init == &n.Ninit { |
| 122 | // If init == &n->ninit and n->ninit is non-nil, |
| 123 | // racewalknode might append it to itself. |
| 124 | // nil it out and handle it separately before putting it back. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 125 | l := n.Ninit |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 126 | |
| 127 | n.Ninit = nil |
| 128 | racewalklist(l, nil) |
| 129 | racewalknode(&n, &l, wr, skip) // recurse with nil n->ninit |
| 130 | appendinit(&n, l) |
| 131 | *np = n |
| 132 | return |
| 133 | } |
| 134 | |
| 135 | racewalklist(n.Ninit, nil) |
| 136 | |
| 137 | switch n.Op { |
| 138 | default: |
Håvard Haugen | 3c9fa38 | 2015-08-30 23:10:03 +0200 | [diff] [blame^] | 139 | Fatalf("racewalk: unknown node type %v", Oconv(int(n.Op), 0)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 140 | |
Russ Cox | 0ad4f8b | 2015-04-17 00:25:10 -0400 | [diff] [blame] | 141 | case OAS, OASWB, OAS2FUNC: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 142 | racewalknode(&n.Left, init, 1, 0) |
| 143 | racewalknode(&n.Right, init, 0, 0) |
| 144 | goto ret |
| 145 | |
| 146 | // can't matter |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 147 | case OCFUNC, OVARKILL: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 148 | goto ret |
| 149 | |
| 150 | case OBLOCK: |
Russ Cox | 3b6e86f | 2015-06-29 15:17:14 -0400 | [diff] [blame] | 151 | var out *NodeList |
| 152 | for l := n.List; l != nil; l = l.Next { |
| 153 | switch l.N.Op { |
| 154 | case OCALLFUNC, OCALLMETH, OCALLINTER: |
| 155 | racewalknode(&l.N, &out, 0, 0) |
| 156 | out = list(out, l.N) |
| 157 | // Scan past OAS nodes copying results off stack. |
| 158 | // Those must not be instrumented, because the |
| 159 | // instrumentation calls will smash the results. |
| 160 | // The assignments are to temporaries, so they cannot |
| 161 | // be involved in races and need not be instrumented. |
| 162 | for l.Next != nil && l.Next.N.Op == OAS && iscallret(l.Next.N.Right) { |
| 163 | l = l.Next |
| 164 | out = list(out, l.N) |
| 165 | } |
| 166 | default: |
| 167 | racewalknode(&l.N, &out, 0, 0) |
| 168 | out = list(out, l.N) |
| 169 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 170 | } |
Russ Cox | 3b6e86f | 2015-06-29 15:17:14 -0400 | [diff] [blame] | 171 | n.List = out |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 172 | goto ret |
| 173 | |
| 174 | case ODEFER: |
| 175 | racewalknode(&n.Left, init, 0, 0) |
| 176 | goto ret |
| 177 | |
| 178 | case OPROC: |
| 179 | racewalknode(&n.Left, init, 0, 0) |
| 180 | goto ret |
| 181 | |
| 182 | case OCALLINTER: |
| 183 | racewalknode(&n.Left, init, 0, 0) |
| 184 | goto ret |
| 185 | |
| 186 | // Instrument dst argument of runtime.writebarrier* calls |
| 187 | // as we do not instrument runtime code. |
| 188 | // typedslicecopy is instrumented in runtime. |
| 189 | case OCALLFUNC: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 190 | racewalknode(&n.Left, init, 0, 0) |
| 191 | goto ret |
| 192 | |
| 193 | case ONOT, |
| 194 | OMINUS, |
| 195 | OPLUS, |
| 196 | OREAL, |
| 197 | OIMAG, |
Shenghou Ma | 8b2503d | 2015-04-03 15:58:18 -0400 | [diff] [blame] | 198 | OCOM, |
| 199 | OSQRT: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 200 | racewalknode(&n.Left, init, wr, 0) |
| 201 | goto ret |
| 202 | |
| 203 | case ODOTINTER: |
| 204 | racewalknode(&n.Left, init, 0, 0) |
| 205 | goto ret |
| 206 | |
| 207 | case ODOT: |
| 208 | racewalknode(&n.Left, init, 0, 1) |
| 209 | callinstr(&n, init, wr, skip) |
| 210 | goto ret |
| 211 | |
| 212 | case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND |
| 213 | racewalknode(&n.Left, init, 0, 0) |
| 214 | |
| 215 | callinstr(&n, init, wr, skip) |
| 216 | goto ret |
| 217 | |
| 218 | case OIND: // *p |
| 219 | racewalknode(&n.Left, init, 0, 0) |
| 220 | |
| 221 | callinstr(&n, init, wr, skip) |
| 222 | goto ret |
| 223 | |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 224 | case OSPTR, OLEN, OCAP: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 225 | racewalknode(&n.Left, init, 0, 0) |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 226 | if Istype(n.Left.Type, TMAP) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 227 | n1 := Nod(OCONVNOP, n.Left, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 228 | n1.Type = Ptrto(Types[TUINT8]) |
| 229 | n1 = Nod(OIND, n1, nil) |
| 230 | typecheck(&n1, Erv) |
| 231 | callinstr(&n1, init, 0, skip) |
| 232 | } |
| 233 | |
| 234 | goto ret |
| 235 | |
| 236 | case OLSH, |
| 237 | ORSH, |
| 238 | OLROT, |
| 239 | OAND, |
| 240 | OANDNOT, |
| 241 | OOR, |
| 242 | OXOR, |
| 243 | OSUB, |
| 244 | OMUL, |
| 245 | OHMUL, |
| 246 | OEQ, |
| 247 | ONE, |
| 248 | OLT, |
| 249 | OLE, |
| 250 | OGE, |
| 251 | OGT, |
| 252 | OADD, |
| 253 | OCOMPLEX: |
| 254 | racewalknode(&n.Left, init, wr, 0) |
| 255 | racewalknode(&n.Right, init, wr, 0) |
| 256 | goto ret |
| 257 | |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 258 | case OANDAND, OOROR: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 259 | racewalknode(&n.Left, init, wr, 0) |
| 260 | |
| 261 | // walk has ensured the node has moved to a location where |
| 262 | // side effects are safe. |
| 263 | // n->right may not be executed, |
| 264 | // so instrumentation goes to n->right->ninit, not init. |
| 265 | racewalknode(&n.Right, &n.Right.Ninit, wr, 0) |
| 266 | |
| 267 | goto ret |
| 268 | |
| 269 | case ONAME: |
| 270 | callinstr(&n, init, wr, skip) |
| 271 | goto ret |
| 272 | |
| 273 | case OCONV: |
| 274 | racewalknode(&n.Left, init, wr, 0) |
| 275 | goto ret |
| 276 | |
| 277 | case OCONVNOP: |
| 278 | racewalknode(&n.Left, init, wr, 0) |
| 279 | goto ret |
| 280 | |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 281 | case ODIV, OMOD: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 282 | racewalknode(&n.Left, init, wr, 0) |
| 283 | racewalknode(&n.Right, init, wr, 0) |
| 284 | goto ret |
| 285 | |
| 286 | case OINDEX: |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 287 | if !Isfixedarray(n.Left.Type) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 288 | racewalknode(&n.Left, init, 0, 0) |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 289 | } else if !islvalue(n.Left) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 290 | // index of unaddressable array, like Map[k][i]. |
| 291 | racewalknode(&n.Left, init, wr, 0) |
| 292 | |
| 293 | racewalknode(&n.Right, init, 0, 0) |
| 294 | goto ret |
| 295 | } |
| 296 | |
| 297 | racewalknode(&n.Right, init, 0, 0) |
| 298 | if n.Left.Type.Etype != TSTRING { |
| 299 | callinstr(&n, init, wr, skip) |
| 300 | } |
| 301 | goto ret |
| 302 | |
Dmitry Vyukov | cd0a8ed | 2015-06-24 19:25:51 +0200 | [diff] [blame] | 303 | case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR: |
Russ Cox | d447279 | 2015-05-06 12:35:53 -0400 | [diff] [blame] | 304 | racewalknode(&n.Left, init, 0, 0) |
Dmitry Vyukov | cd0a8ed | 2015-06-24 19:25:51 +0200 | [diff] [blame] | 305 | racewalknode(&n.Right, init, 0, 0) |
| 306 | goto ret |
| 307 | |
| 308 | case OKEY: |
| 309 | racewalknode(&n.Left, init, 0, 0) |
| 310 | racewalknode(&n.Right, init, 0, 0) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 311 | goto ret |
| 312 | |
| 313 | case OADDR: |
| 314 | racewalknode(&n.Left, init, 0, 1) |
| 315 | goto ret |
| 316 | |
| 317 | // n->left is Type* which is not interesting. |
| 318 | case OEFACE: |
| 319 | racewalknode(&n.Right, init, 0, 0) |
| 320 | |
| 321 | goto ret |
| 322 | |
| 323 | case OITAB: |
| 324 | racewalknode(&n.Left, init, 0, 0) |
| 325 | goto ret |
| 326 | |
| 327 | // should not appear in AST by now |
| 328 | case OSEND, |
| 329 | ORECV, |
| 330 | OCLOSE, |
| 331 | ONEW, |
| 332 | OXCASE, |
| 333 | OXFALL, |
| 334 | OCASE, |
| 335 | OPANIC, |
| 336 | ORECOVER, |
| 337 | OCONVIFACE, |
| 338 | OCMPIFACE, |
| 339 | OMAKECHAN, |
| 340 | OMAKEMAP, |
| 341 | OMAKESLICE, |
| 342 | OCALL, |
| 343 | OCOPY, |
| 344 | OAPPEND, |
| 345 | ORUNESTR, |
| 346 | OARRAYBYTESTR, |
| 347 | OARRAYRUNESTR, |
| 348 | OSTRARRAYBYTE, |
| 349 | OSTRARRAYRUNE, |
| 350 | OINDEXMAP, |
| 351 | // lowered to call |
| 352 | OCMPSTR, |
| 353 | OADDSTR, |
| 354 | ODOTTYPE, |
| 355 | ODOTTYPE2, |
| 356 | OAS2DOTTYPE, |
| 357 | OCALLPART, |
| 358 | // lowered to PTRLIT |
| 359 | OCLOSURE, // lowered to PTRLIT |
| 360 | ORANGE, // lowered to ordinary for loop |
| 361 | OARRAYLIT, // lowered to assignments |
| 362 | OMAPLIT, |
| 363 | OSTRUCTLIT, |
| 364 | OAS2, |
| 365 | OAS2RECV, |
| 366 | OAS2MAPR, |
| 367 | OASOP: |
| 368 | Yyerror("racewalk: %v must be lowered by now", Oconv(int(n.Op), 0)) |
| 369 | |
| 370 | goto ret |
| 371 | |
| 372 | // impossible nodes: only appear in backend. |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 373 | case ORROTC, OEXTEND: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 374 | Yyerror("racewalk: %v cannot exist now", Oconv(int(n.Op), 0)) |
Dmitry Vyukov | 08c4348 | 2015-04-09 10:08:29 +0300 | [diff] [blame] | 375 | goto ret |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 376 | |
Dmitry Vyukov | 08c4348 | 2015-04-09 10:08:29 +0300 | [diff] [blame] | 377 | case OGETG: |
| 378 | Yyerror("racewalk: OGETG can happen only in runtime which we don't instrument") |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 379 | goto ret |
| 380 | |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 381 | case OFOR: |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 382 | if n.Left != nil { |
| 383 | racewalknode(&n.Left, &n.Left.Ninit, 0, 0) |
| 384 | } |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 385 | if n.Right != nil { |
| 386 | racewalknode(&n.Right, &n.Right.Ninit, 0, 0) |
| 387 | } |
| 388 | goto ret |
| 389 | |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 390 | case OIF, OSWITCH: |
| 391 | if n.Left != nil { |
| 392 | racewalknode(&n.Left, &n.Left.Ninit, 0, 0) |
| 393 | } |
| 394 | goto ret |
| 395 | |
| 396 | // just do generic traversal |
| 397 | case OCALLMETH, |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 398 | ORETURN, |
| 399 | ORETJMP, |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 400 | OSELECT, |
| 401 | OEMPTY, |
| 402 | OBREAK, |
| 403 | OCONTINUE, |
| 404 | OFALL, |
| 405 | OGOTO, |
| 406 | OLABEL: |
| 407 | goto ret |
| 408 | |
| 409 | // does not require instrumentation |
| 410 | case OPRINT, // don't bother instrumenting it |
| 411 | OPRINTN, // don't bother instrumenting it |
| 412 | OCHECKNIL, // always followed by a read. |
| 413 | OPARAM, // it appears only in fn->exit to copy heap params back |
| 414 | OCLOSUREVAR, // immutable pointer to captured variable |
| 415 | ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT) |
| 416 | OINDREG, // at this stage, only n(SP) nodes from nodarg |
| 417 | ODCL, // declarations (without value) cannot be races |
| 418 | ODCLCONST, |
| 419 | ODCLTYPE, |
| 420 | OTYPE, |
| 421 | ONONAME, |
| 422 | OLITERAL, |
Dmitry Vyukov | cd0a8ed | 2015-06-24 19:25:51 +0200 | [diff] [blame] | 423 | OTYPESW: // ignored by code generation, do not instrument. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 424 | goto ret |
| 425 | } |
| 426 | |
| 427 | ret: |
| 428 | if n.Op != OBLOCK { // OBLOCK is handled above in a special way. |
| 429 | racewalklist(n.List, init) |
| 430 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 431 | racewalklist(n.Nbody, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 432 | racewalklist(n.Rlist, nil) |
| 433 | *np = n |
| 434 | } |
| 435 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 436 | func isartificial(n *Node) bool { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 437 | // compiler-emitted artificial things that we do not want to instrument, |
| 438 | // cant' possibly participate in a data race. |
| 439 | if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" { |
| 440 | if n.Sym.Name == "_" { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 441 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 442 | } |
| 443 | |
| 444 | // autotmp's are always local |
| 445 | if strings.HasPrefix(n.Sym.Name, "autotmp_") { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 446 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 447 | } |
| 448 | |
| 449 | // statictmp's are read-only |
| 450 | if strings.HasPrefix(n.Sym.Name, "statictmp_") { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 451 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 452 | } |
| 453 | |
| 454 | // go.itab is accessed only by the compiler and runtime (assume safe) |
| 455 | if n.Sym.Pkg != nil && n.Sym.Pkg.Name != "" && n.Sym.Pkg.Name == "go.itab" { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 456 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 457 | } |
| 458 | } |
| 459 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 460 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 461 | } |
| 462 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 463 | func callinstr(np **Node, init **NodeList, wr int, skip int) bool { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 464 | n := *np |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 465 | |
| 466 | //print("callinstr for %+N [ %O ] etype=%E class=%d\n", |
| 467 | // n, n->op, n->type ? n->type->etype : -1, n->class); |
| 468 | |
| 469 | if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 470 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 471 | } |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 472 | t := n.Type |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 473 | if isartificial(n) { |
| 474 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 475 | } |
| 476 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 477 | b := outervalue(n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 478 | |
| 479 | // it skips e.g. stores to ... parameter array |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 480 | if isartificial(b) { |
| 481 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 482 | } |
Josh Bleecher Snyder | 102b806 | 2015-03-27 12:00:07 -0700 | [diff] [blame] | 483 | class := b.Class |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 484 | |
| 485 | // BUG: we _may_ want to instrument PAUTO sometimes |
| 486 | // e.g. if we've got a local variable/method receiver |
| 487 | // that has got a pointer inside. Whether it points to |
| 488 | // the heap or not is impossible to know at compile time |
| 489 | if (class&PHEAP != 0) || class == PPARAMREF || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 490 | hascalls := 0 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 491 | foreach(n, hascallspred, &hascalls) |
| 492 | if hascalls != 0 { |
| 493 | n = detachexpr(n, init) |
| 494 | *np = n |
| 495 | } |
| 496 | |
David Chase | 05d8f1d | 2015-05-22 22:01:01 -0400 | [diff] [blame] | 497 | n = treecopy(n, 0) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 498 | makeaddable(n) |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 499 | var f *Node |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 500 | if t.Etype == TSTRUCT || Isfixedarray(t) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 501 | name := "racereadrange" |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 502 | if wr != 0 { |
| 503 | name = "racewriterange" |
| 504 | } |
| 505 | f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(t.Width)) |
| 506 | } else { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 507 | name := "raceread" |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 508 | if wr != 0 { |
| 509 | name = "racewrite" |
| 510 | } |
| 511 | f = mkcall(name, nil, init, uintptraddr(n)) |
| 512 | } |
| 513 | |
| 514 | *init = list(*init, f) |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 515 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 516 | } |
| 517 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 518 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 519 | } |
| 520 | |
| 521 | // makeaddable returns a node whose memory location is the |
| 522 | // same as n, but which is addressable in the Go language |
| 523 | // sense. |
| 524 | // This is different from functions like cheapexpr that may make |
| 525 | // a copy of their argument. |
| 526 | func makeaddable(n *Node) { |
| 527 | // The arguments to uintptraddr technically have an address but |
| 528 | // may not be addressable in the Go sense: for example, in the case |
| 529 | // of T(v).Field where T is a struct type and v is |
| 530 | // an addressable value. |
| 531 | switch n.Op { |
| 532 | case OINDEX: |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 533 | if Isfixedarray(n.Left.Type) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 534 | makeaddable(n.Left) |
| 535 | } |
| 536 | |
| 537 | // Turn T(v).Field into v.Field |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 538 | case ODOT, OXDOT: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 539 | if n.Left.Op == OCONVNOP { |
| 540 | n.Left = n.Left.Left |
| 541 | } |
| 542 | makeaddable(n.Left) |
| 543 | |
| 544 | // nothing to do |
| 545 | case ODOTPTR: |
| 546 | fallthrough |
| 547 | default: |
| 548 | break |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | func uintptraddr(n *Node) *Node { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 553 | r := Nod(OADDR, n, nil) |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 554 | r.Bounded = true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 555 | r = conv(r, Types[TUNSAFEPTR]) |
| 556 | r = conv(r, Types[TUINTPTR]) |
| 557 | return r |
| 558 | } |
| 559 | |
| 560 | func detachexpr(n *Node, init **NodeList) *Node { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 561 | addr := Nod(OADDR, n, nil) |
| 562 | l := temp(Ptrto(n.Type)) |
| 563 | as := Nod(OAS, l, addr) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 564 | typecheck(&as, Etop) |
| 565 | walkexpr(&as, init) |
| 566 | *init = list(*init, as) |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 567 | ind := Nod(OIND, l, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 568 | typecheck(&ind, Erv) |
| 569 | walkexpr(&ind, init) |
| 570 | return ind |
| 571 | } |
| 572 | |
| 573 | func foreachnode(n *Node, f func(*Node, interface{}), c interface{}) { |
| 574 | if n != nil { |
| 575 | f(n, c) |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | func foreachlist(l *NodeList, f func(*Node, interface{}), c interface{}) { |
| 580 | for ; l != nil; l = l.Next { |
| 581 | foreachnode(l.N, f, c) |
| 582 | } |
| 583 | } |
| 584 | |
| 585 | func foreach(n *Node, f func(*Node, interface{}), c interface{}) { |
| 586 | foreachlist(n.Ninit, f, c) |
| 587 | foreachnode(n.Left, f, c) |
| 588 | foreachnode(n.Right, f, c) |
| 589 | foreachlist(n.List, f, c) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 590 | foreachlist(n.Nbody, f, c) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 591 | foreachlist(n.Rlist, f, c) |
| 592 | } |
| 593 | |
| 594 | func hascallspred(n *Node, c interface{}) { |
| 595 | switch n.Op { |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 596 | case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 597 | (*c.(*int))++ |
| 598 | } |
| 599 | } |
| 600 | |
Keith Randall | cd5b144 | 2015-03-11 12:58:47 -0700 | [diff] [blame] | 601 | // appendinit is like addinit in subr.go |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 602 | // but appends rather than prepends. |
| 603 | func appendinit(np **Node, init *NodeList) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 604 | if init == nil { |
| 605 | return |
| 606 | } |
| 607 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 608 | n := *np |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 609 | switch n.Op { |
| 610 | // There may be multiple refs to this node; |
| 611 | // introduce OCONVNOP to hold init list. |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 612 | case ONAME, OLITERAL: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 613 | n = Nod(OCONVNOP, n, nil) |
| 614 | |
| 615 | n.Type = n.Left.Type |
| 616 | n.Typecheck = 1 |
| 617 | *np = n |
| 618 | } |
| 619 | |
| 620 | n.Ninit = concat(n.Ninit, init) |
| 621 | n.Ullman = UINF |
| 622 | } |