1// Copyright 2011 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 5package gc 6 7import ( 8 "cmd/compile/internal/types" 9 "fmt" 10) 11 12func escapes(all []*Node) { 13 visitBottomUp(all, escapeFuncs) 14} 15 16const ( 17 EscFuncUnknown = 0 + iota 18 EscFuncPlanned 19 EscFuncStarted 20 EscFuncTagged 21) 22 23func min8(a, b int8) int8 { 24 if a < b { 25 return a 26 } 27 return b 28} 29 30func max8(a, b int8) int8 { 31 if a > b { 32 return a 33 } 34 return b 35} 36 37const ( 38 EscUnknown = iota 39 EscNone // Does not escape to heap, result, or parameters. 40 EscHeap // Reachable from the heap 41 EscNever // By construction will not escape. 42) 43 44// funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way. 45func funcSym(fn *Node) *types.Sym { 46 if fn == nil || fn.Func.Nname == nil { 47 return nil 48 } 49 return fn.Func.Nname.Sym 50} 51 52// Mark labels that have no backjumps to them as not increasing e.loopdepth. 53// Walk hasn't generated (goto|label).Left.Sym.Label yet, so we'll cheat 54// and set it to one of the following two. Then in esc we'll clear it again. 55var ( 56 looping Node 57 nonlooping Node 58) 59 60func isSliceSelfAssign(dst, src *Node) bool { 61 // Detect the following special case. 62 // 63 // func (b *Buffer) Foo() { 64 // n, m := ... 65 // b.buf = b.buf[n:m] 66 // } 67 // 68 // This assignment is a no-op for escape analysis, 69 // it does not store any new pointers into b that were not already there. 70 // However, without this special case b will escape, because we assign to OIND/ODOTPTR. 71 // Here we assume that the statement will not contain calls, 72 // that is, that order will move any calls to init. 73 // Otherwise base ONAME value could change between the moments 74 // when we evaluate it for dst and for src. 75 76 // dst is ONAME dereference. 77 if dst.Op != ODEREF && dst.Op != ODOTPTR || dst.Left.Op != ONAME { 78 return false 79 } 80 // src is a slice operation. 81 switch src.Op { 82 case OSLICE, OSLICE3, OSLICESTR: 83 // OK. 84 case OSLICEARR, OSLICE3ARR: 85 // Since arrays are embedded into containing object, 86 // slice of non-pointer array will introduce a new pointer into b that was not already there 87 // (pointer to b itself). After such assignment, if b contents escape, 88 // b escapes as well. If we ignore such OSLICEARR, we will conclude 89 // that b does not escape when b contents do. 90 // 91 // Pointer to an array is OK since it's not stored inside b directly. 92 // For slicing an array (not pointer to array), there is an implicit OADDR. 93 // We check that to determine non-pointer array slicing. 94 if src.Left.Op == OADDR { 95 return false 96 } 97 default: 98 return false 99 } 100 // slice is applied to ONAME dereference. 101 if src.Left.Op != ODEREF && src.Left.Op != ODOTPTR || src.Left.Left.Op != ONAME { 102 return false 103 } 104 // dst and src reference the same base ONAME. 105 return dst.Left == src.Left.Left 106} 107 108// isSelfAssign reports whether assignment from src to dst can 109// be ignored by the escape analysis as it's effectively a self-assignment. 110func isSelfAssign(dst, src *Node) bool { 111 if isSliceSelfAssign(dst, src) { 112 return true 113 } 114 115 // Detect trivial assignments that assign back to the same object. 116 // 117 // It covers these cases: 118 // val.x = val.y 119 // val.x[i] = val.y[j] 120 // val.x1.x2 = val.x1.y2 121 // ... etc 122 // 123 // These assignments do not change assigned object lifetime. 124 125 if dst == nil || src == nil || dst.Op != src.Op { 126 return false 127 } 128 129 switch dst.Op { 130 case ODOT, ODOTPTR: 131 // Safe trailing accessors that are permitted to differ. 132 case OINDEX: 133 if mayAffectMemory(dst.Right) || mayAffectMemory(src.Right) { 134 return false 135 } 136 default: 137 return false 138 } 139 140 // The expression prefix must be both "safe" and identical. 141 return samesafeexpr(dst.Left, src.Left) 142} 143 144// mayAffectMemory reports whether n evaluation may affect program memory state. 145// If expression can't affect it, then it can be safely ignored by the escape analysis. 146func mayAffectMemory(n *Node) bool { 147 // We may want to use "memory safe" black list instead of general 148 // "side-effect free", which can include all calls and other ops 149 // that can affect allocate or change global state. 150 // It's safer to start from a whitelist for now. 151 // 152 // We're ignoring things like division by zero, index out of range, 153 // and nil pointer dereference here. 154 switch n.Op { 155 case ONAME, OCLOSUREVAR, OLITERAL: 156 return false 157 158 // Left+Right group. 159 case OINDEX, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD: 160 return mayAffectMemory(n.Left) || mayAffectMemory(n.Right) 161 162 // Left group. 163 case ODOT, ODOTPTR, ODEREF, OCONVNOP, OCONV, OLEN, OCAP, 164 ONOT, OBITNOT, OPLUS, ONEG, OALIGNOF, OOFFSETOF, OSIZEOF: 165 return mayAffectMemory(n.Left) 166 167 default: 168 return true 169 } 170} 171 172func mustHeapAlloc(n *Node) bool { 173 if n.Type == nil { 174 return false 175 } 176 177 // Parameters are always passed via the stack. 178 if n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) { 179 return false 180 } 181 182 if n.Type.Width > maxStackVarSize { 183 return true 184 } 185 186 if (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize { 187 return true 188 } 189 190 if n.Op == OMAKESLICE && !isSmallMakeSlice(n) { 191 return true 192 } 193 194 return false 195} 196 197// addrescapes tags node n as having had its address taken 198// by "increasing" the "value" of n.Esc to EscHeap. 199// Storage is allocated as necessary to allow the address 200// to be taken. 201func addrescapes(n *Node) { 202 switch n.Op { 203 default: 204 // Unexpected Op, probably due to a previous type error. Ignore. 205 206 case ODEREF, ODOTPTR: 207 // Nothing to do. 208 209 case ONAME: 210 if n == nodfp { 211 break 212 } 213 214 // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping. 215 // on PPARAM it means something different. 216 if n.Class() == PAUTO && n.Esc == EscNever { 217 break 218 } 219 220 // If a closure reference escapes, mark the outer variable as escaping. 221 if n.Name.IsClosureVar() { 222 addrescapes(n.Name.Defn) 223 break 224 } 225 226 if n.Class() != PPARAM && n.Class() != PPARAMOUT && n.Class() != PAUTO { 227 break 228 } 229 230 // This is a plain parameter or local variable that needs to move to the heap, 231 // but possibly for the function outside the one we're compiling. 232 // That is, if we have: 233 // 234 // func f(x int) { 235 // func() { 236 // global = &x 237 // } 238 // } 239 // 240 // then we're analyzing the inner closure but we need to move x to the 241 // heap in f, not in the inner closure. Flip over to f before calling moveToHeap. 242 oldfn := Curfn 243 Curfn = n.Name.Curfn 244 if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE { 245 Curfn = Curfn.Func.Closure 246 } 247 ln := lineno 248 lineno = Curfn.Pos 249 moveToHeap(n) 250 Curfn = oldfn 251 lineno = ln 252 253 // ODOTPTR has already been introduced, 254 // so these are the non-pointer ODOT and OINDEX. 255 // In &x[0], if x is a slice, then x does not 256 // escape--the pointer inside x does, but that 257 // is always a heap pointer anyway. 258 case ODOT, OINDEX, OPAREN, OCONVNOP: 259 if !n.Left.Type.IsSlice() { 260 addrescapes(n.Left) 261 } 262 } 263} 264 265// moveToHeap records the parameter or local variable n as moved to the heap. 266func moveToHeap(n *Node) { 267 if Debug['r'] != 0 { 268 Dump("MOVE", n) 269 } 270 if compiling_runtime { 271 yyerror("%v escapes to heap, not allowed in runtime", n) 272 } 273 if n.Class() == PAUTOHEAP { 274 Dump("n", n) 275 Fatalf("double move to heap") 276 } 277 278 // Allocate a local stack variable to hold the pointer to the heap copy. 279 // temp will add it to the function declaration list automatically. 280 heapaddr := temp(types.NewPtr(n.Type)) 281 heapaddr.Sym = lookup("&" + n.Sym.Name) 282 heapaddr.Orig.Sym = heapaddr.Sym 283 heapaddr.Pos = n.Pos 284 285 // Unset AutoTemp to persist the &foo variable name through SSA to 286 // liveness analysis. 287 // TODO(mdempsky/drchase): Cleaner solution? 288 heapaddr.Name.SetAutoTemp(false) 289 290 // Parameters have a local stack copy used at function start/end 291 // in addition to the copy in the heap that may live longer than 292 // the function. 293 if n.Class() == PPARAM || n.Class() == PPARAMOUT { 294 if n.Xoffset == BADWIDTH { 295 Fatalf("addrescapes before param assignment") 296 } 297 298 // We rewrite n below to be a heap variable (indirection of heapaddr). 299 // Preserve a copy so we can still write code referring to the original, 300 // and substitute that copy into the function declaration list 301 // so that analyses of the local (on-stack) variables use it. 302 stackcopy := newname(n.Sym) 303 stackcopy.Type = n.Type 304 stackcopy.Xoffset = n.Xoffset 305 stackcopy.SetClass(n.Class()) 306 stackcopy.Name.Param.Heapaddr = heapaddr 307 if n.Class() == PPARAMOUT { 308 // Make sure the pointer to the heap copy is kept live throughout the function. 309 // The function could panic at any point, and then a defer could recover. 310 // Thus, we need the pointer to the heap copy always available so the 311 // post-deferreturn code can copy the return value back to the stack. 312 // See issue 16095. 313 heapaddr.Name.SetIsOutputParamHeapAddr(true) 314 } 315 n.Name.Param.Stackcopy = stackcopy 316 317 // Substitute the stackcopy into the function variable list so that 318 // liveness and other analyses use the underlying stack slot 319 // and not the now-pseudo-variable n. 320 found := false 321 for i, d := range Curfn.Func.Dcl { 322 if d == n { 323 Curfn.Func.Dcl[i] = stackcopy 324 found = true 325 break 326 } 327 // Parameters are before locals, so can stop early. 328 // This limits the search even in functions with many local variables. 329 if d.Class() == PAUTO { 330 break 331 } 332 } 333 if !found { 334 Fatalf("cannot find %v in local variable list", n) 335 } 336 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) 337 } 338 339 // Modify n in place so that uses of n now mean indirection of the heapaddr. 340 n.SetClass(PAUTOHEAP) 341 n.Xoffset = 0 342 n.Name.Param.Heapaddr = heapaddr 343 n.Esc = EscHeap 344 if Debug['m'] != 0 { 345 Warnl(n.Pos, "moved to heap: %v", n) 346 } 347} 348 349// This special tag is applied to uintptr variables 350// that we believe may hold unsafe.Pointers for 351// calls into assembly functions. 352const unsafeUintptrTag = "unsafe-uintptr" 353 354// This special tag is applied to uintptr parameters of functions 355// marked go:uintptrescapes. 356const uintptrEscapesTag = "uintptr-escapes" 357 358func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string { 359 name := func() string { 360 if f.Sym != nil { 361 return f.Sym.Name 362 } 363 return fmt.Sprintf("arg#%d", narg) 364 } 365 366 if fn.Nbody.Len() == 0 { 367 // Assume that uintptr arguments must be held live across the call. 368 // This is most important for syscall.Syscall. 369 // See golang.org/issue/13372. 370 // This really doesn't have much to do with escape analysis per se, 371 // but we are reusing the ability to annotate an individual function 372 // argument and pass those annotations along to importing code. 373 if f.Type.Etype == TUINTPTR { 374 if Debug['m'] != 0 { 375 Warnl(f.Pos, "assuming %v is unsafe uintptr", name()) 376 } 377 return unsafeUintptrTag 378 } 379 380 if !types.Haspointers(f.Type) { // don't bother tagging for scalars 381 return "" 382 } 383 384 var esc EscLeaks 385 386 // External functions are assumed unsafe, unless 387 // //go:noescape is given before the declaration. 388 if fn.Func.Pragma&Noescape != 0 { 389 if Debug['m'] != 0 && f.Sym != nil { 390 Warnl(f.Pos, "%v does not escape", name()) 391 } 392 } else { 393 if Debug['m'] != 0 && f.Sym != nil { 394 Warnl(f.Pos, "leaking param: %v", name()) 395 } 396 esc.AddHeap(0) 397 } 398 399 return esc.Encode() 400 } 401 402 if fn.Func.Pragma&UintptrEscapes != 0 { 403 if f.Type.Etype == TUINTPTR { 404 if Debug['m'] != 0 { 405 Warnl(f.Pos, "marking %v as escaping uintptr", name()) 406 } 407 return uintptrEscapesTag 408 } 409 if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR { 410 // final argument is ...uintptr. 411 if Debug['m'] != 0 { 412 Warnl(f.Pos, "marking %v as escaping ...uintptr", name()) 413 } 414 return uintptrEscapesTag 415 } 416 } 417 418 if !types.Haspointers(f.Type) { // don't bother tagging for scalars 419 return "" 420 } 421 422 // Unnamed parameters are unused and therefore do not escape. 423 if f.Sym == nil || f.Sym.IsBlank() { 424 var esc EscLeaks 425 return esc.Encode() 426 } 427 428 n := asNode(f.Nname) 429 loc := e.oldLoc(n) 430 esc := loc.paramEsc 431 esc.Optimize() 432 433 if Debug['m'] != 0 && !loc.escapes { 434 if esc.Empty() { 435 Warnl(f.Pos, "%v does not escape", name()) 436 } 437 if x := esc.Heap(); x >= 0 { 438 if x == 0 { 439 Warnl(f.Pos, "leaking param: %v", name()) 440 } else { 441 // TODO(mdempsky): Mention level=x like below? 442 Warnl(f.Pos, "leaking param content: %v", name()) 443 } 444 } 445 for i := 0; i < numEscResults; i++ { 446 if x := esc.Result(i); x >= 0 { 447 res := fn.Type.Results().Field(i).Sym 448 Warnl(f.Pos, "leaking param: %v to result %v level=%d", name(), res, x) 449 } 450 } 451 } 452 453 return esc.Encode() 454} 455