github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/racewalk.go (about)

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